initial
This commit is contained in:
		
							
								
								
									
										15
									
								
								.compileInject.bat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.compileInject.bat
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | @for /F "tokens=2" %%K in (' | ||||||
|  | 	tasklist /FI "ImageName eq Blockland.exe" /FO LIST ^| findstr /B "PID:" | ||||||
|  | ') do ( | ||||||
|  | 	DllRemover32 %%K BlockLua.dll | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | @call %~dp0compile.bat || exit | ||||||
|  |  | ||||||
|  | @for /F "tokens=2" %%K in (' | ||||||
|  | 	tasklist /FI "ImageName eq Blockland.exe" /FO LIST ^| findstr /B "PID:" | ||||||
|  | ') do ( | ||||||
|  | 	RemoteDllInjector32 %%K %~dp0BlockLua.dll | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | @pause | ||||||
							
								
								
									
										
											BIN
										
									
								
								BlockLua-Unsafe.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								BlockLua-Unsafe.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								BlockLua.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								BlockLua.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										13
									
								
								compile.bat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								compile.bat
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | @echo off | ||||||
|  | cd /d %~dp0 | ||||||
|  |  | ||||||
|  | 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 BlockLua.dll && g++ -DBLLUA_UNSAFE src/bllua4.cpp %buildargs% -o BlockLua-Unsafe.dll | ||||||
|  | @echo off | ||||||
|  |  | ||||||
|  | rem objdump -d BlockLua.dll > BlockLua.dll.dump.txt | ||||||
|  | rem objdump -d BlockLua-Unsafe.dll > BlockLua-Unsafe.dll.dump.txt | ||||||
|  |  | ||||||
|  | pause | ||||||
							
								
								
									
										161
									
								
								inc/lua/lauxlib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								inc/lua/lauxlib.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ | ||||||
|  | ** Auxiliary functions for building Lua libraries | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef lauxlib_h | ||||||
|  | #define lauxlib_h | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* extra error code for `luaL_load' */ | ||||||
|  | #define LUA_ERRFILE     (LUA_ERRERR+1) | ||||||
|  |  | ||||||
|  | typedef struct luaL_Reg { | ||||||
|  |   const char *name; | ||||||
|  |   lua_CFunction func; | ||||||
|  | } luaL_Reg; | ||||||
|  |  | ||||||
|  | LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, | ||||||
|  |                                 const luaL_Reg *l, int nup); | ||||||
|  | LUALIB_API void (luaL_register) (lua_State *L, const char *libname, | ||||||
|  |                                 const luaL_Reg *l); | ||||||
|  | LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); | ||||||
|  | LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); | ||||||
|  | LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); | ||||||
|  | LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); | ||||||
|  | LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, | ||||||
|  |                                                           size_t *l); | ||||||
|  | LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, | ||||||
|  |                                           const char *def, size_t *l); | ||||||
|  | LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); | ||||||
|  | LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); | ||||||
|  |  | ||||||
|  | LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); | ||||||
|  | LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, | ||||||
|  |                                           lua_Integer def); | ||||||
|  |  | ||||||
|  | LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); | ||||||
|  | LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); | ||||||
|  | LUALIB_API void (luaL_checkany) (lua_State *L, int narg); | ||||||
|  |  | ||||||
|  | LUALIB_API int   (luaL_newmetatable) (lua_State *L, const char *tname); | ||||||
|  | LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); | ||||||
|  |  | ||||||
|  | LUALIB_API void (luaL_where) (lua_State *L, int lvl); | ||||||
|  | LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); | ||||||
|  |  | ||||||
|  | LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, | ||||||
|  |                                    const char *const lst[]); | ||||||
|  |  | ||||||
|  | /* pre-defined references */ | ||||||
|  | #define LUA_NOREF       (-2) | ||||||
|  | #define LUA_REFNIL      (-1) | ||||||
|  |  | ||||||
|  | LUALIB_API int (luaL_ref) (lua_State *L, int t); | ||||||
|  | LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); | ||||||
|  |  | ||||||
|  | LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); | ||||||
|  | LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, | ||||||
|  |                                   const char *name); | ||||||
|  | LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); | ||||||
|  |  | ||||||
|  | LUALIB_API lua_State *(luaL_newstate) (void); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, | ||||||
|  |                                                   const char *r); | ||||||
|  |  | ||||||
|  | LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, | ||||||
|  |                                          const char *fname, int szhint); | ||||||
|  |  | ||||||
|  | /* From Lua 5.2. */ | ||||||
|  | LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname); | ||||||
|  | LUALIB_API int luaL_execresult(lua_State *L, int stat); | ||||||
|  | LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, | ||||||
|  | 				 const char *mode); | ||||||
|  | LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, | ||||||
|  | 				   const char *name, const char *mode); | ||||||
|  | LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, | ||||||
|  | 				int level); | ||||||
|  | LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); | ||||||
|  | LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname, | ||||||
|  | 				   int sizehint); | ||||||
|  | LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); | ||||||
|  | LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** =============================================================== | ||||||
|  | ** some useful macros | ||||||
|  | ** =============================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define luaL_argcheck(L, cond,numarg,extramsg)	\ | ||||||
|  | 		((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) | ||||||
|  | #define luaL_checkstring(L,n)	(luaL_checklstring(L, (n), NULL)) | ||||||
|  | #define luaL_optstring(L,n,d)	(luaL_optlstring(L, (n), (d), NULL)) | ||||||
|  | #define luaL_checkint(L,n)	((int)luaL_checkinteger(L, (n))) | ||||||
|  | #define luaL_optint(L,n,d)	((int)luaL_optinteger(L, (n), (d))) | ||||||
|  | #define luaL_checklong(L,n)	((long)luaL_checkinteger(L, (n))) | ||||||
|  | #define luaL_optlong(L,n,d)	((long)luaL_optinteger(L, (n), (d))) | ||||||
|  |  | ||||||
|  | #define luaL_typename(L,i)	lua_typename(L, lua_type(L,(i))) | ||||||
|  |  | ||||||
|  | #define luaL_dofile(L, fn) \ | ||||||
|  | 	(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) | ||||||
|  |  | ||||||
|  | #define luaL_dostring(L, s) \ | ||||||
|  | 	(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) | ||||||
|  |  | ||||||
|  | #define luaL_getmetatable(L,n)	(lua_getfield(L, LUA_REGISTRYINDEX, (n))) | ||||||
|  |  | ||||||
|  | #define luaL_opt(L,f,n,d)	(lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) | ||||||
|  |  | ||||||
|  | /* From Lua 5.2. */ | ||||||
|  | #define luaL_newlibtable(L, l) \ | ||||||
|  | 	lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) | ||||||
|  | #define luaL_newlib(L, l)	(luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0)) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {====================================================== | ||||||
|  | ** Generic Buffer manipulation | ||||||
|  | ** ======================================================= | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | typedef struct luaL_Buffer { | ||||||
|  |   char *p;			/* current position in buffer */ | ||||||
|  |   int lvl;  /* number of strings in the stack (level) */ | ||||||
|  |   lua_State *L; | ||||||
|  |   char buffer[LUAL_BUFFERSIZE]; | ||||||
|  | } luaL_Buffer; | ||||||
|  |  | ||||||
|  | #define luaL_addchar(B,c) \ | ||||||
|  |   ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ | ||||||
|  |    (*(B)->p++ = (char)(c))) | ||||||
|  |  | ||||||
|  | /* compatibility only */ | ||||||
|  | #define luaL_putchar(B,c)	luaL_addchar(B,c) | ||||||
|  |  | ||||||
|  | #define luaL_addsize(B,n)	((B)->p += (n)) | ||||||
|  |  | ||||||
|  | LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); | ||||||
|  | LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); | ||||||
|  | LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); | ||||||
|  | LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); | ||||||
|  | LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); | ||||||
|  | LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* }====================================================== */ | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										402
									
								
								inc/lua/lua.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								inc/lua/lua.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,402 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $ | ||||||
|  | ** Lua - An Extensible Extension Language | ||||||
|  | ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) | ||||||
|  | ** See Copyright Notice at the end of this file | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef lua_h | ||||||
|  | #define lua_h | ||||||
|  |  | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "luaconf.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define LUA_VERSION	"Lua 5.1" | ||||||
|  | #define LUA_RELEASE	"Lua 5.1.4" | ||||||
|  | #define LUA_VERSION_NUM	501 | ||||||
|  | #define LUA_COPYRIGHT	"Copyright (C) 1994-2008 Lua.org, PUC-Rio" | ||||||
|  | #define LUA_AUTHORS	"R. Ierusalimschy, L. H. de Figueiredo & W. Celes" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* mark for precompiled code (`<esc>Lua') */ | ||||||
|  | #define	LUA_SIGNATURE	"\033Lua" | ||||||
|  |  | ||||||
|  | /* option for multiple returns in `lua_pcall' and `lua_call' */ | ||||||
|  | #define LUA_MULTRET	(-1) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** pseudo-indices | ||||||
|  | */ | ||||||
|  | #define LUA_REGISTRYINDEX	(-10000) | ||||||
|  | #define LUA_ENVIRONINDEX	(-10001) | ||||||
|  | #define LUA_GLOBALSINDEX	(-10002) | ||||||
|  | #define lua_upvalueindex(i)	(LUA_GLOBALSINDEX-(i)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* thread status */ | ||||||
|  | #define LUA_OK		0 | ||||||
|  | #define LUA_YIELD	1 | ||||||
|  | #define LUA_ERRRUN	2 | ||||||
|  | #define LUA_ERRSYNTAX	3 | ||||||
|  | #define LUA_ERRMEM	4 | ||||||
|  | #define LUA_ERRERR	5 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | typedef struct lua_State lua_State; | ||||||
|  |  | ||||||
|  | typedef int (*lua_CFunction) (lua_State *L); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** functions that read/write blocks when loading/dumping Lua chunks | ||||||
|  | */ | ||||||
|  | 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); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** prototype for memory-allocation functions | ||||||
|  | */ | ||||||
|  | typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** basic types | ||||||
|  | */ | ||||||
|  | #define LUA_TNONE		(-1) | ||||||
|  |  | ||||||
|  | #define LUA_TNIL		0 | ||||||
|  | #define LUA_TBOOLEAN		1 | ||||||
|  | #define LUA_TLIGHTUSERDATA	2 | ||||||
|  | #define LUA_TNUMBER		3 | ||||||
|  | #define LUA_TSTRING		4 | ||||||
|  | #define LUA_TTABLE		5 | ||||||
|  | #define LUA_TFUNCTION		6 | ||||||
|  | #define LUA_TUSERDATA		7 | ||||||
|  | #define LUA_TTHREAD		8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* minimum Lua stack available to a C function */ | ||||||
|  | #define LUA_MINSTACK	20 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** generic extra include file | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USER_H) | ||||||
|  | #include LUA_USER_H | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* type of numbers in Lua */ | ||||||
|  | typedef LUA_NUMBER lua_Number; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* type for integer functions */ | ||||||
|  | typedef LUA_INTEGER lua_Integer; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** state manipulation | ||||||
|  | */ | ||||||
|  | LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); | ||||||
|  | LUA_API void       (lua_close) (lua_State *L); | ||||||
|  | LUA_API lua_State *(lua_newthread) (lua_State *L); | ||||||
|  |  | ||||||
|  | LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** basic stack manipulation | ||||||
|  | */ | ||||||
|  | LUA_API int   (lua_gettop) (lua_State *L); | ||||||
|  | LUA_API void  (lua_settop) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_pushvalue) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_remove) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_insert) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_replace) (lua_State *L, int idx); | ||||||
|  | LUA_API int   (lua_checkstack) (lua_State *L, int sz); | ||||||
|  |  | ||||||
|  | LUA_API void  (lua_xmove) (lua_State *from, lua_State *to, int n); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** access functions (stack -> C) | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | LUA_API int             (lua_isnumber) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_isstring) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_iscfunction) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_isuserdata) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_type) (lua_State *L, int idx); | ||||||
|  | LUA_API const char     *(lua_typename) (lua_State *L, int tp); | ||||||
|  |  | ||||||
|  | LUA_API int            (lua_equal) (lua_State *L, int idx1, int idx2); | ||||||
|  | LUA_API int            (lua_rawequal) (lua_State *L, int idx1, int idx2); | ||||||
|  | LUA_API int            (lua_lessthan) (lua_State *L, int idx1, int idx2); | ||||||
|  |  | ||||||
|  | LUA_API lua_Number      (lua_tonumber) (lua_State *L, int idx); | ||||||
|  | LUA_API lua_Integer     (lua_tointeger) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_toboolean) (lua_State *L, int idx); | ||||||
|  | LUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len); | ||||||
|  | LUA_API size_t          (lua_objlen) (lua_State *L, int idx); | ||||||
|  | LUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx); | ||||||
|  | LUA_API void	       *(lua_touserdata) (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); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** push functions (C -> stack) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_pushnil) (lua_State *L); | ||||||
|  | LUA_API void  (lua_pushnumber) (lua_State *L, lua_Number n); | ||||||
|  | LUA_API void  (lua_pushinteger) (lua_State *L, lua_Integer n); | ||||||
|  | LUA_API void  (lua_pushlstring) (lua_State *L, const char *s, size_t l); | ||||||
|  | LUA_API void  (lua_pushstring) (lua_State *L, const char *s); | ||||||
|  | LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, | ||||||
|  |                                                       va_list argp); | ||||||
|  | LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); | ||||||
|  | LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); | ||||||
|  | LUA_API void  (lua_pushboolean) (lua_State *L, int b); | ||||||
|  | LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p); | ||||||
|  | LUA_API int   (lua_pushthread) (lua_State *L); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** get functions (Lua -> stack) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_gettable) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_getfield) (lua_State *L, int idx, const char *k); | ||||||
|  | LUA_API void  (lua_rawget) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_rawgeti) (lua_State *L, int idx, int n); | ||||||
|  | LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec); | ||||||
|  | LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); | ||||||
|  | LUA_API int   (lua_getmetatable) (lua_State *L, int objindex); | ||||||
|  | LUA_API void  (lua_getfenv) (lua_State *L, int idx); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** set functions (stack -> Lua) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_settable) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k); | ||||||
|  | LUA_API void  (lua_rawset) (lua_State *L, int idx); | ||||||
|  | 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_setfenv) (lua_State *L, int idx); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** `load' and `call' functions (load and run Lua code) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_call) (lua_State *L, int nargs, int nresults); | ||||||
|  | LUA_API int   (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); | ||||||
|  | LUA_API int   (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); | ||||||
|  | LUA_API int   (lua_load) (lua_State *L, lua_Reader reader, void *dt, | ||||||
|  |                                         const char *chunkname); | ||||||
|  |  | ||||||
|  | LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** coroutine functions | ||||||
|  | */ | ||||||
|  | LUA_API int  (lua_yield) (lua_State *L, int nresults); | ||||||
|  | LUA_API int  (lua_resume) (lua_State *L, int narg); | ||||||
|  | LUA_API int  (lua_status) (lua_State *L); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** garbage-collection function and options | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define LUA_GCSTOP		0 | ||||||
|  | #define LUA_GCRESTART		1 | ||||||
|  | #define LUA_GCCOLLECT		2 | ||||||
|  | #define LUA_GCCOUNT		3 | ||||||
|  | #define LUA_GCCOUNTB		4 | ||||||
|  | #define LUA_GCSTEP		5 | ||||||
|  | #define LUA_GCSETPAUSE		6 | ||||||
|  | #define LUA_GCSETSTEPMUL	7 | ||||||
|  | #define LUA_GCISRUNNING		9 | ||||||
|  |  | ||||||
|  | LUA_API int (lua_gc) (lua_State *L, int what, int data); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** miscellaneous functions | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | LUA_API int   (lua_error) (lua_State *L); | ||||||
|  |  | ||||||
|  | LUA_API int   (lua_next) (lua_State *L, int idx); | ||||||
|  |  | ||||||
|  | LUA_API void  (lua_concat) (lua_State *L, int n); | ||||||
|  |  | ||||||
|  | LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); | ||||||
|  | LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** =============================================================== | ||||||
|  | ** some useful macros | ||||||
|  | ** =============================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define lua_pop(L,n)		lua_settop(L, -(n)-1) | ||||||
|  |  | ||||||
|  | #define lua_newtable(L)		lua_createtable(L, 0, 0) | ||||||
|  |  | ||||||
|  | #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) | ||||||
|  |  | ||||||
|  | #define lua_pushcfunction(L,f)	lua_pushcclosure(L, (f), 0) | ||||||
|  |  | ||||||
|  | #define lua_strlen(L,i)		lua_objlen(L, (i)) | ||||||
|  |  | ||||||
|  | #define lua_isfunction(L,n)	(lua_type(L, (n)) == LUA_TFUNCTION) | ||||||
|  | #define lua_istable(L,n)	(lua_type(L, (n)) == LUA_TTABLE) | ||||||
|  | #define lua_islightuserdata(L,n)	(lua_type(L, (n)) == LUA_TLIGHTUSERDATA) | ||||||
|  | #define lua_isnil(L,n)		(lua_type(L, (n)) == LUA_TNIL) | ||||||
|  | #define lua_isboolean(L,n)	(lua_type(L, (n)) == LUA_TBOOLEAN) | ||||||
|  | #define lua_isthread(L,n)	(lua_type(L, (n)) == LUA_TTHREAD) | ||||||
|  | #define lua_isnone(L,n)		(lua_type(L, (n)) == LUA_TNONE) | ||||||
|  | #define lua_isnoneornil(L, n)	(lua_type(L, (n)) <= 0) | ||||||
|  |  | ||||||
|  | #define lua_pushliteral(L, s)	\ | ||||||
|  | 	lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) | ||||||
|  |  | ||||||
|  | #define lua_setglobal(L,s)	lua_setfield(L, LUA_GLOBALSINDEX, (s)) | ||||||
|  | #define lua_getglobal(L,s)	lua_getfield(L, LUA_GLOBALSINDEX, (s)) | ||||||
|  |  | ||||||
|  | #define lua_tostring(L,i)	lua_tolstring(L, (i), NULL) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** compatibility macros and functions | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define lua_open()	luaL_newstate() | ||||||
|  |  | ||||||
|  | #define lua_getregistry(L)	lua_pushvalue(L, LUA_REGISTRYINDEX) | ||||||
|  |  | ||||||
|  | #define lua_getgccount(L)	lua_gc(L, LUA_GCCOUNT, 0) | ||||||
|  |  | ||||||
|  | #define lua_Chunkreader		lua_Reader | ||||||
|  | #define lua_Chunkwriter		lua_Writer | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* hack */ | ||||||
|  | LUA_API void lua_setlevel	(lua_State *from, lua_State *to); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {====================================================================== | ||||||
|  | ** Debug API | ||||||
|  | ** ======================================================================= | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Event codes | ||||||
|  | */ | ||||||
|  | #define LUA_HOOKCALL	0 | ||||||
|  | #define LUA_HOOKRET	1 | ||||||
|  | #define LUA_HOOKLINE	2 | ||||||
|  | #define LUA_HOOKCOUNT	3 | ||||||
|  | #define LUA_HOOKTAILRET 4 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Event masks | ||||||
|  | */ | ||||||
|  | #define LUA_MASKCALL	(1 << LUA_HOOKCALL) | ||||||
|  | #define LUA_MASKRET	(1 << LUA_HOOKRET) | ||||||
|  | #define LUA_MASKLINE	(1 << LUA_HOOKLINE) | ||||||
|  | #define LUA_MASKCOUNT	(1 << LUA_HOOKCOUNT) | ||||||
|  |  | ||||||
|  | typedef struct lua_Debug lua_Debug;  /* activation record */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Functions to be called by the debuger in specific events */ | ||||||
|  | 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_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_setlocal (lua_State *L, const lua_Debug *ar, int n); | ||||||
|  | LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); | ||||||
|  | LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); | ||||||
|  | LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); | ||||||
|  | LUA_API lua_Hook lua_gethook (lua_State *L); | ||||||
|  | LUA_API int lua_gethookmask (lua_State *L); | ||||||
|  | LUA_API int lua_gethookcount (lua_State *L); | ||||||
|  |  | ||||||
|  | /* From Lua 5.2. */ | ||||||
|  | LUA_API void *lua_upvalueid (lua_State *L, int idx, int n); | ||||||
|  | LUA_API void lua_upvaluejoin (lua_State *L, int idx1, int n1, int idx2, int n2); | ||||||
|  | LUA_API int lua_loadx (lua_State *L, lua_Reader reader, void *dt, | ||||||
|  | 		       const char *chunkname, const char *mode); | ||||||
|  | LUA_API const lua_Number *lua_version (lua_State *L); | ||||||
|  | LUA_API void lua_copy (lua_State *L, int fromidx, int toidx); | ||||||
|  | LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum); | ||||||
|  | LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum); | ||||||
|  |  | ||||||
|  | /* From Lua 5.3. */ | ||||||
|  | LUA_API int lua_isyieldable (lua_State *L); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct lua_Debug { | ||||||
|  |   int event; | ||||||
|  |   const char *name;	/* (n) */ | ||||||
|  |   const char *namewhat;	/* (n) `global', `local', `field', `method' */ | ||||||
|  |   const char *what;	/* (S) `Lua', `C', `main', `tail' */ | ||||||
|  |   const char *source;	/* (S) */ | ||||||
|  |   int currentline;	/* (l) */ | ||||||
|  |   int nups;		/* (u) number of upvalues */ | ||||||
|  |   int linedefined;	/* (S) */ | ||||||
|  |   int lastlinedefined;	/* (S) */ | ||||||
|  |   char short_src[LUA_IDSIZE]; /* (S) */ | ||||||
|  |   /* private part */ | ||||||
|  |   int i_ci;  /* active function */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* }====================================================================== */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /****************************************************************************** | ||||||
|  | * Copyright (C) 1994-2008 Lua.org, PUC-Rio.  All rights reserved. | ||||||
|  | * | ||||||
|  | * Permission is hereby granted, free of charge, to any person obtaining | ||||||
|  | * a copy of this software and associated documentation files (the | ||||||
|  | * "Software"), to deal in the Software without restriction, including | ||||||
|  | * without limitation the rights to use, copy, modify, merge, publish, | ||||||
|  | * distribute, sublicense, and/or sell copies of the Software, and to | ||||||
|  | * permit persons to whom the Software is furnished to do so, subject to | ||||||
|  | * the following conditions: | ||||||
|  | * | ||||||
|  | * The above copyright notice and this permission notice shall be | ||||||
|  | * included in all copies or substantial portions of the Software. | ||||||
|  | * | ||||||
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||||
|  | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||||
|  | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||||
|  | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||||
|  | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||||
|  | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||||
|  | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  | ******************************************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										9
									
								
								inc/lua/lua.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								inc/lua/lua.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | // C++ wrapper for LuaJIT header files. | ||||||
|  |  | ||||||
|  | extern "C" { | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lauxlib.h" | ||||||
|  | #include "lualib.h" | ||||||
|  | #include "luajit.h" | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								inc/lua/lua5.1.lib
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								inc/lua/lua5.1.lib
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										152
									
								
								inc/lua/luaconf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								inc/lua/luaconf.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | /* | ||||||
|  | ** Configuration header. | ||||||
|  | ** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef luaconf_h | ||||||
|  | #define luaconf_h | ||||||
|  |  | ||||||
|  | #ifndef WINVER | ||||||
|  | #define WINVER 0x0501 | ||||||
|  | #endif | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
|  | /* Default path for loading Lua and C modules with require(). */ | ||||||
|  | #if defined(_WIN32) | ||||||
|  | /* | ||||||
|  | ** In Windows, any exclamation mark ('!') in the path is replaced by the | ||||||
|  | ** path of the directory of the executable file of the current process. | ||||||
|  | */ | ||||||
|  | #define LUA_LDIR	"!\\lua\\" | ||||||
|  | #define LUA_CDIR	"!\\" | ||||||
|  | #define LUA_PATH_DEFAULT \ | ||||||
|  |   ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" | ||||||
|  | #define LUA_CPATH_DEFAULT \ | ||||||
|  |   ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" | ||||||
|  | #else | ||||||
|  | /* | ||||||
|  | ** Note to distribution maintainers: do NOT patch the following lines! | ||||||
|  | ** Please read ../doc/install.html#distro and pass PREFIX=/usr instead. | ||||||
|  | */ | ||||||
|  | #ifndef LUA_MULTILIB | ||||||
|  | #define LUA_MULTILIB	"lib" | ||||||
|  | #endif | ||||||
|  | #ifndef LUA_LMULTILIB | ||||||
|  | #define LUA_LMULTILIB	"lib" | ||||||
|  | #endif | ||||||
|  | #define LUA_LROOT	"/usr/local" | ||||||
|  | #define LUA_LUADIR	"/lua/5.1/" | ||||||
|  | #define LUA_LJDIR	"/luajit-2.1.0-beta3/" | ||||||
|  |  | ||||||
|  | #ifdef LUA_ROOT | ||||||
|  | #define LUA_JROOT	LUA_ROOT | ||||||
|  | #define LUA_RLDIR	LUA_ROOT "/share" LUA_LUADIR | ||||||
|  | #define LUA_RCDIR	LUA_ROOT "/" LUA_MULTILIB LUA_LUADIR | ||||||
|  | #define LUA_RLPATH	";" LUA_RLDIR "?.lua;" LUA_RLDIR "?/init.lua" | ||||||
|  | #define LUA_RCPATH	";" LUA_RCDIR "?.so" | ||||||
|  | #else | ||||||
|  | #define LUA_JROOT	LUA_LROOT | ||||||
|  | #define LUA_RLPATH | ||||||
|  | #define LUA_RCPATH | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define LUA_JPATH	";" LUA_JROOT "/share" LUA_LJDIR "?.lua" | ||||||
|  | #define LUA_LLDIR	LUA_LROOT "/share" LUA_LUADIR | ||||||
|  | #define LUA_LCDIR	LUA_LROOT "/" LUA_LMULTILIB LUA_LUADIR | ||||||
|  | #define LUA_LLPATH	";" LUA_LLDIR "?.lua;" LUA_LLDIR "?/init.lua" | ||||||
|  | #define LUA_LCPATH1	";" LUA_LCDIR "?.so" | ||||||
|  | #define LUA_LCPATH2	";" LUA_LCDIR "loadall.so" | ||||||
|  |  | ||||||
|  | #define LUA_PATH_DEFAULT	"./?.lua" LUA_JPATH LUA_LLPATH LUA_RLPATH | ||||||
|  | #define LUA_CPATH_DEFAULT	"./?.so" LUA_LCPATH1 LUA_RCPATH LUA_LCPATH2 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Environment variable names for path overrides and initialization code. */ | ||||||
|  | #define LUA_PATH	"LUA_PATH" | ||||||
|  | #define LUA_CPATH	"LUA_CPATH" | ||||||
|  | #define LUA_INIT	"LUA_INIT" | ||||||
|  |  | ||||||
|  | /* Special file system characters. */ | ||||||
|  | #if defined(_WIN32) | ||||||
|  | #define LUA_DIRSEP	"\\" | ||||||
|  | #else | ||||||
|  | #define LUA_DIRSEP	"/" | ||||||
|  | #endif | ||||||
|  | #define LUA_PATHSEP	";" | ||||||
|  | #define LUA_PATH_MARK	"?" | ||||||
|  | #define LUA_EXECDIR	"!" | ||||||
|  | #define LUA_IGMARK	"-" | ||||||
|  | #define LUA_PATH_CONFIG \ | ||||||
|  |   LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" \ | ||||||
|  |   LUA_EXECDIR "\n" LUA_IGMARK "\n" | ||||||
|  |  | ||||||
|  | /* Quoting in error messages. */ | ||||||
|  | #define LUA_QL(x)	"'" x "'" | ||||||
|  | #define LUA_QS		LUA_QL("%s") | ||||||
|  |  | ||||||
|  | /* Various tunables. */ | ||||||
|  | #define LUAI_MAXSTACK	65500	/* Max. # of stack slots for a thread (<64K). */ | ||||||
|  | #define LUAI_MAXCSTACK	8000	/* Max. # of stack slots for a C func (<10K). */ | ||||||
|  | #define LUAI_GCPAUSE	200	/* Pause GC until memory is at 200%. */ | ||||||
|  | #define LUAI_GCMUL	200	/* Run GC at 200% of allocation speed. */ | ||||||
|  | #define LUA_MAXCAPTURES	32	/* Max. pattern captures. */ | ||||||
|  |  | ||||||
|  | /* Configuration for the frontend (the luajit executable). */ | ||||||
|  | #if defined(luajit_c) | ||||||
|  | #define LUA_PROGNAME	"luajit"  /* Fallback frontend name. */ | ||||||
|  | #define LUA_PROMPT	"> "	/* Interactive prompt. */ | ||||||
|  | #define LUA_PROMPT2	">> "	/* Continuation prompt. */ | ||||||
|  | #define LUA_MAXINPUT	512	/* Max. input line length. */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Note: changing the following defines breaks the Lua 5.1 ABI. */ | ||||||
|  | #define LUA_INTEGER	ptrdiff_t | ||||||
|  | #define LUA_IDSIZE	60	/* Size of lua_Debug.short_src. */ | ||||||
|  | /* | ||||||
|  | ** Size of lauxlib and io.* on-stack buffers. Weird workaround to avoid using | ||||||
|  | ** unreasonable amounts of stack space, but still retain ABI compatibility. | ||||||
|  | ** Blame Lua for depending on BUFSIZ in the ABI, blame **** for wrecking it. | ||||||
|  | */ | ||||||
|  | #define LUAL_BUFFERSIZE	(BUFSIZ > 16384 ? 8192 : BUFSIZ) | ||||||
|  |  | ||||||
|  | /* The following defines are here only for compatibility with luaconf.h | ||||||
|  | ** from the standard Lua distribution. They must not be changed for LuaJIT. | ||||||
|  | */ | ||||||
|  | #define LUA_NUMBER_DOUBLE | ||||||
|  | #define LUA_NUMBER		double | ||||||
|  | #define LUAI_UACNUMBER		double | ||||||
|  | #define LUA_NUMBER_SCAN		"%lf" | ||||||
|  | #define LUA_NUMBER_FMT		"%.14g" | ||||||
|  | #define lua_number2str(s, n)	sprintf((s), LUA_NUMBER_FMT, (n)) | ||||||
|  | #define LUAI_MAXNUMBER2STR	32 | ||||||
|  | #define LUA_INTFRMLEN		"l" | ||||||
|  | #define LUA_INTFRM_T		long | ||||||
|  |  | ||||||
|  | /* Linkage of public API functions. */ | ||||||
|  | #if defined(LUA_BUILD_AS_DLL) | ||||||
|  | #if defined(LUA_CORE) || defined(LUA_LIB) | ||||||
|  | #define LUA_API		__declspec(dllexport) | ||||||
|  | #else | ||||||
|  | #define LUA_API		__declspec(dllimport) | ||||||
|  | #endif | ||||||
|  | #else | ||||||
|  | #define LUA_API		extern | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define LUALIB_API	LUA_API | ||||||
|  |  | ||||||
|  | /* Support for internal assertions. */ | ||||||
|  | #if defined(LUA_USE_ASSERT) || defined(LUA_USE_APICHECK) | ||||||
|  | #include <assert.h> | ||||||
|  | #endif | ||||||
|  | #ifdef LUA_USE_ASSERT | ||||||
|  | #define lua_assert(x)		assert(x) | ||||||
|  | #endif | ||||||
|  | #ifdef LUA_USE_APICHECK | ||||||
|  | #define luai_apicheck(L, o)	{ (void)L; assert(o); } | ||||||
|  | #else | ||||||
|  | #define luai_apicheck(L, o)	{ (void)L; } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										79
									
								
								inc/lua/luajit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								inc/lua/luajit.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | /* | ||||||
|  | ** LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/ | ||||||
|  | ** | ||||||
|  | ** Copyright (C) 2005-2017 Mike Pall. All rights reserved. | ||||||
|  | ** | ||||||
|  | ** Permission is hereby granted, free of charge, to any person obtaining | ||||||
|  | ** a copy of this software and associated documentation files (the | ||||||
|  | ** "Software"), to deal in the Software without restriction, including | ||||||
|  | ** without limitation the rights to use, copy, modify, merge, publish, | ||||||
|  | ** distribute, sublicense, and/or sell copies of the Software, and to | ||||||
|  | ** permit persons to whom the Software is furnished to do so, subject to | ||||||
|  | ** the following conditions: | ||||||
|  | ** | ||||||
|  | ** The above copyright notice and this permission notice shall be | ||||||
|  | ** included in all copies or substantial portions of the Software. | ||||||
|  | ** | ||||||
|  | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||||
|  | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||||
|  | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||||
|  | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||||
|  | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||||
|  | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||||
|  | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  | ** | ||||||
|  | ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef _LUAJIT_H | ||||||
|  | #define _LUAJIT_H | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #define LUAJIT_VERSION		"LuaJIT 2.1.0-beta3" | ||||||
|  | #define LUAJIT_VERSION_NUM	20100  /* Version 2.1.0 = 02.01.00. */ | ||||||
|  | #define LUAJIT_VERSION_SYM	luaJIT_version_2_1_0_beta3 | ||||||
|  | #define LUAJIT_COPYRIGHT	"Copyright (C) 2005-2017 Mike Pall" | ||||||
|  | #define LUAJIT_URL		"http://luajit.org/" | ||||||
|  |  | ||||||
|  | /* Modes for luaJIT_setmode. */ | ||||||
|  | #define LUAJIT_MODE_MASK	0x00ff | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  |   LUAJIT_MODE_ENGINE,		/* Set mode for whole JIT engine. */ | ||||||
|  |   LUAJIT_MODE_DEBUG,		/* Set debug mode (idx = level). */ | ||||||
|  |  | ||||||
|  |   LUAJIT_MODE_FUNC,		/* Change mode for a function. */ | ||||||
|  |   LUAJIT_MODE_ALLFUNC,		/* Recurse into subroutine protos. */ | ||||||
|  |   LUAJIT_MODE_ALLSUBFUNC,	/* Change only the subroutines. */ | ||||||
|  |  | ||||||
|  |   LUAJIT_MODE_TRACE,		/* Flush a compiled trace. */ | ||||||
|  |  | ||||||
|  |   LUAJIT_MODE_WRAPCFUNC = 0x10,	/* Set wrapper mode for C function calls. */ | ||||||
|  |  | ||||||
|  |   LUAJIT_MODE_MAX | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Flags or'ed in to the mode. */ | ||||||
|  | #define LUAJIT_MODE_OFF		0x0000	/* Turn feature off. */ | ||||||
|  | #define LUAJIT_MODE_ON		0x0100	/* Turn feature on. */ | ||||||
|  | #define LUAJIT_MODE_FLUSH	0x0200	/* Flush JIT-compiled code. */ | ||||||
|  |  | ||||||
|  | /* LuaJIT public C API. */ | ||||||
|  |  | ||||||
|  | /* Control the JIT engine. */ | ||||||
|  | LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode); | ||||||
|  |  | ||||||
|  | /* Low-overhead profiling API. */ | ||||||
|  | typedef void (*luaJIT_profile_callback)(void *data, lua_State *L, | ||||||
|  | 					int samples, int vmstate); | ||||||
|  | LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, | ||||||
|  | 				  luaJIT_profile_callback cb, void *data); | ||||||
|  | LUA_API void luaJIT_profile_stop(lua_State *L); | ||||||
|  | LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, | ||||||
|  | 					     int depth, size_t *len); | ||||||
|  |  | ||||||
|  | /* Enforce (dynamic) linker error for version mismatches. Call from main. */ | ||||||
|  | LUA_API void LUAJIT_VERSION_SYM(void); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										43
									
								
								inc/lua/lualib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								inc/lua/lualib.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | /* | ||||||
|  | ** Standard library header. | ||||||
|  | ** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef _LUALIB_H | ||||||
|  | #define _LUALIB_H | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #define LUA_FILEHANDLE	"FILE*" | ||||||
|  |  | ||||||
|  | #define LUA_COLIBNAME	"coroutine" | ||||||
|  | #define LUA_MATHLIBNAME	"math" | ||||||
|  | #define LUA_STRLIBNAME	"string" | ||||||
|  | #define LUA_TABLIBNAME	"table" | ||||||
|  | #define LUA_IOLIBNAME	"io" | ||||||
|  | #define LUA_OSLIBNAME	"os" | ||||||
|  | #define LUA_LOADLIBNAME	"package" | ||||||
|  | #define LUA_DBLIBNAME	"debug" | ||||||
|  | #define LUA_BITLIBNAME	"bit" | ||||||
|  | #define LUA_JITLIBNAME	"jit" | ||||||
|  | #define LUA_FFILIBNAME	"ffi" | ||||||
|  |  | ||||||
|  | LUALIB_API int luaopen_base(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_math(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_string(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_table(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_io(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_os(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_package(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_debug(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_bit(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_jit(lua_State *L); | ||||||
|  | LUALIB_API int luaopen_ffi(lua_State *L); | ||||||
|  |  | ||||||
|  | LUALIB_API void luaL_openlibs(lua_State *L); | ||||||
|  |  | ||||||
|  | #ifndef lua_assert | ||||||
|  | #define lua_assert(x)	((void)0) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										238
									
								
								inc/tsfuncs/BlFuncs.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								inc/tsfuncs/BlFuncs.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | |||||||
|  |  | ||||||
|  | ////////////////////////////////////////////////// | ||||||
|  | // BlFuncs Version 1.0 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Includes | ||||||
|  |  | ||||||
|  | #include "BlHooks.hpp" | ||||||
|  | #include "BlFuncs.hpp" | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Scanned structures | ||||||
|  |  | ||||||
|  | ADDR tsf_mCacheSequence; | ||||||
|  | ADDR tsf_mCacheAllocator; | ||||||
|  | ADDR tsf_gIdDictionary; | ||||||
|  | ADDR tsf_gEvalState_globalVars; | ||||||
|  |  | ||||||
|  | BlFunctionDefIntern(tsf_BlStringTable__insert        ); | ||||||
|  | BlFunctionDefIntern(tsf_BlNamespace__find            ); | ||||||
|  | BlFunctionDefIntern(tsf_BlNamespace__createLocalEntry); | ||||||
|  | BlFunctionDefIntern(tsf_BlDataChunker__freeBlocks    ); | ||||||
|  | BlFunctionDefIntern(tsf_BlCon__evaluate              ); | ||||||
|  | BlFunctionDefIntern(tsf_BlCon__executef              ); | ||||||
|  | BlFunctionDefIntern(tsf_BlCon__executefSimObj        ); | ||||||
|  | BlFunctionDefIntern(tsf_BlCon__getVariable           ); | ||||||
|  | BlFunctionDefIntern(tsf_BlDictionary__addVariable    ); | ||||||
|  | BlFunctionDefIntern(tsf_BlSim__findObject_name       ); | ||||||
|  | BlFunctionDefIntern(tsf_BlStringStack__getArgBuffer  ); | ||||||
|  | BlFunctionDefIntern(tsf_BlSimObject__getDataField    ); | ||||||
|  | BlFunctionDefIntern(tsf_BlSimObject__setDataField    ); | ||||||
|  | BlFunctionDefIntern(tsf_BlCon__getReturnBuffer       ); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // C->TS Args | ||||||
|  |  | ||||||
|  | char* tsf_GetIntArg(signed int value) { | ||||||
|  | 	char* ret = tsf_BlStringStack__getArgBuffer(16); | ||||||
|  | 	snprintf(ret, 16, "%d", value); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | char* tsf_GetFloatArg(float value) { | ||||||
|  | 	char* ret = tsf_BlStringStack__getArgBuffer(32); | ||||||
|  | 	snprintf(ret, 32, "%g", value); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | char* tsf_GetStringArg(char* value) { | ||||||
|  | 	int len = strlen(value)+1; | ||||||
|  | 	char* ret = tsf_BlStringStack__getArgBuffer(len); | ||||||
|  | 	memcpy(ret, value, len); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | char* tsf_GetThisArg(ADDR obj) { | ||||||
|  | 	return tsf_GetIntArg(*(signed int *)(obj + 32)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Eval | ||||||
|  |  | ||||||
|  | const char* tsf_Eval(const char *code) { | ||||||
|  | 	const char *argv[] = {nullptr, code}; | ||||||
|  | 	return tsf_BlCon__evaluate(0, 2, argv); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* tsf_Evalf(const char *fmt, ...) { | ||||||
|  | 	va_list args; | ||||||
|  | 	char code[4096]; | ||||||
|  | 	va_start(args, fmt); | ||||||
|  | 	vsnprintf(code, 4096, fmt, args); | ||||||
|  | 	va_end(args); | ||||||
|  | 	 | ||||||
|  | 	return tsf_Eval((const char*)code); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Objects | ||||||
|  |  | ||||||
|  | ADDR tsf_FindObject(unsigned int id) { | ||||||
|  | 	ADDR obj = *(ADDR*)(*(ADDR*)(tsf_gIdDictionary) + 4*(id & 0xFFF)); | ||||||
|  | 	if(!obj) return 0; | ||||||
|  | 	 | ||||||
|  | 	while(obj && *(unsigned int *)(obj + 32) != id) { | ||||||
|  | 		obj = *(ADDR*)(obj + 16); | ||||||
|  | 		if(!obj) return 0; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return obj; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ADDR tsf_FindObject(const char* name) { | ||||||
|  | 	return (ADDR)tsf_BlSim__findObject_name(name); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ADDR tsf_LookupNamespace(const char* ns, const char* package) { | ||||||
|  | 	const char* ste_package; | ||||||
|  | 	if(package) { | ||||||
|  | 		ste_package = tsf_BlStringTable__insert(package, 0); | ||||||
|  | 	} else { | ||||||
|  | 		ste_package = nullptr; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if(ns) { | ||||||
|  | 		const char* ste_namespace = tsf_BlStringTable__insert(ns, 0); | ||||||
|  | 		return tsf_BlNamespace__find(ste_namespace, ste_package); | ||||||
|  | 	} else { | ||||||
|  | 		return tsf_BlNamespace__find(nullptr, ste_package); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ADDR tsf_LookupNamespace(const char* ns) { | ||||||
|  | 	return tsf_LookupNamespace(ns, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Object Fields | ||||||
|  |  | ||||||
|  | const char* tsf_GetDataField(ADDR simObject, const char* slotName, const char* array) { | ||||||
|  | 	const char *ste_slotName; | ||||||
|  | 	if(slotName) { | ||||||
|  | 		ste_slotName = tsf_BlStringTable__insert(slotName, 0); | ||||||
|  | 	} else { | ||||||
|  | 		ste_slotName = nullptr; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return tsf_BlSimObject__getDataField(simObject, ste_slotName, array); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsf_SetDataField(ADDR simObject, const char* slotName, const char* array, const char* value) { | ||||||
|  | 	const char* ste_slotName; | ||||||
|  | 	if(slotName) { | ||||||
|  | 		ste_slotName = tsf_BlStringTable__insert(slotName, 0); | ||||||
|  | 	} else { | ||||||
|  | 		ste_slotName = nullptr; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	tsf_BlSimObject__setDataField(simObject, ste_slotName, array, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // TS Global Variables | ||||||
|  |  | ||||||
|  | const char *tsf_GetVar(const char* name) { | ||||||
|  | 	return tsf_BlCon__getVariable(name); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsf_AddVarInternal(const char* name, signed int varType, void* data) { | ||||||
|  | 	tsf_BlDictionary__addVariable((ADDR *)tsf_gEvalState_globalVars, name, varType, data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsf_AddVar(const char* name, const char** data) { | ||||||
|  | 	tsf_AddVarInternal(name, 10, data); | ||||||
|  | } | ||||||
|  | void tsf_AddVar(const char* name, signed int* data) { | ||||||
|  | 	tsf_AddVarInternal(name,  4, data); | ||||||
|  | } | ||||||
|  | void tsf_AddVar(const char* name, float* data) { | ||||||
|  | 	tsf_AddVarInternal(name,  8, data); | ||||||
|  | } | ||||||
|  | void tsf_AddVar(const char* name, bool* data) { | ||||||
|  | 	tsf_AddVarInternal(name,  6, data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // TS->C Functions | ||||||
|  |  | ||||||
|  | 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 *ste_fname = tsf_BlStringTable__insert(fname, 0); | ||||||
|  | 	ADDR ns = tsf_LookupNamespace(cname, pname); | ||||||
|  | 	ADDR ent = tsf_BlNamespace__createLocalEntry(ns, ste_fname); | ||||||
|  | 	 | ||||||
|  | 	*(signed int *)tsf_mCacheSequence += 1; | ||||||
|  | 	tsf_BlDataChunker__freeBlocks(*(ADDR *)tsf_mCacheAllocator); | ||||||
|  | 	 | ||||||
|  | 	*(const char**)(ent + 24) = usage ; | ||||||
|  | 	*(signed int* )(ent + 16) = mina  ; | ||||||
|  | 	*(signed int* )(ent + 20) = maxa  ; | ||||||
|  | 	*(signed int* )(ent + 12) = cbtype; | ||||||
|  | 	 | ||||||
|  | 	return ent; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_StringCallback sc, const char* usage, signed int mina, signed int maxa) { | ||||||
|  | 	ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 1, usage, mina, maxa); | ||||||
|  | 	*(tsf_StringCallback *)(ent + 40) = sc; | ||||||
|  | } | ||||||
|  | void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_IntCallback    ic, const char*  usage, signed int mina, signed int maxa) { | ||||||
|  | 	ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 2, usage, mina, maxa); | ||||||
|  | 	*(tsf_IntCallback    *)(ent + 40) = ic; | ||||||
|  | } | ||||||
|  | void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_FloatCallback  fc, const char*  usage, signed int mina, signed int maxa) { | ||||||
|  | 	ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 3, usage, mina, maxa); | ||||||
|  | 	*(tsf_FloatCallback  *)(ent + 40) = fc; | ||||||
|  | } | ||||||
|  | void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_VoidCallback   vc, const char*  usage, signed int mina, signed int maxa) { | ||||||
|  | 	ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 4, usage, mina, maxa); | ||||||
|  | 	*(tsf_VoidCallback   *)(ent + 40) = vc; | ||||||
|  | } | ||||||
|  | void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_BoolCallback   bc, const char*  usage, signed int mina, signed int maxa) { | ||||||
|  | 	ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 5, usage, mina, maxa); | ||||||
|  | 	*(tsf_BoolCallback   *)(ent + 40) = bc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Initialization | ||||||
|  |  | ||||||
|  | bool tsf_InitInternal() { | ||||||
|  | 	BlScanFunctionText(tsf_BlStringTable__insert        , "83 EC 0C 80 3D ? ? ? ? ?"                                                                                                         ); | ||||||
|  | 	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"              ); | ||||||
|  | 	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"                 ); | ||||||
|  | 	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_BlSim__findObject_name       , "57 8B F9 8A 17"                                                                                                                   ); | ||||||
|  | 	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"                               ); | ||||||
|  | 	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"                                                                                                              ); | ||||||
|  | 	 | ||||||
|  | 	ADDR BlScanText   (tsf_mCacheSequenceLoc            , "FF 05 ? ? ? ? B9 ? ? ? ? 8B F8 E8 ? ? ? ? 8B 44 24 1C 89 47 18 8B 44 24 14"                                                       ); | ||||||
|  | 	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"); | ||||||
|  | 	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_mCacheAllocator       = *(ADDR*)(tsf_mCacheAllocatorLoc       + 2); | ||||||
|  | 	tsf_gIdDictionary         = *(ADDR*)(tsf_gIdDictionaryLoc         + 2); | ||||||
|  | 	tsf_gEvalState_globalVars = *(ADDR*)(tsf_gEvalState_globalVarsLoc + 1); | ||||||
|  | 	 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool tsf_DeinitInternal() { | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
							
								
								
									
										109
									
								
								inc/tsfuncs/BlFuncs.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								inc/tsfuncs/BlFuncs.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  |  | ||||||
|  | ////////////////////////////////////////////////// | ||||||
|  | // BlFuncs Version 1.0 | ||||||
|  |  | ||||||
|  | #ifndef _H_BLFUNCS | ||||||
|  | #define _H_BLFUNCS | ||||||
|  |  | ||||||
|  | // Require BlHooks to be included before this header | ||||||
|  | #ifndef _H_BLHOOKS | ||||||
|  | 	#error "BlFuncs.hpp: You must include BlHooks.hpp first" | ||||||
|  | #else | ||||||
|  |  | ||||||
|  | typedef const char * (*tsf_StringCallback)(ADDR, signed int, const char *[]); | ||||||
|  | typedef signed int   (*tsf_IntCallback   )(ADDR, signed int, const char *[]); | ||||||
|  | typedef float        (*tsf_FloatCallback )(ADDR, signed int, const char *[]); | ||||||
|  | typedef void         (*tsf_VoidCallback  )(ADDR, signed int, const char *[]); | ||||||
|  | typedef bool         (*tsf_BoolCallback  )(ADDR, signed int, const char *[]); | ||||||
|  |  | ||||||
|  | /* These functions are used for tsf_BlCon__executefSimObj. | ||||||
|  |    They refer to a special buffer for the argument stack. | ||||||
|  |    For tsf_BlCon__executef, you need to use your own buffers. */ | ||||||
|  | char *tsf_GetIntArg(int); | ||||||
|  | char *tsf_GetFloatArg(float); | ||||||
|  | char *tsf_ScriptThis(ADDR); | ||||||
|  |  | ||||||
|  | const char *tsf_Eval(const char *); | ||||||
|  | const char *tsf_Evalf(const char *, ...); | ||||||
|  |  | ||||||
|  | ADDR tsf_FindObject(unsigned int); | ||||||
|  | ADDR tsf_FindObject(const char *); | ||||||
|  |  | ||||||
|  | ADDR tsf_LookupNamespace(const char *, const char *); | ||||||
|  |  | ||||||
|  | const char *tsf_GetDataField(ADDR, const char *, const char *); | ||||||
|  | void tsf_SetDataField(ADDR, const char *, const char *, const char *); | ||||||
|  |  | ||||||
|  | const char *tsf_GetVar(const char *); | ||||||
|  |  | ||||||
|  | void tsf_AddVarInternal(const char *, signed int, void *); | ||||||
|  | void tsf_AddVar(const char *, const char **); | ||||||
|  | void tsf_AddVar(const char *, signed int *); | ||||||
|  | void tsf_AddVar(const char *, float *); | ||||||
|  | void tsf_AddVar(const char *, bool *); | ||||||
|  |  | ||||||
|  | ADDR tsf_AddConsoleFuncInternal(const char *, const char *, const char *, signed int, const char *, signed int, signed int); | ||||||
|  | void tsf_AddConsoleFunc(const char *, const char *, const char *, tsf_StringCallback, 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(); | ||||||
|  |  | ||||||
|  | extern ADDR tsf_mCacheSequence; | ||||||
|  | extern ADDR tsf_mCacheAllocator; | ||||||
|  | extern ADDR tsf_gIdDictionary; | ||||||
|  | extern ADDR tsf_gEvalState_globalVars; | ||||||
|  |  | ||||||
|  | BlFunctionDefExtern(const char *, __stdcall, tsf_BlStringTable__insert, const char *, bool); | ||||||
|  | BlFunctionDefExtern(ADDR, __fastcall, tsf_BlNamespace__find, const char *, const char *); | ||||||
|  | BlFunctionDefExtern(ADDR, __thiscall, tsf_BlNamespace__createLocalEntry, ADDR, const char *); | ||||||
|  | BlFunctionDefExtern(void, __thiscall, tsf_BlDataChunker__freeBlocks, ADDR); | ||||||
|  | BlFunctionDefExtern(const char *, , tsf_BlCon__evaluate, ADDR, signed int, const char **); | ||||||
|  | BlFunctionDefExtern(const char *, , tsf_BlCon__executef, signed int, ...); | ||||||
|  | BlFunctionDefExtern(const char *, , tsf_BlCon__executefSimObj, ADDR *, signed int, ...); | ||||||
|  | BlFunctionDefExtern(const char *, __thiscall, tsf_BlCon__getVariable, const char *); | ||||||
|  | BlFunctionDefExtern(void, __thiscall, tsf_BlDictionary__addVariable, ADDR *, const char *, signed int, void *); | ||||||
|  | BlFunctionDefExtern(ADDR *, __thiscall, tsf_BlSim__findObject_name, const char *); | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  | #define BlEval tsf_Eval | ||||||
|  | #define BlEvalf tsf_Evalf | ||||||
|  |  | ||||||
|  | #define BlIntArg tsf_GetIntArg | ||||||
|  | #define BlFloatArg tsf_GetFloatArg | ||||||
|  | #define BlThisArg tsf_GetThisArg | ||||||
|  | #define BlStringArg(x) tsf_GetStringArg((char*)x) | ||||||
|  | #define BlReturnBuffer tsf_BlCon__getReturnBuffer | ||||||
|  |  | ||||||
|  | #define BlObject tsf_FindObject | ||||||
|  | #define BlNamespace tsf_LookupNamespace | ||||||
|  |  | ||||||
|  | #define BlGetField tsf_GetDataField | ||||||
|  | #define BlSetField tsf_SetDataField | ||||||
|  |  | ||||||
|  | #define BlGetVar tsf_GetVar | ||||||
|  | #define BlAddVar tsf_AddVar | ||||||
|  |  | ||||||
|  | #define BlAddFunction tsf_AddConsoleFunc | ||||||
|  |  | ||||||
|  | #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 | ||||||
|  | #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) | ||||||
|  | #define BlCall(...) \ | ||||||
|  | 	tsf_BlCon__executef(__NUM_LIST(__VA_ARGS__), __VA_ARGS__) | ||||||
|  | #define BlCallObj(obj, ...) \ | ||||||
|  | 	tsf_BlCon__executefSimObj((ADDR*)obj, __NUM_LIST(__VA_ARGS__), __VA_ARGS__) | ||||||
|  |  | ||||||
|  | #define BlFuncsInit() if(!tsf_InitInternal()) { return false; } | ||||||
|  | #define BlFuncsDeinit() if(!tsf_DeinitInternal()) { return false; } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
							
								
								
									
										207
									
								
								inc/tsfuncs/BlHooks.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								inc/tsfuncs/BlHooks.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | |||||||
|  |  | ||||||
|  | ////////////////////////////////////////////////// | ||||||
|  | // RedoBlHooks Version 3.0 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Includes | ||||||
|  |  | ||||||
|  | #include <Windows.h> | ||||||
|  | #include <Psapi.h> | ||||||
|  | //#include <map> | ||||||
|  |  | ||||||
|  | #include "BlHooks.hpp" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Scanned structures | ||||||
|  |  | ||||||
|  | BlFunctionDefIntern(tsh_BlPrintf); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Sig Scanning | ||||||
|  |  | ||||||
|  | ADDR ImageBase; | ||||||
|  | ADDR ImageSize; | ||||||
|  |  | ||||||
|  | void tsh_i_InitScanner(){ | ||||||
|  | 	HMODULE module = GetModuleHandle(NULL); | ||||||
|  | 	if(module) { | ||||||
|  | 		MODULEINFO info; | ||||||
|  | 		GetModuleInformation(GetCurrentProcess(), module, &info, sizeof(MODULEINFO)); | ||||||
|  | 		ImageBase = (ADDR)info.lpBaseOfDll; | ||||||
|  | 		ImageSize = info.SizeOfImage; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool tsh_i_CompareData(BYTE *data, BYTE *pattern, char* mask){ | ||||||
|  | 	for (; *mask; ++data, ++pattern, ++mask){ | ||||||
|  | 		if (*mask=='x' && *data!=*pattern) | ||||||
|  | 			return false; | ||||||
|  | 	} | ||||||
|  | 	return (*mask)==0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ADDR tsh_i_FindPattern(ADDR imageBase, ADDR imageSize, BYTE *pattern, char *mask){ | ||||||
|  | 	for (ADDR i=imageBase; i < imageBase+imageSize; i++){ | ||||||
|  | 		if(tsh_i_CompareData((PBYTE)i, pattern, mask)){ | ||||||
|  | 			return i; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Convert a text-style pattern into code-style | ||||||
|  | void tsh_i_PatternTextToCode(char* text, char** opatt, char** omask) { | ||||||
|  | 	unsigned int len = strlen(text); | ||||||
|  | 	char* patt = (char*)malloc(len); | ||||||
|  | 	char* mask = (char*)malloc(len); | ||||||
|  | 	 | ||||||
|  | 	int outidx = 0; | ||||||
|  | 	int val = 0; | ||||||
|  | 	bool uk = false; | ||||||
|  | 	for(unsigned int i=0; i<len; i++){ | ||||||
|  | 		char c = text[i]; | ||||||
|  | 		if(c=='?'){ | ||||||
|  | 			uk = true; | ||||||
|  | 		}else if(c>='0' && c<='9'){ | ||||||
|  | 			val = (val<<4) + (c-'0'); | ||||||
|  | 		}else if(c>='A' && c<='F'){ | ||||||
|  | 			val = (val<<4) + (c-'A'+10); | ||||||
|  | 		}else if(c>='a' && c<='f'){ | ||||||
|  | 			val = (val<<4) + (c-'a'+10); | ||||||
|  | 		}else if(c==' '){ | ||||||
|  | 			patt[outidx] = uk ? 0 : val; | ||||||
|  | 			mask[outidx] = uk ? '?' : 'x'; | ||||||
|  | 			val = 0; | ||||||
|  | 			uk = false; | ||||||
|  | 			outidx++; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	patt[outidx] = uk ? 0 : val; | ||||||
|  | 	mask[outidx] = uk ? '?' : 'x'; | ||||||
|  | 	outidx++; | ||||||
|  | 	patt[outidx] = 0; | ||||||
|  | 	mask[outidx] = 0; | ||||||
|  | 	 | ||||||
|  | 	*opatt = patt; | ||||||
|  | 	*omask = mask; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Public functions for sig scanning | ||||||
|  |  | ||||||
|  | // Scan using code-style pattern | ||||||
|  | ADDR tsh_ScanCode(char* pattern, char* mask) { | ||||||
|  | 	return tsh_i_FindPattern(ImageBase, ImageSize-strlen(mask), (BYTE*)pattern, mask); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Scan using a text-style pattern | ||||||
|  | ADDR tsh_ScanText(char* text) { | ||||||
|  | 	char* patt; | ||||||
|  | 	char* mask; | ||||||
|  | 	tsh_i_PatternTextToCode(text, &patt, &mask); | ||||||
|  | 	 | ||||||
|  | 	ADDR res = tsh_ScanCode(patt, mask); | ||||||
|  | 	 | ||||||
|  | 	free(patt); | ||||||
|  | 	free(mask); | ||||||
|  | 	 | ||||||
|  | 	return res; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Call Patching and Hooking | ||||||
|  |  | ||||||
|  | // Remove protection from address | ||||||
|  | //std::map<ADDR, std::pair<ADDR, ADDR>> tsh_DeprotectedAddresses; | ||||||
|  |  | ||||||
|  | void tsh_DeprotectAddress(ADDR length, ADDR location) { | ||||||
|  | 	DWORD oldProtection; | ||||||
|  | 	VirtualProtect((void*)location, length, PAGE_EXECUTE_READWRITE, &oldProtection); | ||||||
|  | 	//tsh_DeprotectedAddresses[location] = {length, oldProtection}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Patch a string of bytes by deprotecting and then overwriting | ||||||
|  | void tsh_PatchBytes(ADDR length, ADDR location, BYTE* repl) { | ||||||
|  | 	tsh_DeprotectAddress(length, location); | ||||||
|  | 	memcpy((void*)location, (void*)repl, (size_t)length); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsh_PatchByte(ADDR location, BYTE value) { | ||||||
|  | 	tsh_PatchBytes(location, 1, &value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsh_ReplaceInt(ADDR addr, int rval) { | ||||||
|  | 	tsh_PatchBytes(4, addr, (BYTE*)(&rval)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int tsh_i_CallOffset(ADDR instr, ADDR func) { | ||||||
|  | 	return func - (instr+4); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsh_i_ReplaceCall(ADDR instr, ADDR target) { | ||||||
|  | 	tsh_ReplaceInt(instr, tsh_i_CallOffset(instr, target)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsh_i_PatchCopy(ADDR dest, ADDR src, unsigned int len) { | ||||||
|  | 	for(unsigned int i=0; i<len; i++){ | ||||||
|  | 		tsh_PatchByte(dest+i, *((BYTE*)(src+i))); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsh_HookFunction(ADDR victim, ADDR detour, BYTE* origbytes) { | ||||||
|  | 	memcpy(origbytes, (BYTE*)victim, 6); //save old data | ||||||
|  | 	 | ||||||
|  | 	*(BYTE*)victim = 0xE9; //jmp rel32 | ||||||
|  | 	*(ADDR*)(victim+1) = (detour - (victim+5)); // jump offset | ||||||
|  | 	*(BYTE*)(victim+5) = 0xC3; //retn | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void tsh_UnhookFunction(ADDR victim, BYTE* origbytes){ | ||||||
|  | 	tsh_i_PatchCopy(victim, (ADDR)origbytes, 6); //restore old data | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int tsh_PatchAllMatchesCode(ADDR len, char* patt, char* mask, char* replace, bool debugprint){ | ||||||
|  | 	int numpatched = 0; | ||||||
|  | 	for(ADDR i=ImageBase; i<ImageBase+ImageSize-len; i++){ | ||||||
|  | 		if(tsh_i_CompareData((BYTE*)i, (BYTE*)patt, mask)){ | ||||||
|  | 			if(debugprint) BlPrintf("RedoBlHooks: Patching call at %08x", i); | ||||||
|  | 			 | ||||||
|  | 			numpatched++; | ||||||
|  | 			tsh_DeprotectAddress(i, len); | ||||||
|  | 			for(ADDR c=0; c<len; c++){ | ||||||
|  | 				tsh_PatchByte(i+c, replace[c]); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return numpatched; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int tsh_PatchAllMatchesHex(ADDR len, char* text, char* replace, bool debugprint) { | ||||||
|  | 	char* patt; | ||||||
|  | 	char* mask; | ||||||
|  | 	tsh_i_PatternTextToCode(text, &patt, &mask); | ||||||
|  | 	 | ||||||
|  | 	int res = tsh_PatchAllMatchesCode(len, patt, mask, replace, debugprint); | ||||||
|  | 	 | ||||||
|  | 	free(patt); | ||||||
|  | 	free(mask); | ||||||
|  | 	 | ||||||
|  | 	return res; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Initialization | ||||||
|  |  | ||||||
|  | bool tsh_InitInternal(){ | ||||||
|  | 	tsh_i_InitScanner(); | ||||||
|  | 	 | ||||||
|  | 	BlScanFunctionText(tsh_BlPrintf, "8D 44 24 08 33 D2 50 FF 74 24 08 33 C9 E8 ? ? ? ? 83 C4 08 C3"); | ||||||
|  | 	 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool tsh_DeinitInternal() { | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										122
									
								
								inc/tsfuncs/BlHooks.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								inc/tsfuncs/BlHooks.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | |||||||
|  |  | ||||||
|  | ////////////////////////////////////////////////// | ||||||
|  | // RedoBlHooks Version 3.0 | ||||||
|  |  | ||||||
|  | #ifndef _H_BLHOOKS | ||||||
|  | #define _H_BLHOOKS | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Typedefs | ||||||
|  |  | ||||||
|  | typedef unsigned char BYTE; | ||||||
|  | typedef unsigned int  ADDR; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Prototypes | ||||||
|  |  | ||||||
|  | bool tsh_InitInternal(); | ||||||
|  | bool tsh_DeinitInternal(); | ||||||
|  | ADDR tsh_ScanCode(char*, char*); | ||||||
|  | ADDR tsh_ScanText(char*); | ||||||
|  | void tsh_HookFunction(ADDR, ADDR, BYTE*); | ||||||
|  | void tsh_UnhookFunction(ADDR, BYTE*); | ||||||
|  | int  tsh_PatchAllMatches(unsigned int, char*, char*, char*, bool); | ||||||
|  | void tsh_PatchByte(ADDR, BYTE); | ||||||
|  | void tsh_PatchBytes(unsigned int, ADDR, BYTE*); | ||||||
|  | void tsh_PatchInt(ADDR, int); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Debug print settings | ||||||
|  |  | ||||||
|  | #ifndef TSH_NO_DEBUG_PRINT | ||||||
|  | 	#define tsh_DEBUGPRINT false | ||||||
|  | #else | ||||||
|  | 	#define tsh_DEBUGPRINT true | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Function short names | ||||||
|  |  | ||||||
|  | // Use in code when the def is not shared in a header | ||||||
|  | #define BlFunctionDef(returnType, convention, name, ...) \ | ||||||
|  | 	typedef returnType (convention *tsh_##name##FnT)(__VA_ARGS__); \ | ||||||
|  | 	tsh_##name##FnT name; | ||||||
|  | // Use in header for shared function defs when a BlFunctionDefIntern exists in code | ||||||
|  | #define BlFunctionDefExtern(returnType, convention, name, ...) \ | ||||||
|  | 	typedef returnType (convention *tsh_##name##FnT)(__VA_ARGS__); \ | ||||||
|  | 	extern tsh_##name##FnT name; | ||||||
|  | // Use in code for shared function defs when a BlFunctionDefExtern exists in header | ||||||
|  | #define BlFunctionDefIntern(name) \ | ||||||
|  | 	tsh_##name##FnT name; | ||||||
|  |  | ||||||
|  | // Scan for and assign the pattern to the variable, or err and return if not found | ||||||
|  | #define BlScanFunctionCode(target, patt, mask) \ | ||||||
|  | 	target = (tsh_##target##FnT)tsh_ScanCode((char*)patt, (char*)mask); \ | ||||||
|  | 	if(!target){ \ | ||||||
|  | 		BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \ | ||||||
|  | 		return false; \ | ||||||
|  | 	}else{ \ | ||||||
|  | 		if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \ | ||||||
|  | 	} | ||||||
|  | #define BlScanFunctionText(target, text) \ | ||||||
|  | 	target = (tsh_##target##FnT)tsh_ScanText((char*)text); \ | ||||||
|  | 	if(!target){ \ | ||||||
|  | 		BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \ | ||||||
|  | 		return false; \ | ||||||
|  | 	}else{ \ | ||||||
|  | 		if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \ | ||||||
|  | 	} | ||||||
|  | #define BlScanCode(target, patt, mask) \ | ||||||
|  | 	target = tsh_ScanCode((char*)patt, (char*)mask); \ | ||||||
|  | 	if(!target){ \ | ||||||
|  | 		BlPrintf("RedoBlHooks | Cannot find pattern "#target"!"); \ | ||||||
|  | 		return false; \ | ||||||
|  | 	}else{ \ | ||||||
|  | 		if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \ | ||||||
|  | 	} | ||||||
|  | #define BlScanText(target, text) \ | ||||||
|  | 	target = tsh_ScanText((char*)text); \ | ||||||
|  | 	if(!target){ \ | ||||||
|  | 		BlPrintf("RedoBlHooks | Cannot find "#target"!"); \ | ||||||
|  | 		return false; \ | ||||||
|  | 	}else{ \ | ||||||
|  | 		if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | // Use in code to define the data and functions for hooking a function | ||||||
|  | #define BlFunctionHookDef(func) \ | ||||||
|  | 	BYTE tsh_BlFunctionHook##func##Data[6]; \ | ||||||
|  | 	void func##HookOn(){ tsh_HookFunction((ADDR)func, (ADDR)func##Hook, tsh_BlFunctionHook##func##Data); } \ | ||||||
|  | 	void func##HookOff(){ tsh_UnhookFunction((ADDR)func, tsh_BlFunctionHook##func##Data); } | ||||||
|  | // Use in code to initialize the hook once | ||||||
|  | #define BlFunctionHookInit(func) \ | ||||||
|  | 	tsh_DeprotectAddress(6, (ADDR)func); \ | ||||||
|  | 	func##HookOn(); | ||||||
|  |  | ||||||
|  | // Replace all matches of the pattern with the given byte string | ||||||
|  | #define BlPatchAllMatchesCode(len, patt, mask, repl) \ | ||||||
|  | 	tsh_PatchAllMatchesCode((ADDR)len, (char*)patt, (char*)mask, (char*)repl, tsh_DEBUGPRINT); | ||||||
|  | #define BlPatchAllMatchesText(len, text, repl) \ | ||||||
|  | 	tsh_PatchAllMatchesCode((ADDR)len, (char*)text, (char*)repl, tsh_DEBUGPRINT); | ||||||
|  |  | ||||||
|  | // Deprotect and replace one byte | ||||||
|  | #define BlPatchByte(addr, byte) \ | ||||||
|  | 	tsh_PatchByte((ADDR)addr, (BYTE)byte); | ||||||
|  | // Deprotect and replace a byte string | ||||||
|  | #define BlPatchBytes(len, addr, repl) \ | ||||||
|  | 	tsh_PatchBytes((ADDR)len, (ADDR)addr, (BYTE*)repl); | ||||||
|  |  | ||||||
|  | // BlPrintf(char* format, ...) | ||||||
|  | #define BlPrintf(...) if(tsh_BlPrintf) { tsh_BlPrintf(__VA_ARGS__); } | ||||||
|  | // BlHooksInit() -> bool: success | ||||||
|  | #define BlHooksInit() if(!tsh_InitInternal()) { BlPrintf("BlHooksInit failed"); return false; } | ||||||
|  | // BlHooksDeinit() -> bool: success | ||||||
|  | #define BlHooksDeinit() if(!tsh_DeinitInternal()) { BlPrintf("BlHooksDeinit failed"); return false; } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Scanned structures | ||||||
|  |  | ||||||
|  | BlFunctionDefExtern(void, , tsh_BlPrintf, const char*, ...); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										
											BIN
										
									
								
								lua5.1.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								lua5.1.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								lualib/https.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								lualib/https.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										292
									
								
								lualib/ltn12.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								lualib/ltn12.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,292 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- LTN12 - Filters, sources, sinks and pumps. | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local string = require("string") | ||||||
|  | local table = require("table") | ||||||
|  | local base = _G | ||||||
|  | module("ltn12") | ||||||
|  |  | ||||||
|  | filter = {} | ||||||
|  | source = {} | ||||||
|  | sink = {} | ||||||
|  | pump = {} | ||||||
|  |  | ||||||
|  | -- 2048 seems to be better in windows... | ||||||
|  | BLOCKSIZE = 2048 | ||||||
|  | _VERSION = "LTN12 1.0.1" | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Filter stuff | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- returns a high level filter that cycles a low-level filter | ||||||
|  | function filter.cycle(low, ctx, extra) | ||||||
|  |     base.assert(low) | ||||||
|  |     return function(chunk) | ||||||
|  |         local ret | ||||||
|  |         ret, ctx = low(ctx, chunk, extra) | ||||||
|  |         return ret | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- chains a bunch of filters together | ||||||
|  | -- (thanks to Wim Couwenberg) | ||||||
|  | function filter.chain(...) | ||||||
|  |     local n = table.getn(arg) | ||||||
|  |     local top, index = 1, 1 | ||||||
|  |     local retry = "" | ||||||
|  |     return function(chunk) | ||||||
|  |         retry = chunk and retry | ||||||
|  |         while true do | ||||||
|  |             if index == top then | ||||||
|  |                 chunk = arg[index](chunk) | ||||||
|  |                 if chunk == "" or top == n then return chunk | ||||||
|  |                 elseif chunk then index = index + 1 | ||||||
|  |                 else | ||||||
|  |                     top = top+1 | ||||||
|  |                     index = top | ||||||
|  |                 end | ||||||
|  |             else | ||||||
|  |                 chunk = arg[index](chunk or "") | ||||||
|  |                 if chunk == "" then | ||||||
|  |                     index = index - 1 | ||||||
|  |                     chunk = retry | ||||||
|  |                 elseif chunk then | ||||||
|  |                     if index == n then return chunk | ||||||
|  |                     else index = index + 1 end | ||||||
|  |                 else base.error("filter returned inappropriate nil") end | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Source stuff | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- create an empty source | ||||||
|  | local function empty() | ||||||
|  |     return nil | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function source.empty() | ||||||
|  |     return empty | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- returns a source that just outputs an error | ||||||
|  | function source.error(err) | ||||||
|  |     return function() | ||||||
|  |         return nil, err | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- creates a file source | ||||||
|  | function source.file(handle, io_err) | ||||||
|  |     if handle then | ||||||
|  |         return function() | ||||||
|  |             local chunk = handle:read(BLOCKSIZE) | ||||||
|  |             if not chunk then handle:close() end | ||||||
|  |             return chunk | ||||||
|  |         end | ||||||
|  |     else return source.error(io_err or "unable to open file") end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- turns a fancy source into a simple source | ||||||
|  | function source.simplify(src) | ||||||
|  |     base.assert(src) | ||||||
|  |     return function() | ||||||
|  |         local chunk, err_or_new = src() | ||||||
|  |         src = err_or_new or src | ||||||
|  |         if not chunk then return nil, err_or_new | ||||||
|  |         else return chunk end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- creates string source | ||||||
|  | function source.string(s) | ||||||
|  |     if s then | ||||||
|  |         local i = 1 | ||||||
|  |         return function() | ||||||
|  |             local chunk = string.sub(s, i, i+BLOCKSIZE-1) | ||||||
|  |             i = i + BLOCKSIZE | ||||||
|  |             if chunk ~= "" then return chunk | ||||||
|  |             else return nil end | ||||||
|  |         end | ||||||
|  |     else return source.empty() end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- creates rewindable source | ||||||
|  | function source.rewind(src) | ||||||
|  |     base.assert(src) | ||||||
|  |     local t = {} | ||||||
|  |     return function(chunk) | ||||||
|  |         if not chunk then | ||||||
|  |             chunk = table.remove(t) | ||||||
|  |             if not chunk then return src() | ||||||
|  |             else return chunk end | ||||||
|  |         else | ||||||
|  |             table.insert(t, chunk) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function source.chain(src, f) | ||||||
|  |     base.assert(src and f) | ||||||
|  |     local last_in, last_out = "", "" | ||||||
|  |     local state = "feeding" | ||||||
|  |     local err | ||||||
|  |     return function() | ||||||
|  |         if not last_out then | ||||||
|  |             base.error('source is empty!', 2) | ||||||
|  |         end | ||||||
|  |         while true do | ||||||
|  |             if state == "feeding" then | ||||||
|  |                 last_in, err = src() | ||||||
|  |                 if err then return nil, err end | ||||||
|  |                 last_out = f(last_in) | ||||||
|  |                 if not last_out then | ||||||
|  |                     if last_in then | ||||||
|  |                         base.error('filter returned inappropriate nil') | ||||||
|  |                     else | ||||||
|  |                         return nil | ||||||
|  |                     end | ||||||
|  |                 elseif last_out ~= "" then | ||||||
|  |                     state = "eating" | ||||||
|  |                     if last_in then last_in = "" end | ||||||
|  |                     return last_out | ||||||
|  |                 end | ||||||
|  |             else | ||||||
|  |                 last_out = f(last_in) | ||||||
|  |                 if last_out == "" then | ||||||
|  |                     if last_in == "" then | ||||||
|  |                         state = "feeding" | ||||||
|  |                     else | ||||||
|  |                         base.error('filter returned ""') | ||||||
|  |                     end | ||||||
|  |                 elseif not last_out then | ||||||
|  |                     if last_in then | ||||||
|  |                         base.error('filter returned inappropriate nil') | ||||||
|  |                     else | ||||||
|  |                         return nil | ||||||
|  |                     end | ||||||
|  |                 else | ||||||
|  |                     return last_out | ||||||
|  |                 end | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- creates a source that produces contents of several sources, one after the | ||||||
|  | -- other, as if they were concatenated | ||||||
|  | -- (thanks to Wim Couwenberg) | ||||||
|  | function source.cat(...) | ||||||
|  |     local src = table.remove(arg, 1) | ||||||
|  |     return function() | ||||||
|  |         while src do | ||||||
|  |             local chunk, err = src() | ||||||
|  |             if chunk then return chunk end | ||||||
|  |             if err then return nil, err end | ||||||
|  |             src = table.remove(arg, 1) | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Sink stuff | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- creates a sink that stores into a table | ||||||
|  | function sink.table(t) | ||||||
|  |     t = t or {} | ||||||
|  |     local f = function(chunk, err) | ||||||
|  |         if chunk then table.insert(t, chunk) end | ||||||
|  |         return 1 | ||||||
|  |     end | ||||||
|  |     return f, t | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- turns a fancy sink into a simple sink | ||||||
|  | function sink.simplify(snk) | ||||||
|  |     base.assert(snk) | ||||||
|  |     return function(chunk, err) | ||||||
|  |         local ret, err_or_new = snk(chunk, err) | ||||||
|  |         if not ret then return nil, err_or_new end | ||||||
|  |         snk = err_or_new or snk | ||||||
|  |         return 1 | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- creates a file sink | ||||||
|  | function sink.file(handle, io_err) | ||||||
|  |     if handle then | ||||||
|  |         return function(chunk, err) | ||||||
|  |             if not chunk then | ||||||
|  |                 handle:close() | ||||||
|  |                 return 1 | ||||||
|  |             else return handle:write(chunk) end | ||||||
|  |         end | ||||||
|  |     else return sink.error(io_err or "unable to open file") end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- creates a sink that discards data | ||||||
|  | local function null() | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function sink.null() | ||||||
|  |     return null | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- creates a sink that just returns an error | ||||||
|  | function sink.error(err) | ||||||
|  |     return function() | ||||||
|  |         return nil, err | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- chains a sink with a filter | ||||||
|  | function sink.chain(f, snk) | ||||||
|  |     base.assert(f and snk) | ||||||
|  |     return function(chunk, err) | ||||||
|  |         if chunk ~= "" then | ||||||
|  |             local filtered = f(chunk) | ||||||
|  |             local done = chunk and "" | ||||||
|  |             while true do | ||||||
|  |                 local ret, snkerr = snk(filtered, err) | ||||||
|  |                 if not ret then return nil, snkerr end | ||||||
|  |                 if filtered == done then return 1 end | ||||||
|  |                 filtered = f(done) | ||||||
|  |             end | ||||||
|  |         else return 1 end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Pump stuff | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- pumps one chunk from the source to the sink | ||||||
|  | function pump.step(src, snk) | ||||||
|  |     local chunk, src_err = src() | ||||||
|  |     local ret, snk_err = snk(chunk, src_err) | ||||||
|  |     if chunk and ret then return 1 | ||||||
|  |     else return nil, src_err or snk_err end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- pumps all data from a source to a sink, using a step function | ||||||
|  | function pump.all(src, snk, step) | ||||||
|  |     base.assert(src and snk) | ||||||
|  |     step = step or pump.step | ||||||
|  |     while true do | ||||||
|  |         local ret, err = step(src, snk) | ||||||
|  |         if not ret then | ||||||
|  |             if err then return nil, err | ||||||
|  |             else return 1 end | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
							
								
								
									
										87
									
								
								lualib/mime.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								lualib/mime.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- MIME support for the Lua language. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- Conforming to RFCs 2045-2049 | ||||||
|  | -- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local base = _G | ||||||
|  | local ltn12 = require("ltn12") | ||||||
|  | local mime = require("mime.core") | ||||||
|  | --local io = require("io") | ||||||
|  | local string = require("string") | ||||||
|  | module("mime") | ||||||
|  |  | ||||||
|  | -- encode, decode and wrap algorithm tables | ||||||
|  | encodet = {} | ||||||
|  | decodet = {} | ||||||
|  | wrapt = {} | ||||||
|  |  | ||||||
|  | -- creates a function that chooses a filter by name from a given table | ||||||
|  | local function choose(table) | ||||||
|  |     return function(name, opt1, opt2) | ||||||
|  |         if base.type(name) ~= "string" then | ||||||
|  |             name, opt1, opt2 = "default", name, opt1 | ||||||
|  |         end | ||||||
|  |         local f = table[name or "nil"] | ||||||
|  |         if not f then  | ||||||
|  |             base.error("unknown key (" .. base.tostring(name) .. ")", 3) | ||||||
|  |         else return f(opt1, opt2) end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- define the encoding filters | ||||||
|  | encodet['base64'] = function() | ||||||
|  |     return ltn12.filter.cycle(b64, "") | ||||||
|  | end | ||||||
|  |  | ||||||
|  | encodet['quoted-printable'] = function(mode) | ||||||
|  |     return ltn12.filter.cycle(qp, "", | ||||||
|  |         (mode == "binary") and "=0D=0A" or "\r\n") | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- define the decoding filters | ||||||
|  | decodet['base64'] = function() | ||||||
|  |     return ltn12.filter.cycle(unb64, "") | ||||||
|  | end | ||||||
|  |  | ||||||
|  | decodet['quoted-printable'] = function() | ||||||
|  |     return ltn12.filter.cycle(unqp, "") | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function format(chunk) | ||||||
|  |     if chunk then | ||||||
|  |         if chunk == "" then return "''" | ||||||
|  |         else return string.len(chunk) end | ||||||
|  |     else return "nil" end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- define the line-wrap filters | ||||||
|  | wrapt['text'] = function(length) | ||||||
|  |     length = length or 76 | ||||||
|  |     return ltn12.filter.cycle(wrp, length, length) | ||||||
|  | end | ||||||
|  | wrapt['base64'] = wrapt['text'] | ||||||
|  | wrapt['default'] = wrapt['text'] | ||||||
|  |  | ||||||
|  | wrapt['quoted-printable'] = function() | ||||||
|  |     return ltn12.filter.cycle(qpwrp, 76, 76) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- function that choose the encoding, decoding or wrap algorithm | ||||||
|  | encode = choose(encodet) | ||||||
|  | decode = choose(decodet) | ||||||
|  | wrap = choose(wrapt) | ||||||
|  |  | ||||||
|  | -- define the end-of-line normalization filter | ||||||
|  | function normalize(marker) | ||||||
|  |     return ltn12.filter.cycle(eol, 0, marker) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- high level stuffing filter | ||||||
|  | function stuff() | ||||||
|  |     return ltn12.filter.cycle(dot, 2) | ||||||
|  | end | ||||||
							
								
								
									
										
											BIN
										
									
								
								lualib/mime/core.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								lualib/mime/core.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										133
									
								
								lualib/socket.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								lualib/socket.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- LuaSocket helper module | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local base = _G | ||||||
|  | local string = require("string") | ||||||
|  | local math = require("math") | ||||||
|  | local socket = require("socket.core") | ||||||
|  | module("socket") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Exported auxiliar functions | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function connect(address, port, laddress, lport) | ||||||
|  |     local sock, err = socket.tcp() | ||||||
|  |     if not sock then return nil, err end | ||||||
|  |     if laddress then | ||||||
|  |         local res, err = sock:bind(laddress, lport, -1) | ||||||
|  |         if not res then return nil, err end | ||||||
|  |     end | ||||||
|  |     local res, err = sock:connect(address, port) | ||||||
|  |     if not res then return nil, err end | ||||||
|  |     return sock | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function bind(host, port, backlog) | ||||||
|  |     local sock, err = socket.tcp() | ||||||
|  |     if not sock then return nil, err end | ||||||
|  |     sock:setoption("reuseaddr", true) | ||||||
|  |     local res, err = sock:bind(host, port) | ||||||
|  |     if not res then return nil, err end | ||||||
|  |     res, err = sock:listen(backlog) | ||||||
|  |     if not res then return nil, err end | ||||||
|  |     return sock | ||||||
|  | end | ||||||
|  |  | ||||||
|  | try = newtry() | ||||||
|  |  | ||||||
|  | function choose(table) | ||||||
|  |     return function(name, opt1, opt2) | ||||||
|  |         if base.type(name) ~= "string" then | ||||||
|  |             name, opt1, opt2 = "default", name, opt1 | ||||||
|  |         end | ||||||
|  |         local f = table[name or "nil"] | ||||||
|  |         if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) | ||||||
|  |         else return f(opt1, opt2) end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Socket sources and sinks, conforming to LTN12 | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- create namespaces inside LuaSocket namespace | ||||||
|  | sourcet = {} | ||||||
|  | sinkt = {} | ||||||
|  |  | ||||||
|  | BLOCKSIZE = 2048 | ||||||
|  |  | ||||||
|  | sinkt["close-when-done"] = function(sock) | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function(self, chunk, err) | ||||||
|  |             if not chunk then | ||||||
|  |                 sock:close() | ||||||
|  |                 return 1 | ||||||
|  |             else return sock:send(chunk) end | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | sinkt["keep-open"] = function(sock) | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function(self, chunk, err) | ||||||
|  |             if chunk then return sock:send(chunk) | ||||||
|  |             else return 1 end | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | sinkt["default"] = sinkt["keep-open"] | ||||||
|  |  | ||||||
|  | sink = choose(sinkt) | ||||||
|  |  | ||||||
|  | sourcet["by-length"] = function(sock, length) | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function() | ||||||
|  |             if length <= 0 then return nil end | ||||||
|  |             local size = math.min(socket.BLOCKSIZE, length) | ||||||
|  |             local chunk, err = sock:receive(size) | ||||||
|  |             if err then return nil, err end | ||||||
|  |             length = length - string.len(chunk) | ||||||
|  |             return chunk | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | sourcet["until-closed"] = function(sock) | ||||||
|  |     local done | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function() | ||||||
|  |             if done then return nil end | ||||||
|  |             local chunk, err, partial = sock:receive(socket.BLOCKSIZE) | ||||||
|  |             if not err then return chunk | ||||||
|  |             elseif err == "closed" then | ||||||
|  |                 sock:close() | ||||||
|  |                 done = 1 | ||||||
|  |                 return partial | ||||||
|  |             else return nil, err end | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | sourcet["default"] = sourcet["until-closed"] | ||||||
|  |  | ||||||
|  | source = choose(sourcet) | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								lualib/socket/core.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								lualib/socket/core.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										281
									
								
								lualib/socket/ftp.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								lualib/socket/ftp.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,281 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- FTP support for the Lua language | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local base = _G | ||||||
|  | local table = require("table") | ||||||
|  | local string = require("string") | ||||||
|  | local math = require("math") | ||||||
|  | local socket = require("socket") | ||||||
|  | local url = require("socket.url") | ||||||
|  | local tp = require("socket.tp") | ||||||
|  | local ltn12 = require("ltn12") | ||||||
|  | module("socket.ftp") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Program constants | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- timeout in seconds before the program gives up on a connection | ||||||
|  | TIMEOUT = 60 | ||||||
|  | -- default port for ftp service | ||||||
|  | PORT = 21 | ||||||
|  | -- this is the default anonymous password. used when no password is | ||||||
|  | -- provided in url. should be changed to your e-mail. | ||||||
|  | USER = "ftp" | ||||||
|  | PASSWORD = "anonymous@anonymous.org" | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Low level FTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local metat = { __index = {} } | ||||||
|  |  | ||||||
|  | function open(server, port, create) | ||||||
|  |     local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create)) | ||||||
|  |     local f = base.setmetatable({ tp = tp }, metat) | ||||||
|  |     -- make sure everything gets closed in an exception | ||||||
|  |     f.try = socket.newtry(function() f:close() end) | ||||||
|  |     return f | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:portconnect() | ||||||
|  |     self.try(self.server:settimeout(TIMEOUT)) | ||||||
|  |     self.data = self.try(self.server:accept()) | ||||||
|  |     self.try(self.data:settimeout(TIMEOUT)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:pasvconnect() | ||||||
|  |     self.data = self.try(socket.tcp()) | ||||||
|  |     self.try(self.data:settimeout(TIMEOUT)) | ||||||
|  |     self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:login(user, password) | ||||||
|  |     self.try(self.tp:command("user", user or USER)) | ||||||
|  |     local code, reply = self.try(self.tp:check{"2..", 331}) | ||||||
|  |     if code == 331 then | ||||||
|  |         self.try(self.tp:command("pass", password or PASSWORD)) | ||||||
|  |         self.try(self.tp:check("2..")) | ||||||
|  |     end | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:pasv() | ||||||
|  |     self.try(self.tp:command("pasv")) | ||||||
|  |     local code, reply = self.try(self.tp:check("2..")) | ||||||
|  |     local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" | ||||||
|  |     local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) | ||||||
|  |     self.try(a and b and c and d and p1 and p2, reply) | ||||||
|  |     self.pasvt = { | ||||||
|  |         ip = string.format("%d.%d.%d.%d", a, b, c, d), | ||||||
|  |         port = p1*256 + p2 | ||||||
|  |     } | ||||||
|  |     if self.server then | ||||||
|  |         self.server:close() | ||||||
|  |         self.server = nil | ||||||
|  |     end | ||||||
|  |     return self.pasvt.ip, self.pasvt.port | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:port(ip, port) | ||||||
|  |     self.pasvt = nil | ||||||
|  |     if not ip then | ||||||
|  |         ip, port = self.try(self.tp:getcontrol():getsockname()) | ||||||
|  |         self.server = self.try(socket.bind(ip, 0)) | ||||||
|  |         ip, port = self.try(self.server:getsockname()) | ||||||
|  |         self.try(self.server:settimeout(TIMEOUT)) | ||||||
|  |     end | ||||||
|  |     local pl = math.mod(port, 256) | ||||||
|  |     local ph = (port - pl)/256 | ||||||
|  |     local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") | ||||||
|  |     self.try(self.tp:command("port", arg)) | ||||||
|  |     self.try(self.tp:check("2..")) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:send(sendt) | ||||||
|  |     self.try(self.pasvt or self.server, "need port or pasv first") | ||||||
|  |     -- if there is a pasvt table, we already sent a PASV command | ||||||
|  |     -- we just get the data connection into self.data | ||||||
|  |     if self.pasvt then self:pasvconnect() end | ||||||
|  |     -- get the transfer argument and command | ||||||
|  |     local argument = sendt.argument or | ||||||
|  |         url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) | ||||||
|  |     if argument == "" then argument = nil end | ||||||
|  |     local command = sendt.command or "stor" | ||||||
|  |     -- send the transfer command and check the reply | ||||||
|  |     self.try(self.tp:command(command, argument)) | ||||||
|  |     local code, reply = self.try(self.tp:check{"2..", "1.."}) | ||||||
|  |     -- if there is not a a pasvt table, then there is a server | ||||||
|  |     -- and we already sent a PORT command | ||||||
|  |     if not self.pasvt then self:portconnect() end | ||||||
|  |     -- get the sink, source and step for the transfer | ||||||
|  |     local step = sendt.step or ltn12.pump.step | ||||||
|  |     local readt = {self.tp.c} | ||||||
|  |     local checkstep = function(src, snk) | ||||||
|  |         -- check status in control connection while downloading | ||||||
|  |         local readyt = socket.select(readt, nil, 0) | ||||||
|  |         if readyt[tp] then code = self.try(self.tp:check("2..")) end | ||||||
|  |         return step(src, snk) | ||||||
|  |     end | ||||||
|  |     local sink = socket.sink("close-when-done", self.data) | ||||||
|  |     -- transfer all data and check error | ||||||
|  |     self.try(ltn12.pump.all(sendt.source, sink, checkstep)) | ||||||
|  |     if string.find(code, "1..") then self.try(self.tp:check("2..")) end | ||||||
|  |     -- done with data connection | ||||||
|  |     self.data:close() | ||||||
|  |     -- find out how many bytes were sent | ||||||
|  |     local sent = socket.skip(1, self.data:getstats()) | ||||||
|  |     self.data = nil | ||||||
|  |     return sent | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receive(recvt) | ||||||
|  |     self.try(self.pasvt or self.server, "need port or pasv first") | ||||||
|  |     if self.pasvt then self:pasvconnect() end | ||||||
|  |     local argument = recvt.argument or | ||||||
|  |         url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) | ||||||
|  |     if argument == "" then argument = nil end | ||||||
|  |     local command = recvt.command or "retr" | ||||||
|  |     self.try(self.tp:command(command, argument)) | ||||||
|  |     local code = self.try(self.tp:check{"1..", "2.."}) | ||||||
|  |     if not self.pasvt then self:portconnect() end | ||||||
|  |     local source = socket.source("until-closed", self.data) | ||||||
|  |     local step = recvt.step or ltn12.pump.step | ||||||
|  |     self.try(ltn12.pump.all(source, recvt.sink, step)) | ||||||
|  |     if string.find(code, "1..") then self.try(self.tp:check("2..")) end | ||||||
|  |     self.data:close() | ||||||
|  |     self.data = nil | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:cwd(dir) | ||||||
|  |     self.try(self.tp:command("cwd", dir)) | ||||||
|  |     self.try(self.tp:check(250)) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:type(type) | ||||||
|  |     self.try(self.tp:command("type", type)) | ||||||
|  |     self.try(self.tp:check(200)) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:greet() | ||||||
|  |     local code = self.try(self.tp:check{"1..", "2.."}) | ||||||
|  |     if string.find(code, "1..") then self.try(self.tp:check("2..")) end | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:quit() | ||||||
|  |     self.try(self.tp:command("quit")) | ||||||
|  |     self.try(self.tp:check("2..")) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:close() | ||||||
|  |     if self.data then self.data:close() end | ||||||
|  |     if self.server then self.server:close() end | ||||||
|  |     return self.tp:close() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- High level FTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local function override(t) | ||||||
|  |     if t.url then | ||||||
|  |         local u = url.parse(t.url) | ||||||
|  |         for i,v in base.pairs(t) do | ||||||
|  |             u[i] = v | ||||||
|  |         end | ||||||
|  |         return u | ||||||
|  |     else return t end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function tput(putt) | ||||||
|  |     putt = override(putt) | ||||||
|  |     socket.try(putt.host, "missing hostname") | ||||||
|  |     local f = open(putt.host, putt.port, putt.create) | ||||||
|  |     f:greet() | ||||||
|  |     f:login(putt.user, putt.password) | ||||||
|  |     if putt.type then f:type(putt.type) end | ||||||
|  |     f:pasv() | ||||||
|  |     local sent = f:send(putt) | ||||||
|  |     f:quit() | ||||||
|  |     f:close() | ||||||
|  |     return sent | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local default = { | ||||||
|  | 	path = "/", | ||||||
|  | 	scheme = "ftp" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | local function parse(u) | ||||||
|  |     local t = socket.try(url.parse(u, default)) | ||||||
|  |     socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") | ||||||
|  |     socket.try(t.host, "missing hostname") | ||||||
|  |     local pat = "^type=(.)$" | ||||||
|  |     if t.params then | ||||||
|  |         t.type = socket.skip(2, string.find(t.params, pat)) | ||||||
|  |         socket.try(t.type == "a" or t.type == "i", | ||||||
|  |             "invalid type '" .. t.type .. "'") | ||||||
|  |     end | ||||||
|  |     return t | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function sput(u, body) | ||||||
|  |     local putt = parse(u) | ||||||
|  |     putt.source = ltn12.source.string(body) | ||||||
|  |     return tput(putt) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | put = socket.protect(function(putt, body) | ||||||
|  |     if base.type(putt) == "string" then return sput(putt, body) | ||||||
|  |     else return tput(putt) end | ||||||
|  | end) | ||||||
|  |  | ||||||
|  | local function tget(gett) | ||||||
|  |     gett = override(gett) | ||||||
|  |     socket.try(gett.host, "missing hostname") | ||||||
|  |     local f = open(gett.host, gett.port, gett.create) | ||||||
|  |     f:greet() | ||||||
|  |     f:login(gett.user, gett.password) | ||||||
|  |     if gett.type then f:type(gett.type) end | ||||||
|  |     f:pasv() | ||||||
|  |     f:receive(gett) | ||||||
|  |     f:quit() | ||||||
|  |     return f:close() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function sget(u) | ||||||
|  |     local gett = parse(u) | ||||||
|  |     local t = {} | ||||||
|  |     gett.sink = ltn12.sink.table(t) | ||||||
|  |     tget(gett) | ||||||
|  |     return table.concat(t) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | command = socket.protect(function(cmdt) | ||||||
|  |     cmdt = override(cmdt) | ||||||
|  |     socket.try(cmdt.host, "missing hostname") | ||||||
|  |     socket.try(cmdt.command, "missing command") | ||||||
|  |     local f = open(cmdt.host, cmdt.port, cmdt.create) | ||||||
|  |     f:greet() | ||||||
|  |     f:login(cmdt.user, cmdt.password) | ||||||
|  |     f.try(f.tp:command(cmdt.command, cmdt.argument)) | ||||||
|  |     if cmdt.check then f.try(f.tp:check(cmdt.check)) end | ||||||
|  |     f:quit() | ||||||
|  |     return f:close() | ||||||
|  | end) | ||||||
|  |  | ||||||
|  | get = socket.protect(function(gett) | ||||||
|  |     if base.type(gett) == "string" then return sget(gett) | ||||||
|  |     else return tget(gett) end | ||||||
|  | end) | ||||||
|  |  | ||||||
							
								
								
									
										350
									
								
								lualib/socket/http.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								lualib/socket/http.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,350 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- HTTP/1.1 client support for the Lua language. | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: http.lua,v 1.70 2007/03/12 04:08:40 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ------------------------------------------------------------------------------- | ||||||
|  | local socket = require("socket") | ||||||
|  | local url = require("socket.url") | ||||||
|  | local ltn12 = require("ltn12") | ||||||
|  | local mime = require("mime") | ||||||
|  | local string = require("string") | ||||||
|  | local base = _G | ||||||
|  | local table = require("table") | ||||||
|  | module("socket.http") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Program constants | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- connection timeout in seconds | ||||||
|  | TIMEOUT = 60 | ||||||
|  | -- default port for document retrieval | ||||||
|  | PORT = 80 | ||||||
|  | -- user agent field sent in request | ||||||
|  | USERAGENT = socket._VERSION | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Reads MIME headers from a connection, unfolding where needed | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local function receiveheaders(sock, headers) | ||||||
|  |     local line, name, value, err | ||||||
|  |     headers = headers or {} | ||||||
|  |     -- get first line | ||||||
|  |     line, err = sock:receive() | ||||||
|  |     if err then return nil, err end | ||||||
|  |     -- headers go until a blank line is found | ||||||
|  |     while line ~= "" do | ||||||
|  |         -- get field-name and value | ||||||
|  |         name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) | ||||||
|  |         if not (name and value) then return nil, "malformed reponse headers" end | ||||||
|  |         name = string.lower(name) | ||||||
|  |         -- get next line (value might be folded) | ||||||
|  |         line, err  = sock:receive() | ||||||
|  |         if err then return nil, err end | ||||||
|  |         -- unfold any folded values | ||||||
|  |         while string.find(line, "^%s") do | ||||||
|  |             value = value .. line | ||||||
|  |             line = sock:receive() | ||||||
|  |             if err then return nil, err end | ||||||
|  |         end | ||||||
|  |         -- save pair in table | ||||||
|  |         if headers[name] then headers[name] = headers[name] .. ", " .. value | ||||||
|  |         else headers[name] = value end | ||||||
|  |     end | ||||||
|  |     return headers | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Extra sources and sinks | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | socket.sourcet["http-chunked"] = function(sock, headers) | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function() | ||||||
|  |             -- get chunk size, skip extention | ||||||
|  |             local line, err = sock:receive() | ||||||
|  |             if err then return nil, err end | ||||||
|  |             local size = base.tonumber(string.gsub(line, ";.*", ""), 16) | ||||||
|  |             if not size then return nil, "invalid chunk size" end | ||||||
|  |             -- was it the last chunk? | ||||||
|  |             if size > 0 then | ||||||
|  |                 -- if not, get chunk and skip terminating CRLF | ||||||
|  |                 local chunk, err, part = sock:receive(size) | ||||||
|  |                 if chunk then sock:receive() end | ||||||
|  |                 return chunk, err | ||||||
|  |             else | ||||||
|  |                 -- if it was, read trailers into headers table | ||||||
|  |                 headers, err = receiveheaders(sock, headers) | ||||||
|  |                 if not headers then return nil, err end | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | socket.sinkt["http-chunked"] = function(sock) | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function(self, chunk, err) | ||||||
|  |             if not chunk then return sock:send("0\r\n\r\n") end | ||||||
|  |             local size = string.format("%X\r\n", string.len(chunk)) | ||||||
|  |             return sock:send(size ..  chunk .. "\r\n") | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Low level HTTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local metat = { __index = {} } | ||||||
|  |  | ||||||
|  | function open(host, port, create) | ||||||
|  |     -- create socket with user connect function, or with default | ||||||
|  |     local c = socket.try((create or socket.tcp)()) | ||||||
|  |     local h = base.setmetatable({ c = c }, metat) | ||||||
|  |     -- create finalized try | ||||||
|  |     h.try = socket.newtry(function() h:close() end) | ||||||
|  |     -- set timeout before connecting | ||||||
|  |     h.try(c:settimeout(TIMEOUT)) | ||||||
|  |     h.try(c:connect(host, port or PORT)) | ||||||
|  |     -- here everything worked | ||||||
|  |     return h | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:sendrequestline(method, uri) | ||||||
|  |     local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) | ||||||
|  |     return self.try(self.c:send(reqline)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:sendheaders(headers) | ||||||
|  |     local h = "\r\n" | ||||||
|  |     for i, v in base.pairs(headers) do | ||||||
|  |         h = i .. ": " .. v .. "\r\n" .. h | ||||||
|  |     end | ||||||
|  |     self.try(self.c:send(h)) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:sendbody(headers, source, step) | ||||||
|  |     source = source or ltn12.source.empty() | ||||||
|  |     step = step or ltn12.pump.step | ||||||
|  |     -- if we don't know the size in advance, send chunked and hope for the best | ||||||
|  |     local mode = "http-chunked" | ||||||
|  |     if headers["content-length"] then mode = "keep-open" end | ||||||
|  |     return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receivestatusline() | ||||||
|  |     local status = self.try(self.c:receive(5)) | ||||||
|  |     -- identify HTTP/0.9 responses, which do not contain a status line | ||||||
|  |     -- this is just a heuristic, but is what the RFC recommends | ||||||
|  |     if status ~= "HTTP/" then return nil, status end | ||||||
|  |     -- otherwise proceed reading a status line | ||||||
|  |     status = self.try(self.c:receive("*l", status)) | ||||||
|  |     local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) | ||||||
|  |     return self.try(base.tonumber(code), status) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receiveheaders() | ||||||
|  |     return self.try(receiveheaders(self.c)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receivebody(headers, sink, step) | ||||||
|  |     sink = sink or ltn12.sink.null() | ||||||
|  |     step = step or ltn12.pump.step | ||||||
|  |     local length = base.tonumber(headers["content-length"]) | ||||||
|  |     local t = headers["transfer-encoding"] -- shortcut | ||||||
|  |     local mode = "default" -- connection close | ||||||
|  |     if t and t ~= "identity" then mode = "http-chunked" | ||||||
|  |     elseif base.tonumber(headers["content-length"]) then mode = "by-length" end | ||||||
|  |     return self.try(ltn12.pump.all(socket.source(mode, self.c, length), | ||||||
|  |         sink, step)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receive09body(status, sink, step) | ||||||
|  |     local source = ltn12.source.rewind(socket.source("until-closed", self.c)) | ||||||
|  |     source(status) | ||||||
|  |     return self.try(ltn12.pump.all(source, sink, step)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:close() | ||||||
|  |     return self.c:close() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- High level HTTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local function adjusturi(reqt) | ||||||
|  |     local u = reqt | ||||||
|  |     -- if there is a proxy, we need the full url. otherwise, just a part. | ||||||
|  |     if not reqt.proxy and not PROXY then | ||||||
|  |         u = { | ||||||
|  |            path = socket.try(reqt.path, "invalid path 'nil'"), | ||||||
|  |            params = reqt.params, | ||||||
|  |            query = reqt.query, | ||||||
|  |            fragment = reqt.fragment | ||||||
|  |         } | ||||||
|  |     end | ||||||
|  |     return url.build(u) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function adjustproxy(reqt) | ||||||
|  |     local proxy = reqt.proxy or PROXY | ||||||
|  |     if proxy then | ||||||
|  |         proxy = url.parse(proxy) | ||||||
|  |         return proxy.host, proxy.port or 3128 | ||||||
|  |     else | ||||||
|  |         return reqt.host, reqt.port | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function adjustheaders(reqt) | ||||||
|  |     -- default headers | ||||||
|  |     local lower = { | ||||||
|  |         ["user-agent"] = USERAGENT, | ||||||
|  |         ["host"] = reqt.host, | ||||||
|  |         ["connection"] = "close, TE", | ||||||
|  |         ["te"] = "trailers" | ||||||
|  |     } | ||||||
|  |     -- if we have authentication information, pass it along | ||||||
|  |     if reqt.user and reqt.password then | ||||||
|  |         lower["authorization"] =  | ||||||
|  |             "Basic " ..  (mime.b64(reqt.user .. ":" .. reqt.password)) | ||||||
|  |     end | ||||||
|  |     -- override with user headers | ||||||
|  |     for i,v in base.pairs(reqt.headers or lower) do | ||||||
|  |         lower[string.lower(i)] = v | ||||||
|  |     end | ||||||
|  |     return lower | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- default url parts | ||||||
|  | local default = { | ||||||
|  |     host = "", | ||||||
|  |     port = PORT, | ||||||
|  |     path ="/", | ||||||
|  |     scheme = "http" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | local function adjustrequest(reqt) | ||||||
|  |     -- parse url if provided | ||||||
|  |     local nreqt = reqt.url and url.parse(reqt.url, default) or {} | ||||||
|  |     -- explicit components override url | ||||||
|  |     for i,v in base.pairs(reqt) do nreqt[i] = v end | ||||||
|  |     if nreqt.port == "" then nreqt.port = 80 end | ||||||
|  |     socket.try(nreqt.host and nreqt.host ~= "",  | ||||||
|  |         "invalid host '" .. base.tostring(nreqt.host) .. "'") | ||||||
|  |     -- compute uri if user hasn't overriden | ||||||
|  |     nreqt.uri = reqt.uri or adjusturi(nreqt) | ||||||
|  |     -- ajust host and port if there is a proxy | ||||||
|  |     nreqt.host, nreqt.port = adjustproxy(nreqt) | ||||||
|  |     -- adjust headers in request | ||||||
|  |     nreqt.headers = adjustheaders(nreqt) | ||||||
|  |     return nreqt | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function shouldredirect(reqt, code, headers) | ||||||
|  |     return headers.location and | ||||||
|  |            string.gsub(headers.location, "%s", "") ~= "" and | ||||||
|  |            (reqt.redirect ~= false) and | ||||||
|  |            (code == 301 or code == 302) and | ||||||
|  |            (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") | ||||||
|  |            and (not reqt.nredirects or reqt.nredirects < 5) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function shouldreceivebody(reqt, code) | ||||||
|  |     if reqt.method == "HEAD" then return nil end | ||||||
|  |     if code == 204 or code == 304 then return nil end | ||||||
|  |     if code >= 100 and code < 200 then return nil end | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- forward declarations | ||||||
|  | local trequest, tredirect | ||||||
|  |  | ||||||
|  | function tredirect(reqt, location) | ||||||
|  |     local result, code, headers, status = trequest { | ||||||
|  |         -- the RFC says the redirect URL has to be absolute, but some | ||||||
|  |         -- servers do not respect that | ||||||
|  |         url = url.absolute(reqt.url, location), | ||||||
|  |         source = reqt.source, | ||||||
|  |         sink = reqt.sink, | ||||||
|  |         headers = reqt.headers, | ||||||
|  |         proxy = reqt.proxy,  | ||||||
|  |         nredirects = (reqt.nredirects or 0) + 1, | ||||||
|  |         create = reqt.create | ||||||
|  |     }    | ||||||
|  |     -- pass location header back as a hint we redirected | ||||||
|  |     headers = headers or {} | ||||||
|  |     headers.location = headers.location or location | ||||||
|  |     return result, code, headers, status | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function trequest(reqt) | ||||||
|  |     -- we loop until we get what we want, or | ||||||
|  |     -- until we are sure there is no way to get it | ||||||
|  |     local nreqt = adjustrequest(reqt) | ||||||
|  |     local h = open(nreqt.host, nreqt.port, nreqt.create) | ||||||
|  |     -- send request line and headers | ||||||
|  |     h:sendrequestline(nreqt.method, nreqt.uri) | ||||||
|  |     h:sendheaders(nreqt.headers) | ||||||
|  |     -- if there is a body, send it | ||||||
|  |     if nreqt.source then | ||||||
|  |         h:sendbody(nreqt.headers, nreqt.source, nreqt.step)  | ||||||
|  |     end | ||||||
|  |     local code, status = h:receivestatusline() | ||||||
|  |     -- if it is an HTTP/0.9 server, simply get the body and we are done | ||||||
|  |     if not code then | ||||||
|  |         h:receive09body(status, nreqt.sink, nreqt.step) | ||||||
|  |         return 1, 200 | ||||||
|  |     end | ||||||
|  |     local headers | ||||||
|  |     -- ignore any 100-continue messages | ||||||
|  |     while code == 100 do  | ||||||
|  |         headers = h:receiveheaders() | ||||||
|  |         code, status = h:receivestatusline() | ||||||
|  |     end | ||||||
|  |     headers = h:receiveheaders() | ||||||
|  |     -- at this point we should have a honest reply from the server | ||||||
|  |     -- we can't redirect if we already used the source, so we report the error  | ||||||
|  |     if shouldredirect(nreqt, code, headers) and not nreqt.source then | ||||||
|  |         h:close() | ||||||
|  |         return tredirect(reqt, headers.location) | ||||||
|  |     end | ||||||
|  |     -- here we are finally done | ||||||
|  |     if shouldreceivebody(nreqt, code) then | ||||||
|  |         h:receivebody(headers, nreqt.sink, nreqt.step) | ||||||
|  |     end | ||||||
|  |     h:close() | ||||||
|  |     return 1, code, headers, status | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function srequest(u, b) | ||||||
|  |     local t = {} | ||||||
|  |     local reqt = { | ||||||
|  |         url = u, | ||||||
|  |         sink = ltn12.sink.table(t) | ||||||
|  |     } | ||||||
|  |     if b then | ||||||
|  |         reqt.source = ltn12.source.string(b) | ||||||
|  |         reqt.headers = { | ||||||
|  |             ["content-length"] = string.len(b), | ||||||
|  |             ["content-type"] = "application/x-www-form-urlencoded" | ||||||
|  |         } | ||||||
|  |         reqt.method = "POST" | ||||||
|  |     end | ||||||
|  |     local code, headers, status = socket.skip(1, trequest(reqt)) | ||||||
|  |     return table.concat(t), code, headers, status | ||||||
|  | end | ||||||
|  |  | ||||||
|  | request = socket.protect(function(reqt, body) | ||||||
|  |     if base.type(reqt) == "string" then return srequest(reqt, body) | ||||||
|  |     else return trequest(reqt) end | ||||||
|  | end) | ||||||
							
								
								
									
										251
									
								
								lualib/socket/smtp.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								lualib/socket/smtp.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- SMTP client support for the Lua language. | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local base = _G | ||||||
|  | local coroutine = require("coroutine") | ||||||
|  | local string = require("string") | ||||||
|  | local math = require("math") | ||||||
|  | local os = require("os") | ||||||
|  | local socket = require("socket") | ||||||
|  | local tp = require("socket.tp") | ||||||
|  | local ltn12 = require("ltn12") | ||||||
|  | local mime = require("mime") | ||||||
|  | module("socket.smtp") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Program constants | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- timeout for connection | ||||||
|  | TIMEOUT = 60 | ||||||
|  | -- default server used to send e-mails | ||||||
|  | SERVER = "localhost" | ||||||
|  | -- default port | ||||||
|  | PORT = 25 | ||||||
|  | -- domain used in HELO command and default sendmail | ||||||
|  | -- If we are under a CGI, try to get from environment | ||||||
|  | DOMAIN = os.getenv("SERVER_NAME") or "localhost" | ||||||
|  | -- default time zone (means we don't know) | ||||||
|  | ZONE = "-0000" | ||||||
|  |  | ||||||
|  | --------------------------------------------------------------------------- | ||||||
|  | -- Low level SMTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local metat = { __index = {} } | ||||||
|  |  | ||||||
|  | function metat.__index:greet(domain) | ||||||
|  |     self.try(self.tp:check("2..")) | ||||||
|  |     self.try(self.tp:command("EHLO", domain or DOMAIN)) | ||||||
|  |     return socket.skip(1, self.try(self.tp:check("2.."))) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:mail(from) | ||||||
|  |     self.try(self.tp:command("MAIL", "FROM:" .. from)) | ||||||
|  |     return self.try(self.tp:check("2..")) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:rcpt(to) | ||||||
|  |     self.try(self.tp:command("RCPT", "TO:" .. to)) | ||||||
|  |     return self.try(self.tp:check("2..")) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:data(src, step) | ||||||
|  |     self.try(self.tp:command("DATA")) | ||||||
|  |     self.try(self.tp:check("3..")) | ||||||
|  |     self.try(self.tp:source(src, step)) | ||||||
|  |     self.try(self.tp:send("\r\n.\r\n")) | ||||||
|  |     return self.try(self.tp:check("2..")) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:quit() | ||||||
|  |     self.try(self.tp:command("QUIT")) | ||||||
|  |     return self.try(self.tp:check("2..")) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:close() | ||||||
|  |     return self.tp:close() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:login(user, password) | ||||||
|  |     self.try(self.tp:command("AUTH", "LOGIN")) | ||||||
|  |     self.try(self.tp:check("3..")) | ||||||
|  |     self.try(self.tp:command(mime.b64(user))) | ||||||
|  |     self.try(self.tp:check("3..")) | ||||||
|  |     self.try(self.tp:command(mime.b64(password))) | ||||||
|  |     return self.try(self.tp:check("2..")) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:plain(user, password) | ||||||
|  |     local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) | ||||||
|  |     self.try(self.tp:command("AUTH", auth)) | ||||||
|  |     return self.try(self.tp:check("2..")) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:auth(user, password, ext) | ||||||
|  |     if not user or not password then return 1 end | ||||||
|  |     if string.find(ext, "AUTH[^\n]+LOGIN") then | ||||||
|  |         return self:login(user, password) | ||||||
|  |     elseif string.find(ext, "AUTH[^\n]+PLAIN") then | ||||||
|  |         return self:plain(user, password) | ||||||
|  |     else | ||||||
|  |         self.try(nil, "authentication not supported") | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- send message or throw an exception | ||||||
|  | function metat.__index:send(mailt) | ||||||
|  |     self:mail(mailt.from) | ||||||
|  |     if base.type(mailt.rcpt) == "table" then | ||||||
|  |         for i,v in base.ipairs(mailt.rcpt) do | ||||||
|  |             self:rcpt(v) | ||||||
|  |         end | ||||||
|  |     else | ||||||
|  |         self:rcpt(mailt.rcpt) | ||||||
|  |     end | ||||||
|  |     self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function open(server, port, create) | ||||||
|  |     local tp = socket.try(tp.connect(server or SERVER, port or PORT, | ||||||
|  |         TIMEOUT, create)) | ||||||
|  |     local s = base.setmetatable({tp = tp}, metat) | ||||||
|  |     -- make sure tp is closed if we get an exception | ||||||
|  |     s.try = socket.newtry(function() | ||||||
|  |         s:close() | ||||||
|  |     end) | ||||||
|  |     return s | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- convert headers to lowercase | ||||||
|  | local function lower_headers(headers) | ||||||
|  |     local lower = {} | ||||||
|  |     for i,v in base.pairs(headers or lower) do | ||||||
|  |         lower[string.lower(i)] = v | ||||||
|  |     end | ||||||
|  |     return lower | ||||||
|  | end | ||||||
|  |  | ||||||
|  | --------------------------------------------------------------------------- | ||||||
|  | -- Multipart message source | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- returns a hopefully unique mime boundary | ||||||
|  | local seqno = 0 | ||||||
|  | local function newboundary() | ||||||
|  |     seqno = seqno + 1 | ||||||
|  |     return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), | ||||||
|  |         math.random(0, 99999), seqno) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- send_message forward declaration | ||||||
|  | local send_message | ||||||
|  |  | ||||||
|  | -- yield the headers all at once, it's faster | ||||||
|  | local function send_headers(headers) | ||||||
|  |     local h = "\r\n" | ||||||
|  |     for i,v in base.pairs(headers) do | ||||||
|  |         h = i .. ': ' .. v .. "\r\n" .. h | ||||||
|  |     end | ||||||
|  |     coroutine.yield(h) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- yield multipart message body from a multipart message table | ||||||
|  | local function send_multipart(mesgt) | ||||||
|  |     -- make sure we have our boundary and send headers | ||||||
|  |     local bd = newboundary() | ||||||
|  |     local headers = lower_headers(mesgt.headers or {}) | ||||||
|  |     headers['content-type'] = headers['content-type'] or 'multipart/mixed' | ||||||
|  |     headers['content-type'] = headers['content-type'] .. | ||||||
|  |         '; boundary="' ..  bd .. '"' | ||||||
|  |     send_headers(headers) | ||||||
|  |     -- send preamble | ||||||
|  |     if mesgt.body.preamble then | ||||||
|  |         coroutine.yield(mesgt.body.preamble) | ||||||
|  |         coroutine.yield("\r\n") | ||||||
|  |     end | ||||||
|  |     -- send each part separated by a boundary | ||||||
|  |     for i, m in base.ipairs(mesgt.body) do | ||||||
|  |         coroutine.yield("\r\n--" .. bd .. "\r\n") | ||||||
|  |         send_message(m) | ||||||
|  |     end | ||||||
|  |     -- send last boundary | ||||||
|  |     coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") | ||||||
|  |     -- send epilogue | ||||||
|  |     if mesgt.body.epilogue then | ||||||
|  |         coroutine.yield(mesgt.body.epilogue) | ||||||
|  |         coroutine.yield("\r\n") | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- yield message body from a source | ||||||
|  | local function send_source(mesgt) | ||||||
|  |     -- make sure we have a content-type | ||||||
|  |     local headers = lower_headers(mesgt.headers or {}) | ||||||
|  |     headers['content-type'] = headers['content-type'] or | ||||||
|  |         'text/plain; charset="iso-8859-1"' | ||||||
|  |     send_headers(headers) | ||||||
|  |     -- send body from source | ||||||
|  |     while true do | ||||||
|  |         local chunk, err = mesgt.body() | ||||||
|  |         if err then coroutine.yield(nil, err) | ||||||
|  |         elseif chunk then coroutine.yield(chunk) | ||||||
|  |         else break end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- yield message body from a string | ||||||
|  | local function send_string(mesgt) | ||||||
|  |     -- make sure we have a content-type | ||||||
|  |     local headers = lower_headers(mesgt.headers or {}) | ||||||
|  |     headers['content-type'] = headers['content-type'] or | ||||||
|  |         'text/plain; charset="iso-8859-1"' | ||||||
|  |     send_headers(headers) | ||||||
|  |     -- send body from string | ||||||
|  |     coroutine.yield(mesgt.body) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- message source | ||||||
|  | function send_message(mesgt) | ||||||
|  |     if base.type(mesgt.body) == "table" then send_multipart(mesgt) | ||||||
|  |     elseif base.type(mesgt.body) == "function" then send_source(mesgt) | ||||||
|  |     else send_string(mesgt) end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- set defaul headers | ||||||
|  | local function adjust_headers(mesgt) | ||||||
|  |     local lower = lower_headers(mesgt.headers) | ||||||
|  |     lower["date"] = lower["date"] or | ||||||
|  |         os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) | ||||||
|  |     lower["x-mailer"] = lower["x-mailer"] or socket._VERSION | ||||||
|  |     -- this can't be overriden | ||||||
|  |     lower["mime-version"] = "1.0" | ||||||
|  |     return lower | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function message(mesgt) | ||||||
|  |     mesgt.headers = adjust_headers(mesgt) | ||||||
|  |     -- create and return message source | ||||||
|  |     local co = coroutine.create(function() send_message(mesgt) end) | ||||||
|  |     return function() | ||||||
|  |         local ret, a, b = coroutine.resume(co) | ||||||
|  |         if ret then return a, b | ||||||
|  |         else return nil, a end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | --------------------------------------------------------------------------- | ||||||
|  | -- High level SMTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | send = socket.protect(function(mailt) | ||||||
|  |     local s = open(mailt.server, mailt.port, mailt.create) | ||||||
|  |     local ext = s:greet(mailt.domain) | ||||||
|  |     s:auth(mailt.user, mailt.password, ext) | ||||||
|  |     s:send(mailt) | ||||||
|  |     s:quit() | ||||||
|  |     return s:close() | ||||||
|  | end) | ||||||
							
								
								
									
										123
									
								
								lualib/socket/tp.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								lualib/socket/tp.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Unified SMTP/FTP subsystem | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local base = _G | ||||||
|  | local string = require("string") | ||||||
|  | local socket = require("socket") | ||||||
|  | local ltn12 = require("ltn12") | ||||||
|  | module("socket.tp") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Program constants | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | TIMEOUT = 60 | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Implementation | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- gets server reply (works for SMTP and FTP) | ||||||
|  | local function get_reply(c) | ||||||
|  |     local code, current, sep | ||||||
|  |     local line, err = c:receive() | ||||||
|  |     local reply = line | ||||||
|  |     if err then return nil, err end | ||||||
|  |     code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) | ||||||
|  |     if not code then return nil, "invalid server reply" end | ||||||
|  |     if sep == "-" then -- reply is multiline | ||||||
|  |         repeat | ||||||
|  |             line, err = c:receive() | ||||||
|  |             if err then return nil, err end | ||||||
|  |             current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) | ||||||
|  |             reply = reply .. "\n" .. line | ||||||
|  |         -- reply ends with same code | ||||||
|  |         until code == current and sep == " " | ||||||
|  |     end | ||||||
|  |     return code, reply | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- metatable for sock object | ||||||
|  | local metat = { __index = {} } | ||||||
|  |  | ||||||
|  | function metat.__index:check(ok) | ||||||
|  |     local code, reply = get_reply(self.c) | ||||||
|  |     if not code then return nil, reply end | ||||||
|  |     if base.type(ok) ~= "function" then | ||||||
|  |         if base.type(ok) == "table" then | ||||||
|  |             for i, v in base.ipairs(ok) do | ||||||
|  |                 if string.find(code, v) then | ||||||
|  |                     return base.tonumber(code), reply | ||||||
|  |                 end | ||||||
|  |             end | ||||||
|  |             return nil, reply | ||||||
|  |         else | ||||||
|  |             if string.find(code, ok) then return base.tonumber(code), reply | ||||||
|  |             else return nil, reply end | ||||||
|  |         end | ||||||
|  |     else return ok(base.tonumber(code), reply) end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:command(cmd, arg) | ||||||
|  |     if arg then | ||||||
|  |         return self.c:send(cmd .. " " .. arg.. "\r\n") | ||||||
|  |     else | ||||||
|  |         return self.c:send(cmd .. "\r\n") | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:sink(snk, pat) | ||||||
|  |     local chunk, err = c:receive(pat) | ||||||
|  |     return snk(chunk, err) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:send(data) | ||||||
|  |     return self.c:send(data) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receive(pat) | ||||||
|  |     return self.c:receive(pat) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:getfd() | ||||||
|  |     return self.c:getfd() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:dirty() | ||||||
|  |     return self.c:dirty() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:getcontrol() | ||||||
|  |     return self.c | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:source(source, step) | ||||||
|  |     local sink = socket.sink("keep-open", self.c) | ||||||
|  |     local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) | ||||||
|  |     return ret, err | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- closes the underlying c | ||||||
|  | function metat.__index:close() | ||||||
|  |     self.c:close() | ||||||
|  | 	return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- connect with server and return c object | ||||||
|  | function connect(host, port, timeout, create) | ||||||
|  |     local c, e = (create or socket.tcp)() | ||||||
|  |     if not c then return nil, e end | ||||||
|  |     c:settimeout(timeout or TIMEOUT) | ||||||
|  |     local r, e = c:connect(host, port) | ||||||
|  |     if not r then | ||||||
|  |         c:close() | ||||||
|  |         return nil, e | ||||||
|  |     end | ||||||
|  |     return base.setmetatable({c = c}, metat) | ||||||
|  | end | ||||||
|  |  | ||||||
							
								
								
									
										297
									
								
								lualib/socket/url.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								lualib/socket/url.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,297 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- URI parsing, composition and relative URL resolution | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local string = require("string") | ||||||
|  | local base = _G | ||||||
|  | local table = require("table") | ||||||
|  | module("socket.url") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Module version | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | _VERSION = "URL 1.0.1" | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Encodes a string into its escaped hexadecimal representation | ||||||
|  | -- Input | ||||||
|  | --   s: binary string to be encoded | ||||||
|  | -- Returns | ||||||
|  | --   escaped representation of string binary | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function escape(s) | ||||||
|  |     return string.gsub(s, "([^A-Za-z0-9_])", function(c) | ||||||
|  |         return string.format("%%%02x", string.byte(c)) | ||||||
|  |     end) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Protects a path segment, to prevent it from interfering with the | ||||||
|  | -- url parsing. | ||||||
|  | -- Input | ||||||
|  | --   s: binary string to be encoded | ||||||
|  | -- Returns | ||||||
|  | --   escaped representation of string binary | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local function make_set(t) | ||||||
|  | 	local s = {} | ||||||
|  | 	for i,v in base.ipairs(t) do | ||||||
|  | 		s[t[i]] = 1 | ||||||
|  | 	end | ||||||
|  | 	return s | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- these are allowed withing a path segment, along with alphanum | ||||||
|  | -- other characters must be escaped | ||||||
|  | local segment_set = make_set { | ||||||
|  |     "-", "_", ".", "!", "~", "*", "'", "(", | ||||||
|  | 	")", ":", "@", "&", "=", "+", "$", ",", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | local function protect_segment(s) | ||||||
|  | 	return string.gsub(s, "([^A-Za-z0-9_])", function (c) | ||||||
|  | 		if segment_set[c] then return c | ||||||
|  | 		else return string.format("%%%02x", string.byte(c)) end | ||||||
|  | 	end) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Encodes a string into its escaped hexadecimal representation | ||||||
|  | -- Input | ||||||
|  | --   s: binary string to be encoded | ||||||
|  | -- Returns | ||||||
|  | --   escaped representation of string binary | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function unescape(s) | ||||||
|  |     return string.gsub(s, "%%(%x%x)", function(hex) | ||||||
|  |         return string.char(base.tonumber(hex, 16)) | ||||||
|  |     end) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Builds a path from a base path and a relative path | ||||||
|  | -- Input | ||||||
|  | --   base_path | ||||||
|  | --   relative_path | ||||||
|  | -- Returns | ||||||
|  | --   corresponding absolute path | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local function absolute_path(base_path, relative_path) | ||||||
|  |     if string.sub(relative_path, 1, 1) == "/" then return relative_path end | ||||||
|  |     local path = string.gsub(base_path, "[^/]*$", "") | ||||||
|  |     path = path .. relative_path | ||||||
|  |     path = string.gsub(path, "([^/]*%./)", function (s) | ||||||
|  |         if s ~= "./" then return s else return "" end | ||||||
|  |     end) | ||||||
|  |     path = string.gsub(path, "/%.$", "/") | ||||||
|  |     local reduced | ||||||
|  |     while reduced ~= path do | ||||||
|  |         reduced = path | ||||||
|  |         path = string.gsub(reduced, "([^/]*/%.%./)", function (s) | ||||||
|  |             if s ~= "../../" then return "" else return s end | ||||||
|  |         end) | ||||||
|  |     end | ||||||
|  |     path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) | ||||||
|  |         if s ~= "../.." then return "" else return s end | ||||||
|  |     end) | ||||||
|  |     return path | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Parses a url and returns a table with all its parts according to RFC 2396 | ||||||
|  | -- The following grammar describes the names given to the URL parts | ||||||
|  | -- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment> | ||||||
|  | -- <authority> ::= <userinfo>@<host>:<port> | ||||||
|  | -- <userinfo> ::= <user>[:<password>] | ||||||
|  | -- <path> :: = {<segment>/}<segment> | ||||||
|  | -- Input | ||||||
|  | --   url: uniform resource locator of request | ||||||
|  | --   default: table with default values for each field | ||||||
|  | -- Returns | ||||||
|  | --   table with the following fields, where RFC naming conventions have | ||||||
|  | --   been preserved: | ||||||
|  | --     scheme, authority, userinfo, user, password, host, port, | ||||||
|  | --     path, params, query, fragment | ||||||
|  | -- Obs: | ||||||
|  | --   the leading '/' in {/<path>} is considered part of <path> | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function parse(url, default) | ||||||
|  |     -- initialize default parameters | ||||||
|  |     local parsed = {} | ||||||
|  |     for i,v in base.pairs(default or parsed) do parsed[i] = v end | ||||||
|  |     -- empty url is parsed to nil | ||||||
|  |     if not url or url == "" then return nil, "invalid url" end | ||||||
|  |     -- remove whitespace | ||||||
|  |     -- url = string.gsub(url, "%s", "") | ||||||
|  |     -- get fragment | ||||||
|  |     url = string.gsub(url, "#(.*)$", function(f) | ||||||
|  |         parsed.fragment = f | ||||||
|  |         return "" | ||||||
|  |     end) | ||||||
|  |     -- get scheme | ||||||
|  |     url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", | ||||||
|  |         function(s) parsed.scheme = s; return "" end) | ||||||
|  |     -- get authority | ||||||
|  |     url = string.gsub(url, "^//([^/]*)", function(n) | ||||||
|  |         parsed.authority = n | ||||||
|  |         return "" | ||||||
|  |     end) | ||||||
|  |     -- get query stringing | ||||||
|  |     url = string.gsub(url, "%?(.*)", function(q) | ||||||
|  |         parsed.query = q | ||||||
|  |         return "" | ||||||
|  |     end) | ||||||
|  |     -- get params | ||||||
|  |     url = string.gsub(url, "%;(.*)", function(p) | ||||||
|  |         parsed.params = p | ||||||
|  |         return "" | ||||||
|  |     end) | ||||||
|  |     -- path is whatever was left | ||||||
|  |     if url ~= "" then parsed.path = url end | ||||||
|  |     local authority = parsed.authority | ||||||
|  |     if not authority then return parsed end | ||||||
|  |     authority = string.gsub(authority,"^([^@]*)@", | ||||||
|  |         function(u) parsed.userinfo = u; return "" end) | ||||||
|  |     authority = string.gsub(authority, ":([^:]*)$", | ||||||
|  |         function(p) parsed.port = p; return "" end) | ||||||
|  |     if authority ~= "" then parsed.host = authority end | ||||||
|  |     local userinfo = parsed.userinfo | ||||||
|  |     if not userinfo then return parsed end | ||||||
|  |     userinfo = string.gsub(userinfo, ":([^:]*)$", | ||||||
|  |         function(p) parsed.password = p; return "" end) | ||||||
|  |     parsed.user = userinfo | ||||||
|  |     return parsed | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Rebuilds a parsed URL from its components. | ||||||
|  | -- Components are protected if any reserved or unallowed characters are found | ||||||
|  | -- Input | ||||||
|  | --   parsed: parsed URL, as returned by parse | ||||||
|  | -- Returns | ||||||
|  | --   a stringing with the corresponding URL | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function build(parsed) | ||||||
|  |     local ppath = parse_path(parsed.path or "") | ||||||
|  |     local url = build_path(ppath) | ||||||
|  |     if parsed.params then url = url .. ";" .. parsed.params end | ||||||
|  |     if parsed.query then url = url .. "?" .. parsed.query end | ||||||
|  | 	local authority = parsed.authority | ||||||
|  | 	if parsed.host then | ||||||
|  | 		authority = parsed.host | ||||||
|  | 		if parsed.port then authority = authority .. ":" .. parsed.port end | ||||||
|  | 		local userinfo = parsed.userinfo | ||||||
|  | 		if parsed.user then | ||||||
|  | 			userinfo = parsed.user | ||||||
|  | 			if parsed.password then | ||||||
|  | 				userinfo = userinfo .. ":" .. parsed.password | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		if userinfo then authority = userinfo .. "@" .. authority end | ||||||
|  | 	end | ||||||
|  |     if authority then url = "//" .. authority .. url end | ||||||
|  |     if parsed.scheme then url = parsed.scheme .. ":" .. url end | ||||||
|  |     if parsed.fragment then url = url .. "#" .. parsed.fragment end | ||||||
|  |     -- url = string.gsub(url, "%s", "") | ||||||
|  |     return url | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Builds a absolute URL from a base and a relative URL according to RFC 2396 | ||||||
|  | -- Input | ||||||
|  | --   base_url | ||||||
|  | --   relative_url | ||||||
|  | -- Returns | ||||||
|  | --   corresponding absolute url | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function absolute(base_url, relative_url) | ||||||
|  |     if base.type(base_url) == "table" then | ||||||
|  |         base_parsed = base_url | ||||||
|  |         base_url = build(base_parsed) | ||||||
|  |     else | ||||||
|  |         base_parsed = parse(base_url) | ||||||
|  |     end | ||||||
|  |     local relative_parsed = parse(relative_url) | ||||||
|  |     if not base_parsed then return relative_url | ||||||
|  |     elseif not relative_parsed then return base_url | ||||||
|  |     elseif relative_parsed.scheme then return relative_url | ||||||
|  |     else | ||||||
|  |         relative_parsed.scheme = base_parsed.scheme | ||||||
|  |         if not relative_parsed.authority then | ||||||
|  |             relative_parsed.authority = base_parsed.authority | ||||||
|  |             if not relative_parsed.path then | ||||||
|  |                 relative_parsed.path = base_parsed.path | ||||||
|  |                 if not relative_parsed.params then | ||||||
|  |                     relative_parsed.params = base_parsed.params | ||||||
|  |                     if not relative_parsed.query then | ||||||
|  |                         relative_parsed.query = base_parsed.query | ||||||
|  |                     end | ||||||
|  |                 end | ||||||
|  |             else     | ||||||
|  |                 relative_parsed.path = absolute_path(base_parsed.path or "", | ||||||
|  |                     relative_parsed.path) | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |         return build(relative_parsed) | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Breaks a path into its segments, unescaping the segments | ||||||
|  | -- Input | ||||||
|  | --   path | ||||||
|  | -- Returns | ||||||
|  | --   segment: a table with one entry per segment | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function parse_path(path) | ||||||
|  | 	local parsed = {} | ||||||
|  | 	path = path or "" | ||||||
|  | 	--path = string.gsub(path, "%s", "") | ||||||
|  | 	string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) | ||||||
|  | 	for i = 1, table.getn(parsed) do | ||||||
|  | 		parsed[i] = unescape(parsed[i]) | ||||||
|  | 	end | ||||||
|  | 	if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end | ||||||
|  | 	if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end | ||||||
|  | 	return parsed | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Builds a path component from its segments, escaping protected characters. | ||||||
|  | -- Input | ||||||
|  | --   parsed: path segments | ||||||
|  | --   unsafe: if true, segments are not protected before path is built | ||||||
|  | -- Returns | ||||||
|  | --   path: corresponding path stringing | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function build_path(parsed, unsafe) | ||||||
|  | 	local path = "" | ||||||
|  | 	local n = table.getn(parsed) | ||||||
|  | 	if unsafe then | ||||||
|  | 		for i = 1, n-1 do | ||||||
|  | 			path = path .. parsed[i] | ||||||
|  | 			path = path .. "/" | ||||||
|  | 		end | ||||||
|  | 		if n > 0 then | ||||||
|  | 			path = path .. parsed[n] | ||||||
|  | 			if parsed.is_directory then path = path .. "/" end | ||||||
|  | 		end | ||||||
|  | 	else | ||||||
|  | 		for i = 1, n-1 do | ||||||
|  | 			path = path .. protect_segment(parsed[i]) | ||||||
|  | 			path = path .. "/" | ||||||
|  | 		end | ||||||
|  | 		if n > 0 then | ||||||
|  | 			path = path .. protect_segment(parsed[n]) | ||||||
|  | 			if parsed.is_directory then path = path .. "/" end | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 	if parsed.is_absolute then path = "/" .. path end | ||||||
|  | 	return path | ||||||
|  | end | ||||||
							
								
								
									
										36
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  |  | ||||||
|  | # BlockLua | ||||||
|  | ## How to Install | ||||||
|  | - Install RedBlocklandLoader | ||||||
|  | - Copy `BlockLua.dll`, `lua5.1.dll`, into the `modules` folder next to `Blockland.exe` | ||||||
|  | - Optional: Copy the `lualib` folder into `modules` | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Quick Reference | ||||||
|  |  | ||||||
|  | ### From TorqueScript | ||||||
|  | `'print('hello world')` - Execute Lua code in the console by prepending a `'` (single quote)   | ||||||
|  | `luaeval("code");` - Eval Lua code from Torque   | ||||||
|  | `luacall("funcName", %args);` - Eval Lua code from Torque   | ||||||
|  | `luaexec("fileName");` - Execute a Lua file from Torque   | ||||||
|  | `luaget("varName");` - Read a Lua global variable | ||||||
|  | `luaset("varName");` - Write a Lua global variable | ||||||
|  |  | ||||||
|  | ### From Lua | ||||||
|  | `bl.funcName(args)` - Call a TorqueScript function   | ||||||
|  | `bl.varName` - Read a TorqueScript global variable   | ||||||
|  | `bl['varName']` - Read a TorqueScript global variable (with special characters in the name, or from an array)   | ||||||
|  | `bl.set('varName', value)` - Write a TorqueScript global variable   | ||||||
|  |  | ||||||
|  | ### Accessing Torque Objects from Lua | ||||||
|  | `bl.objectName` - Access a Torque object by name   | ||||||
|  | `bl[objectID]` - Access a Torque object by ID (or name)   | ||||||
|  | `bl.objectName.field` - Read a field or Lua key from a Torque object   | ||||||
|  | `bl.objectName:set('field', value)` - Write a field on a Torque object   | ||||||
|  | `bl.objectName.key = value` - Associate Lua data with a Torque object   | ||||||
|  | `bl.objectName:method(args)` - Call a Torque object method   | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Unsafe Mode | ||||||
|  | BlockLua-Unsafe.dll can be used in place of BlockLua.dll, to remove the sandboxing of Lua code.   | ||||||
|  | This allows Lua code to access any file and use any library, including ffi.   | ||||||
							
								
								
									
										105
									
								
								src/bllua4.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/bllua4.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | // BlockLua (bllua4): Simple Lua interface for TorqueScript | ||||||
|  |  | ||||||
|  | // Includes | ||||||
|  |  | ||||||
|  | #include <Windows.h> | ||||||
|  | #include <Psapi.h> | ||||||
|  |  | ||||||
|  | #include "lua.hpp" | ||||||
|  | #include "BlHooks.cpp" | ||||||
|  | #include "BlFuncs.cpp" | ||||||
|  |  | ||||||
|  | #include "luainterp.cpp" | ||||||
|  | #include "lualibts.cpp" | ||||||
|  | lua_State* gL; | ||||||
|  | #include "tsliblua.cpp" | ||||||
|  |  | ||||||
|  | // Global variables | ||||||
|  |  | ||||||
|  | // Setup | ||||||
|  |  | ||||||
|  | // Hack to encode the contents of text files as static strings | ||||||
|  | #define INCLUDE_BIN(varname, filename) \ | ||||||
|  | 	asm("_" #varname ": .incbin \"" filename "\""); \ | ||||||
|  | 	asm(".byte 0"); \ | ||||||
|  | 	extern char varname[]; | ||||||
|  | INCLUDE_BIN(bll_fileLuaEnvSafe, "lua-env-safe.lua"); | ||||||
|  | INCLUDE_BIN(bll_fileLuaEnv    , "lua-env.lua"); | ||||||
|  | INCLUDE_BIN(bll_fileTsEnv     , "ts-env.cs" ); | ||||||
|  | INCLUDE_BIN(bll_fileLuaStd        , "util/std.lua"); | ||||||
|  | INCLUDE_BIN(bll_fileLuaVector     , "util/vector.lua"); | ||||||
|  | INCLUDE_BIN(bll_fileLuaLibts      , "util/libts.lua"); | ||||||
|  | INCLUDE_BIN(bll_fileTsLibtsSupport, "util/libts-support.cs"); | ||||||
|  | INCLUDE_BIN(bll_fileLuaLibbl      , "util/libbl.lua" ); | ||||||
|  | INCLUDE_BIN(bll_fileLuaLibblTypes , "util/libbl-types.lua"); | ||||||
|  | INCLUDE_BIN(bll_fileTsLibblSupport, "util/libbl-support.cs"); | ||||||
|  | INCLUDE_BIN(bll_fileLoadaddons    , "util/loadaddons.cs"); | ||||||
|  |  | ||||||
|  | #define BLL_LOAD_LUA(lstate, vname) \ | ||||||
|  | 	if(!bll_LuaEval(lstate, vname)) { \ | ||||||
|  | 		BlPrintf("  Error executing " #vname); \ | ||||||
|  | 		return false; \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | bool init() { | ||||||
|  | 	BlHooksInit(); | ||||||
|  | 	BlPrintf("BlockLua: Loading"); | ||||||
|  | 	 | ||||||
|  | 	BlFuncsInit(); | ||||||
|  | 	 | ||||||
|  | 	// Initialize Lua environment | ||||||
|  | 	gL = lua_open(); | ||||||
|  | 	luaL_openlibs(gL); | ||||||
|  | 	 | ||||||
|  | 	// Expose TS API to Lua | ||||||
|  | 	llibbl_init(gL); | ||||||
|  | 	 | ||||||
|  | 	// Set up Lua environment | ||||||
|  | 	BLL_LOAD_LUA(gL, bll_fileLuaEnv); | ||||||
|  | 	#ifndef BLLUA_UNSAFE | ||||||
|  | 		BLL_LOAD_LUA(gL, bll_fileLuaEnvSafe); | ||||||
|  | 	#endif | ||||||
|  | 	 | ||||||
|  | 	// 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); | ||||||
|  | 	 | ||||||
|  | 	// Load utilities | ||||||
|  | 	BLL_LOAD_LUA(gL, bll_fileLuaStd); | ||||||
|  | 	BLL_LOAD_LUA(gL, bll_fileLuaVector); | ||||||
|  | 	BLL_LOAD_LUA(gL, bll_fileLuaLibts); | ||||||
|  | 	BlEval(bll_fileTsLibtsSupport); | ||||||
|  | 	BLL_LOAD_LUA(gL, bll_fileLuaLibbl); | ||||||
|  | 	BLL_LOAD_LUA(gL, bll_fileLuaLibblTypes); | ||||||
|  | 	BlEval(bll_fileTsLibblSupport); | ||||||
|  | 	BlEval(bll_fileLoadaddons); | ||||||
|  | 	 | ||||||
|  | 	BlEval("$_bllua_active = 1;"); | ||||||
|  | 	 | ||||||
|  | 	BlPrintf("  BlockLua: Done Loading"); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool deinit() { | ||||||
|  | 	BlPrintf("BlockLua: Unloading"); | ||||||
|  | 	 | ||||||
|  | 	BlEval("deactivatePackage(_bllua_main);"); | ||||||
|  | 	BlEval("$_bllua_active = 0;"); | ||||||
|  | 	bll_LuaEval(gL, "for _,f in pairs(_bllua_on_unload) do f() end"); | ||||||
|  | 	 | ||||||
|  | 	lua_close(gL); | ||||||
|  | 	 | ||||||
|  | 	BlHooksDeinit(); | ||||||
|  | 	BlFuncsDeinit(); | ||||||
|  | 	 | ||||||
|  | 	BlPrintf("  BlockLua: Done Unloading"); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool __stdcall DllMain(HINSTANCE hinstance, DWORD reason, void* reserved) { | ||||||
|  | 	switch(reason) { | ||||||
|  | 		case DLL_PROCESS_ATTACH: return init(); | ||||||
|  | 		case DLL_PROCESS_DETACH: return deinit(); | ||||||
|  | 		default                : return true; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										142
									
								
								src/lua-env-safe.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/lua-env-safe.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | -- Sanitize the Lua environment to: | ||||||
|  | --   Prevent scripts from accessing files outside the game directory | ||||||
|  | --   Prevent usage of libraries other than  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- Utility: Convert a list of strings into a map of string->true | ||||||
|  | local function tmap(t) local u = {}; for _, n in ipairs(t) do u[n] = true end; return u; end | ||||||
|  |  | ||||||
|  | -- Save banned global variables for wrapping with safe functions | ||||||
|  | local old_io = io | ||||||
|  | local old_require = require | ||||||
|  | local old_os = os | ||||||
|  | local old_debug = debug | ||||||
|  | local old_package = package | ||||||
|  |  | ||||||
|  | -- Remove all global variables except a whitelist | ||||||
|  | local ok_names = tmap { | ||||||
|  | 	'_G', '_bllua_ts', '_bllua_on_unload', '_bllua_on_error', | ||||||
|  | 	'string', 'table', 'math', 'coroutine', 'bit', | ||||||
|  | 	'pairs', 'ipairs', 'next', 'unpack', 'select', | ||||||
|  | 	'error', 'assert', 'pcall', 'xpcall', | ||||||
|  | 	'type', 'tostring', 'tonumber', | ||||||
|  | 	'loadstring', | ||||||
|  | 	'getmetatable', 'setmetatable', | ||||||
|  | 	'rawget', 'rawset', 'rawequal', 'rawlen', | ||||||
|  | 	'module', '_VERSION', | ||||||
|  | } | ||||||
|  | local not_ok_names = {} | ||||||
|  | for n, _ in pairs(_G) do | ||||||
|  | 	if not ok_names[n] then | ||||||
|  | 		table.insert(not_ok_names, n) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | for _, n in ipairs(not_ok_names) do | ||||||
|  | 	_G[n] = nil | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Sanitize file paths to point only to allowed files within the game directory | ||||||
|  | -- List of allowed directories for reading/writing | ||||||
|  | local allowed_dirs = tmap { | ||||||
|  | 	'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 | ||||||
|  | -- Note that even without this protection, exploiting would still require somehow | ||||||
|  | --   getting a file within the allowed directories to autorun, | ||||||
|  | --   so this is just a precaution. | ||||||
|  | local disallowed_exts = tmap { | ||||||
|  | 	-- windows | ||||||
|  | 	'bat','bin','cab','cmd','com','cpl','ex_','exe','gadget','inf','ins','inx','isu', | ||||||
|  | 	'job','jse','lnk','msc','msi','msp','mst','paf','pif','ps1','reg','rgs','scr', | ||||||
|  | 	'sct','shb','shs','u3p','vb','vbe','vbs','vbscript','ws','wsf','wsh', | ||||||
|  | 	-- linux | ||||||
|  | 	'csh','ksh','out','run','sh', | ||||||
|  | 	-- mac/other | ||||||
|  | 	'action','apk','app','command','ipa','osx','prg','workflow', | ||||||
|  | } | ||||||
|  | -- Arguments: file name (relative to game directory), boolean true if only reading | ||||||
|  | -- Return: clean file path if allowed (or nil if disallowed), | ||||||
|  | --         error string (or nil if allowed) | ||||||
|  | local function safe_path(fn, readonly) | ||||||
|  | 	fn = fn:gsub('\\', '/') | ||||||
|  | 	fn = fn:gsub('^ +', '') | ||||||
|  | 	fn = fn:gsub(' +$', '') | ||||||
|  | 	-- whitelist characters | ||||||
|  | 	local ic = fn:find('[^a-zA-Z0-9_%-/ %.]') | ||||||
|  | 	if ic then | ||||||
|  | 		return nil, 'Filename \''..fn..'\' contains invalid character \''.. | ||||||
|  | 			fn:sub(ic, ic)..'\' at position '..ic | ||||||
|  | 	end | ||||||
|  | 	-- disallow up-dirs, absolute paths, and relative paths | ||||||
|  | 	-- './' and '../' are possible in scripts, because they're processed into | ||||||
|  | 	-- absolute paths in util.lua before reaching here | ||||||
|  | 	if fn:find('^%.') or fn:find('%.%.') or fn:find(':') or fn:find('^/') then | ||||||
|  | 		return nil, 'Filename \''..fn..'\' contains invalid sequence' | ||||||
|  | 	end | ||||||
|  | 	-- allow only whitelisted dirs | ||||||
|  | 	local dir = fn:match('^([^/]+)/') | ||||||
|  | 	if (not dir) or ( | ||||||
|  | 		(not allowed_dirs[dir:lower()]) and | ||||||
|  | 		((not readonly) or (not allowed_dirs_readonly[dir:lower()])) ) then | ||||||
|  | 		return nil, 'filename is in disallowed directory '..(dir or 'nil') | ||||||
|  | 	end | ||||||
|  | 	-- disallow blacklisted extensions or no extension | ||||||
|  | 	local ext = fn:match('%.([^/%.]+)$') | ||||||
|  | 	if (not ext) or (disallowed_exts[ext:lower()]) then | ||||||
|  | 		return nil, 'Filename \''..fn..'\' has disallowed extension \''.. | ||||||
|  | 			(ext or '')..'\'' | ||||||
|  | 	end | ||||||
|  | 	return fn, nil | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Wrap io.open with path sanitization | ||||||
|  | function _bllua_io_open(fn, md) | ||||||
|  | 	md = md or 'r' | ||||||
|  | 	local readonly = md=='r' or md=='rb' | ||||||
|  | 	local fns, err = safe_path(fn, readonly) | ||||||
|  | 	if fns then | ||||||
|  | 		return old_io.open(fns, md) | ||||||
|  | 	else | ||||||
|  | 		return nil, err | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | -- Allow io.type (works on file handles returned by io.open) | ||||||
|  | function _bllua_io_type(f) | ||||||
|  | 	return old_io.type(f) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Wrap require with a blacklist for unsafe built-in modules | ||||||
|  | -- List of allowed packages to require | ||||||
|  | -- Note that util.lua wraps this and provides 'require', | ||||||
|  | --   only falling back here if the package is not found in user files | ||||||
|  | local disallowed_packages = tmap { | ||||||
|  | 	'ffi', 'debug', 'package', 'io', 'os', | ||||||
|  | 	'_bllua_ts', | ||||||
|  | } | ||||||
|  | function _bllua_requiresecure(name) | ||||||
|  | 	if name:find('[^a-zA-Z0-9_%-%.]') or name:find('%.%.') or | ||||||
|  | 		name:find('^%.') or name:find('%.$') then | ||||||
|  | 		error('require: package name contains invalid character', 3) | ||||||
|  | 	elseif disallowed_packages[name] then | ||||||
|  | 		error('require: attempt to require disallowed module \''..name..'\'', 3) | ||||||
|  | 	else | ||||||
|  | 		-- todo: reimplement require to not use package.* stuff? | ||||||
|  | 		return old_require(name) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | package = { | ||||||
|  | 	seeall = old_package.seeall, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | -- Provide limited debug | ||||||
|  | debug = { | ||||||
|  | 	traceback = old_debug.traceback, | ||||||
|  | 	getinfo = old_debug.getinfo, | ||||||
|  | 	getfilename = old_debug.getfilename, -- defined in lua.env.lua | ||||||
|  | } | ||||||
|  |  | ||||||
|  | _bllua_ts.echo('  Executed bllua-env-safe.lua') | ||||||
							
								
								
									
										40
									
								
								src/lua-env.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/lua-env.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | -- Set up the Lua environment | ||||||
|  |  | ||||||
|  | -- Point old require to modules/lua in game directory | ||||||
|  | package.path = 'modules\\lualib\\?.lua;modules\\lualib\\?\\init.lua;' | ||||||
|  | package.cpath = 'modules\\lualib\\?.dll;' | ||||||
|  |  | ||||||
|  | -- Set up table of unload/cleanup callbacks | ||||||
|  | _bllua_on_unload = {} | ||||||
|  |  | ||||||
|  | -- Utility for getting the current filename | ||||||
|  | function debug.getfilename(level) | ||||||
|  | 	if type(level) == 'number' then level = level+1 end | ||||||
|  | 	local info = debug.getinfo(level) | ||||||
|  | 	if not info then return nil end | ||||||
|  | 	local filename = info.source:match('^%-%-%[%[([^%]]+)%]%]') | ||||||
|  | 	return filename | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Called when pcall fails on a ts->lua call, used to print detailed error info | ||||||
|  | function _bllua_on_error(err) | ||||||
|  | 	err = err:match(': (.+)$') or err | ||||||
|  | 	local tracelines = {err} | ||||||
|  | 	local level = 2 | ||||||
|  | 	while true do | ||||||
|  | 		local info = debug.getinfo(level) | ||||||
|  | 		if not info then break end | ||||||
|  | 		local filename = debug.getfilename(level) or info.short_src | ||||||
|  | 		local funcname = info.name | ||||||
|  | 		if funcname=='dofile' then break end | ||||||
|  | 		table.insert(tracelines, string.format('%s:%s in function \'%s\'', | ||||||
|  | 			filename, | ||||||
|  | 			info.currentline==-1 and '' or info.currentline..':', | ||||||
|  | 			funcname | ||||||
|  | 		)) | ||||||
|  | 		level = level+1 | ||||||
|  | 	end | ||||||
|  | 	return table.concat(tracelines, '\n') | ||||||
|  | end | ||||||
|  |  | ||||||
|  | _bllua_ts.echo('  Executed bllua-env.lua') | ||||||
							
								
								
									
										68
									
								
								src/luainterp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/luainterp.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  |  | ||||||
|  | // Handle errors with a Lua function, defined in lua-env.lua | ||||||
|  | int bll_error_handler(lua_State *L) { | ||||||
|  | 	lua_getfield(L, LUA_GLOBALSINDEX, "_bllua_on_error"); | ||||||
|  | 	if (!lua_isfunction(L, -1)) { | ||||||
|  | 		BlPrintf("  Lua error handler: _bllua_on_error not defined."); | ||||||
|  | 		lua_pop(L, 1); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// move function to before arg | ||||||
|  | 	int hpos = lua_gettop(L) - 1; | ||||||
|  | 	lua_insert(L, hpos); | ||||||
|  | 	 | ||||||
|  | 	lua_pcall(L, 1, 1, 0); | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | int bll_pcall(lua_State* L, int nargs, int nret) { | ||||||
|  | 	// calculate stack position for message handler | ||||||
|  | 	int hpos = lua_gettop(L) - nargs; | ||||||
|  | 	// push custom error message handler | ||||||
|  | 	lua_pushcfunction(L, bll_error_handler); | ||||||
|  | 	// move it before function and arguments | ||||||
|  | 	lua_insert(L, hpos); | ||||||
|  | 	// call lua_pcall function with custom handler | ||||||
|  | 	int ret = lua_pcall(L, nargs, nret, hpos); | ||||||
|  | 	// remove custom error message handler from stack | ||||||
|  | 	lua_remove(L, hpos); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Display the last Lua error in the BL console | ||||||
|  | void bll_printError(lua_State* L, const char* operation, const char* item) { | ||||||
|  | 	//error_handler(L); | ||||||
|  | 	BlPrintf("\x03Lua error: %s", lua_tostring(L, -1)); | ||||||
|  | 	BlPrintf("\x03  (%s: %s)", operation, item); | ||||||
|  | 	lua_pop(L, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Eval a string of Lua code | ||||||
|  | bool bll_LuaEval(lua_State* L, const char* str) { | ||||||
|  | 	if(luaL_loadbuffer(L, str, strlen(str), "input") || bll_pcall(L, 0, 1)) { | ||||||
|  | 		bll_printError(L, "eval", str); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Convert a Lua stack entry into a string for providing to TS | ||||||
|  | // 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]; | ||||||
|  | bool bll_toarg(lua_State* L, char* buf, int i, bool err) { | ||||||
|  | 	if(lua_isstring(L, i)) { | ||||||
|  | 		const char* str = lua_tostring(L, i); | ||||||
|  | 		if(strlen(str) >= BLL_ARG_MAX) { | ||||||
|  | 			if(err) luaL_error(L, "argument to TS is too long - max length is 8192"); | ||||||
|  | 			return true; | ||||||
|  | 		} else { | ||||||
|  | 			strcpy(buf, str); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if(err) luaL_error(L, "argument to TS must be a string"); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										159
									
								
								src/lualibts.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								src/lualibts.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | |||||||
|  | //run ../compile | ||||||
|  |  | ||||||
|  | // TS -> Lua API | ||||||
|  |  | ||||||
|  | // Call a TS function from Lua, push the result to Lua stack | ||||||
|  | int bll_TsCall(lua_State* L, const char* oname, const char* fname, int argc, int ofs) { | ||||||
|  | 	ADDR obj = (ADDR)NULL; | ||||||
|  | 	if(oname) { | ||||||
|  | 		obj = BlObject(oname); | ||||||
|  | 		if(!obj) { return luaL_error(L, "Lua->TS call: Object not found"); } | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if(argc > BLL_ARG_COUNT) { return luaL_error(L, "Lua->TS call: Too many arguments (Max is 19)"); } | ||||||
|  | 	 | ||||||
|  | 	char* argv[BLL_ARG_COUNT]; | ||||||
|  | 	for(int i=0; i<argc; i++) { | ||||||
|  | 		char* argbuf = bll_arg_buffer[i]; | ||||||
|  | 		argv[i] = argbuf; | ||||||
|  | 		bll_toarg(L, argbuf, i+ofs+1, true); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// /:^| / | ||||||
|  | 	const char* res; | ||||||
|  | 	if(obj) { | ||||||
|  | 		switch(argc) { | ||||||
|  | 			case  0: res = BlCallObj(obj, fname); break; // no idea why this happens sometimes, it shouldnt be possible | ||||||
|  | 			case  1: res = BlCallObj(obj, fname); break; | ||||||
|  | 			case  2: res = BlCallObj(obj, fname, argv[0]); break; | ||||||
|  | 			case  3: res = BlCallObj(obj, fname, argv[0], argv[1]); break; | ||||||
|  | 			case  4: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2]); break; | ||||||
|  | 			case  5: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3]); break; | ||||||
|  | 			case  6: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4]); break; | ||||||
|  | 			case  7: 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 { | ||||||
|  | 		switch(argc) { | ||||||
|  | 			case  0: res = BlCall(fname); break; | ||||||
|  | 			case  1: res = BlCall(fname, argv[0]); break; | ||||||
|  | 			case  2: res = BlCall(fname, argv[0], argv[1]); break; | ||||||
|  | 			case  3: res = BlCall(fname, argv[0], argv[1], argv[2]); break; | ||||||
|  | 			case  4: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3]); break; | ||||||
|  | 			case  5: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4]); break; | ||||||
|  | 			case  6: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); break; | ||||||
|  | 			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)"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	lua_pushstring(L, res); | ||||||
|  | 	 | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | // Lua lib function: ts.call | ||||||
|  | int bll_lua_tscall(lua_State* L) { | ||||||
|  | 	int argc = lua_gettop(L)-1; // number of arguments after function name | ||||||
|  | 	if(argc < 0) return luaL_error(L, "_bllua_ts.call: Must provide a function name"); | ||||||
|  | 	 | ||||||
|  | 	const char* fname = luaL_checkstring(L, 1); | ||||||
|  | 	 | ||||||
|  | 	return bll_TsCall(L, NULL, fname, argc, 1); | ||||||
|  | } | ||||||
|  | // Lua lib function: ts.callobj | ||||||
|  | int bll_lua_tscallobj(lua_State* L) { | ||||||
|  | 	int argc = 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"); | ||||||
|  | 	 | ||||||
|  | 	const char* oname = luaL_checkstring(L, 1); | ||||||
|  | 	const char* fname = luaL_checkstring(L, 2); | ||||||
|  | 	 | ||||||
|  | 	return bll_TsCall(L, oname, fname, argc, 2); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Lua lib function: ts.getvar | ||||||
|  | int bll_lua_tsgetvar(lua_State* L) { | ||||||
|  | 	const char* vname = luaL_checkstring(L, 1); | ||||||
|  | 	 | ||||||
|  | 	const char* var = BlGetVar(vname); | ||||||
|  | 	lua_pushstring(L, var); | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Lua lib function: ts.getfield | ||||||
|  | int bll_lua_tsgetfield(lua_State* L) { | ||||||
|  | 	const char* oname = luaL_checkstring(L, 1); | ||||||
|  | 	const char* vname = luaL_checkstring(L, 2); | ||||||
|  | 	ADDR obj = BlObject(oname); | ||||||
|  | 	if(!obj) { return luaL_error(L, "_bllua_ts.getfield: Object not found"); } | ||||||
|  | 	 | ||||||
|  | 	const char* val = BlGetField(obj, vname, NULL); | ||||||
|  | 	lua_pushstring(L, val); | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | // Lua lib function: ts.setfield | ||||||
|  | int bll_lua_tssetfield(lua_State* L) { | ||||||
|  | 	const char* oname = luaL_checkstring(L, 1); | ||||||
|  | 	const char* vname = luaL_checkstring(L, 2); | ||||||
|  | 	const char* val   = luaL_checkstring(L, 3); | ||||||
|  | 	ADDR obj = BlObject(oname); | ||||||
|  | 	if(!obj) { return luaL_error(L, "_bllua_ts.setfield: Object not found"); } | ||||||
|  | 	 | ||||||
|  | 	BlSetField(obj, vname, NULL, val); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Lua lib function: ts.eval | ||||||
|  | int bll_lua_tseval(lua_State* L) { | ||||||
|  | 	const char* str = luaL_checkstring(L, 1); | ||||||
|  | 	const char* res = BlEval(str); | ||||||
|  | 	lua_pushstring(L, res); | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Lua lib function: ts.echo | ||||||
|  | // Print to BL console - used in Lua print implementation | ||||||
|  | int bll_lua_tsecho(lua_State* L) { | ||||||
|  | 	const char* str = luaL_checkstring(L, 1); | ||||||
|  | 	BlPrintf("%s", str); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const luaL_Reg bll_lua_reg[] = { | ||||||
|  | 	{"call"    , bll_lua_tscall    }, | ||||||
|  | 	{"callobj" , bll_lua_tscallobj }, | ||||||
|  | 	{"getvar"  , bll_lua_tsgetvar  }, | ||||||
|  | 	{"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); | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								src/ts-env.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/ts-env.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | // Built-in functions | ||||||
|  | // Eval'd after BLLua4 has loaded the Lua environment and API | ||||||
|  |  | ||||||
|  | // Public Lua library for TS | ||||||
|  | function luacall(%func, %a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p) { | ||||||
|  | 	if($_bllua_active) | ||||||
|  | 		return _bllua_luacall(%func, %a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p); | ||||||
|  | } | ||||||
|  | function luaexec(%fn) { | ||||||
|  | 	if($_bllua_active) | ||||||
|  | 		return _bllua_luacall("_bllua_exec", %fn); | ||||||
|  | } | ||||||
|  | function luaeval(%code) { | ||||||
|  | 	if($_bllua_active) | ||||||
|  | 		return _bllua_luacall("_bllua_eval", %code); | ||||||
|  | } | ||||||
|  | function luaget(%name) { | ||||||
|  | 	if($_bllua_active) | ||||||
|  | 		return _bllua_luacall("_bllua_getvar", %name); | ||||||
|  | } | ||||||
|  | function luaset(%name, %val) { | ||||||
|  | 	if($_bllua_active) | ||||||
|  | 		_bllua_luacall("_bllua_setvar", %name, %val); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | echo("  Executed bllua-env.cs"); | ||||||
							
								
								
									
										25
									
								
								src/tsliblua.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/tsliblua.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  |  | ||||||
|  | // Call a Lua function from TS, return true if success - result will be on Lua stack | ||||||
|  | bool bll_LuaCall(const char* fname, int argc, const char* argv[]) { | ||||||
|  | 	lua_getglobal(gL, fname); | ||||||
|  | 	for(int i=0; i<argc; i++) { | ||||||
|  | 		lua_pushstring(gL, argv[i]); | ||||||
|  | 	} | ||||||
|  | 	if(bll_pcall(gL, argc, 1)) { | ||||||
|  | 		bll_printError(gL, "call", fname); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TS lib function: luacall | ||||||
|  | const char* bll_ts_luacall(ADDR obj, int argc, const char* argv[]) { | ||||||
|  | 	if(argc<2) return ""; | ||||||
|  | 	 | ||||||
|  | 	if(!bll_LuaCall(argv[1], argc-2, &argv[2])) { return ""; } | ||||||
|  | 	 | ||||||
|  | 	char* retbuf = BlReturnBuffer(BLL_ARG_MAX); | ||||||
|  | 	bll_toarg(gL, retbuf, -1, false); // provide returned value to ts | ||||||
|  | 	lua_pop(gL, 1); // pop returned value | ||||||
|  | 	return retbuf; | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								src/util/libbl-support.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/util/libbl-support.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  |  | ||||||
|  | // Private - Utilities used by libbl from the Lua side | ||||||
|  |  | ||||||
|  | package _bllua_smartEval { | ||||||
|  | 	// Allow prepending ' to console commands to eval in lua instead of TS | ||||||
|  | 	// Also wraps TS lines with echo(...); if they don't end in ; or } | ||||||
|  | 	function ConsoleEntry::eval() { | ||||||
|  | 		%text = ConsoleEntry.getValue(); | ||||||
|  | 		if(getSubStr(%text, 0, 1)$="\'") { // lua eval | ||||||
|  | 			if($_bllua_active) { | ||||||
|  | 				%text = getSubStr(%text, 1, strLen(%text)); | ||||||
|  | 				echo("Lua ==> " @ %text); | ||||||
|  | 				luacall("_bllua_smarteval", %text); | ||||||
|  | 			} else { | ||||||
|  | 				echo("Lua: not loaded"); | ||||||
|  | 			} | ||||||
|  | 			ConsoleEntry.setValue(""); | ||||||
|  | 		} else { | ||||||
|  | 			%textT = trim(%text); | ||||||
|  | 			if(strLen(%textT)>0) { | ||||||
|  | 				%lastChar = getSubStr(%textT, strLen(%textT)-1, 1); | ||||||
|  | 				if(%lastChar!$=";" && %lastChar!$="}") { | ||||||
|  | 					%text = "echo(" @ %text @ ");"; | ||||||
|  | 					ConsoleEntry.setValue(%text); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			parent::eval(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | activatePackage(_bllua_smartEval); | ||||||
|  |  | ||||||
|  | package _bllua_objectDeletionHook { | ||||||
|  | 	// Hook object deletion to clean up its lua data | ||||||
|  | 	function SimObject::onRemove(%obj) { | ||||||
|  | 		// note: no parent function exists by default, | ||||||
|  | 		// and this is loaded before any addons | ||||||
|  | 		//parent::onRemove(%obj); | ||||||
|  | 		if($_bllua_active) luacall("_bllua_objectDeleted", %obj); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | activatePackage(_bllua_objectDeletionHook); | ||||||
|  |  | ||||||
|  | echo("  Executed libbl-support.cs"); | ||||||
							
								
								
									
										3706
									
								
								src/util/libbl-types.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3706
									
								
								src/util/libbl-types.lua
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										840
									
								
								src/util/libbl.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										840
									
								
								src/util/libbl.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,840 @@ | |||||||
|  | -- bl library | ||||||
|  | -- Main lua-side functionality of bllua, | ||||||
|  | -- provided through the global table 'bl.' | ||||||
|  |  | ||||||
|  | -- todo: set | ||||||
|  |  | ||||||
|  | local _bllua_ts = ts | ||||||
|  |  | ||||||
|  | bl = bl or {} | ||||||
|  |  | ||||||
|  | -- Misc | ||||||
|  | -- Apply a function to each element in a list, building a new list from the returns | ||||||
|  | local function map(t,f) | ||||||
|  | 	local u = {} | ||||||
|  | 	for i,v in ipairs(t) do | ||||||
|  | 		u[i] = f(v) | ||||||
|  | 	end | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | local function isValidFuncName(name) | ||||||
|  | 	return type(name)=='string' and name:find('^[a-zA-Z0-9_]+$') | ||||||
|  | end | ||||||
|  | local function isValidFuncNameNs(name) | ||||||
|  | 	return type(name)=='string' and ( | ||||||
|  | 		name:find('^[a-zA-Z0-9_]+$') or | ||||||
|  | 		name:find('^[a-zA-Z0-9_]+::[a-zA-Z0-9_]+$') ) | ||||||
|  | end | ||||||
|  | local function isValidFuncNameNsArgn(name) | ||||||
|  | 	return type(name)=='string' and ( | ||||||
|  | 		name:find('^[a-zA-Z0-9_]+$') or | ||||||
|  | 		name:find('^[a-zA-Z0-9_]+%.[a-zA-Z0-9_]+$') or | ||||||
|  | 		name:find('^[a-zA-Z0-9_]+::[a-zA-Z0-9_]+$') or | ||||||
|  | 		name:find('^[a-zA-Z0-9_]+:[0-9]+$') or | ||||||
|  | 		name:find('^[a-zA-Z0-9_]+::[a-zA-Z0-9_]+:[0-9]+$') ) | ||||||
|  | end | ||||||
|  | -- Whether a var can be converted into a TS vector | ||||||
|  | local function isTsVector(val) | ||||||
|  | 	if type(val)~='table' then return false end | ||||||
|  | 	if #val~=3 and #val~=2 then return false end | ||||||
|  | 	if val.__is_vector then return true end | ||||||
|  | 	for _,v in ipairs(val) do | ||||||
|  | 		if type(v)~='number' then return false end | ||||||
|  | 	end | ||||||
|  | 	return true | ||||||
|  | end | ||||||
|  | -- Use strings for object types instead of integer bitmasks like in TS | ||||||
|  | local tsTypesByName = { | ||||||
|  | 	['all']            =        -1, | ||||||
|  | 	['static']         =         1, | ||||||
|  | 	['environment']    =         2, | ||||||
|  | 	['terrain']        =         4, | ||||||
|  | 	['water']          =        16, | ||||||
|  | 	['trigger']        =        32, | ||||||
|  | 	['marker']         =        64, | ||||||
|  | 	['gamebase']       =      1024, | ||||||
|  | 	['shapebase']      =      2048, | ||||||
|  | 	['camera']         =      4096, | ||||||
|  | 	['staticshape']    =      8192, | ||||||
|  | 	['player']         =     16384, | ||||||
|  | 	['item']           =     32768, | ||||||
|  | 	['vehicle']        =     65536, | ||||||
|  | 	['vehicleblocker'] =    131072, | ||||||
|  | 	['projectile']     =    262144, | ||||||
|  | 	['explosion']      =    524288, | ||||||
|  | 	['corpse']         =   1048576, | ||||||
|  | 	['debris']         =   4194304, | ||||||
|  | 	['physicalzone']   =   8388608, | ||||||
|  | 	['staticts']       =  16777216, | ||||||
|  | 	['brick']          =  33554432, | ||||||
|  | 	['brickalways']    =  67108864, | ||||||
|  | 	['staticrendered'] = 134217728, | ||||||
|  | 	['damagableitem']  = 268435456, | ||||||
|  | } | ||||||
|  | local tsTypesByNum = {} | ||||||
|  | for k,v in pairs(tsTypesByName) do | ||||||
|  | 	tsTypesByNum[v] = k | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Type conversion | ||||||
|  | local toTsObject | ||||||
|  | -- Convert a string from TS into a boolean | ||||||
|  | -- Note: Nonempty nonnumeric strings evaluate to 1, unlike in TS | ||||||
|  | local function tsBool(v) return v~='' and v~='0' end | ||||||
|  | -- Convert a Lua var into a TS string, or error if not possible | ||||||
|  | local function valToTs(val) | ||||||
|  | 	if val==nil then                 -- nil -> '' | ||||||
|  | 		return '' | ||||||
|  | 	elseif type(val)=='boolean' then -- bool -> 0 or 1 | ||||||
|  | 		return val and '1' or '0' | ||||||
|  | 	elseif type(val)=='number' then  -- number | ||||||
|  | 		return tostring(val) | ||||||
|  | 	elseif type(val)=='string' then  -- string | ||||||
|  | 		return val | ||||||
|  | 	elseif type(val)=='table' then | ||||||
|  | 		if val._tsObjectId then      -- object -> object id | ||||||
|  | 			return tostring(val._tsObjectId) | ||||||
|  | 		elseif isTsVector(val) then  -- vector - > 3 numbers | ||||||
|  | 			return table.concat(val, ' ') | ||||||
|  | 		elseif #val==2 and isTsVector(val[1]) and isTsVector(val[2]) then | ||||||
|  | 			-- box - > 6 numbers | ||||||
|  | 			return table.concat(val[1], ' ')..' '..table.concat(val[2], ' ') | ||||||
|  | 		else | ||||||
|  | 			error('valToTs: could not convert table', 3) | ||||||
|  | 		end | ||||||
|  | 	else | ||||||
|  | 		error('valToTs: could not convert '..type(val), 3) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | bl._forceType = bl._forceType or {} | ||||||
|  | local function valFromTs(val, name) | ||||||
|  | 	if type(val)~='string' then | ||||||
|  | 		error('valFromTs: expected string, got '..type(val), 3) end | ||||||
|  | 	if name then | ||||||
|  | 		local nameL = name:lower() | ||||||
|  | 		if bl._forceType[nameL] then | ||||||
|  | 			local typ = bl._forceType[nameL] | ||||||
|  | 			if typ=='boolean' then | ||||||
|  | 				return tsBool(val) | ||||||
|  | 			elseif typ=='object' then | ||||||
|  | 				return toTsObject(val) | ||||||
|  | 			else | ||||||
|  | 				error('valFromTs: invalid force type '..typ, 3) | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 	-- '' -> nil | ||||||
|  | 	if val=='' then return nil end | ||||||
|  | 	-- number | ||||||
|  | 	local num = tonumber(val) | ||||||
|  | 	if num then return num end | ||||||
|  | 	-- vector | ||||||
|  | 	local xS,yS,zS = val:match('^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') | ||||||
|  | 	if xS then return vector{tonumber(xS),tonumber(yS),tonumber(zS)} end | ||||||
|  | 	local x1S,y1S,z1S,x2S,y2S,z2S = val:match( | ||||||
|  | 		'^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+) '.. | ||||||
|  | 		'(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') | ||||||
|  | 	-- box (2 vectors) | ||||||
|  | 	if x1S then return { | ||||||
|  | 		vector{tonumber(x1S),tonumber(y1S),tonumber(z1S)}, | ||||||
|  | 		vector{tonumber(x2S),tonumber(y2S),tonumber(z2S)} } end | ||||||
|  | 	-- string | ||||||
|  | 	return val | ||||||
|  | end | ||||||
|  | local function arglistFromTs(name, argsS) | ||||||
|  | 	local args = {} | ||||||
|  | 	for i,arg in ipairs(argsS) do | ||||||
|  | 		args[i] = valFromTs(arg, name..':'..i) | ||||||
|  | 	end | ||||||
|  | 	return args | ||||||
|  | end | ||||||
|  | local function arglistToTs(args) | ||||||
|  | 	return map(args, valToTs) | ||||||
|  | end | ||||||
|  | function bl.type(name,typ) | ||||||
|  | 	if typ~='bool' and typ~='boolean' and typ~='object' and typ~=nil then | ||||||
|  | 		error('bl.type: can only set type to \'bool\' or \'object\' or nil', 2) end | ||||||
|  | 	if not isValidFuncNameNsArgn(name) then | ||||||
|  | 		error('bl.type: invalid function or variable name \''..name..'\'', 2) end | ||||||
|  | 	if typ=='bool' then typ='boolean' end | ||||||
|  | 	bl._forceType[name:lower()] = typ | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- Value detection | ||||||
|  |  | ||||||
|  | local function isTsObject(t) | ||||||
|  | 	return type(t)=='table' and t._tsObjectId~=nil | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function tsIsObject(name) return tsBool(_bllua_ts.call('isObject', name)) end | ||||||
|  | local function tsIsFunction(name) return tsBool(_bllua_ts.call('isFunction', name)) end | ||||||
|  | local function tsIsFunctionNs(ns, name) return tsBool(_bllua_ts.call('isFunction', ns, name)) end | ||||||
|  | local function tsIsFunctionNsname(nsname) | ||||||
|  | 	local ns, name = nsname:match('^([^:]+)::([^:]+)$') | ||||||
|  | 	if ns then return tsIsFunctionNs(ns, name) | ||||||
|  | 	else return tsIsFunction(nsname) end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function bl.isObject(obj) | ||||||
|  | 	if isTsObject(obj) then | ||||||
|  | 		obj = obj._tsObjectId | ||||||
|  | 	elseif type(obj)=='number' then | ||||||
|  | 		obj = tostring(obj) | ||||||
|  | 	elseif type(obj)~='string' then | ||||||
|  | 		error('bl.isObject: argument #1: expected torque object, number, or string', 2) | ||||||
|  | 	end | ||||||
|  | 	return tsIsObject(obj) | ||||||
|  | end | ||||||
|  | function bl.isFunction(a1, a2) | ||||||
|  | 	if type(a1)~='string' then | ||||||
|  | 		error('bl.isFunction: argument #1: expected string', 2) end | ||||||
|  | 	if a2 then | ||||||
|  | 		if type(a2)~='string' then | ||||||
|  | 			error('bl.isFunction: argument #2: expected string', 2) end | ||||||
|  | 		return tsIsFunctionNs(a1, a2) | ||||||
|  | 	else | ||||||
|  | 		return tsIsFunction(a1) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Torque object pseudo-class | ||||||
|  | bl._objectUserMetas = bl._objectUserMetas or {} | ||||||
|  | function bl.class(name, inherit) | ||||||
|  | 	if not ( type(name)=='string' and isValidFuncName(name) ) then | ||||||
|  | 		error('bl.class: argument #1: invalid class name', 2) end | ||||||
|  | 	if not ( inherit==nil or (type(inherit)=='string' and isValidFuncName(inherit)) ) then | ||||||
|  | 		error('bl.class: argument #2: inherit name must be a string or nil', 2) end | ||||||
|  | 	name = name:lower() | ||||||
|  | 	 | ||||||
|  | 	local met = bl._objectUserMetas[name] or {} | ||||||
|  | 	bl._objectUserMetas[name] = met | ||||||
|  | 	met._name = name | ||||||
|  | 	 | ||||||
|  | 	if inherit then | ||||||
|  | 		inherit = inherit:lower() | ||||||
|  | 		 | ||||||
|  | 		local inh = bl._objectUserMetas[inherit] | ||||||
|  | 		if not inh then error('bl.class: argument #2: \''..inherit..'\' is not the '.. | ||||||
|  | 			'name of an existing class', 2) end | ||||||
|  | 		 | ||||||
|  | 		local inhI = bl._objectUserMetas[name]._inherit | ||||||
|  | 		if inhI and inhI~=inh then | ||||||
|  | 			error('bl.class: argument #2: class already exists and '.. | ||||||
|  | 				'inherits a different parent.', 2) end | ||||||
|  | 		 | ||||||
|  | 		bl._objectUserMetas[name]._inherit = inh | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local function objectInheritedMetas(name) | ||||||
|  | 	local inh = bl._objectUserMetas[name:lower()] | ||||||
|  | 	return function() | ||||||
|  | 		local inhP = inh | ||||||
|  | 		if inhP==nil then return nil end | ||||||
|  | 		inh = inh._inherit | ||||||
|  | 		return inhP | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local tsObjectMeta = { | ||||||
|  | 	-- __index: Called when accessing fields that don't exist in the object itself | ||||||
|  | 	-- Return torque member function or value | ||||||
|  | 	__index = function(t, name) | ||||||
|  | 		if rawget(t,'_deleted') then | ||||||
|  | 			error('ts object index: object no longer exists', 2) end | ||||||
|  | 		if type(name)~='string' and type(name)~='number' then | ||||||
|  | 			error('ts object index: index must be a string or number', 2) end | ||||||
|  | 		if getmetatable(t)[name] then | ||||||
|  | 			return getmetatable(t)[name] | ||||||
|  | 		elseif type(name)=='number' then | ||||||
|  | 			if not tsIsFunctionNs(rawget(t,'_tsNamespace'), 'getObject') then | ||||||
|  | 				error('ts object __index: index is number, but object does not have '.. | ||||||
|  | 					'getObject method', 2) end | ||||||
|  | 			return toTsObject(_bllua_ts.callobj(t._tsObjectId, 'getObject', | ||||||
|  | 				tostring(name))) | ||||||
|  | 		else | ||||||
|  | 			for inh in objectInheritedMetas(rawget(t,'_tsClassName')) do | ||||||
|  | 				if inh[name] then return inh[name] end | ||||||
|  | 			end | ||||||
|  | 			if tsIsFunctionNs(rawget(t,'_tsNamespace'), name) then | ||||||
|  | 				return function(t, ...) | ||||||
|  | 					local args = {...} | ||||||
|  | 					local argsS = arglistToTs(args) | ||||||
|  | 					return valFromTs(_bllua_ts.callobj(rawget(t,'_tsObjectId'), name, unpack(argsS)), | ||||||
|  | 						rawget(t,'_tsNamespace')..'::'..name) | ||||||
|  | 				end | ||||||
|  | 			else | ||||||
|  | 				return valFromTs(_bllua_ts.getfield(rawget(t,'_tsObjectId'), name), | ||||||
|  | 					rawget(t,'_tsNamespace')..'.'..name) | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | 	-- __newindex: Called when setting fields on the object | ||||||
|  | 	-- Set lua data | ||||||
|  | 	-- Use :set() to set Torque data | ||||||
|  | 	__newindex = function(t, name, val) | ||||||
|  | 		if rawget(t,'_deleted') then | ||||||
|  | 			error('ts object newindex: object no longer exists', 2) end | ||||||
|  | 		if type(name)~='string' then | ||||||
|  | 			error('ts object newindex: index must be a string', 2) end | ||||||
|  | 		rawset(t, name, val) | ||||||
|  | 		-- create strong reference since it's now storing lua data | ||||||
|  | 		bl._objectsStrong[rawget(t,'_tsObjectId')] = t | ||||||
|  | 	end, | ||||||
|  | 	-- object:set(fieldName, value) | ||||||
|  | 	-- Use to set torque data | ||||||
|  | 	set = function(t, name, val) | ||||||
|  | 		if t==nil or type(t)~='table' or not t._tsObjectId then | ||||||
|  | 			error('ts object method: be sure to use :func() not .func()', 2) end | ||||||
|  | 		if t._deleted then | ||||||
|  | 			error('ts object method: object no longer exists', 2) end | ||||||
|  | 		if type(name)~='string' then | ||||||
|  | 			error('ts object :set(): index must be a string', 2) end | ||||||
|  | 		_bllua_ts.setfield(t._tsObjectId, name, valToTs(val)) | ||||||
|  | 	end, | ||||||
|  | 	-- __tostring: Called when printing | ||||||
|  | 	-- Display a nice info string | ||||||
|  | 	__tostring = function(t) | ||||||
|  | 		return 'torque:'..t._tsNamespace..':'..t._tsObjectId.. | ||||||
|  | 			(t._tsName~='' and ('('..t._tsName..')') or '') | ||||||
|  | 	end, | ||||||
|  | 	-- #object | ||||||
|  | 	-- If the object has a getCount method, return its count | ||||||
|  | 	__len = function(t) | ||||||
|  | 		if t._deleted then | ||||||
|  | 			error('ts object __len: object no longer exists', 2) end | ||||||
|  | 		if not tsIsFunctionNs(t._tsNamespace, 'getCount') then | ||||||
|  | 			error('ts object __len: object has no getCount method', 2) end | ||||||
|  | 		return tonumber(_bllua_ts.callobj(t._tsObjectId, 'getCount')) | ||||||
|  | 	end, | ||||||
|  | 	-- object:iterate() | ||||||
|  | 	-- Return an iterator for Torque objects with the getCount and getObject methods | ||||||
|  | 	-- for index, object in group:iterate() do ... end | ||||||
|  | 	iterate = function(t) | ||||||
|  | 		if t==nil then | ||||||
|  | 			error('ts object method: be sure to use :func() not .func()', 2) end | ||||||
|  | 		if t._deleted then | ||||||
|  | 			error('ts object method: object no longer exists', 2) end | ||||||
|  | 		if not ( | ||||||
|  | 			tsIsFunctionNs(t._tsNamespace, 'getCount' ) and | ||||||
|  | 			tsIsFunctionNs(t._tsNamespace, 'getObject')) then | ||||||
|  | 			error('ts object :iterate() - '.. | ||||||
|  | 				'Object does not have getCount and getObject methods', 2) end | ||||||
|  | 		local count = tonumber(_bllua_ts.callobj(t._tsObjectId, 'getCount')) | ||||||
|  | 		local idx = 0 | ||||||
|  | 		return function() | ||||||
|  | 			if idx < count then | ||||||
|  | 				local obj = toTsObject(_bllua_ts.callobj(t._tsObjectId, | ||||||
|  | 					'getObject', tostring(idx))) | ||||||
|  | 				idx = idx+1 | ||||||
|  | 				return idx-1, obj | ||||||
|  | 			else | ||||||
|  | 				return nil | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | 	-- Wrap some Torque functions for performance and error checking | ||||||
|  | 	getName = function(t) | ||||||
|  | 		if t==nil then | ||||||
|  | 			error('ts object method: be sure to use :func() not .func()', 2) end | ||||||
|  | 		if t._deleted then | ||||||
|  | 			error('ts object method: object no longer exists', 2) end | ||||||
|  | 		return t._tsName | ||||||
|  | 	end, | ||||||
|  | 	getId = function(t) | ||||||
|  | 		if t==nil then | ||||||
|  | 			error('ts object method: be sure to use :func() not .func()', 2) end | ||||||
|  | 		if t._deleted then | ||||||
|  | 			error('ts object method: object no longer exists', 2) end | ||||||
|  | 		return tonumber(t._tsObjectId) | ||||||
|  | 	end, | ||||||
|  | 	getType = function(t) | ||||||
|  | 		if t==nil then | ||||||
|  | 			error('ts object method: be sure to use :func() not .func()', 2) end | ||||||
|  | 		if t._deleted then | ||||||
|  | 			error('ts object method: object no longer exists', 2) end | ||||||
|  | 		return tsTypesByNum[_bllua_ts.callobj(t._tsObjectId, 'getType')] | ||||||
|  | 	end, | ||||||
|  | 	---- Schedule method for objects | ||||||
|  | 	--schedule = function(t, time, cb, ...) | ||||||
|  | 	--	if type(t)~='table' or not t._tsObjectId then | ||||||
|  | 	--		error('ts object method: be sure to use :func() not .func()', 2) end | ||||||
|  | 	--	if t._deleted then | ||||||
|  | 	--		error('ts object method: object no longer exists', 2) end | ||||||
|  | 	--	if type(time)~='number' then | ||||||
|  | 	--		error('ts object schedule: argument #2: time must be a number', 2) end | ||||||
|  | 	--	if type(cb)~='function' then | ||||||
|  | 	--		error('ts object schedule: argument #3: callback must be a function', 2) end | ||||||
|  | 	--	local args = {...} | ||||||
|  | 	--	bl.schedule(time, function() | ||||||
|  | 	--		if tsBool(__bllua_ts.call('isObject', t._tsObjectId)) then | ||||||
|  | 	--			pcall(cb, unpack(args)) | ||||||
|  | 	--		end | ||||||
|  | 	--	end) | ||||||
|  | 	--end, | ||||||
|  | 	exists = function(t) | ||||||
|  | 		if t==nil then | ||||||
|  | 			error('ts object method: be sure to use :func() not .func()', 2) end | ||||||
|  | 		if t._deleted then | ||||||
|  | 			return false end | ||||||
|  | 		return tsIsObject(t._tsObjectId) | ||||||
|  | 	end, | ||||||
|  | } | ||||||
|  | -- Weak-values table for caching Torque object references | ||||||
|  | -- Objects in this table can be garbage collected if there are no other refs to them | ||||||
|  | if not bl._objectsWeak then | ||||||
|  | 	bl._objectsWeak = {} | ||||||
|  | 	setmetatable(bl._objectsWeak, { __mode='v' }) | ||||||
|  | end | ||||||
|  | -- Strong table for preserving Torque object references containing lua data | ||||||
|  | -- If an object in this table, it will remain here and in the Weak table until deleted | ||||||
|  | if not bl._objectsStrong then | ||||||
|  | 	bl._objectsStrong = {} | ||||||
|  | end | ||||||
|  | -- Hook object deletion to clean up its lua data | ||||||
|  | -- idS is expected to be the object ID number, NOT the object name | ||||||
|  | function _bllua_objectDeleted(idS) | ||||||
|  | 	local obj = bl._objectsWeak[idS] | ||||||
|  | 	if obj then | ||||||
|  | 		obj._deleted = true | ||||||
|  | 		bl._objectsStrong[idS] = nil | ||||||
|  | 		bl._objectsWeak[idS] = nil | ||||||
|  | 		bl._objectsWeak[obj._tsName:lower()] = nil | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Return a Torque object for the object ID string, or create one if none exists | ||||||
|  | toTsObject = function(idiS) | ||||||
|  | 	if type(idiS)~='string' then | ||||||
|  | 		error('toTsObject: input must be a string', 2) end | ||||||
|  | 	idiS = idiS:lower() | ||||||
|  | 	if bl._objectsWeak[idiS] then return bl._objectsWeak[idiS] end | ||||||
|  | 	 | ||||||
|  | 	if not tsBool(_bllua_ts.call('isObject', idiS)) then | ||||||
|  | 		--error('toTsObject: object \''..idiS..'\' does not exist', 2) end | ||||||
|  | 		return nil end | ||||||
|  | 	 | ||||||
|  | 	local className = _bllua_ts.callobj(idiS, 'getClassName') | ||||||
|  | 	local obj = { | ||||||
|  | 		_tsObjectId  = _bllua_ts.callobj(idiS, 'getId'       ), | ||||||
|  | 		_tsName      = _bllua_ts.callobj(idiS, 'getName'     ), | ||||||
|  | 		_tsNamespace = className, | ||||||
|  | 		_tsClassName = className:lower() | ||||||
|  | 	} | ||||||
|  | 	setmetatable(obj, tsObjectMeta) | ||||||
|  | 	 | ||||||
|  | 	bl._objectsWeak[obj._tsObjectId    ] = obj | ||||||
|  | 	bl._objectsWeak[obj._tsName:lower()] = obj | ||||||
|  | 	return obj | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Metatable for the global bl library | ||||||
|  | -- Allows accessing Torque objects, variables, and functions by indexing it | ||||||
|  | local tsMeta = { | ||||||
|  | 	-- __index: Called when accessing fields that don't exist in the table itself | ||||||
|  | 	-- Allow indexing by object id: bl[1234] | ||||||
|  | 	-- by object name: bl.mainMenuGui | ||||||
|  | 	-- by function name: bl.quit() | ||||||
|  | 	-- by variable name: bl.iAmAdmin | ||||||
|  | 	__index = function(t, name) | ||||||
|  | 		if getmetatable(t)[name] then | ||||||
|  | 			return getmetatable(t)[name] | ||||||
|  | 		elseif bl._objectUserMetas[name:lower()] then | ||||||
|  | 			return bl._objectUserMetas[name:lower()] | ||||||
|  | 		else | ||||||
|  | 			if type(name)=='number' then | ||||||
|  | 				return toTsObject(tostring(name)) | ||||||
|  | 			elseif name:find('::') then | ||||||
|  | 				local ns, rest = name:match('^([^:]+)::(.+)$') | ||||||
|  | 				if not ns then error('ts index: invalid name \''..name..'\'', 2) end | ||||||
|  | 				if not rest:find('::') and tsIsFunction(ns, rest) then | ||||||
|  | 					error('ts index: can\'t call a namespaced function from lua', 2) | ||||||
|  | 				else | ||||||
|  | 					return valFromTs(_bllua_ts.getvar(name), name) | ||||||
|  | 				end | ||||||
|  | 			elseif tsIsFunction(name) then | ||||||
|  | 				return function(...) | ||||||
|  | 					local args = {...} | ||||||
|  | 					local argsS = arglistToTs(args) | ||||||
|  | 					return valFromTs(_bllua_ts.call(name, unpack(argsS)), name) | ||||||
|  | 				end | ||||||
|  | 			elseif tsIsObject(name) then | ||||||
|  | 				return toTsObject(name) | ||||||
|  | 			else | ||||||
|  | 				return valFromTs(_bllua_ts.getvar(name), name) | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | -- bl.set(name, value) | ||||||
|  | -- Used to set global variables | ||||||
|  | function bl.set(t, name, val) | ||||||
|  | 	_bllua_ts.call('_bllua_set_var', name, valToTs(val)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Utility functions | ||||||
|  | function bl.call(func, ...) | ||||||
|  | 	local args = {...} | ||||||
|  | 	local argsS = arglistToTs(args) | ||||||
|  | 	return _bllua_ts.call(func, unpack(argsS)) | ||||||
|  | end | ||||||
|  | function bl.eval(code) | ||||||
|  | 	return valFromTs(_bllua_ts.call('eval', code)) | ||||||
|  | end | ||||||
|  | function bl.exec(file) | ||||||
|  | 	return valFromTs(_bllua_ts.call('exec', file)) | ||||||
|  | end | ||||||
|  | function bl.tobool(val) | ||||||
|  | 	return val~=nil and | ||||||
|  | 		val~=false and | ||||||
|  | 		--val~='' and | ||||||
|  | 		--val~='0' and | ||||||
|  | 		val~=0 | ||||||
|  | end | ||||||
|  | function bl.toobject(id) | ||||||
|  | 	if type(id)=='table' and id._tsObjectId then | ||||||
|  | 		return id | ||||||
|  | 	elseif type(id)=='string' or type(id)=='number' then | ||||||
|  | 		return toTsObject(tostring(id)) | ||||||
|  | 	else | ||||||
|  | 		error('bl.toobject: id must be a ts object, number, or string', 2) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | function bl.array(name, ...) | ||||||
|  | 	local rest = {...} | ||||||
|  | 	return name..table.concat(rest, '_') | ||||||
|  | end | ||||||
|  | function _bllua_call(name, ...) | ||||||
|  | 	-- todo: call ts->lua using this instead of directly | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- bl.schedule: Use TS's schedule function to schedule lua calls | ||||||
|  | -- bl.schedule(time, function, args...) | ||||||
|  | bl._scheduleTable  = bl._scheduleTable  or {} | ||||||
|  | bl._scheduleNextId = bl._scheduleNextId or 1 | ||||||
|  | local function cancelTsSched(sched) | ||||||
|  | 	if not (sched and sched.handle) then | ||||||
|  | 		error('schedule:cancel() - invalid object', 2) | ||||||
|  | 	end | ||||||
|  | 	_bllua_ts.call('cancel', sched.handle) | ||||||
|  | 	bl._scheduleTable[id] = nil | ||||||
|  | end | ||||||
|  | function bl.schedule(time, cb, ...) | ||||||
|  | 	local id = bl._scheduleNextId | ||||||
|  | 	bl._scheduleNextId = bl._scheduleNextId+1 | ||||||
|  | 	local args = {...} | ||||||
|  | 	local handle = tonumber(_bllua_ts.call('schedule', | ||||||
|  | 		time, 0, 'luacall', '_bllua_schedule_callback', id)) | ||||||
|  | 	local sch = { | ||||||
|  | 		callback = cb, | ||||||
|  | 		args = args, | ||||||
|  | 		handle = handle, | ||||||
|  | 		cancel = cancelTsSched, | ||||||
|  | 	} | ||||||
|  | 	bl._scheduleTable[id] = sch | ||||||
|  | 	return sch | ||||||
|  | end | ||||||
|  | function _bllua_schedule_callback(id) | ||||||
|  | 	id = tonumber(id) | ||||||
|  | 	local sch = bl._scheduleTable[id] | ||||||
|  | 	if not sch then error('_ts_schedule_callback: no schedule with id '..id) end | ||||||
|  | 	bl._scheduleTable[sched_id] = nil | ||||||
|  | 	sch.callback(unpack(sch.args)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- serverCmd and clientCmd | ||||||
|  | -- bl.serverCmd('suicide', function(client) client.player:kill() end) | ||||||
|  | bl._cmds = bl._cmds or {} | ||||||
|  | function _bllua_process_cmd(cmdS, clientS, ...) | ||||||
|  | 	local client = toTsObject(clientS) | ||||||
|  | 	local argsS = {...} | ||||||
|  | 	local args = arglistFromTs(cmdS, argsS) | ||||||
|  | 	local func = bl._cmds[cmdS] | ||||||
|  | 	if not func then error('_bllua_process_cmd: no cmd named \''..cmd..'\'') end | ||||||
|  | 	pcall(func, client, unpack(args)) | ||||||
|  | end | ||||||
|  | local function addCmd(cmd, func) | ||||||
|  | 	if not isValidFuncName(cmd) then | ||||||
|  | 		error('addCmd: invalid function name \''..tostring(cmd)..'\'') end | ||||||
|  | 	bl._servercmds[cmd] = func | ||||||
|  | 	local arglist = '%a,%b,%c,%d,%e,%f,%g,%h' | ||||||
|  | 	_bllua_ts.eval('function '..cmd..'(%cl,'..arglist..'){'.. | ||||||
|  | 		'luacall(_bllua_process_cmd,"'..cmd..'",%cl,'..arglist..');}') | ||||||
|  | end | ||||||
|  | function bl.serverCmd(name, func) addCmd('serverCmd'..name, func) end | ||||||
|  | function bl.clientCmd(name, func) addCmd('clientCmd'..name, func) end | ||||||
|  |  | ||||||
|  | -- Hooks (using TS packages) | ||||||
|  | local function isPackageActive(pkg) | ||||||
|  | 	local numpkgs = tonumber(_bllua_ts.call('getNumActivePackages')) | ||||||
|  | 	for i = 0, numpkgs-1 do | ||||||
|  | 		local apkg = _bllua_ts.call('getActivePackage', tostring(i)) | ||||||
|  | 		if apkg==pkg then return true end | ||||||
|  | 	end | ||||||
|  | 	return false | ||||||
|  | end | ||||||
|  | local function activatePackage(pkg) | ||||||
|  | 	if not isPackageActive(pkg) then | ||||||
|  | 		_bllua_ts.call('activatePackage', pkg) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local function deactivatePackage(pkg) | ||||||
|  | 	if isPackageActive(pkg) then | ||||||
|  | 		_bllua_ts.call('deactivatePackage', pkg) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | bl._hooks = bl._hooks or {} | ||||||
|  | function _bllua_process_hook(pkgS, nameS, timeS, ...) | ||||||
|  | 	local argsS = {...} | ||||||
|  | 	local args = arglistFromTs(nameS, argsS) | ||||||
|  | 	 | ||||||
|  | 	local func = bl._hooks[pkgS] and bl._hooks[pkgS][nameS] and | ||||||
|  | 		bl._hooks[pkgS][nameS][timeS] | ||||||
|  | 	if not func then | ||||||
|  | 		error('_bllua_process_hook: no hook for '..pkgS..':'..nameS..':'..timeS) end | ||||||
|  | 	 | ||||||
|  | 	pcall(func, args) | ||||||
|  | end | ||||||
|  | local function updateHook(pkg, name, hk) | ||||||
|  | 	local arglist = '%a,%b,%c,%d,%e,%f,%g,%h' | ||||||
|  | 	local beforeCode = hk.before and | ||||||
|  | 		('luacall("_bllua_process_hook", "'..pkg..'", "'..name.. | ||||||
|  | 			'", "before", '..arglist..');') or '' | ||||||
|  | 	local parentCode = hk.override and | ||||||
|  | 		('luacall("_bllua_process_hook", "'..pkg..'", "'..name.. | ||||||
|  | 			'", "override", '..arglist..');') or | ||||||
|  | 		(tsIsFunctionNsname(name) and | ||||||
|  | 			('parent::'..name:match('[^:]+$')..'('..arglist..');') or '') | ||||||
|  | 	local afterCode = hk.after and | ||||||
|  | 		('luacall("_bllua_process_hook", "'..pkg..'", "'..name.. | ||||||
|  | 			'", "after", '..arglist..');') or '' | ||||||
|  | 	bl.eval('package '..pkg..'{function '..name..'('..arglist..'){'.. | ||||||
|  | 		beforeCode..parentCode..afterCode..'}};') | ||||||
|  | end | ||||||
|  | function bl.hook(pkg, name, time, func) | ||||||
|  | 	if not isValidFuncName(pkg) then | ||||||
|  | 		error('bl.hook: argument #1: invalid package name \''..tostring(pkg)..'\'', 2) end | ||||||
|  | 	if not isValidFuncNameNs(name) then | ||||||
|  | 		error('bl.hook: argument #2: invalid function name \''..tostring(name)..'\'', 2) end | ||||||
|  | 	if time~='before' and time~='after' and time~='override' then | ||||||
|  | 		error('bl.hook: argument #3: time must be one of '.. | ||||||
|  | 			'\'before\' \'after\' \'override\'', 2) end | ||||||
|  | 	if type(func)~='function' then | ||||||
|  | 		error('bl.hook: argument #4: expected a function', 2) end | ||||||
|  | 	 | ||||||
|  | 	bl._hooks[pkg]             = bl._hooks[pkg]       or {} | ||||||
|  | 	bl._hooks[pkg][name]       = bl._hooks[pkg][name] or {} | ||||||
|  | 	bl._hooks[pkg][name][time] = func | ||||||
|  | 	 | ||||||
|  | 	updateHook(pkg, name, bl._hooks[pkg][name]) | ||||||
|  | 	activatePackage(pkg) | ||||||
|  | end | ||||||
|  | function bl.unhook(pkg, name, time) | ||||||
|  | 	if not isValidFuncName(pkg) then | ||||||
|  | 		error('bl.unhook: argument #1: invalid package name \''..tostring(pkg)..'\'', 2) end | ||||||
|  | 	if not isValidFuncNameNs(name) then | ||||||
|  | 		error('bl.unhook: argument #2: invalid function name \''..tostring(name)..'\'', 2) end | ||||||
|  | 	if time~='before' and time~='after' and time~='override' then | ||||||
|  | 		error('bl.unhook: argument #3: time must be one of '.. | ||||||
|  | 			'\'before\' \'after\' \'override\'', 2) end | ||||||
|  | 	 | ||||||
|  | 	if not name then | ||||||
|  | 		if bl._hooks[pkg] then | ||||||
|  | 			for name,hk in pairs(bl._hooks[pkg]) do | ||||||
|  | 				updateHook(pkg, name, {}) | ||||||
|  | 			end | ||||||
|  | 			bl._hooks[pkg] = nil | ||||||
|  | 		else | ||||||
|  | 			--error('bl.unhook: no hooks registered under package name \''.. | ||||||
|  | 			--	pkg..'\'', 2) | ||||||
|  | 		end | ||||||
|  | 		deactivatePackage(pkg) | ||||||
|  | 	else | ||||||
|  | 		if bl._hooks[pkg][name] then | ||||||
|  | 			if not time then | ||||||
|  | 				bl._hooks[pkg][name] = nil | ||||||
|  | 				if table.isempty(bl._hooks[pkg]) then | ||||||
|  | 					bl._hooks[pkg] = nil | ||||||
|  | 					deactivatePackage(pkg) | ||||||
|  | 				end | ||||||
|  | 				updateHook(pkg, name, {}) | ||||||
|  | 			else | ||||||
|  | 				if time~='before' and time~='after' and time~='override' then | ||||||
|  | 					error('bl.unhook: argument #3: time must be nil or one of '.. | ||||||
|  | 						'\'before\' \'after\' \'override\'', 2) end | ||||||
|  | 				bl._hooks[pkg][name][time] = nil | ||||||
|  | 				if table.isempty(bl._hooks[pkg][name]) and table.empty(bl._hooks[pkg]) then | ||||||
|  | 					bl._hooks[pkg] = nil | ||||||
|  | 					deactivatePackage(pkg) | ||||||
|  | 				end | ||||||
|  | 				updateHook(pkg, name, bl._hooks[pkg][name]) | ||||||
|  | 			end | ||||||
|  | 		else | ||||||
|  | 			--error('bl.unhook: no hooks registered for function \''..name.. | ||||||
|  | 			--	'\' under package name \''..pkg..'\'', 2) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Container search/raycast | ||||||
|  | local function vecToTs(v) | ||||||
|  | 	if not isTsVector(v) then | ||||||
|  | 		error('vecToTs: argument is not a vector', 3) end | ||||||
|  | 	return table.concat(v, ' ') | ||||||
|  | end | ||||||
|  | local function maskToTs(mask) | ||||||
|  | 	if type(mask)=='string' then | ||||||
|  | 		local val = tsTypesByName[mask:lower()] | ||||||
|  | 		if not val then | ||||||
|  | 			error('maskToTs: invalid mask \''..mask..'\'', 3) end | ||||||
|  | 		return tostring(val) | ||||||
|  | 	elseif type(mask)=='table' then | ||||||
|  | 		local tval = 0 | ||||||
|  | 		local seen = {} | ||||||
|  | 		for i,v in ipairs(mask) do | ||||||
|  | 			if not seen[v] then | ||||||
|  | 				local val = tsTypesByName[v:lower()] | ||||||
|  | 				if not val then | ||||||
|  | 					error('maskToTs: invalid mask \''..v.. | ||||||
|  | 						'\' at index '..i..' in mask list', 3) end | ||||||
|  | 				tval = tval + val | ||||||
|  | 				seen[v] = true | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		return tostring(tval) | ||||||
|  | 	else | ||||||
|  | 		error('maskToTs: mask must be a string or table', 3) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local function objToTs(obj) | ||||||
|  | 	if type(obj)=='number' or type(obj)=='string' then | ||||||
|  | 		return tostring(obj) | ||||||
|  | 	elseif type(obj)=='table' and obj._tsObjectId then | ||||||
|  | 		return tostring(obj._tsObjectId) | ||||||
|  | 	else | ||||||
|  | 		error('objToTs: invalid object \''..tostring(obj)..'\'', 3) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | function bl.raycast(start, stop, mask, ignores) | ||||||
|  | 	local startS = vecToTs(start) | ||||||
|  | 	local stopS = vecToTs(start) | ||||||
|  | 	local maskS = maskToTs(mask) | ||||||
|  | 	local ignoresS = {} | ||||||
|  | 	for _,v in ipairs(ignores) do | ||||||
|  | 		table.insert(ignoresS, objToTs(v)) | ||||||
|  | 	end | ||||||
|  | 	 | ||||||
|  | 	local retS = _bllua_ts.call('containerRaycast', startS, stopS, maskS, unpack(ignoresS)) | ||||||
|  | 	 | ||||||
|  | 	if retS=='0' then | ||||||
|  | 		return nil | ||||||
|  | 	else | ||||||
|  | 		local hitS, pxS,pyS,pzS, nxS,nyS,nzS = retS:match('^([0-9]+) '.. | ||||||
|  | 			'(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+) '.. | ||||||
|  | 			'(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') | ||||||
|  | 		local hit = toTsObject(hitS) | ||||||
|  | 		local pos = vector{tonumber(pxS),tonumber(pyS),tonumber(pzS)} | ||||||
|  | 		local norm = vector{tonumber(nxS),tonumber(nyS),tonumber(nzS)} | ||||||
|  | 		return hit, pos, norm | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local function tsContainerSearchIterator() | ||||||
|  | 	local retS = _bllua_ts.call('containerSearchNext') | ||||||
|  | 	if retS=='0' then | ||||||
|  | 		return nil | ||||||
|  | 	else | ||||||
|  | 		return toTsObject(retS) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | function bl.boxSearch(pos, size, mask) | ||||||
|  | 	local posS = vecToTs(pos) | ||||||
|  | 	local sizeS = vecToTs(size) | ||||||
|  | 	local maskS = maskToTs(mask) | ||||||
|  | 	 | ||||||
|  | 	_bllua_ts.call('initContainerBoxSearch', posS, sizeS, maskS) | ||||||
|  | 	return tsContainerSearchIterator | ||||||
|  | end | ||||||
|  | function bl.radiusSearch(pos, radius, mask) | ||||||
|  | 	local posS = vecToTs(pos) | ||||||
|  | 	if type(radius)~='number' then | ||||||
|  | 		error('bl.radiusSearch: argument #2: radius must be a number', 2) end | ||||||
|  | 	local radiusS = tostring(radius) | ||||||
|  | 	local maskS = maskToTs(mask) | ||||||
|  | 	 | ||||||
|  | 	_bllua_ts.call('initContainerRadiusSearch', posS, radiusS, maskS) | ||||||
|  | 	return tsContainerSearchIterator | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Print/Talk/Echo | ||||||
|  | local function valsToString(vals) | ||||||
|  | 	local strs = {} | ||||||
|  | 	for i,v in ipairs(vals) do | ||||||
|  | 		strs[i] = table.tostring(v) | ||||||
|  | 	end | ||||||
|  | 	return table.concat(strs, '    ') | ||||||
|  | end | ||||||
|  | bl.echo = function(...) | ||||||
|  | 	local str = valsToString({...}) | ||||||
|  | 	_bllua_ts.call('echo', str) | ||||||
|  | end | ||||||
|  | print = bl.echo | ||||||
|  | bl.talk = function(...) | ||||||
|  | 	local str = valsToString({...}) | ||||||
|  | 	_bllua_ts.call('echo', str) | ||||||
|  | 	_bllua_ts.call('talk', str) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function createTsObj(keyword, class, name, inherit, props) | ||||||
|  | 	local propsT = {} | ||||||
|  | 	for k,v in pairs(props) do | ||||||
|  | 		if not isValidFuncName(k) then | ||||||
|  | 			error('bl.new/datablock: invalid property name \''..k..'\'') end | ||||||
|  | 		table.insert(propsT, k..'="'..valToTs(v)..'";') | ||||||
|  | 	end | ||||||
|  | 	 | ||||||
|  | 	local objS = _bllua_ts.eval( | ||||||
|  | 		'return '..keyword..' '..class..'('.. | ||||||
|  | 		(name or '')..(inherit and (':'..inherit) or '')..'){'.. | ||||||
|  | 		table.concat(propsT)..'};') | ||||||
|  | 	local obj = toTsObject(objS) | ||||||
|  | 	if not obj then | ||||||
|  | 		error('bl.new/datablock: failed to create object', 3) end | ||||||
|  | 	 | ||||||
|  | 	return obj | ||||||
|  | end | ||||||
|  | local function parseTsDecl(decl) | ||||||
|  | 	local class, name, inherit | ||||||
|  | 	if decl:find(' ') then -- class ... | ||||||
|  | 		local cl, rest = decl:match('^([^ ]+) ([^ ]+)$') | ||||||
|  | 		class = cl | ||||||
|  | 		if rest:find(':') then -- class name:inherit | ||||||
|  | 			name, inherit = rest:match('^([^:]*):([^:]+)$') | ||||||
|  | 			if not name then class = nil end -- error | ||||||
|  | 			if name=='' then name = nil end -- class :inherit | ||||||
|  | 		else | ||||||
|  | 			name = rest | ||||||
|  | 		end | ||||||
|  | 	else -- class | ||||||
|  | 		class = decl | ||||||
|  | 	end | ||||||
|  | 	if not ( | ||||||
|  | 		isValidFuncName(class) and | ||||||
|  | 		(name==nil or isValidFuncName(name)) and | ||||||
|  | 		(inherit==nil or isValidFuncName(inherit)) ) then | ||||||
|  | 		error('bl.new/datablock: invalid decl \''..decl..'\'\n'.. | ||||||
|  | 			'must be of the format: \'className\', \'className name\', '.. | ||||||
|  | 			'\'className :inherit\', or \'className name:inherit\'', 3) end | ||||||
|  | 	return class, name, inherit | ||||||
|  | end | ||||||
|  | function bl.new(decl, props) | ||||||
|  | 	local class, name, inherit = parseTsDecl(decl) | ||||||
|  | 	return createTsObj('new', class, name, inherit, props) | ||||||
|  | end | ||||||
|  | function bl.datablock(decl, props) | ||||||
|  | 	local class, name, inherit = parseTsDecl(decl) | ||||||
|  | 	return createTsObj('datablock', class, name, inherit, props) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | setmetatable(bl, tsMeta) | ||||||
|  |  | ||||||
|  | print('  Executed libbl.lua') | ||||||
							
								
								
									
										52
									
								
								src/util/libts-support.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/util/libts-support.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  |  | ||||||
|  | // Read an entire file as text and return its contents as a string | ||||||
|  | // Used for reading files from zips | ||||||
|  | function _bllua_ReadEntireFile(%fn) { | ||||||
|  | 	%text = ""; | ||||||
|  | 	%file = new FileObject(); | ||||||
|  | 	%file.openForRead(%fn); | ||||||
|  | 	while (!%file.isEOF()) { %text = %text @ %file.readLine() @ "\r\n"; } | ||||||
|  | 	%file.close(); | ||||||
|  | 	%file.delete(); | ||||||
|  | 	return %text; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Hack to create/set global variables | ||||||
|  | // since there's no easy way to do this from the DLL directly | ||||||
|  | function _bllua_set_var(%name, %val) { | ||||||
|  | 	%first = strLwr(getSubStr(%name, 0, 1)); | ||||||
|  | 	%rest = getSubStr(%name, 1, strLen(%name)); | ||||||
|  | 	switch$(%first) { | ||||||
|  | 		case "a": $a[%rest] = %val; return; | ||||||
|  | 		case "b": $b[%rest] = %val; return; | ||||||
|  | 		case "c": $c[%rest] = %val; return; | ||||||
|  | 		case "d": $d[%rest] = %val; return; | ||||||
|  | 		case "e": $e[%rest] = %val; return; | ||||||
|  | 		case "f": $f[%rest] = %val; return; | ||||||
|  | 		case "g": $g[%rest] = %val; return; | ||||||
|  | 		case "h": $h[%rest] = %val; return; | ||||||
|  | 		case "i": $i[%rest] = %val; return; | ||||||
|  | 		case "j": $j[%rest] = %val; return; | ||||||
|  | 		case "k": $k[%rest] = %val; return; | ||||||
|  | 		case "l": $l[%rest] = %val; return; | ||||||
|  | 		case "m": $m[%rest] = %val; return; | ||||||
|  | 		case "n": $n[%rest] = %val; return; | ||||||
|  | 		case "o": $o[%rest] = %val; return; | ||||||
|  | 		case "p": $p[%rest] = %val; return; | ||||||
|  | 		case "q": $q[%rest] = %val; return; | ||||||
|  | 		case "r": $r[%rest] = %val; return; | ||||||
|  | 		case "s": $s[%rest] = %val; return; | ||||||
|  | 		case "t": $t[%rest] = %val; return; | ||||||
|  | 		case "u": $u[%rest] = %val; return; | ||||||
|  | 		case "v": $v[%rest] = %val; return; | ||||||
|  | 		case "w": $w[%rest] = %val; return; | ||||||
|  | 		case "x": $x[%rest] = %val; return; | ||||||
|  | 		case "y": $y[%rest] = %val; return; | ||||||
|  | 		case "z": $z[%rest] = %val; return; | ||||||
|  | 		case "_": $_[%rest] = %val; return; | ||||||
|  | 	} | ||||||
|  | 	error("_bllua_set_var: invalid variable name " @ %name); | ||||||
|  | 	return ""; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | echo("  Executed libts-support.cs"); | ||||||
							
								
								
									
										212
									
								
								src/util/libts.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/util/libts.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | |||||||
|  |  | ||||||
|  | -- 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 only has access to the sandboxed lua environment, just like user code. | ||||||
|  |  | ||||||
|  | ts = _bllua_ts | ||||||
|  |  | ||||||
|  | -- Provide limited OS functions | ||||||
|  | os = os or {} | ||||||
|  | function os.time() return math.floor(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 | ||||||
|  | -- Used to wrap io.open to allow reading from zips (using TS) | ||||||
|  | -- Not perfect because TS file I/O sucks | ||||||
|  | --   Can't read nulls, can't distinguish between CRLF and LF. | ||||||
|  | -- Todo someday: actually read the zip in lua? | ||||||
|  | local file_meta = { | ||||||
|  | 	read = function(file, mode) | ||||||
|  | 		file:_init() | ||||||
|  | 		if not file or type(file)~='table' or not file._is_file then error('File:read: Not a file', 2) end | ||||||
|  | 		if file._is_open ~= true then error('File:read: File is closed', 2) end | ||||||
|  | 		if mode=='*n' then | ||||||
|  | 			local ws, n = file.data:match('^([ \t\r\n]*)([0-9%.%-e]+)', file.pos) | ||||||
|  | 			if n then | ||||||
|  | 				file.pos = file.pos + #ws + #n | ||||||
|  | 				return n | ||||||
|  | 			else | ||||||
|  | 				return nil | ||||||
|  | 			end | ||||||
|  | 		elseif mode=='*a' then | ||||||
|  | 			local d = file.data:sub(file.pos, #file.data) | ||||||
|  | 			file.pos = #file.data + 1 | ||||||
|  | 			return d | ||||||
|  | 		elseif mode=='*l' then | ||||||
|  | 			local l, ws = file.data:match('^([^\r\n]*)(\r?\n)', file.pos) | ||||||
|  | 			if not l then | ||||||
|  | 				l = file.data:match('^([^\r\n]*)$', file.pos); ws = ''; | ||||||
|  | 				if l=='' then return nil end | ||||||
|  | 			end | ||||||
|  | 			if l then | ||||||
|  | 				file.pos = file.pos + #l + #ws | ||||||
|  | 				return l | ||||||
|  | 			else | ||||||
|  | 				return nil | ||||||
|  | 			end | ||||||
|  | 		elseif type(mode)=='number' then | ||||||
|  | 			local d = file.data:sub(file.pos, file.pos+mode) | ||||||
|  | 			file.pos = file.pos + #d | ||||||
|  | 			return d | ||||||
|  | 		else | ||||||
|  | 			error('File:read: Invalid mode \''..mode..'\'', 2) | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | 	lines = function(file) | ||||||
|  | 		file:_init() | ||||||
|  | 		return function() | ||||||
|  | 			return file:read('*l') | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | 	close = function(file) | ||||||
|  | 		if not file._is_open then error('File:close: File is not open', 2) end | ||||||
|  | 		file._is_open = false | ||||||
|  | 	end, | ||||||
|  | 	__index = function(f, k) return rawget(f, k) or getmetatable(f)[k] end, | ||||||
|  | 	_init = function(f) | ||||||
|  | 		if not f.data then | ||||||
|  | 			f.data = _bllua_ts.call('_bllua_ReadEntireFile', f.filename) | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | } | ||||||
|  | local function new_file_obj(fn) | ||||||
|  | 	local file = { | ||||||
|  | 		_is_file = true, | ||||||
|  | 		_is_open = true, | ||||||
|  | 		pos = 1, | ||||||
|  | 		__index = file_meta.__index, | ||||||
|  | 		filename = fn, | ||||||
|  | 		data = nil, | ||||||
|  | 	} | ||||||
|  | 	setmetatable(file, file_meta) | ||||||
|  | 	return file | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function tflip(t) local u = {}; for _, n in ipairs(t) do u[n] = true end; return u; end | ||||||
|  | local allowed_zip_dirs = tflip{ | ||||||
|  | 	'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders' | ||||||
|  | } | ||||||
|  | local function io_open_absolute(fn, mode) | ||||||
|  | 	-- if file exists, use original mode | ||||||
|  | 	local res, err = _bllua_io_open(fn, mode) | ||||||
|  | 	if res then return res end | ||||||
|  | 	 | ||||||
|  | 	-- otherwise, if TS sees file but Lua doesn't, it must be in a zip, so use TS reader | ||||||
|  | 	local dir = fn:match('^[^/]+') | ||||||
|  | 	if not allowed_zip_dirs[dir:lower()] then return nil, 'File is not in one of the allowed directories' end | ||||||
|  | 	local exist = _bllua_ts.call('isFile', fn) == '1' | ||||||
|  | 	if not exist then return nil, err end | ||||||
|  | 	 | ||||||
|  | 	if mode~=nil and mode~='r' and mode~='rb' then | ||||||
|  | 		return nil, 'Files in zips can only be opened in read mode' end | ||||||
|  | 	 | ||||||
|  | 	-- return a temp lua file object with the data | ||||||
|  | 	local fi = new_file_obj(fn) | ||||||
|  | 	return fi | ||||||
|  | end | ||||||
|  |  | ||||||
|  | io = io or {} | ||||||
|  | function io.open(fn, mode, errn) | ||||||
|  | 	errn = errn or 1 | ||||||
|  | 	 | ||||||
|  | 	-- try to open the file with relative path, otherwise use absolute path | ||||||
|  | 	local curfn = debug.getfilename(errn + 1) or _bllua_ts.getvar('Con::File') | ||||||
|  | 	if curfn == '' then curfn = nil end | ||||||
|  | 	if fn:find('^%.') then | ||||||
|  | 		local relfn = curfn and fn:find('^%./') and | ||||||
|  | 			curfn:gsub('[^/]+$', '')..fn:gsub('^%./', '') | ||||||
|  | 		if relfn then | ||||||
|  | 			local fi, err = io_open_absolute(relfn, mode, errn+1) | ||||||
|  | 			return fi, err, relfn | ||||||
|  | 		else | ||||||
|  | 			return nil, 'Invalid path', fn | ||||||
|  | 		end | ||||||
|  | 	else | ||||||
|  | 		local fi, err = io_open_absolute(fn, mode, errn+1) | ||||||
|  | 		return fi, err, fn | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | function io.lines(fn) | ||||||
|  | 	local fi, err, fn2 = io.open(fn, nil, 2) | ||||||
|  | 	if not fi then error('Error opening file \''..fn2..'\': '..err, 2) end | ||||||
|  | 	return fi:lines() | ||||||
|  | end | ||||||
|  | function io.type(f) | ||||||
|  | 	if type(f)=='table' and f._is_file then | ||||||
|  | 		return f._is_open and 'file' or 'closed file' | ||||||
|  | 	else | ||||||
|  | 		return _bllua_io_type(f) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- provide dofile | ||||||
|  | function dofile(fn, errn) | ||||||
|  | 	errn = errn or 1 | ||||||
|  | 	 | ||||||
|  | 	local fi, err, fn2 = io.open(fn, 'r', errn+1) | ||||||
|  | 	if not fi then error('Error executing file \''..fn2..'\': '..err, errn+1) end | ||||||
|  | 	 | ||||||
|  | 	print('Executing '..fn2) | ||||||
|  | 	local text = fi:read('*a') | ||||||
|  | 	fi:close() | ||||||
|  | 	return assert(loadstring('--[['..fn2..']]'..text))() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- provide require (just a wrapper for dofile) | ||||||
|  | -- searches for ?.lua and ?/init.lua in the following directories: | ||||||
|  | --   location of current file | ||||||
|  | --   blockland directory | ||||||
|  | --   current add-on | ||||||
|  | local function file_exists(fn, errn) | ||||||
|  | 	local fi, err, fn2 = io.open(fn, 'r', errn+1) | ||||||
|  | 	if fi then | ||||||
|  | 		fi:close() | ||||||
|  | 		return fn2 | ||||||
|  | 	else | ||||||
|  | 		return nil | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | function require(mod) | ||||||
|  | 	local fp = mod:gsub('%.', '/') | ||||||
|  | 	local fns = { | ||||||
|  | 		'./'..fp..'.lua',      -- local file | ||||||
|  | 		'./'..fp..'/init.lua', -- local library | ||||||
|  | 		fp..'.lua',            -- global file | ||||||
|  | 		fp..'/init.lua',       -- global library | ||||||
|  | 	} | ||||||
|  | 	if fp:lower():find('^add-ons/') then | ||||||
|  | 		local addonpath = fp:lower():match('^add-ons/[^/]+')..'/' | ||||||
|  | 		table.insert(fns, addonpath..fp..'.lua')      -- add-on file | ||||||
|  | 		table.insert(fns, addonpath..fp..'/init.lua') -- add-on library | ||||||
|  | 	end | ||||||
|  | 	for _,fn in ipairs(fns) do | ||||||
|  | 		local fne = file_exists(fn, 2) | ||||||
|  | 		if fne then | ||||||
|  | 			return dofile(fne, 2) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 	return _bllua_requiresecure(mod) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- Exposure to TS | ||||||
|  | function _bllua_getvar(name) return _G[name] end | ||||||
|  | function _bllua_setvar(name, val) _G[name] = val end | ||||||
|  | function _bllua_eval(code) return loadstring(code)() end | ||||||
|  | function _bllua_exec(fn) return dofile(fn, 2) end | ||||||
|  |  | ||||||
|  | local function isValidCode(code) | ||||||
|  | 	local f,e = loadstring(code) | ||||||
|  | 	return f~=nil | ||||||
|  | end | ||||||
|  | function _bllua_smarteval(code) | ||||||
|  | 	if (not code:find('^print%(')) and isValidCode('print('..code..')') then | ||||||
|  | 		code = 'print('..code..')' end | ||||||
|  | 	local f,e = loadstring(code) | ||||||
|  | 	if f then | ||||||
|  | 		return f() | ||||||
|  | 	else | ||||||
|  | 		print(e) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | _bllua_ts.call('echo', '  Executed libts.lua') | ||||||
							
								
								
									
										412
									
								
								src/util/loadaddons.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										412
									
								
								src/util/loadaddons.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,412 @@ | |||||||
|  | // Package to allow add-ons to use server.lua or client.lua | ||||||
|  | // instead of or in addition to server.cs or client.cs | ||||||
|  | // Relevant .lua files are is executed before .cs files. | ||||||
|  |  | ||||||
|  | function _bllua_strEndsWith(%str, %sch) { | ||||||
|  | 	%schL = strLen(%sch); | ||||||
|  | 	return getSubStr(%str, strLen(%str)-%schL, %schL) $= %sch; | ||||||
|  | } | ||||||
|  | //function _bllua_strRemoveEnd(%str, %sch) { | ||||||
|  | //	%schL = strLen(%sch); | ||||||
|  | //	return getSubStr(%str, 0, strLen(%str)-%schL); | ||||||
|  | //} | ||||||
|  | function _bllua_fileIsExecCs(%fn) { | ||||||
|  | 	return | ||||||
|  | 		_bllua_strEndsWith(%fn, "/server.cs" ) || | ||||||
|  | 		_bllua_strEndsWith(%fn, "/server.lua") || | ||||||
|  | 		_bllua_strEndsWith(%fn, "/client.cs" ) || | ||||||
|  | 		_bllua_strEndsWith(%fn, "/client.lua"); | ||||||
|  | } | ||||||
|  | function _bllua_execAddon(%dirName, %type) { | ||||||
|  | 	%i = 0; | ||||||
|  | 	%fnLua = "Add-Ons/" @ %dirName @ "/" @ %type @ ".lua"; | ||||||
|  | 	if(isFile(%fnLua)) { luaexec(%fnLua); %i++; } | ||||||
|  | 	%fnCs  = "Add-Ons/" @ %dirName @ "/" @ %type @ ".cs"; | ||||||
|  | 	if(isFile(%fnCs )) { exec(%fnCs ); %i++; } | ||||||
|  | 	if(%i==0) { | ||||||
|  | 		error("Error Loading Add-On " @ %dirName @ ": Neither " @ | ||||||
|  | 			%type @ ".cs nor " @ %type @ ".lua exist"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Rewrite built-in functions that scan for server.cs or client.cs | ||||||
|  | //   and make them scan for server.lua or client.lua as well | ||||||
|  | // Note: I had to completely override several large functions, | ||||||
|  | //   many of which are highly redundant, because Badspot didn't know | ||||||
|  | //   what functional decomposition was when he wrote this shit. | ||||||
|  | package _bllua_addon_exec { | ||||||
|  | 	function CustomGameGuiServer::populateAddOnList() { | ||||||
|  | 		deleteVariables("$CustomGameGuiServer::AddOn*"); | ||||||
|  | 		$CustomGameGuiServer::AddOnCount = 0; | ||||||
|  | 		%pattern = "Add-Ons/*/server.*"; | ||||||
|  | 		%filename = findFirstFile(%pattern); | ||||||
|  | 		while(isFile(%filename)) { | ||||||
|  | 			if(_bllua_fileIsExecCs(%filename)) { | ||||||
|  | 				%path = filePath(%filename); | ||||||
|  | 				%dirName = getSubStr(%path, strlen("Add-Ons/"), strlen(%path) - strlen("Add-Ons/")); | ||||||
|  | 				if(!%seenDirName[%dirName]) { | ||||||
|  | 					%seenDirName[%dirName] = 1; | ||||||
|  | 					%varName = getSafeVariableName(%dirName); | ||||||
|  | 					if(isValidAddOn(%dirName, 1)) { | ||||||
|  | 						$CustomGameGuiServer::AddOn[$CustomGameGuiServer::AddOnCount] = %dirName; | ||||||
|  | 						$CustomGameGuiServer::AddOnCount++; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			%filename = findNextFile(%pattern); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	function GameModeGuiServer::GetMissingAddOns(%filename) { | ||||||
|  | 		if(!isFile(%filename)) { | ||||||
|  | 			error("ERROR: GameModeGuiServer::GetMissingAddOns(" @ %filename @ ") - file does not exist"); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		%path = filePath(%filename); | ||||||
|  | 		%missingAddons = ""; | ||||||
|  | 		%descriptionFile = %path @ "/description.txt"; | ||||||
|  | 		%previewFile = %path @ "/preview.jpg"; | ||||||
|  | 		%thumbFile = %path @ "/thumb.jpg"; | ||||||
|  | 		%saveFile = %path @ "/save.bls"; | ||||||
|  | 		%colorSetFile = %path @ "/colorSet.txt"; | ||||||
|  | 		if(!isFile(%descriptionFile)) | ||||||
|  | 			%missingAddons = %missingAddons TAB %descriptionFile; | ||||||
|  | 		if(!isFile(%previewFile)) | ||||||
|  | 			%missingAddons = %missingAddons TAB %previewFile; | ||||||
|  | 		if(!isFile(%thumbFile)) | ||||||
|  | 			%missingAddons = %missingAddons TAB %thumbFile; | ||||||
|  | 		if(!isFile(%saveFile)) | ||||||
|  | 			%missingAddons = %missingAddons TAB %saveFile; | ||||||
|  | 		if(!isFile(%colorSetFile)) | ||||||
|  | 			%missingAddons = %missingAddons TAB %colorSetFile; | ||||||
|  | 		%file = new FileObject(""){}; | ||||||
|  | 		%file.openForRead(%filename); | ||||||
|  | 		while(!%file.isEOF()) { | ||||||
|  | 			%line = %file.readLine(); | ||||||
|  | 			%label = getWord(%line, 0); | ||||||
|  | 			%value = trim(getWords(%line, 1, 999)); | ||||||
|  | 			if(%label !$= "") { | ||||||
|  | 				if(getSubStr(%label, 0, 2) !$= "//") { | ||||||
|  | 					if(%label $= "ADDON") { | ||||||
|  | 						if(!isFile("Add-Ons/" @ %value @ "/description.txt") || | ||||||
|  | 							(!isFile("Add-Ons/" @ %value @ "/server.cs" ) && | ||||||
|  | 							 !isFile("Add-Ons/" @ %value @ "/server.lua")    ) ) { | ||||||
|  | 							if(strlen(%missingAddons) > 0) | ||||||
|  | 								%missingAddons = %missingAddons TAB %value; | ||||||
|  | 							else | ||||||
|  | 								%missingAddons = %value; | ||||||
|  | 						} | ||||||
|  | 					} else { | ||||||
|  | 						if(%label $= "MUSIC") { | ||||||
|  | 							if(!isFile("Add-Ons/Music/" @ %value @ ".ogg")) { | ||||||
|  | 								if(strlen(%missingAddons) > 0) | ||||||
|  | 									%missingAddons = %missingAddons TAB %value @ ".ogg"; | ||||||
|  | 								else | ||||||
|  | 									%missingAddons = %value; | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		%file.close(); | ||||||
|  | 		%file.delete(); | ||||||
|  | 		return %missingAddons; | ||||||
|  | 	} | ||||||
|  | 	function loadAddOns() { | ||||||
|  | 		echo(""); | ||||||
|  | 		updateAddOnList(); | ||||||
|  | 		echo("---------  Loading Add-Ons (+BlockLua) ---------"); | ||||||
|  | 		deleteVariables("$AddOnLoaded__*"); | ||||||
|  | 		%dir = "Add-Ons/*/server.*"; | ||||||
|  | 		%filename = findFirstFile(%dir); | ||||||
|  | 		%dirCount = 0; | ||||||
|  | 		if(isFile("Add-Ons/System_ReturnToBlockland/server.cs")) { | ||||||
|  | 			%dirNameList[%dirCount] = "System_ReturnToBlockland"; | ||||||
|  | 			%dirCount++; | ||||||
|  | 		} | ||||||
|  | 		while(%filename !$= "") { | ||||||
|  | 			if(_bllua_fileIsExecCs(%filename)) { | ||||||
|  | 				%path = filePath(%filename); | ||||||
|  | 				%dirName = getSubStr(%path, strlen("Add-Ons/"), strlen(%path) - strlen("Add-Ons/")); | ||||||
|  | 				if(!%seenDirName[%dirName]) { | ||||||
|  | 					%seenDirName[%dirName] = 1; | ||||||
|  | 					if(%dirName !$= "System_ReturnToBlockland") { | ||||||
|  | 						%dirNameList[%dirCount] = %dirName; | ||||||
|  | 						%dirCount++; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			%filename = findNextFile(%dir); | ||||||
|  | 		} | ||||||
|  | 		for(%addOnItr = 0; %addOnItr < %dirCount; %addOnItr++) { | ||||||
|  | 			%dirName = %dirNameList[%addOnItr]; | ||||||
|  | 			%varName = getSafeVariableName(%dirName); | ||||||
|  | 			if(!$Server::Dedicated) { | ||||||
|  | 				if(getRealTime() - $lastProgressBarTime > 200) { | ||||||
|  | 					LoadingProgress.setValue(%addOnItr / %dirCount); | ||||||
|  | 					$lastProgressBarTime = getRealTime(); | ||||||
|  | 					Canvas.repaint(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if($AddOn__[%varName] $= 1 && isValidAddOn(%dirName)) { | ||||||
|  | 				if(%dirName $= "JVS_Content" && $AddOn__["Support_LegacyDoors"] $= 1) { | ||||||
|  | 					echo("  Skipping JVS_Content in favor of Support_LegacyDoors"); | ||||||
|  | 				} else if(!$AddOnLoaded__[%varName]) { | ||||||
|  | 					$AddOnLoaded__[%varName] = 1; | ||||||
|  | 					%zipFile = "Add-Ons/" @ %dirName @ ".zip"; | ||||||
|  | 					if(isFile(%zipFile)) { | ||||||
|  | 						%zipCRC = getFileCRC(%zipFile); | ||||||
|  | 						echo("\c5Loading Add-On: " @ %dirName @ " \c2(CRC:" @ %zipCRC @ ")"); | ||||||
|  | 					} else { | ||||||
|  | 						echo("\c5Loading Add-On: " @ %dirName); | ||||||
|  | 					} | ||||||
|  | 					if(VerifyAddOnScripts(%dirName)==0) { | ||||||
|  | 						echo("\c3ADD-ON " @ %dirName @ " CONTAINS SYNTAX ERRORS\n"); | ||||||
|  | 					} else { | ||||||
|  | 						%oldDBCount = DataBlockGroup.getCount(); | ||||||
|  | 						_bllua_execAddon(%dirName, "server"); | ||||||
|  | 						%dbDiff = DataBlockGroup.getCount() - %oldDBCount; | ||||||
|  | 						echo("\c2" @ %dbDiff @ " datablocks added."); | ||||||
|  | 						echo(""); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		echo(""); | ||||||
|  | 	} | ||||||
|  | 	function loadGameModeAddOns() { | ||||||
|  | 		echo(""); | ||||||
|  | 		echo("---------  Loading Add-Ons (Game Mode) (+BlockLua) ---------"); | ||||||
|  | 		deleteVariables("$AddOnLoaded__*"); | ||||||
|  | 		for(%i=0; %i<$GameMode::AddOnCount; %i++) { | ||||||
|  | 			%dirName = $GameMode::AddOn[%i]; | ||||||
|  | 			%varName = getSafeVariableName(%dirName); | ||||||
|  | 			if(!$Server::Dedicated) { | ||||||
|  | 				if(getRealTime() - $lastProgressBarTime > 200) { | ||||||
|  | 					LoadingProgress.setValue(%i / $GameMode::AddOnCount); | ||||||
|  | 					$lastProgressBarTime = getRealTime(); | ||||||
|  | 					Canvas.repaint(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if(!isValidAddOn(%dirName)) { | ||||||
|  | 				error("ERROR: Invalid add-on \'" @ %dirName @ "\' specified for game mode \'" @ $GameModeArg @ "\'"); | ||||||
|  | 			} else { | ||||||
|  | 				$AddOnLoaded__[%varName] = 1; | ||||||
|  | 				%zipFile = "Add-Ons/" @ %dirName @ ".zip"; | ||||||
|  | 				if(isFile(%zipFile)) { | ||||||
|  | 					%zipCRC = getFileCRC(%zipFile); | ||||||
|  | 					echo("\c5Loading Add-On: " @ %dirName @ " \c2(CRC:" @ %zipCRC @ ")"); | ||||||
|  | 				} else { | ||||||
|  | 					echo("\c5Loading Add-On: " @ %dirName); | ||||||
|  | 				} | ||||||
|  | 				if(VerifyAddOnScripts(%dirName) == 0) { | ||||||
|  | 					echo("\c3ADD-ON " @ %dirName @ " CONTAINS SYNTAX ERRORS\n"); | ||||||
|  | 				} else { | ||||||
|  | 					%oldDBCount = DataBlockGroup.getCount(); | ||||||
|  | 					_bllua_execAddon(%dirName, "server"); | ||||||
|  | 					%dbDiff = DataBlockGroup.getCount() - %oldDBCount; | ||||||
|  | 					echo("\c2" @ %dbDiff @ " datablocks added."); | ||||||
|  | 					echo(""); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		echo(""); | ||||||
|  | 	} | ||||||
|  | 	function loadClientAddOns() { | ||||||
|  | 		echo(""); | ||||||
|  | 		echo("---------  Loading Client Add-Ons (+BlockLua) ---------"); | ||||||
|  | 		if(isFile("base/server/crapOns_Cache.cs")) | ||||||
|  | 			exec("base/server/crapOns_Cache.cs"); | ||||||
|  | 		%dir = "Add-Ons/*/client.*"; | ||||||
|  | 		%filename = findFirstFile(%dir); | ||||||
|  | 		%dirCount = 0; | ||||||
|  | 		if(isFile("Add-Ons/System_ReturnToBlockland/client.cs")) { | ||||||
|  | 			%dirNameList[%dirCount] = "System_ReturnToBlockland"; | ||||||
|  | 			%dirCount++; | ||||||
|  | 		} | ||||||
|  | 		while(%filename !$= "") { | ||||||
|  | 			if(_bllua_fileIsExecCs(%filename)) { | ||||||
|  | 				%path = filePath(%filename); | ||||||
|  | 				%dirName = getSubStr(%path, strlen("Add-Ons/"), strlen(%path) - strlen("Add-Ons/")); | ||||||
|  | 				if(!%seenDirName[%dirName]) { | ||||||
|  | 					%seenDirName[%dirName] = 1; | ||||||
|  | 					if(%dirName !$= "System_ReturnToBlockland") { | ||||||
|  | 						%dirNameList[%dirCount] = %dirName; | ||||||
|  | 						%dirCount++; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			%filename = findNextFile(%dir); | ||||||
|  | 		} | ||||||
|  | 		for(%i=0; %i<%dirCount; %i++) { | ||||||
|  | 			%dirName = %dirNameList[%i]; | ||||||
|  | 			%varName = getSafeVariableName(%dirName); | ||||||
|  | 			echo(""); | ||||||
|  | 			echo("Client checking Add-On: " @ %dirName); | ||||||
|  | 			if(!clientIsValidAddOn(%dirName, 1)) { | ||||||
|  | 				//deleteVariables("$AddOn__" @ %varName); // wtf | ||||||
|  | 			} else { | ||||||
|  | 				%name = %dirName; | ||||||
|  | 				%zipFile = "Add-Ons/" @ %dirName @ ".zip"; | ||||||
|  | 				if(isFile(%zipFile)) { | ||||||
|  | 					%zipCRC = getFileCRC(%zipFile); | ||||||
|  | 					echo("\c5Loading Add-On: " @ %dirName @ " \c2(CRC:" @ %zipCRC @ ")"); | ||||||
|  | 				} else { | ||||||
|  | 					echo("\c5Loading Add-On: " @ %dirName); | ||||||
|  | 				} | ||||||
|  | 				if(ClientVerifyAddOnScripts(%dirName)==0) | ||||||
|  | 					echo("\c3ADD-ON " @ %dirName @ " CONTAINS SYNTAX ERRORS\n"); | ||||||
|  | 				else | ||||||
|  | 					_bllua_execAddon(%dirName, "client"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		echo(""); | ||||||
|  | 	} | ||||||
|  | 	function updateAddOnList() { | ||||||
|  | 		echo("\n--------- Updating Add-On List (+BlockLua) ---------"); | ||||||
|  | 		deleteVariables("$AddOn__*"); | ||||||
|  | 		if(isFile("config/server/ADD_ON_LIST.cs")) { | ||||||
|  | 			exec("config/server/ADD_ON_LIST.cs"); | ||||||
|  | 		} else { | ||||||
|  | 			exec("base/server/defaultAddOnList.cs"); | ||||||
|  | 		} | ||||||
|  | 		if(isFile("base/server/crapOns_Cache.cs")) { | ||||||
|  | 			exec("base/server/crapOns_Cache.cs"); | ||||||
|  | 		} | ||||||
|  | 		%dir = "Add-Ons/*/server.*"; | ||||||
|  | 		%fileCount = getFileCount(%dir); | ||||||
|  | 		%filename = findFirstFile(%dir); | ||||||
|  | 		while(%filename !$= "") { | ||||||
|  | 			if(_bllua_fileIsExecCs(%filename)) { | ||||||
|  | 				%path = filePath(%filename); | ||||||
|  | 				%dirName = getSubStr(%path, strlen("Add-Ons/"), strlen(%path) - strlen("Add-Ons/")); | ||||||
|  | 				if(!%seenDirName[%dirName]) { | ||||||
|  | 					%seenDirName[%dirName] = 1; | ||||||
|  | 					%varName = getSafeVariableName(%dirName); | ||||||
|  | 					echo("Checking Add-On " @ %dirName); | ||||||
|  | 					if(!isValidAddOn(%dirName, 1)) { | ||||||
|  | 						deleteVariables("$AddOn__" @ %varName); | ||||||
|  | 					} else { | ||||||
|  | 						if (mFloor($AddOn__[%varName]) <= 0) | ||||||
|  | 							$AddOn__[%varName] = -1; | ||||||
|  | 						else | ||||||
|  | 							$AddOn__[%varName] = 1; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			%filename = findNextFile(%dir); | ||||||
|  | 		} | ||||||
|  | 		echo(""); | ||||||
|  | 		export("$AddOn__*", "config/server/ADD_ON_LIST.cs"); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | activatePackage(_bllua_addon_exec); | ||||||
|  |  | ||||||
|  | // Have to make new versions of these because packaging them is blocked FSFR | ||||||
|  | function forceRequiredAddOn_L(%dirName) { | ||||||
|  | 	if(%dirName $= "JVS_Content") { | ||||||
|  | 		if($GameModeArg $= "") { | ||||||
|  | 			if($AddOn__["Support_LegacyDoors"] == 1 || !isFile("add-ons/JVS_Content/server.cs") || ($AddOn__["Support_LegacyDoors"] != 1 && $AddOn__["JVS_Content"] != 1)) { | ||||||
|  | 				%dirName = "Support_LegacyDoors"; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			%foundJVSContent = 0; | ||||||
|  | 			for(%i=0; %i<$GameMode::AddOnCount; %i++) { | ||||||
|  | 				if ($GameMode::AddOn[%i] $= "JVS_Content") { | ||||||
|  | 					%foundJVSContent = 1; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if(!%foundJVSContent) | ||||||
|  | 				%dirName = "Support_LegacyDoors"; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if(strstr(%dirName, " ") != -1) | ||||||
|  | 		%dirName = strreplace(%dirName, " ", "_"); | ||||||
|  | 	//if(strstr(%dirName, "/") != -1) | ||||||
|  | 	//	return $Error::AddOn_Nested; | ||||||
|  | 	%varName = getSafeVariableName(%dirName); | ||||||
|  | 	if($GameModeArg !$= "") { | ||||||
|  | 		%foundIt = 0; | ||||||
|  | 		for(%i=0; %i<$GameMode::AddOnCount; %i++) { | ||||||
|  | 			if ($GameMode::AddOn[%i] $= %dirName) { | ||||||
|  | 				%foundIt = 1; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if(!%foundIt) { | ||||||
|  | 			error("ERROR: ForceRequiredAddOn_L(\'" @ %dirName @ "\') - you can\'t force load an add-on that is not included in gamemode.txt"); | ||||||
|  | 			if (GameWindowExists() && !$Server::Dedicated) { | ||||||
|  | 				schedule(11, 0, MessageBoxOK, "Game Mode Error", "Required add-on " @ %dirName @ " should be added to gamemode.txt"); | ||||||
|  | 			} | ||||||
|  | 			if (!isEventPending($disconnectEvent)) { | ||||||
|  | 				$disconnectEvent = schedule(10, 0, disconnect); | ||||||
|  | 			} | ||||||
|  | 			return $Error::AddOn_NotFound; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if($AddOnLoaded__[%varName] == 1) | ||||||
|  | 		return $Error::None; | ||||||
|  | 	if($AddOn__[%varName] $= "" && $GameModeArg $= "" || !isValidAddOn(%dirName)) { | ||||||
|  | 		error("ERROR: ForceRequiredAddOn() - " @ %dirName @ " is not a valid add-on"); | ||||||
|  | 		return $Error::AddOn_NotFound; | ||||||
|  | 	} | ||||||
|  | 	echo("  Loading Add-On " @ %dirName @ ""); | ||||||
|  | 	_bllua_execAddon(%dirName, "server"); | ||||||
|  | 	$AddOnLoaded__[%varName] = 1; | ||||||
|  | 	if($AddOn__[%varName] $= 1) | ||||||
|  | 		return $Error::None; | ||||||
|  | 	else | ||||||
|  | 		return $Error::AddOn_Disabled; | ||||||
|  | } | ||||||
|  | function loadRequiredAddOn_L(%dirName) { | ||||||
|  | 	if(%dirName $= "JVS_Content") { | ||||||
|  | 		if($GameModeArg $= "") { | ||||||
|  | 			if($AddOn__["Support_LegacyDoors"] == 1 || !isFile("add-ons/JVS_Content/server.cs") || ($AddOn__["Support_LegacyDoors"] != 1 && $AddOn__["JVS_Content"] != 1)) { | ||||||
|  | 				%dirName = "Support_LegacyDoors"; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			%foundJVSContent = 0; | ||||||
|  | 			for(%i=0; %i<$GameMode::AddOnCount; %i++) { | ||||||
|  | 				if ($GameMode::AddOn[%i] $= "JVS_Content") { | ||||||
|  | 					%foundJVSContent = 1; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if(!%foundJVSContent) | ||||||
|  | 				%dirName = "Support_LegacyDoors"; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if(strstr(%dirName, " ") != -1) | ||||||
|  | 		%dirName = strreplace(%dirName, " ", "_"); | ||||||
|  | 	//if(strstr(%dirName, "/") != -1) | ||||||
|  | 	//	return $Error::AddOn_Nested; | ||||||
|  | 	%varName = getSafeVariableName(%dirName); | ||||||
|  | 	if ($GameModeArg !$= "") { | ||||||
|  | 		%foundIt = 0; | ||||||
|  | 		for(%i=0; %i<$GameMode::AddOnCount; %i++) { | ||||||
|  | 			if ($GameMode::AddOn[%i] $= %dirName) { | ||||||
|  | 				%foundIt = 1; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if(!%foundIt) { | ||||||
|  | 			error("ERROR: LoadRequiredAddOn_L(\'" @ %dirName @ "\') - you can\'t force load an add-on that is not included in gamemode.txt"); | ||||||
|  | 			if (GameWindowExists() && !$Server::Dedicated) | ||||||
|  | 				schedule(11, 0, MessageBoxOK, "Game Mode Error", "Required add-on " @ %dirName @ " should be added to gamemode.txt"); | ||||||
|  | 			if (!isEventPending($disconnectEvent)) | ||||||
|  | 				$disconnectEvent = schedule(10, 0, disconnect); | ||||||
|  | 			return $Error::AddOn_NotFound; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if($AddOnLoaded__[%varName] == 1) | ||||||
|  | 		return $Error::None; | ||||||
|  | 	if($AddOn__[%varName] $= 1) { | ||||||
|  | 		echo("  Loading Add-On " @ %dirName @ ""); | ||||||
|  | 		_bllua_execAddon(%dirName, "server"); | ||||||
|  | 		$AddOnLoaded__[%varName] = 1; | ||||||
|  | 		return $Error::None; | ||||||
|  | 	} else { | ||||||
|  | 		return $Error::AddOn_Disabled; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | echo("  Executed loadaddons.cs"); | ||||||
							
								
								
									
										360
									
								
								src/util/std.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										360
									
								
								src/util/std.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,360 @@ | |||||||
|  |  | ||||||
|  | -- Basic functionality that should be standard in Lua | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- Table / List | ||||||
|  | -- Whether a table contains no keys | ||||||
|  | function table.empty(t) | ||||||
|  | 	for _,_ in pairs(t) do return false end | ||||||
|  | 	return true | ||||||
|  | end | ||||||
|  | -- Apply a function to each key in a table | ||||||
|  | function table.map(f, ...) | ||||||
|  | 	local ts = {...} | ||||||
|  | 	local u = {} | ||||||
|  | 	for k,_ in pairs(ts[1]) do | ||||||
|  | 		local args = {} | ||||||
|  | 		for j=1,#ts do args[j] = ts[j][i] end | ||||||
|  | 		u[i] = f(unpack(args)) | ||||||
|  | 	end | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | function table.map_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(unpack(args)) | ||||||
|  | 	end | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | -- Swap keys/values | ||||||
|  | function table.swap(t) | ||||||
|  | 	local u = {} | ||||||
|  | 	for k,v in pairs(t) do u[v] = k end | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | -- Reverse a list | ||||||
|  | function table.reverse(l) | ||||||
|  | 	local m = {} | ||||||
|  | 	for i=1,#l do m[#l-i+1] = l[i] end | ||||||
|  | 	return m | ||||||
|  | end | ||||||
|  | -- Convert i->v to v->true | ||||||
|  | function table.values(l) | ||||||
|  | 	local u = {} | ||||||
|  | 	for _,v in ipairs(l) do u[v] = true end | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | -- Make a list of keys | ||||||
|  | function table.keys(t) | ||||||
|  | 	local u = {} | ||||||
|  | 	for k,_ in pairs(t) do table.insert(u, k) end | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | -- Whether a table is a list/array (has only monotonic integer keys) | ||||||
|  | function table.islist(t) | ||||||
|  | 	local n = 0 | ||||||
|  | 	for i,_ in pairs(t) do | ||||||
|  | 		if type(i)~='number' or i%1~=0 then return false end | ||||||
|  | 		n = n+1 | ||||||
|  | 	end | ||||||
|  | 	return n==#t | ||||||
|  | end | ||||||
|  | -- Append contents of other tables to first table | ||||||
|  | function table.append(t, ...) | ||||||
|  | 	local a = {...} | ||||||
|  | 	for _,u in ipairs(a) do | ||||||
|  | 		for _,v in ipairs(u) do table.insert(t,v) end | ||||||
|  | 	end | ||||||
|  | 	return t | ||||||
|  | end | ||||||
|  | -- Create a new table containing all keys from any number of tables | ||||||
|  | -- latter tables in the arg list override prior ones | ||||||
|  | -- overlaps, NOT appends, integer keys | ||||||
|  | function table.join(...) | ||||||
|  | 	local ts = {...} | ||||||
|  | 	local w = {} | ||||||
|  | 	for _,t in ipairs(ts) do | ||||||
|  | 		for k,v in pairs(t) do w[k] = v end | ||||||
|  | 	end | ||||||
|  | 	return w | ||||||
|  | end | ||||||
|  | -- Whether a table contains a certain value in any key | ||||||
|  | function table.contains(t,s) | ||||||
|  | 	for _,v in pairs(t) do | ||||||
|  | 		if v==s then return true end | ||||||
|  | 	end | ||||||
|  | 	return false | ||||||
|  | end | ||||||
|  | function table.contains_list(t,s) | ||||||
|  | 	for _,v in ipairs(t) do | ||||||
|  | 		if v==s then return true end | ||||||
|  | 	end | ||||||
|  | 	return false | ||||||
|  | end | ||||||
|  | -- Copy a table to another table | ||||||
|  | function table.copy(t) | ||||||
|  | 	local u = {} | ||||||
|  | 	for k,v in pairs(t) do u[k] = v end | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | function table.copy_list(l) | ||||||
|  | 	local m = {} | ||||||
|  | 	for i,v in ipairs(l) do m[i] = v end | ||||||
|  | 	return m | ||||||
|  | end | ||||||
|  | -- Sort a table in a new copy | ||||||
|  | function table.sortcopy(t, f) | ||||||
|  | 	local u = table.copy_list(t) | ||||||
|  | 	table.sort(u, f) | ||||||
|  | 	return u | ||||||
|  | end | ||||||
|  | -- Remove a value from a table | ||||||
|  | function table.removevalue(t, r) | ||||||
|  | 	local rem = {} | ||||||
|  | 	for k,v in pairs(t) do | ||||||
|  | 		if v==r then table.insert(rem, k) end | ||||||
|  | 	end | ||||||
|  | 	for _,k in ipairs(rem) do t[k] = nil end | ||||||
|  | end | ||||||
|  | function table.removevalue_list(t, r) | ||||||
|  | 	for i = #t, 1, -1 do | ||||||
|  | 		if t[i]==r then | ||||||
|  | 			table.remove(t, i) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | -- Export tables into formatted executable strings | ||||||
|  | local function tabs(tabLevel) | ||||||
|  | 	return ('  '):rep(tabLevel) | ||||||
|  | end | ||||||
|  | local valueToString | ||||||
|  | local function tableToString(t, tabLevel, seen) | ||||||
|  | 	if type(t)~='table' or (getmetatable(t) and getmetatable(t).__tostring) then | ||||||
|  | 		return tostring(t) | ||||||
|  | 	elseif table.islist(t) then | ||||||
|  | 		if #t==0 then | ||||||
|  | 			return '{}' | ||||||
|  | 		else | ||||||
|  | 			local strs = {} | ||||||
|  | 			local containsTables = false | ||||||
|  | 			for _,v in ipairs(t) do | ||||||
|  | 				if type(v)=='table' then containsTables = true end | ||||||
|  | 				table.insert(strs, valueToString(v, tabLevel+1, seen)..',') | ||||||
|  | 			end | ||||||
|  | 			if containsTables or #t>3 then | ||||||
|  | 				return '{\n'..tabs(tabLevel+1) | ||||||
|  | 					..table.concat(strs, '\n'..tabs(tabLevel+1)) | ||||||
|  | 					..'\n'..tabs(tabLevel)..'}' | ||||||
|  | 			else | ||||||
|  | 				return '{ '..table.concat(strs, ' ')..' }' | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	else | ||||||
|  | 		local containsNonStringKeys = false | ||||||
|  | 		for k,v in pairs(t) do | ||||||
|  | 			if type(k)~='string' or k:find('[^a-zA-Z0-9_]') then | ||||||
|  | 				containsNonStringKeys = true | ||||||
|  | 			elseif type(k)=='table' then | ||||||
|  | 				error('table.tostring: table contains a table as key, cannot serialize') | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		local strs = {} | ||||||
|  | 		if containsNonStringKeys then | ||||||
|  | 			for k,v in pairs(t) do | ||||||
|  | 				table.insert(strs, '\n'..tabs(tabLevel+1) | ||||||
|  | 					..'['..valueToString(k, tabLevel+1, seen)..'] = ' | ||||||
|  | 					..valueToString(v, tabLevel+1, seen)..',') | ||||||
|  | 			end | ||||||
|  | 		else | ||||||
|  | 			for k,v in pairs(t) do | ||||||
|  | 				table.insert(strs, '\n'..tabs(tabLevel+1) | ||||||
|  | 					..k..' = '..valueToString(v, tabLevel+1, seen)..',') | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		return '{'..table.concat(strs)..'\n'..tabs(tabLevel)..'}' | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | valueToString = function(v, tabLevel, seen) | ||||||
|  | 	local t = type(v) | ||||||
|  | 	if t=='table' then | ||||||
|  | 		if seen[v] then | ||||||
|  | 			return 'nil --[[ already seen: '..tostring(v)..' ]]' | ||||||
|  | 		else | ||||||
|  | 			seen[v] = true | ||||||
|  | 			return tableToString(v, tabLevel, seen) | ||||||
|  | 		end | ||||||
|  | 	elseif t=='string' then | ||||||
|  | 		return '\''..string.escape(v)..'\'' | ||||||
|  | 	elseif t=='number' or t=='boolean' then | ||||||
|  | 		return tostring(v) | ||||||
|  | 	else | ||||||
|  | 		--error('table.tostring: table contains a '..t..' value, cannot serialize') | ||||||
|  | 		return 'nil --[[ cannot serialize '..t..': '..tostring(v)..' ]]' | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | function table.tostring(t) | ||||||
|  | 	return tableToString(t, 0, {}) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- String | ||||||
|  |  | ||||||
|  | -- Split string into table by separator | ||||||
|  | -- or by chars if no separator given | ||||||
|  | -- if regex is not true, sep is treated as a regex pattern | ||||||
|  | function string.split(str, sep, noregex) | ||||||
|  | 	if type(str)~='string' then | ||||||
|  | 		error('string.split: argument #1: expected string, got '..type(str), 2) end | ||||||
|  | 	if sep==nil or sep=='' then | ||||||
|  | 		local t = {} | ||||||
|  | 		local ns = #str | ||||||
|  | 		for x = 1, ns do | ||||||
|  | 			table.insert(t, str:sub(x, x)) | ||||||
|  | 		end | ||||||
|  | 		return t | ||||||
|  | 	elseif type(sep)=='string' then | ||||||
|  | 		local t = {} | ||||||
|  | 		if #str>0 then | ||||||
|  | 			local first = 1 | ||||||
|  | 			while true do | ||||||
|  | 				local last, newfirst = str:find(sep, first, noregex) | ||||||
|  | 				if not last then break end | ||||||
|  | 				table.insert(t, str:sub(first, last-1)) | ||||||
|  | 				first = newfirst+1 | ||||||
|  | 			end | ||||||
|  | 			table.insert(t, str:sub(first, #str)) | ||||||
|  | 		end | ||||||
|  | 		return t | ||||||
|  | 	else | ||||||
|  | 		error( | ||||||
|  | 			'string.split: argument #2: expected string or nil, got '..type(sep), 2) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | -- Split string to a list of char bytes | ||||||
|  | function string.bytes(s) | ||||||
|  | 	local b = {} | ||||||
|  | 	for i=1,#s do | ||||||
|  | 		local c = s:sub(i,i) | ||||||
|  | 		table.insert(b, c:byte()) | ||||||
|  | 	end | ||||||
|  | 	return b | ||||||
|  | end | ||||||
|  | -- Trim leading and trailing whitespace | ||||||
|  | function string.trim(s, ws) | ||||||
|  | 	ws = ws or '[ \t\r\t]' | ||||||
|  | 	return s:gsub('^'..ws..'+', ''):gsub(ws..'+$', '')..'' | ||||||
|  | end | ||||||
|  | -- String slicing and searching using [] operator | ||||||
|  | local str_meta = getmetatable('') | ||||||
|  | local str_meta_index_old= str_meta.__index | ||||||
|  | function str_meta.__index(s,k) | ||||||
|  | 	if type(k)=='string' then | ||||||
|  | 		return str_meta_index_old[k] | ||||||
|  | 	elseif type(k)=='number' then | ||||||
|  | 		if k<0 then k = #s+k+1 end | ||||||
|  | 		return string.sub(s,k,k) | ||||||
|  | 	elseif type(k)=='table' then | ||||||
|  | 		local a = k[1]<0 and (#s+k[1]+1) or k[1] | ||||||
|  | 		local b = k[2]<0 and (#s+k[2]+1) or k[2] | ||||||
|  | 		return string.sub(s,a,b) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | -- String iterator | ||||||
|  | function string.chars(s) | ||||||
|  | 	local i = 0 | ||||||
|  | 	return function() | ||||||
|  | 		i = i+1 | ||||||
|  | 		if i<=#s then return s:sub(i,i) | ||||||
|  | 		else return nil end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | -- Escape sequences | ||||||
|  | local defaultEscapes = { | ||||||
|  | 	['\\'] = '\\\\', | ||||||
|  | 	['\''] = '\\\'', | ||||||
|  | 	['\"'] = '\\\"', | ||||||
|  | 	['\t'] = '\\t', | ||||||
|  | 	['\r'] = '\\r', | ||||||
|  | 	['\n'] = '\\n', | ||||||
|  | 	['\0'] = '\\0', | ||||||
|  | } | ||||||
|  | function string.escape(s, escapes) | ||||||
|  | 	escapes = escapes or defaultEscapes | ||||||
|  | 	local t = {} | ||||||
|  | 	for i=1,#s do | ||||||
|  | 		local c = s:sub(i,i) | ||||||
|  | 		table.insert(t, escapes[c] or c) | ||||||
|  | 	end | ||||||
|  | 	return table.concat(t) | ||||||
|  | end | ||||||
|  | local defaultEscapeChar = '\\' | ||||||
|  | local defaultUnescapes = { | ||||||
|  | 	['\\'] = '\\', | ||||||
|  | 	['\''] = '\'', | ||||||
|  | 	['\"'] = '\"', | ||||||
|  | 	['t'] = '\t', | ||||||
|  | 	['r'] = '\r', | ||||||
|  | 	['n'] = '\n', | ||||||
|  | 	['0'] = '\0', | ||||||
|  | } | ||||||
|  | function string.unescape(s, escapeChar, unescapes) | ||||||
|  | 	escapeChar = escapeChar or defaultEscapeChar | ||||||
|  | 	unescapes = unescapes or defaultUnescapes | ||||||
|  | 	local t = {} | ||||||
|  | 	local inEscape = false | ||||||
|  | 	for i=1,#s do | ||||||
|  | 		local c = s:sub(i,i) | ||||||
|  | 		if inEscape then | ||||||
|  | 			table.insert(t, unescapes[c] | ||||||
|  | 				or error('string.unescape: invalid escape sequence: \'' | ||||||
|  | 					..escapeChar..c..'\'')) | ||||||
|  | 		elseif c==escapeChar then | ||||||
|  | 			inEscape = true | ||||||
|  | 		else | ||||||
|  | 			table.insert(t, c) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 	return table.concat(t) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- IO | ||||||
|  |  | ||||||
|  | io = io or {} | ||||||
|  | -- Read entire file at once, return nil,err if access failed | ||||||
|  | function io.readfile(filename) | ||||||
|  | 	local fi,err = io.open(filename, 'rb') | ||||||
|  | 	if not fi then return nil,err end | ||||||
|  | 	local s = fi:read("*a") | ||||||
|  | 	fi:close() | ||||||
|  | 	return s | ||||||
|  | end | ||||||
|  | -- Write data to file all at once, return true if success / false,err if failure | ||||||
|  | function io.writefile(filename, data) | ||||||
|  | 	local fi,err = io.open(filename, 'wb') | ||||||
|  | 	if not fi then return false,err end | ||||||
|  | 	fi:write(data) | ||||||
|  | 	fi:close() | ||||||
|  | 	return true,nil | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- Math | ||||||
|  |  | ||||||
|  | -- Round | ||||||
|  | function math.round(x) | ||||||
|  | 	return math.floor(x+0.5) | ||||||
|  | end | ||||||
|  | -- Mod that accounts for floating point inaccuracy | ||||||
|  | function math.mod(a,b) | ||||||
|  | 	local m = a%b | ||||||
|  | 	if m==0 or math.abs(m)<1e-15 or math.abs(m-b)<1e-15 then return 0 | ||||||
|  | 	else return m end | ||||||
|  | end | ||||||
|  | -- Clamp value between min and max | ||||||
|  | function math.clamp(v, n, x) | ||||||
|  | 	return math.min(x, math.max(v, n)) | ||||||
|  | end | ||||||
							
								
								
									
										219
									
								
								src/util/vector.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								src/util/vector.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | |||||||
|  |  | ||||||
|  | -- Vector math class with operators | ||||||
|  | local vector_meta | ||||||
|  | local vector_new | ||||||
|  | local function vector_check(v, n, name, argn) | ||||||
|  | 	if not v.__is_vector then | ||||||
|  | 		error('vector '..name..': argument #'..(argn or 1) | ||||||
|  | 			..': expected vector, got '..type(v), n+1) end | ||||||
|  | end | ||||||
|  | local function vector_checksamelen(v1, v2, name) | ||||||
|  | 	vector_check(v1, 3, name, 1) | ||||||
|  | 	vector_check(v2, 3, name, 2) | ||||||
|  | 	if #v1~=#v2 then | ||||||
|  | 		error('vector '..name..': vector lengths do not match (lengths are ' | ||||||
|  | 			..#v1..' and '..#v2..')', 3) end | ||||||
|  | 	return #v1 | ||||||
|  | end | ||||||
|  | local function vector_checklen(v1, v2, name, len) | ||||||
|  | 	vector_check(v1, 3, name, 1) | ||||||
|  | 	vector_check(v2, 3, name, 2) | ||||||
|  | 	if #v1~=len or #v2~=len then | ||||||
|  | 		error('vector '..name..': vector lengths are not '..len..' (lengths are ' | ||||||
|  | 			..#v1..' and '..#v2..')', 3) end | ||||||
|  | end | ||||||
|  | local function vector_opnnn(name, op) | ||||||
|  | 	return function(v1, v2) | ||||||
|  | 		local len = vector_checksamelen(v1, v2, name) | ||||||
|  | 		local v3 = {} | ||||||
|  | 		for i = 1, len do | ||||||
|  | 			v3[i] = op(v1[i], v2[i]) | ||||||
|  | 		end | ||||||
|  | 		return vector_new(v3) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local function vector_opnxn(name, op) | ||||||
|  | 	return function(v1, v2) | ||||||
|  | 		local v1v = type(v1)=='table' and v1.__is_vector | ||||||
|  | 		local v2v = type(v2)=='table' and v2.__is_vector | ||||||
|  | 		if v1v and v2v then | ||||||
|  | 			local len = vector_checksamelen(v1, v2, name) | ||||||
|  | 			local v3 = {} | ||||||
|  | 			for i = 1, len do | ||||||
|  | 				v3[i] = op(v1[i], v2[i]) | ||||||
|  | 			end | ||||||
|  | 			return vector_new(v3) | ||||||
|  | 		else | ||||||
|  | 			if v2v then v1,v2 = v2,v1 end | ||||||
|  | 			local len = #v1 | ||||||
|  | 			local v3 = {} | ||||||
|  | 			for i = 1, len do | ||||||
|  | 				v3[i] = op(v1[i], v2) | ||||||
|  | 			end | ||||||
|  | 			return vector_new(v3) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local function vector_opn0n(name, op) | ||||||
|  | 	return function(v1) | ||||||
|  | 		--vector_check(v1, 1, name) | ||||||
|  | 		local len = #v1 | ||||||
|  | 		local v2 = {} | ||||||
|  | 		for i = 1, len do | ||||||
|  | 			v2[i] = op(v1[i]) | ||||||
|  | 		end | ||||||
|  | 		return vector_new(v2) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | local vector_indices = {x = 1, y = 2, z = 3, w = 4, r = 1, g = 2, b = 3, a = 4} | ||||||
|  | local vector_meta = { | ||||||
|  | 	__is_vector = true, | ||||||
|  | 	__index = function(t, k) | ||||||
|  | 		if tonumber(k) then return rawget(t, k) | ||||||
|  | 		elseif vector_indices[k] then return rawget(t, vector_indices[k]) | ||||||
|  | 		else return getmetatable(t)[k] | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | 	__newindex = function(t, k, v) | ||||||
|  | 		if tonumber(k) then rawset(t, k, v) | ||||||
|  | 		elseif vector_indices[k] then rawset(t, vector_indices[k], v) | ||||||
|  | 		else return | ||||||
|  | 		end | ||||||
|  | 	end, | ||||||
|  | 	__add = vector_opnnn('add', function(x1, x2) return x1+x2 end), | ||||||
|  | 	__sub = vector_opnnn('sub', function(x1, x2) return x1-x2 end), | ||||||
|  | 	__mul = vector_opnxn('mul', function(x1, x2) return x1*x2 end), | ||||||
|  | 	__div = vector_opnxn('div', function(x1, x2) return x1/x2 end), | ||||||
|  | 	__pow = vector_opnxn('pow', function(x1, x2) return x1^x2 end), | ||||||
|  | 	__unm = vector_opn0n('inv', function(x1) return -x1 end), | ||||||
|  | 	__concat = nil, | ||||||
|  | 	--__len = function(v1) return #v1 end, | ||||||
|  | 	__len = nil, | ||||||
|  | 	__eq = function(v1, v2) | ||||||
|  | 		local len = vector_checksamelen(v1, v2, 'equals') | ||||||
|  | 		for i = 1, len do | ||||||
|  | 			if v1[i]~=v2[i] then return false end | ||||||
|  | 		end | ||||||
|  | 		return true | ||||||
|  | 	end, | ||||||
|  | 	__lt = nil, | ||||||
|  | 	__le = nil, | ||||||
|  | 	__call = nil, | ||||||
|  | 	abs = vector_opn0n('abs', math.abs), | ||||||
|  | 	length = function(v1) | ||||||
|  | 		--vector_check(v1, 2, 'length') | ||||||
|  | 		local len = #v1 | ||||||
|  | 		local l = 0 | ||||||
|  | 		for i = 1, len do | ||||||
|  | 			l = l + v1[i]^2 | ||||||
|  | 		end | ||||||
|  | 		return math.sqrt(l) | ||||||
|  | 	end, | ||||||
|  | 	normalize = function(v1) | ||||||
|  | 		--vector_check(v1, 2, 'normal') | ||||||
|  | 		local length = v1:length() | ||||||
|  | 		local len = #v1 | ||||||
|  | 		local v3 = {} | ||||||
|  | 		for i = 1, len do | ||||||
|  | 			if length==0 then v3[i] = 0 | ||||||
|  | 			else v3[i] = v1[i]/length end | ||||||
|  | 		end | ||||||
|  | 		return vector_new(v3) | ||||||
|  | 	end, | ||||||
|  | 	__tostring = function(v1) | ||||||
|  | 		--vector_check(v1, 2, 'tostring') | ||||||
|  | 		local st = {} | ||||||
|  | 		local len = #v1 | ||||||
|  | 		for i = 1, len do | ||||||
|  | 			table.insert(st, tostring(v1[i])) | ||||||
|  | 		end | ||||||
|  | 		return '{ '..table.concat(st, ', ')..' }' | ||||||
|  | 	end, | ||||||
|  | 	unpack = function(v1) return unpack(v1) end, | ||||||
|  | 	floor = vector_opn0n('floor', function(x1) return math.floor(x1) end), | ||||||
|  | 	ceil  = vector_opn0n('ceil' , function(x1) return math.ceil (x1) end), | ||||||
|  | 	round = vector_opn0n('round', function(x1) return math.floor(x1+0.5) end), | ||||||
|  | 	dot = function(v1, v2) | ||||||
|  | 		local len = vector_checksamelen(v1, v2, 'dot') | ||||||
|  | 		local x = 0 | ||||||
|  | 		for i = 1, len do | ||||||
|  | 			x = x + v1[i]*v2[i] | ||||||
|  | 		end | ||||||
|  | 		return x | ||||||
|  | 	end, | ||||||
|  | 	cross = function(v1, v2) | ||||||
|  | 		vector_checklen(v1, v2, 'cross', 3) | ||||||
|  | 		return vector_new{ | ||||||
|  | 			v1[2]*v2[3] - v1[3]*v2[2], | ||||||
|  | 			v1[3]*v2[1] - v1[1]*v2[3], | ||||||
|  | 			v1[1]*v2[2] - v1[2]*v2[1], | ||||||
|  | 		} | ||||||
|  | 	end, | ||||||
|  | 	rotateByAngleId = function(v1, r) | ||||||
|  | 		--vector_check(v1, 2, 'rotate') | ||||||
|  | 		if type(r)~='number' or r%1~=0 then | ||||||
|  | 			error('vector rotateByAngleId: invalid rotation '..tostring(r), 2) end | ||||||
|  | 		r = r%4 | ||||||
|  | 		local v2 | ||||||
|  | 		if     r==0 then v2 = vector_new{  v1[1],  v1[2], v1[3] } | ||||||
|  | 		elseif r==1 then v2 = vector_new{  v1[2], -v1[1], v1[3] } | ||||||
|  | 		elseif r==2 then 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 | ||||||
|  | 	end, | ||||||
|  | 	rotate2d = function(v, r) | ||||||
|  | 		--vector_check(v, 2, 'rotate2d') | ||||||
|  | 		if type(r)~='number' then | ||||||
|  | 			error('vector rotate2d: invalid rotation '..tostring(r), 2) end | ||||||
|  | 		local len = math.sqrt(v[1]^2 + v[2]^2) | ||||||
|  | 		local ang = math.atan2(v[2], v[1]) + r | ||||||
|  | 		local v2 = vector_new{ math.cos(ang)*len, math.sin(ang)*len } | ||||||
|  | 		return v2 | ||||||
|  | 	end, | ||||||
|  | 	tsString = function(v) | ||||||
|  | 		--vector_check(v, 2, 'tsString') | ||||||
|  | 		return table.concat(v, ' ') | ||||||
|  | 	end, | ||||||
|  | 	distance = function(v1, v2) | ||||||
|  | 		local len = vector_checksamelen(v1, v2, 'distance') | ||||||
|  | 		local sum = 0 | ||||||
|  | 		for i=1,len do | ||||||
|  | 			sum = sum + (v1[i] - v2[i])^2 | ||||||
|  | 		end | ||||||
|  | 		return math.sqrt(sum) | ||||||
|  | 	end, | ||||||
|  | 	copy = function(v) | ||||||
|  | 		--vector_check(v, 2, 'copy') | ||||||
|  | 		return vector_new(v) | ||||||
|  | 	end, | ||||||
|  | } | ||||||
|  | vector_new = function(vi) | ||||||
|  | 	if vi then | ||||||
|  | 		if type(vi)=='string' then | ||||||
|  | 			local vi2 = {} | ||||||
|  | 			for val in vi:gmatch('[0-9%.%-e]+') do | ||||||
|  | 				table.insert(vi2, tonumber(val)) | ||||||
|  | 			end | ||||||
|  | 			vi = vi2 | ||||||
|  | 		elseif type(vi)~='table' then | ||||||
|  | 			error('vector: argument #1: expected input table, got '..type(vi), 2) | ||||||
|  | 		end | ||||||
|  | 		local v = {} | ||||||
|  | 		if #vi>0 then | ||||||
|  | 			for i = 1, #vi do v[i] = vi[i] end | ||||||
|  | 		else | ||||||
|  | 			for n, i in pairs(vector_indices) do v[i] = vi[n] end | ||||||
|  | 			if #v==0 then | ||||||
|  | 				error('vector: argument #1: table contains no values', 2) | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		setmetatable(v, vector_meta) | ||||||
|  | 		return v | ||||||
|  | 	else | ||||||
|  | 		error('vector: argument #1: expected input table, got nil', 2) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | vector = vector_new | ||||||
|  | return vector_new | ||||||
		Reference in New Issue
	
	Block a user
	 Redo
					Redo