diff --git a/README.md b/README.md index dcae413..834e724 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ # Payday 2 BLT An open source Lua hook for Payday 2, designed and created for ease of use for both players and modders. This is the developer repository, and should only be used if you know what you're doing. If you don't, visit the website at [PaydayMods.com](http://paydaymods.com/) to get an up-to-date drag-drop install. +The Lua component of the BLT which controls mod loading can be found in it's own repository, [Payday-2-BLT-Lua](https://github.com/JamesWilko/Payday-2-BLT-Lua). ## Download Visit [PaydayMods.com](http://paydaymods.com/) to get the latest stable download. +## Documentation +Documentation for the BLT can be found on the [GitHub Wiki](https://github.com/JamesWilko/Payday-2-BLT/wiki) for the project. + ## Dependencies Payday2 BLT requires the following dependencies, which are all statically linked. * OpenSSL @@ -25,10 +29,27 @@ I had to add SAFESEH handling to the MASM objects in order for this to be compat ### Detours A compiled version of detours is included, and all terms of the included Microsoft Research Shared Source License Agreement (detours_license.rtf) are applicable. -## Documentation -All documentation can be found via the navigation bar the Payday Mods site, or just [go to it directly](http://payday-2-blt-docs.readthedocs.org/en/latest/). It's also available on [GitHub](https://github.com/JamesWilko/Payday-2-BLT-Docs) (or click the 'Edit on GitHub' button) so that you can contribute stuff if you need to, or need us to fix something. -The documentation is written in [markdown](http://daringfireball.net/projects/markdown/) with the help of [MkDocs](http://www.mkdocs.org/) and [ReadTheDocs](https://readthedocs.org/). - -## Developers -The Payday 2 BLT was made by [James Wilkinson](http://jameswilko.com/) and [SirWaddlesworth](http://sirwaddlesworth.com/). We're friendly guys sometimes, so if you need help or want to discuss something you can reach us at any of the contact details we've got listed [over here](http://paydaymods.com/contact/). -We've also had help from a bunch of other people who've tested stuff, reported bugs, suggested changes, and everthing else. So thanks to GREAT BIG BUSHY BEARD, Kail, Dougley, and everybody else! +## Contributors +- Payday 2 BLT Team + * [James Wilkinson](http://jameswilko.com/) ([Twitter](http://twitter.com/_JamesWilko)) + * [SirWaddlesworth](http://genj.io/) + * [Will Donohoe](https://will.io/) + +- Contributors, Translators, Testers and more + * saltisgood + * Kail + * Dougley + * awcjack + * BangL + * chromKa + * xDarkWolf + * Luffyyy + * NHellFire + * TdlQ + * Mrucux7 + * Simon + * goontest + * aayanl + * cjur3 + * Kilandor + * and others who haven't been added yet diff --git a/proj/IPHLPAPI.vcxproj b/proj/IPHLPAPI.vcxproj index 47b9319..0edf0d2 100644 --- a/proj/IPHLPAPI.vcxproj +++ b/proj/IPHLPAPI.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -13,6 +13,7 @@ {74B4EB55-E7B5-428A-9FAF-BE86058A56A6} IPHLPAPI + 10.0.14393.0 diff --git a/src/InitiateState.cpp b/src/InitiateState.cpp index 88bb807..910642a 100644 --- a/src/InitiateState.cpp +++ b/src/InitiateState.cpp @@ -1,5 +1,4 @@ #include "InitState.h" - #define WIN32_LEAN_AND_MEAN 1 #include #include @@ -13,478 +12,691 @@ #include #include +// Code taken from LuaJIT 2.1.0-beta2 namespace pd2hook { -struct lua_State; - -typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); -typedef int(*lua_CFunction) (lua_State *L); -typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); -typedef struct luaL_Reg { - const char* name; - lua_CFunction func; -} luaL_Reg; - -CREATE_CALLABLE_SIGNATURE(lua_call, void, "\x55\x8B\xEC\x83\xE4\xF8\x56\x8B\xF1\xB9\x00\x00\x00\x00\x66\xFF\x46\x34", "xxxxxxxxxx????xxxx", 0, lua_State*, int, int) -CREATE_CALLABLE_SIGNATURE(lua_pcall, int, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x08\x56\x8B\xF2", "xxxxxxxxxxxx", 0, lua_State*, int, int, int) -//CREATE_CALLABLE_SIGNATURE(lua_gettop, int, "\x8B\x4C\x24\x04\x8B\x41\x08\x2B\x41\x0C", "xxxxxxxxxx", 0, lua_State*) -CREATE_CALLABLE_SIGNATURE(lua_settop, void, "\x85\xD2\x78\x2E\x8B\x41\x0C\xC1\xE2\x03\x03\xC2", "xxxxxxxxxxxx", 0, lua_State*, int) -CREATE_CALLABLE_SIGNATURE(lua_tolstring, const char*, "\x56\x57\x8B\xFA\x8B\xF1\xE8\x00\x00\x00\x00\x8B\xC8\x83\x79\x04\x04", "xxxxxxx????xxxxxx", 0, lua_State*, int, size_t*) -CREATE_CALLABLE_SIGNATURE(luaL_loadfile, int, "\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\x00\x00\x00\x00\x53\x55\x56\x57\x8B\xEA\x8B\xF9\xC7\x44\x24\x00\x00\x00\x00\x00\x8B\x5F\x08\x2B\x5F\x0C", "xxxxxxxx????xxxxxxxxxxx?????xxxxxx", 0, lua_State*, const char*) -//CREATE_CALLABLE_SIGNATURE(lua_load, int, "\x8B\x4C\x24\x10\x33\xD2\x83\xEC\x18\x3B\xCA", "xxxxxxxxxxx", 0, lua_State*, lua_Reader, void*, const char*) -CREATE_CALLABLE_SIGNATURE(lua_setfield, void, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x0C\x53\x56\x57\x8B\xF9\xE8\x00\x00\x00\x00\x8B\x55\x08\x8B\xF2\x8B\xD8\x8D\x4E\x01\x8D\x49\x00\x8A\x06\x46\x84\xC0\x75\xF9\x2B\xF1\x56\x8B\xCF\xE8\x00\x00\x00\x00\x89\x44\x24\x14\x8B\x47\x08\x83\xE8\x08\x50", "xxxxxxxxxxxxxxx????xxxxxxxxxxxxxxxxxxxxxxxxxx????xxxxxxxxxxx", 0, lua_State*, int, const char*) -CREATE_CALLABLE_SIGNATURE(lua_createtable, void, "\x53\x56\x57\x8B\xF9\x8B\xDA\x8B\x4F\x10\x8B\x41\x48\x85\xC0\x74\x0B\x48\x89\x41\x48\x8B\xCF\xE8\x00\x00\x00\x00\x8B\x4F\x10\x8B\x41\x4C\x3B\x41\x40\x72\x07\x8B\xCF\xE8\x00\x00\x00\x00\xFF\x74\x24\x10\x8B\x77\x08\x8B\xD3\x8B\xCF\xE8\x00\x00\x00\x00\x83\xC4\x04\x89\x06\xC7\x46\x04\x05\x00\x00\x00", "xxxxxxxxxxxxxxxxxxxxxxxx????xxxxxxxxxxxxxx????xxxxxxxxxxxx????xxxxxxxxx???", 0, lua_State*, int, int) -CREATE_CALLABLE_SIGNATURE(lua_insert, void, "\x53\x57\x8B\xD9\xE8\x00\x00\x00\x00\x8B\x53\x08\x8B\xF8\x3B\xD7", "xxxxx????xxxxxxx", 0, lua_State*, int) -//CREATE_CALLABLE_SIGNATURE(lua_newstate, lua_State*, "\x53\x55\x8B\x6C\x24\x0C\x56\x57\x8B\x7C\x24\x18\x68\x00\x00\x00\x00\x33\xDB", "xxxxxxxxxxxxx????xx", 0, lua_Alloc, void*) -CREATE_CALLABLE_SIGNATURE(lua_close, void, "\x55\x8B\xEC\x83\xE4\xF8\x51\x56\x8B\x71\x10\x8B\x76\x70", "xxxxxxxxxxxxxx", 0, lua_State*) - -CREATE_CALLABLE_SIGNATURE(lua_rawset, void, "\x51\x53\x55\x56\x57\x8B\xF1\xE8\x00\x00\x00\x00", "xxxxxxxx????", 0, lua_State*, int) -//CREATE_CALLABLE_SIGNATURE(lua_settable, void, "\x8B\x4C\x24\x08\x56\x8B\x74\x24\x08\x8B\xD6\xE8\x00\x00\x00\x00\x8B\x4E\x08\x8D\x51\xF8", "xxxxxxxxxxxx????xxxxxx", 0, lua_State*, int) - -//CREATE_CALLABLE_SIGNATURE(lua_pushnumber, void, "\x8B\x44\x24\x04\x8B\x48\x08\xF3\x0F\x10\x44\x24\x08", "xxxxxxxxxxxxx", 0, lua_State*, double) -//CREATE_CALLABLE_SIGNATURE(lua_pushinteger, void, "\x8B\x44\x24\x04\x8B\x48\x08\xF3\x0F\x2A\x44\x24\x08", "xxxxxxxxxxxxx", 0, lua_State*, ptrdiff_t) -//CREATE_CALLABLE_SIGNATURE(lua_pushboolean, void, "\x8B\x44\x24\x04\x8B\x48\x08\x33", "xxxxxxxx", 0, lua_State*, bool) -CREATE_CALLABLE_SIGNATURE(lua_pushcclosure, void, "\x83\xEC\x08\x53\x55\x56\x8B\xF1\x57\x8B\x4E\x10\x89\x54\x24\x14", "xxxxxxxxxxxxxxxx", 0, lua_State*, lua_CFunction, int); -CREATE_CALLABLE_SIGNATURE(lua_pushlstring, void, "\x53\x56\x57\x8B\xF9\x8B\xDA\x8B\x4F\x10\x8B\x41\x48\x85\xC0\x74\x0B\x48\x89\x41\x48\x8B\xCF\xE8\x00\x00\x00\x00\x8B\x4F\x10\x8B\x41\x4C\x3B\x41\x40\x72\x07\x8B\xCF\xE8\x00\x00\x00\x00\xFF\x74\x24\x10\x8B\x77\x08\x8B\xD3\x8B\xCF\xE8\x00\x00\x00\x00\x83\xC4\x04\x89\x06\xC7\x46\x04\x04\x00\x00\x00", "xxxxxxxxxxxxxxxxxxxxxxxx????xxxxxxxxxxxxxx????xxxxxxxxxxxx????xxxxxxxxx???", 0, lua_State*, const char*, size_t) - -CREATE_CALLABLE_SIGNATURE(luaI_openlib, void, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x14\x53\x56\x8B\xDA\x57\x8B\xF1\x85\xDB\x0F\x84\x00\x00\x00\x00\x8B\x4D\x08", "xxxxxxxxxxxxxxxxxxxx????xxx", 0, lua_State*, const char*, const luaL_Reg*, int) -CREATE_CALLABLE_SIGNATURE(luaL_ref, int, "\x83\xEC\x0C\x56\x8B\xF1\x57\x8B\x46\x08\x83\xC0\xF8\x3D\x00\x00\x00\x00\x74\x12", "xxxxxxxxxxxxxx????xx", 0, lua_State*, int); -//CREATE_CALLABLE_SIGNATURE(lua_rawgeti, void, "\x8B\x4C\x24\x08\x56\x8B\x74\x24\x08\x8B\xD6\xE8\x00\x00\x00\x00\x8B\x4C\x24\x10", "xxxxxxxxxxxx????xxxx", 0, lua_State*, int, int); -CREATE_CALLABLE_SIGNATURE(luaL_unref, void, "\x56\x57\x8B\x7C\x24\x0C\x8B\xF1\x85\xFF\x78\x5B", "xxxxxxxxxxxx", 0, lua_State*, int, int); -CREATE_CALLABLE_CLASS_SIGNATURE(do_game_update, void*, "\x56\xFF\x74\x24\x0C\x8B\xF1\xBA\x00\x00\x00\x00\x8B\x0E", "xxxxxxxx????xx", 0, int*, int*) -CREATE_CALLABLE_CLASS_SIGNATURE(luaL_newstate, int, "\x51\x8B\x44\x24\x10\x53\x56\x57\x8B\xF9\x85\xC0", "xxxxxxxxxxxx", 0, char, char, int) - -// lua c-functions - + struct lua_State; + + typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + typedef int(*lua_CFunction) (lua_State *L); + typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + typedef struct luaL_Reg { + const char* name; + lua_CFunction func; + } luaL_Reg; + + // From src/luaconf.h +#define LUA_NUMBER double + + // From src/lua.h + // type of numbers in Lua + typedef LUA_NUMBER lua_Number; + 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); + + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_call, void, "\x8B\x44\x24\x08\x8B\x54\x24\x04\xFF\x44\x24\x0C\x8D\x0C\xC5\x00", "xxxxxxxxxxxxxxxx", 0, lua_State*, int, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_pcall, int, "\x8B\x54\x24\x04\x8B\x4C\x24\x10\x53\x56\x8B\x72\x08\x8A", "xxxxxxxxxxxxxx", 0, lua_State*, int, int, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_gettop, int, "\x8B\x4C\x24\x04\x8B\x41\x14\x2B\x41\x10\xC1\xF8\x03\xC3", "xxxxxxxxxxxxxx", 0, lua_State*) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_settop, void, "\x8B\x44\x24\x08\x85\xC0\x78\x5B\x53\x56\x8B\x74\x24\x0C\x57\x8B", "xxxxxxxxxxxxxxxx", 0, lua_State*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_toboolean, int, "\xFF\x74\x24\x08\xFF\x74\x24\x08\xE8\x00\x00\x00\x00\x83\xC4\x08\x83\x78\x04\xFE\x1B\xC0\xF7\xD8\xC3", "xxxxxxxxx????xxxxxxxxxxxx", 0, lua_State*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_tointeger, ptrdiff_t, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x08\xFF\x75\x0C\xFF\x75\x08\xE8\x00\x00\x00\x00\x8B\x48\x04\x83\xC4\x08\x83\xF9\xF2\x73\x0C\xF2\x0F\x10\x00\xF2\x0F\x2C\xC0\x8B\xE5\x5D\xC3\x83\xF9\xFB\x75\x26", "xxxxxxxxxxxxxxxx????xxxxxxxxxxxxxxxxxxxxxxxxxxxx", 0, lua_State*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_tonumber, lua_Number, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x08\xFF\x75\x0C\xFF\x75\x08\xE8\x00\x00\x00\x00\x8B\x48\x04\x83\xC4\x08\x83\xF9\xF2\x77\x06\xDD", "xxxxxxxxxxxxxxxx????xxxxxxxxxxxx", 0, lua_State*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_tolstring, const char*, "\x83\xEC\x24\xA1\x00\x00\x00\x00\x33\xC4\x89\x44\x24\x20\x53\x8B\x5C\x24\x2C\x56\x8B\x74\x24\x34", "xxxx????xxxxxxxxxxxxxxxx", 0, lua_State*, int, size_t*) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_objlen, size_t, "\x83\xEC\x24\xA1\x00\x00\x00\x00\x33\xC4\x89\x44\x24\x20\x8B\x44", "xxxx????xxxxxxxx", 0, lua_State*, int) + // This is actually luaL_loadfilex() (as per Lua 5.2) now. The new parameter corresponds to mode, and specifying NULL causes Lua + // to default to "bt", i.e. 'binary and text' + // https://www.lua.org/manual/5.2/manual.html#luaL_loadfilex + // https://www.lua.org/manual/5.2/manual.html#pdf-load + CREATE_NORMAL_CALLABLE_SIGNATURE(luaL_loadfilex, int, "\x81\xEC\x08\x02\x00\x00\xA1\x00\x00\x00\x00\x33\xC4\x89\x84\x24", "xxxxxxx????xxxxx", 0, lua_State*, const char*, const char*) + CREATE_NORMAL_CALLABLE_SIGNATURE(luaL_loadstring, int, "\x8B\x54\x24\x08\x83\xEC\x08\x8B\xC2\x56\x8D\x70\x01\x8D\x49\x00", "xxxxxxxxxxxxxxxx", 0, lua_State*, const char*) + //CREATE_CALLABLE_SIGNATURE(lua_load, int, "\x8B\x4C\x24\x10\x33\xD2\x83\xEC\x18\x3B\xCA", "xxxxxxxxxxx", 0, lua_State*, lua_Reader, void*, const char*) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_getfield, void, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x08\x56\x8B\x75\x08\x57\xFF\x75", "xxxxxxxxxxxxxxxx", 0, lua_State*, int, const char*) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_setfield, void, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x10\x56\x8B\x75\x08\x57\xFF\x75", "xxxxxxxxxxxxxxxx", 0, lua_State*, int, const char*) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_createtable, void, "\x56\x8B\x74\x24\x08\x8B\x4E\x08\x8B\x41\x14\x3B\x41\x18\x72\x07\x8B\xCE\xE8\x00\x00\x00\x00\x8B\x44\x24\x10\x85\xC0\x74\x12\x83", "xxxxxxxxxxxxxxxxxxx????xxxxxxxxx", 0, lua_State*, int, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_insert, void, "\x8B\x4C\x24\x08\x56\x57\x85\xC9\x7E\x21\x8B\x54\x24\x0C\x8D\x71", "xxxxxxxxxxxxxxxx", 0, lua_State*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_replace, void, "\x56\x57\x8B\x7C\x24\x10\x81\xFF\xEE\xD8\xFF\xFF\x75\x16\x8B\x4C\x24\x0C\x5F\x8B\x41\x14\x8D\x71\x14\x8B\x40\xF8\x83\x06\xF8\x89", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 0, lua_State*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_remove, void, "\x8B\x4C\x24\x08\x56\x57\x85\xC9\x7E\x21\x8B\x7C\x24\x0C\x8B\x47\x10\x8B\x57\x14\x8D\x77\x14\x8D\x04\xC8\x83\xC0\xF8\x3B\xC2\x72", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 0, lua_State*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_newstate, lua_State*, "\x53\x8B\x5C\x24\x0C\x55\x8B\x6C\x24\x0C\x56\x57\x68\x40\x10\x00\x00\x6A\x00\x6A\x00\x53\xFF\xD5\x8B\xF0\x83\xC4\x10\x8D\x7E\x30", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 0, lua_Alloc, void*) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_close, void, "\x8B\x44\x24\x04\x53\x56\x57\x8B\x78\x08\x8B\x77\x74\x56\xE8", "xxxxxxxxxxxxxxx", 0, lua_State*) + + //CREATE_CALLABLE_SIGNATURE(lua_rawset, void, "\x51\x53\x55\x56\x57\x8B\xF1\xE8\x00\x00\x00\x00", "xxxxxxxx????", 0, lua_State*, int) + // Reviving lua_settable() since the function exists again, and because the Crimefest 2015 alternative relied upon internal Lua + // VM functions, which do not apply to LuaJIT + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_settable, void, "\x56\xFF\x74\x24\x0C\x8B\x74\x24\x0C\x56\xE8\x00\x00\x00\x00\x8B\x4E\x14\x83\xE9\x10\x51\x50\x56", "xxxxxxxxxxx????xxxxxxxxx", 0, lua_State*, int) + + //CREATE_CALLABLE_SIGNATURE(lua_pushnumber, void, "\x8B\x44\x24\x04\x8B\x48\x08\xF3\x0F\x10\x44\x24\x08", "xxxxxxxxxxxxx", 0, lua_State*, double) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_pushinteger, void, "\x66\x0F\x6E\x44\x24\x08\x8B\x4C\x24\x04\xF3\x0F\xE6\xC0\x8B\x41", "xxxxxxxxxxxxxxxx", 0, lua_State*, ptrdiff_t) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_pushboolean, void, "\x8B\x4C\x24\x04\x33\xC0\x39\x44\x24\x08\xBA\xFE\xFF\xFF\xFF\x0F", "xxxxxxxxxxxxxxxx", 0, lua_State*, bool) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_pushcclosure, void, "\x56\x8B\x74\x24\x08\x8B\x4E\x08\x8B\x41\x14\x3B\x41\x18\x72\x07\x8B\xCE\xE8\x00\x00\x00\x00\x8B\x46\x10\x8B\x40\xF8\x80\x78\x05", "xxxxxxxxxxxxxxxxxxx????xxxxxxxxx", 0, lua_State*, lua_CFunction, int); + // lua_pushstring()'s signature was found before lua_pushlstring()'s, so I'm leaving it here now since it's valid anyway + // It was used as a quick and dirty - and broken - workaround since most lua_pushlstring() calls are inlined, but it ended up + // breaking HTTP downloads of zip archives due to its sensitivity to premature null characters. A non-inlined signature for + // lua_pushlstring() was found by cross-referencing the string 'loaders' to lj_cf_package_require(), which is part of LuaJIT + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_pushlstring, void, "\x56\x8B\x74\x24\x08\x8B\x4E\x08\x8B\x41\x14\x3B\x41\x18\x72\x07\x8B\xCE\xE8\x00\x00\x00\x00\xFF", "xxxxxxxxxxxxxxxxxxx????x", 0, lua_State*, const char*, size_t) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_pushstring, void, "\x56\x8B\x74\x24\x08\x57\x8B\x7C\x24\x10\x85\xFF\x75\x0C\x8B\x46", "xxxxxxxxxxxxxxxx", 0, lua_State*, const char*) + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_checkstack, int, "\x8B\x54\x24\x08\x81\xFA\x40\x1F\x00\x00\x7F\x38\x8B\x4C\x24\x04", "xxxxxxxxxxxxxxxx", 0, lua_State*, int) + + // luaI_openlib() is really luaL_openlib(), see lauxlib.h in Lua 5.1's source code + CREATE_NORMAL_CALLABLE_SIGNATURE(luaI_openlib, void, "\x55\x8B\xEC\x83\xE4\xF8\xF2\x0F\x10\x00\x00\x00\x00\x00\x83\xEC\x08\x56\x8B\x75\x08\x57\x8B\x46\x14\xF2\x0F", "xxxxxxxxx?????xxxxxxxxxxxxx", 0, lua_State*, const char*, const luaL_Reg*, int) + CREATE_NORMAL_CALLABLE_SIGNATURE(luaL_ref, int, "\x55\x8B\xEC\x83\xE4\xF8\x8B\x55\x0C\x83\xEC\x08\x8D\x82\x0F\x27\x00\x00\x56\x8B\x75\x08\x57", "xxxxxxxxxxxxxxxx", 0, lua_State*, int); + // Reviving lua_rawgeti() since the function exists again, and because the Crimefest 2015 alternative relied upon internal Lua VM + // functions, which do not apply to LuaJIT + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_rawgeti, void, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x0C\x56\xFF\x75\x0C\x8B\x75\x08\x56\xE8", "xxxxxxxxxxxxxxxxxx", 0, lua_State*, int, int); + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_rawseti, void, "\x53\x56\x8B\x74\x24\x0C\x57\xFF\x74\x24\x14\x56\xE8\x00\x00\x00\x00\x8B\x38\x8B", "xxxxxxxxxxxxx????xxx", 0, lua_State*, int, int); + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_type, int, "\x56\xFF\x74\x24\x0C\x8B\x74\x24\x0C\x56\xE8\x00\x00\x00\x00\x8B\xD0\x83\xC4\x08\x8B\x4A\x04\x83\xF9\xF2\x77\x07\xB8\x03\x00\x00\x00\x5E\xC3\x8B\x46\x08\x05\x90", "xxxxxxxxxxx????xxxxxxxxxxxxxxxxxxxxxxxxx", 0, lua_State*, int); + CREATE_NORMAL_CALLABLE_SIGNATURE(lua_typename, const char*, "\x8B\x44\x24\x08\x8B\x00\x00\x00\x00\x00\x00\xC3\xCC", "xxxxx??????xx", 0, lua_State*, int); + CREATE_NORMAL_CALLABLE_SIGNATURE(luaL_unref, void, "\x53\x8B\x5C\x24\x10\x85\xDB\x78\x67\x56\x8B\x74\x24\x0C\x57\x8B", "xxxxxxxxxxxxxxxx", 0, lua_State*, int, int); + CREATE_CALLABLE_CLASS_SIGNATURE(do_game_update, void*, "\x56\xFF\x74\x24\x0C\x8B\xF1\x68\x00\x00\x00\x00\xFF\x36\xE8", "xxxxxxxx????xxx", 0, int*, int*) + CREATE_CALLABLE_CLASS_SIGNATURE(luaL_newstate, int, "\x53\x56\x33\xDB\x57\x8B\xF1\x39\x5C\x24\x18\x0F", "xxxxxxxxxxxx", 0, char, char, int) + + // lua c-functions + + // From src/lua.h + // Pseudo-indices #define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) #define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) -// more bloody lua shit + // From src/lauxlib.h +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + + // more bloody lua shit + // Thread status; 0 is OK #define LUA_YIELD 1 #define LUA_ERRRUN 2 #define LUA_ERRSYNTAX 3 #define LUA_ERRMEM 4 #define LUA_ERRERR 5 + // From src/lauxlib.h + // Extra error code for 'luaL_load' #define LUA_ERRFILE (LUA_ERRERR+1) -struct luastackState { - char a[8]; - int top; - int base; -}; - -union LValue { - void* gc; - void* p; - float n; - int b; -}; - -struct TValue { - LValue value; - int tt; -}; - -typedef TValue *StkId; - -struct lua_State { - char a[8]; - StkId top; - StkId base; -}; - -class LTable; - -CREATE_CALLABLE_SIGNATURE(index2adr, TValue*, "\x85\xD2\x7E\x13\x8B\x41\x0C\x4A\x8D\x14\xD0\x3B\x51\x08\xB8\x00\x00\x00\x00", "xxxxxxxxxxxxxxx????", 0, lua_State*, int) -CREATE_CALLABLE_SIGNATURE(luaV_settable, void, "\x83\xEC\x08\x53\x55\x56\x8B\xEA\x57\x89\x6C\x24\x10\x8B\xD9\xC7\x44\x24\x00\x00\x00\x00\x00\xEB\x07\x8D\xA4\x24\x00\x00\x00\x00", "xxxxxxxxxxxxxxxxxx?????xxxxx????", 0, lua_State*, TValue*, TValue*, StkId) -CREATE_CALLABLE_SIGNATURE(luaH_getnum, TValue*, "\x51\x57\x8B\xF9\x8D\x42\xFF\x3B\x47\x1C\x73\x0C", "xxxxxxxxxxxx", 0, LTable*, int) - -void lua_rawgeti(lua_State* L, int idx, int n) { - StkId o; - o = index2adr(L, idx); - LTable* ott = (LTable*)(o)->value.gc; - TValue* o2 = luaH_getnum(ott, n); - L->top->value = o2->value; - L->top->tt = o2->tt; - L->top++; -} - -void lua_settable(lua_State* L, int idx) { - StkId t; - t = index2adr(L, idx); - luaV_settable(L, t, L->top - 2, L->top - 1); - L->top -= 2; -} - -int lua_gettop(lua_State* L) { - return L->top - L->base; -} - -void lua_pushboolean(lua_State* L, int b) -{ - TValue* i_o = L->top; - i_o->value.b = (b != 0); - i_o->tt = 1; // LUA_TBOOLEAN - L->top++; -} + // From src/lua.h + // Option for multiple returns in 'lua_pcall' and 'lua_call' +#define LUA_MULTRET (-1) +#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 + +#define lua_pop(L,n) lua_settop(L, -(n)-1) +#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_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + std::list activeStates; + void add_active_state(lua_State* L) + { + activeStates.push_back(L); + } -void lua_pushinteger(lua_State* L, int n) -{ - TValue* i_o = L->top; - i_o->value.n = static_cast(n); - i_o->tt = 3; // LUA_TNUMBER - L->top++; -} - -std::list activeStates; -void add_active_state(lua_State* L){ - activeStates.push_back(L); -} - -void remove_active_state(lua_State* L){ - activeStates.remove(L); -} - -bool check_active_state(lua_State* L){ - PD2HOOK_TRACE_FUNC; - std::list::iterator it; - for (it = activeStates.begin(); it != activeStates.end(); it++){ - if (*it == L) { - return true; + void remove_active_state(lua_State* L) + { + activeStates.remove(L); + } + + bool check_active_state(lua_State* L) + { + std::list::iterator it; + for (it = activeStates.begin(); it != activeStates.end(); it++) + { + if (*it == L) + { + return true; + } } + return false; } - return false; -} -void __fastcall lua_newcall(lua_State* L, int args, int returns){ - PD2HOOK_TRACE_FUNC; - int result = lua_pcall(L, args, returns, 0); - if (result != 0) { - size_t len; - PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); + FuncDetour* luaCallDetour = nullptr; + + // Not tracking the error count here so it can automatically be reset to 0 whenever the Lua state is deleted and re-created (e.g. + // when transitioning to / from the menu to a level) + void NotifyErrorOverlay(lua_State* L, const char* message) + { + lua_getglobal(L, "NotifyErrorOverlay"); + if (lua_isfunction(L, -1)) + { + int args = 0; + if (message) + { + lua_pushstring(L, message); + args = 1; + } + int error = lua_pcall(L, args, 0, 0); + if (error == LUA_ERRRUN) + { + // Don't bother logging the error since the error overlay is designed to be an optional component, just pop the error + // message off the stack to keep it balanced + lua_pop(L, 1); + return; + } + } + else + { + lua_pop(L, 1); + static bool printed = false; + if (!printed) + { + printf("Warning: Failed to find the NotifyErrorOverlay function in the Lua environment; no in-game notifications will be displayed for caught errors\n"); + printed = true; + } + } } - PD2HOOK_LOG_LOG("lua call"); -} - -int luaH_getcontents(lua_State* L, bool files){ - PD2HOOK_TRACE_FUNC; - size_t len; - const char* dirc = lua_tolstring(L, 1, &len); - std::string dir(dirc, len); - std::vector directories; - - try { - directories = Util::GetDirectoryContents(dir, files); + + void lua_newcall(lua_State* L, int args, int returns) + { + // https://stackoverflow.com/questions/30021904/lua-set-default-error-handler/30022216#30022216 + lua_getglobal(L, "debug"); + lua_getfield(L, -1, "traceback"); + lua_remove(L, -2); + // Do not index from the top (i.e. use a negative index) as this has the potential to mess up if the callee function returns + // values /and/ lua_pcall() is set up with a > 0 nresults argument + int errorhandler = lua_gettop(L) - args - 1; + lua_insert(L, errorhandler); + + int result = lua_pcall(L, args, returns, errorhandler); + if (result != 0) + { + size_t len; + const char* message = lua_tolstring(L, -1, &len); + PD2HOOK_LOG_ERROR(message); + NotifyErrorOverlay(L, message); + // This call pops the error message off the stack + lua_pop(L, 1); + // No, don't touch this variable anymore + message = nullptr; + } + // This call removes the error handler from the stack. Do not use lua_pop() as the callee function's return values may be + // present, which would pop one of those instead and leave the error handler on the stack + lua_remove(L, errorhandler); } - catch (const Util::IOException& e){ - PD2HOOK_LOG_EXCEPTION(e); - lua_pushboolean(L, false); + + int luaF_ispcallforced(lua_State* L) + { + lua_pushboolean(L, luaCallDetour ? true : false); return 1; } - lua_createtable(L, 0, 0); + int luaF_forcepcalls(lua_State* L) + { + int args = lua_gettop(L); // Number of arguments + if (args < 1) + { + PD2HOOK_LOG_WARN("blt.forcepcalls(): Called with no arguments, ignoring request"); + return 0; + } - std::vector::iterator it; - int index = 1; - for (it = directories.begin(); it < directories.end(); it++){ - if (*it == "." || *it == "..") continue; - lua_pushinteger(L, index); - lua_pushlstring(L, it->c_str(), it->length()); - lua_settable(L, -3); - index++; + if (lua_toboolean(L, 1)) + { + if (!luaCallDetour) + { + luaCallDetour = new FuncDetour((void**)&lua_call, lua_newcall); + PD2HOOK_LOG_LOG("blt.forcepcalls(): Protected calls will now be forced"); + } + // else Logging::Log("blt.forcepcalls(): Protected calls are already being forced, ignoring request", Logging::LOGGING_WARN); + } + else + { + if (luaCallDetour) + { + delete luaCallDetour; + luaCallDetour = nullptr; + PD2HOOK_LOG_LOG("blt.forcepcalls(): Protected calls are no longer being forced"); + } + // else Logging::Log("blt.forcepcalls(): Protected calls are not currently being forced, ignoring request", Logging::LOGGING_WARN); + } + return 0; } - return 1; -} - -int luaF_getdir(lua_State* L){ - PD2HOOK_TRACE_FUNC; - return luaH_getcontents(L, true); -} - -int luaF_getfiles(lua_State* L){ - return luaH_getcontents(L, false); -} - -int luaF_directoryExists(lua_State* L){ - PD2HOOK_TRACE_FUNC; - size_t len; - const char* dirc = lua_tolstring(L, 1, &len); - bool doesExist = Util::DirectoryExists(dirc); - lua_pushboolean(L, doesExist); - return 1; -} - -int luaF_unzipfile(lua_State* L){ - PD2HOOK_TRACE_FUNC; - size_t len; - const char* archivePath = lua_tolstring(L, 1, &len); - const char* extractPath = lua_tolstring(L, 2, &len); - - pd2hook::ExtractZIPArchive(archivePath, extractPath); - return 0; -} - -int luaF_removeDirectory(lua_State* L){ - PD2HOOK_TRACE_FUNC; - size_t len; - const char* directory = lua_tolstring(L, 1, &len); - bool success = Util::RemoveEmptyDirectory(directory); - lua_pushboolean(L, success); - return 1; -} - -int luaF_pcall(lua_State* L){ - PD2HOOK_TRACE_FUNC; - int args = lua_gettop(L); - - int result = lua_pcall(L, args - 1, -1, 0); - if (result == LUA_ERRRUN){ + int luaH_getcontents(lua_State* L, bool files) + { size_t len; - PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); - return 0; - } - lua_pushboolean(L, result == 0); - lua_insert(L, 1); + const char* dirc = lua_tolstring(L, 1, &len); + std::string dir(dirc, len); + std::vector directories; + + try + { + directories = Util::GetDirectoryContents(dir, files); + } + catch (int unused) + { + // Okay, okay - now shut up, compiler + (void)unused; + lua_pushboolean(L, false); + return 1; + } - //if (result != 0) return 1; + lua_createtable(L, 0, 0); + + std::vector::iterator it; + int index = 1; + for (it = directories.begin(); it < directories.end(); it++) + { + if (*it == "." || *it == "..") continue; + lua_pushinteger(L, index); + lua_pushlstring(L, it->c_str(), it->length()); + lua_settable(L, -3); + index++; + } - return lua_gettop(L); -} + return 1; + } -int luaF_dofile(lua_State* L){ - PD2HOOK_TRACE_FUNC; + int luaF_getdir(lua_State* L) + { + return luaH_getcontents(L, true); + } - int n = lua_gettop(L); + int luaF_getfiles(lua_State* L) + { + return luaH_getcontents(L, false); + } - size_t length = 0; - const char* filename = lua_tolstring(L, 1, &length); - int error = luaL_loadfile(L, filename); - if (error == LUA_ERRSYNTAX){ + int luaF_directoryExists(lua_State* L) + { size_t len; - PD2HOOK_LOG_ERROR(filename << " - " << lua_tolstring(L, -1, &len)); + const char* dirc = lua_tolstring(L, 1, &len); + bool doesExist = Util::DirectoryExists(dirc); + lua_pushboolean(L, doesExist); + return 1; } - error = lua_pcall(L, 0, 0, 0); - if (error == LUA_ERRRUN){ + + int luaF_unzipfile(lua_State* L) + { size_t len; - PD2HOOK_LOG_ERROR(filename << " - " << lua_tolstring(L, -1, &len)); - } - return 0; -} - -struct lua_http_data { - int funcRef; - int progressRef; - int requestIdentifier; - lua_State* L; -}; - -void return_lua_http(void* data, std::string& urlcontents){ - PD2HOOK_TRACE_FUNC; - lua_http_data* ourData = (lua_http_data*)data; - if (!check_active_state(ourData->L)) { - delete ourData; - return; + const char* archivePath = lua_tolstring(L, 1, &len); + const char* extractPath = lua_tolstring(L, 2, &len); + + pd2hook::ExtractZIPArchive(archivePath, extractPath); + return 0; } - lua_rawgeti(ourData->L, LUA_REGISTRYINDEX, ourData->funcRef); - lua_pushlstring(ourData->L, urlcontents.c_str(), urlcontents.length()); - lua_pushinteger(ourData->L, ourData->requestIdentifier); - lua_pcall(ourData->L, 2, 0, 0); - luaL_unref(ourData->L, LUA_REGISTRYINDEX, ourData->funcRef); - luaL_unref(ourData->L, LUA_REGISTRYINDEX, ourData->progressRef); - delete ourData; -} - -void progress_lua_http(void* data, long progress, long total){ - PD2HOOK_TRACE_FUNC; - lua_http_data* ourData = (lua_http_data*)data; - - if (!check_active_state(ourData->L)){ - return; + int luaF_removeDirectory(lua_State* L) + { + size_t len; + const char* directory = lua_tolstring(L, 1, &len); + bool success = Util::RemoveEmptyDirectory(directory); + lua_pushboolean(L, success); + return 1; } - if (ourData->progressRef == 0) return; - lua_rawgeti(ourData->L, LUA_REGISTRYINDEX, ourData->progressRef); - lua_pushinteger(ourData->L, ourData->requestIdentifier); - lua_pushinteger(ourData->L, progress); - lua_pushinteger(ourData->L, total); - lua_pcall(ourData->L, 3, 0, 0); -} + int luaF_pcall(lua_State* L) + { + int args = lua_gettop(L) - 1; + + lua_getglobal(L, "debug"); + lua_getfield(L, -1, "traceback"); + lua_remove(L, -2); + // Do not index from the top (i.e. don't use a negative index) + int errorhandler = lua_gettop(L) - args - 1; + lua_insert(L, errorhandler); + + int result = lua_pcall(L, args, LUA_MULTRET, errorhandler); + // lua_pcall() automatically pops the callee function and its arguments off the stack. Then, if no errors were encountered + // during execution, it pushes the return values onto the stack, if any. Otherwise, if an error was encountered, it pushes + // the error message onto the stack, which should manually be popped off when done using to keep the stack balanced + if (result == LUA_ERRRUN) + { + size_t len; + PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); + // This call pops the error message off the stack + lua_pop(L, 1); + return 0; + } + // Do not use lua_pop() as the callee function's return values may be present, which would pop one of those instead and leave + // the error handler on the stack + lua_remove(L, errorhandler); + lua_pushboolean(L, result == 0); + lua_insert(L, 1); -static int HTTPReqIdent = 0; + //if (result != 0) return 1; -int luaF_dohttpreq(lua_State* L){ - PD2HOOK_TRACE_FUNC; - PD2HOOK_LOG_LOG("Incoming HTTP Request/Request"); + return lua_gettop(L); + } - int args = lua_gettop(L); - int progressReference = 0; - if (args >= 3){ - progressReference = luaL_ref(L, LUA_REGISTRYINDEX); + int luaF_dofile(lua_State* L) + { + + int n = lua_gettop(L); + + size_t length = 0; + const char* filename = lua_tolstring(L, 1, &length); + int error = luaL_loadfilex(L, filename, nullptr); + if (error != 0) + { + size_t len; + // Logging::Log(filename, Logging::LOGGING_ERROR); + PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); + } + else + { + lua_getglobal(L, "debug"); + lua_getfield(L, -1, "traceback"); + lua_remove(L, -2); + // Example stack for visualization purposes: + // 3 debug.traceback + // 2 compiled code as a self-contained function + // 1 filename + // Do not index from the top (i.e. don't use a negative index) + int errorhandler = 2; + lua_insert(L, errorhandler); + + error = lua_pcall(L, 0, 0, errorhandler); + if (error == LUA_ERRRUN) + { + size_t len; + // Logging::Log(filename, Logging::LOGGING_ERROR); + PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); + // This call pops the error message off the stack + lua_pop(L, 1); + } + // Do not use lua_pop() as the callee function's return values may be present, which would pop one of those instead and + // leave the error handler on the stack + lua_remove(L, errorhandler); + } + return 0; } - int functionReference = luaL_ref(L, LUA_REGISTRYINDEX); - size_t len; - const char* url_c = lua_tolstring(L, 1, &len); - std::string url = std::string(url_c, len); + struct lua_http_data + { + int funcRef; + int progressRef; + int requestIdentifier; + lua_State* L; + }; - PD2HOOK_LOG_LOG(std::string(url_c, len) << " - " << functionReference); + void return_lua_http(void* data, std::string& urlcontents) + { + lua_http_data* ourData = (lua_http_data*)data; + if (!check_active_state(ourData->L)) + { + delete ourData; + return; + } - lua_http_data* ourData = new lua_http_data(); - ourData->funcRef = functionReference; - ourData->progressRef = progressReference; - ourData->L = L; + lua_rawgeti(ourData->L, LUA_REGISTRYINDEX, ourData->funcRef); + lua_pushlstring(ourData->L, urlcontents.c_str(), urlcontents.length()); + lua_pushinteger(ourData->L, ourData->requestIdentifier); + lua_pcall(ourData->L, 2, 0, 0); + luaL_unref(ourData->L, LUA_REGISTRYINDEX, ourData->funcRef); + luaL_unref(ourData->L, LUA_REGISTRYINDEX, ourData->progressRef); + delete ourData; + } - HTTPReqIdent++; - ourData->requestIdentifier = HTTPReqIdent; + void progress_lua_http(void* data, long progress, long total) + { + lua_http_data* ourData = (lua_http_data*)data; - std::unique_ptr reqItem(new HTTPItem()); - reqItem->call = return_lua_http; - reqItem->data = ourData; - reqItem->url = url; + if (!check_active_state(ourData->L)) + { + return; + } - if (progressReference != 0){ - reqItem->progress = progress_lua_http; + if (ourData->progressRef == 0) return; + lua_rawgeti(ourData->L, LUA_REGISTRYINDEX, ourData->progressRef); + lua_pushinteger(ourData->L, ourData->requestIdentifier); + lua_pushinteger(ourData->L, progress); + lua_pushinteger(ourData->L, total); + lua_pcall(ourData->L, 3, 0, 0); } - HTTPManager::GetSingleton()->LaunchHTTPRequest(std::move(reqItem)); - lua_pushinteger(L, HTTPReqIdent); - return 1; -} + int luaF_directoryhash(lua_State* L) + { + PD2HOOK_TRACE_FUNC; + int n = lua_gettop(L); -namespace -{ -std::unique_ptr gbl_mConsole; -} + size_t length = 0; + const char* filename = lua_tolstring(L, 1, &length); + std::string hash = Util::GetDirectoryHash(filename); + lua_pushlstring(L, hash.c_str(), hash.length()); + + return 1; + } -int luaF_createconsole(lua_State* L){ - PD2HOOK_TRACE_FUNC; - if (!gbl_mConsole) + int luaF_filehash(lua_State* L) { - gbl_mConsole.reset(new CConsole()); - } - return 0; -} - -int luaF_destroyconsole(lua_State* L){ - PD2HOOK_TRACE_FUNC; - gbl_mConsole.reset(); - return 0; -} - -int luaF_print(lua_State* L){ - PD2HOOK_TRACE_FUNC; - size_t len; - const char* str = lua_tolstring(L, 1, &len); - PD2HOOK_LOG_LUA(str); - //Logging::Log("aaaaaa", Logging::LOGGING_LUA); - return 0; -} - -int updates = 0; -std::thread::id main_thread_id; - -void* __fastcall do_game_update_new(void* thislol, int edx, int* a, int* b){ - // If someone has a better way of doing this, I'd like to know about it. - // I could save the this pointer? - // I'll check if it's even different at all later. - if (std::this_thread::get_id() != main_thread_id){ - return do_game_update(thislol, a, b); + int n = lua_gettop(L); + size_t l = 0; + const char * fileName = lua_tolstring(L, 1, &l); + std::string hash = Util::GetFileHash(fileName); + lua_pushlstring(L, hash.c_str(), hash.length()); + return 1; } - lua_State* L = reinterpret_cast(thislol); - if (updates == 0){ - HTTPManager::GetSingleton()->init_locks(); - } + static int HTTPReqIdent = 0; + int luaF_dohttpreq(lua_State* L) + { + PD2HOOK_LOG_LOG("Incoming HTTP Request/Request"); - if (updates > 1){ - EventQueueMaster::GetSingleton().ProcessEvents(); - } + int args = lua_gettop(L); + int progressReference = 0; + if (args >= 3) + { + progressReference = luaL_ref(L, LUA_REGISTRYINDEX); + } - updates++; - return do_game_update(thislol, a, b); -} + int functionReference = luaL_ref(L, LUA_REGISTRYINDEX); + size_t len; + const char* url_c = lua_tolstring(L, 1, &len); + std::string url = std::string(url_c, len); -// Random dude who wrote what's his face? -// I 'unno, I stole this method from the guy who wrote the 'underground-light-lua-hook' -// Mine worked fine, but this seems more elegant. -int __fastcall luaL_newstate_new(void* thislol, int edx, char no, char freakin, int clue){ - PD2HOOK_TRACE_FUNC; - int ret = luaL_newstate(thislol, no, freakin, clue); + PD2HOOK_LOG_LOG(std::string(url_c, len) << " - " << functionReference); - lua_State* L = (lua_State*)*((void**)thislol); - PD2HOOK_LOG_LUA("Lua State: " << L); - if (!L) return ret; - //int stack_size = lua_gettop(L); - //printf("%d\n", stack_size); + lua_http_data* ourData = new lua_http_data(); + ourData->funcRef = functionReference; + ourData->progressRef = progressReference; + ourData->L = L; - add_active_state(L); + HTTPReqIdent++; + ourData->requestIdentifier = HTTPReqIdent; - CREATE_LUA_FUNCTION(luaF_print, "log"); - CREATE_LUA_FUNCTION(luaF_pcall, "pcall"); - CREATE_LUA_FUNCTION(luaF_dofile, "dofile"); - CREATE_LUA_FUNCTION(luaF_unzipfile, "unzip"); - CREATE_LUA_FUNCTION(luaF_dohttpreq, "dohttpreq"); + std::unique_ptr reqItem(new HTTPItem()); + reqItem->call = return_lua_http; + reqItem->data = ourData; + reqItem->url = url; - luaL_Reg consoleLib[] = { { "CreateConsole", luaF_createconsole }, { "DestroyConsole", luaF_destroyconsole }, { NULL, NULL } }; - luaI_openlib(L, "console", consoleLib, 0); + if (progressReference != 0) + { + reqItem->progress = progress_lua_http; + } - luaL_Reg fileLib[] = { { "GetDirectories", luaF_getdir }, { "GetFiles", luaF_getfiles }, { "RemoveDirectory", luaF_removeDirectory }, { "DirectoryExists", luaF_directoryExists }, { NULL, NULL } }; - luaI_openlib(L, "file", fileLib, 0); + HTTPManager::GetSingleton()->LaunchHTTPRequest(std::move(reqItem)); + lua_pushinteger(L, HTTPReqIdent); + return 1; + } - //lua_settop(L, stack_size); - int result; - PD2HOOK_LOG_LOG("Initiating Hook"); + CConsole* gbl_mConsole = NULL; - result = luaL_loadfile(L, "mods/base/base.lua"); - if (result == LUA_ERRSYNTAX){ - size_t len; - PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); - return ret; + int luaF_createconsole(lua_State* L) + { + if (gbl_mConsole) return 0; + gbl_mConsole = new CConsole(); + return 0; } - result = lua_pcall(L, 0, 1, 0); - if (result == LUA_ERRRUN){ - size_t len; - PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); - return ret; + + int luaF_destroyconsole(lua_State* L) + { + if (!gbl_mConsole) return 0; + delete gbl_mConsole; + gbl_mConsole = NULL; + return 0; } - //CREATE_LUA_FUNCTION(luaF_pcall, "pcall") - //CREATE_LUA_FUNCTION(luaF_dofile, "dofile") - /*CREATE_LUA_FUNCTION(luaF_dohttpreq, "dohttpreq") + int luaF_print(lua_State* L) + { + int top = lua_gettop(L); + std::stringstream stream; + for (int i = 0; i < top; ++i) + { + size_t len; + const char* str = lua_tolstring(L, i + 1, &len); + stream << (i > 0 ? " " : "") << str; + } + PD2HOOK_LOG_LUA(stream.str()); - CREATE_LUA_FUNCTION(luaF_unzipfile, "unzip") + return 0; + } - */ - return ret; -} + int luaF_moveDirectory(lua_State * L) + { + int top = lua_gettop(L); + size_t lf = 0; + const char * fromStr = lua_tolstring(L, 1, &lf); + size_t ld = 0; + const char * destStr = lua_tolstring(L, 2, &ld); + + bool success = Util::MoveDirectory(fromStr, destStr); + lua_pushboolean(L, success); + return 1; + } -void __fastcall luaF_close(lua_State* L){ - PD2HOOK_TRACE_FUNC; - remove_active_state(L); - lua_close(L); -} + int updates = 0; + std::thread::id main_thread_id; -void InitiateStates(){ - PD2HOOK_TRACE_FUNC; + void* __fastcall do_game_update_new(void* thislol, int edx, int* a, int* b) + { - main_thread_id = std::this_thread::get_id(); + // If someone has a better way of doing this, I'd like to know about it. + // I could save the this pointer? + // I'll check if it's even different at all later. + if (std::this_thread::get_id() != main_thread_id) + { + return do_game_update(thislol, a, b); + } - SignatureSearch::Search(); + lua_State* L = (lua_State*)*((void**)thislol); + if (updates == 0) + { + HTTPManager::GetSingleton()->init_locks(); + } + if (updates > 1) + { + EventQueueMaster::GetSingleton().ProcessEvents(); + } + + updates++; + return do_game_update(thislol, a, b); + } - FuncDetour* gameUpdateDetour = new FuncDetour((void**)&do_game_update, do_game_update_new); - FuncDetour* newStateDetour = new FuncDetour((void**)&luaL_newstate, luaL_newstate_new); - //FuncDetour* luaCallDetour = new FuncDetour((void**)&lua_call, lua_newcall); - FuncDetour* luaCloseDetour = new FuncDetour((void**)&lua_close, luaF_close); -} + // Random dude who wrote what's his face? + // I 'unno, I stole this method from the guy who wrote the 'underground-light-lua-hook' + // Mine worked fine, but this seems more elegant. + int __fastcall luaL_newstate_new(void* thislol, int edx, char no, char freakin, int clue) + { + int ret = luaL_newstate(thislol, no, freakin, clue); + + lua_State* L = (lua_State*)*((void**)thislol); + printf("Lua State: %p\n", (void*)L); + if (!L) return ret; + + add_active_state(L); + + lua_pushcclosure(L, luaF_print, 0); + lua_setfield(L, LUA_GLOBALSINDEX, "log"); + + lua_pushcclosure(L, luaF_pcall, 0); + lua_setfield(L, LUA_GLOBALSINDEX, "pcall"); + + lua_pushcclosure(L, luaF_dofile, 0); + lua_setfield(L, LUA_GLOBALSINDEX, "dofile"); + + lua_pushcclosure(L, luaF_unzipfile, 0); + lua_setfield(L, LUA_GLOBALSINDEX, "unzip"); + + lua_pushcclosure(L, luaF_dohttpreq, 0); + lua_setfield(L, LUA_GLOBALSINDEX, "dohttpreq"); + + luaL_Reg consoleLib[] = { + { "CreateConsole", luaF_createconsole }, + { "DestroyConsole", luaF_destroyconsole }, + { NULL, NULL } + }; + luaI_openlib(L, "console", consoleLib, 0); + + luaL_Reg fileLib[] = { + { "GetDirectories", luaF_getdir }, + { "GetFiles", luaF_getfiles }, + { "RemoveDirectory", luaF_removeDirectory }, + { "DirectoryExists", luaF_directoryExists }, + { "DirectoryHash", luaF_directoryhash }, + { "FileHash", luaF_filehash }, + { "MoveDirectory", luaF_moveDirectory }, + { NULL, NULL } + }; + luaI_openlib(L, "file", fileLib, 0); + + // Keeping everything in lowercase since IspcallForced / IsPCallForced and Forcepcalls / ForcePCalls look rather weird anyway + luaL_Reg bltLib[] = { + { "ispcallforced", luaF_ispcallforced }, + { "forcepcalls", luaF_forcepcalls }, + { NULL, NULL } + }; + luaI_openlib(L, "blt", bltLib, 0); + + int result; + PD2HOOK_LOG_LOG("Initiating Hook"); + + result = luaL_loadfilex(L, "mods/base/base.lua", nullptr); + if (result == LUA_ERRSYNTAX) + { + size_t len; + PD2HOOK_LOG_ERROR(lua_tolstring(L, -1, &len)); + return ret; + } + result = lua_pcall(L, 0, 1, 0); + if (result == LUA_ERRRUN) + { + size_t len; + PD2HOOK_LOG_LOG(lua_tolstring(L, -1, &len)); + return ret; + } + + return ret; + } + + void luaF_close(lua_State* L) + { + remove_active_state(L); + lua_close(L); + } + + void InitiateStates() + { + main_thread_id = std::this_thread::get_id(); + + SignatureSearch::Search(); + + FuncDetour* gameUpdateDetour = new FuncDetour((void**)&do_game_update, do_game_update_new); + FuncDetour* newStateDetour = new FuncDetour((void**)&luaL_newstate, luaL_newstate_new); + FuncDetour* luaCloseDetour = new FuncDetour((void**)&lua_close, luaF_close); + } + + void DestroyStates() + { + // Okay... let's not do that. + // I don't want to keep this in memory, but it CRASHES THE SHIT OUT if you delete this after all is said and done. + if (gbl_mConsole) delete gbl_mConsole; + } -void DestroyStates(){ - PD2HOOK_TRACE_FUNC; - // Okay... let's not do that. - // I don't want to keep this in memory, but it CRASHES THE SHIT OUT if you delete this after all is said and done. - gbl_mConsole.reset(); -} -} +} \ No newline at end of file diff --git a/src/http/http.cpp b/src/http/http.cpp index e85a5a9..65d0e3f 100644 --- a/src/http/http.cpp +++ b/src/http/http.cpp @@ -119,7 +119,10 @@ void launch_thread_http(HTTPItem *raw_item){ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 900L); + curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 30L); + curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1000L); if (item->progress){ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, http_progress_call); diff --git a/src/signatures/signatures.cpp b/src/signatures/signatures.cpp index bbde7d3..1b8e777 100644 --- a/src/signatures/signatures.cpp +++ b/src/signatures/signatures.cpp @@ -5,19 +5,10 @@ #include #include "signatures.h" -#include "util/util.h" -#include -#include - -namespace pd2hook -{ -namespace -{ -MODULEINFO GetModuleInfo(const std::string& szModule) +MODULEINFO GetModuleInfo(std::string szModule) { - PD2HOOK_TRACE_FUNC; - MODULEINFO modinfo = { nullptr, 0, nullptr }; + MODULEINFO modinfo = { 0 }; HMODULE hModule = GetModuleHandle(szModule.c_str()); if (hModule == 0) return modinfo; @@ -25,100 +16,60 @@ MODULEINFO GetModuleInfo(const std::string& szModule) return modinfo; } -const MODULEINFO& GetPd2ModuleInfo() -{ - static const MODULEINFO modinfo = GetModuleInfo("payday2_win32_release.exe"); - return modinfo; -} -const char *FindPattern(const char *pattern, const char *mask) +unsigned long FindPattern(char* module, const char* funcname, const char* pattern, const char* mask) { - PD2HOOK_TRACE_FUNC; - const auto& modInfo = GetPd2ModuleInfo(); - const char * const base = reinterpret_cast(modInfo.lpBaseOfDll); - const DWORD size = modInfo.SizeOfImage; - decltype(size) patternLength = strlen(mask); - for (std::remove_const::type i = 0; i < size - patternLength; ++i) - { + MODULEINFO mInfo = GetModuleInfo(module); + DWORD base = (DWORD)mInfo.lpBaseOfDll; + DWORD size = (DWORD)mInfo.SizeOfImage; + DWORD patternLength = (DWORD)strlen(mask); + for (DWORD i = 0; i < size - patternLength; i++){ bool found = true; - for (decltype(i) j = 0; j < patternLength && found; ++j) - { - found &= mask[j] == '?' || pattern[j] == base[i + j]; + for (DWORD j = 0; j < patternLength; j++){ + found &= mask[j] == '?' || pattern[j] == *(char*)(base + i + j); } - - if (found) - { + if (found) { +// printf("Found %s: 0x%p\n", funcname, base + i); return base + i; } } - - return nullptr; + printf("Warning: Failed to locate function %s\n", funcname); + return NULL; } -bool FindUnassignedSignaturesPredicate(const SignatureF& s) -{ - return *s.address == nullptr; -} +std::vector* allSignatures = NULL; -std::vector allSignatures; -} +SignatureSearch::SignatureSearch(const char* funcname, void* adress, const char* signature, const char* mask, int offset){ + // lazy-init, container gets 'emptied' when initialized on compile. + if (!allSignatures){ + allSignatures = new std::vector(); + } -SignatureSearch::SignatureSearch(const void** adress, const char* signature, const char* mask, int offset){ - SignatureF ins = { signature, mask, offset, adress }; - allSignatures.push_back(ins); + SignatureF ins = { funcname, signature, mask, offset, adress }; + allSignatures->push_back(ins); } void SignatureSearch::Search(){ - PD2HOOK_TRACE_FUNC; - PD2HOOK_LOG_LOG("Scanning for signatures."); - - std::for_each(allSignatures.begin(), allSignatures.end(), [](SignatureF& s) { *s.address = FindPattern(s.signature, s.mask) + s.offset; }); - - const auto end = allSignatures.cend(); - auto it = std::find_if(allSignatures.cbegin(), end, FindUnassignedSignaturesPredicate); - int unassigned_count = 0; - while (it != end) - { - ++unassigned_count; - PD2HOOK_LOG_WARN("Didn't find signature with pattern: " << it->signature << ", and mask: " << it->mask); - it = std::find_if(it, end, FindUnassignedSignaturesPredicate); - } - - if (unassigned_count) - { - PD2HOOK_LOG_WARN("Total: " << unassigned_count << " signatures not found."); + printf("Scanning for signatures.\n"); + std::vector::iterator it; + for (it = allSignatures->begin(); it < allSignatures->end(); it++){ + *((void**)it->address) = (void*)(FindPattern("payday2_win32_release.exe", it->funcname, it->signature, it->mask) + it->offset); } - - PD2HOOK_LOG_LOG("Signatures Found."); } FuncDetour::FuncDetour(void** oldF, void* newF) : oldFunction(oldF), newFunction(newF){ - PD2HOOK_TRACE_FUNC; //DetourRestoreAfterWith(); -#define PD2_DETOUR_CHK_PARAM(param) if(!param) { PD2HOOK_LOG_WARN(#param " is null"); } - PD2_DETOUR_CHK_PARAM(oldF) - PD2_DETOUR_CHK_PARAM(*oldF) - PD2_DETOUR_CHK_PARAM(newF) - - LONG result; -#define PD2_DETOUR_CHK_FUNC(func) if((result = func) != ERROR_SUCCESS) { PD2HOOK_LOG_WARN(#func " returns " << result); } - PD2_DETOUR_CHK_FUNC(DetourTransactionBegin()) - PD2_DETOUR_CHK_FUNC(DetourUpdateThread(GetCurrentThread())) - PD2_DETOUR_CHK_FUNC(DetourAttach(oldF, newF)) - PD2_DETOUR_CHK_FUNC(DetourTransactionCommit()) - -#undef PD2_DETOUR_CHK_PARAM -#undef PD2_DETOUR_CHK_FUNC + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach(oldF, newF); + LONG result = DetourTransactionCommit(); } FuncDetour::~FuncDetour(){ - PD2HOOK_TRACE_FUNC; DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(oldFunction, newFunction); LONG result = DetourTransactionCommit(); -} - } \ No newline at end of file diff --git a/src/signatures/signatures.h b/src/signatures/signatures.h index c7884ca..4e76c27 100644 --- a/src/signatures/signatures.h +++ b/src/signatures/signatures.h @@ -1,155 +1,41 @@ #ifndef __SIGNATURE_HEADER__ #define __SIGNATURE_HEADER__ -#include -#include +#include +#include -namespace pd2hook -{ -// Credit to https://functionalcpp.wordpress.com/2013/08/05/function-traits/ for the function traits concept -template -struct function_traits; -template -struct function_traits : function_traits {}; -template -struct function_traits : function_traits {}; -template -struct function_traits -{ - using return_type = R; - - static const size_t arity = sizeof...(Args); - using args = std::tuple; - - template - struct argument : std::tuple_element - {}; -}; - -namespace impl -{ -template -struct ArgumentsSize; -template -struct ArgumentsSize> : ArgumentsSize {}; -template<> -struct ArgumentsSize<> : std::integral_constant {}; -template -struct ArgumentsSize : std::integral_constant::value> -{}; - -template -struct RegisterUse; -template -struct RegisterUse> : RegisterUse {}; -template -struct RegisterUse : std::integral_constant {}; -template -struct RegisterUse : std::integral_constant::value) : RegisterUse::value> {}; -template -struct RegisterUse : std::integral_constant::value> {}; -} - -template -struct StackCleanSize -{ - using args = typename function_traits::args; - - using total_size = impl::ArgumentsSize; - using registeruse_size = impl::RegisterUse; - using stackcleansize = std::integral_constant; -}; - -template -struct LuaCaller; - -// The 0 StackAdjust specialisations aren't strictly necessary but they likely save at least 1 op. -template -struct LuaCaller -{ - static Ret call(Ret(__fastcall *lua_fn)(Args...), Args... args) - { - return lua_fn(args...); - } -}; - -template -struct LuaCaller -{ - static void call(void(__fastcall *lua_fn)(Args...), Args... args) - { - lua_fn(args...); - } -}; - -template -struct LuaCaller -{ - static void call(void(__fastcall *lua_fn)(Args...), Args... args) - { - // Don't attempt to use StackAdjust directly in the asm. Trust me. - const int adj = StackAdjust; - lua_fn(args...); - __asm { __asm add esp, adj } - } -}; - -template -struct LuaCaller -{ - static Ret call(Ret(__fastcall *lua_fn)(Args...), Args... args) - { - const int adj = StackAdjust; - Ret val = lua_fn(args...); - __asm { __asm add esp, adj } - return val; - } -}; -} - -/* - This macro declares a function signature of the hooked lua function plus a variable to hold its address. It also generates a wrapper function that automatically - adjusts the stack to clean up after the call. The stack clean up is calculated as: - TOTAL ARGUMENT SIZE = SIZE(Arg0) + SIZE(Arg1) + SIZE(Arg2) + ... + SIZE(ArgN), where SIZE = sizeof(Arg) rounded up to the nearest DWORD boundary. - REGISTER USE SIZE = The size of the arguments that are passed by the registers in the __fastcall. Basically how many arguments there are smaller than or equal to a DWORD up to a maximum of 2. - STACK CLEAN UP SIZE = TOTAL ARGUMENT SIZE - REGISTER USE SIZE - - If the hooked functions are found differently in the future (perhaps not requiring stack adjustments?) then the LuaCaller can be changed accordingly. -*/ #define CREATE_CALLABLE_SIGNATURE(name, retn, signature, mask, offset, ...) \ typedef retn(__fastcall *name ## ptr)(__VA_ARGS__); \ - name ## ptr name ## ptr_val = nullptr; \ - template retn name ## impl(Args... args) { return pd2hook::LuaCaller::stackcleansize::value, __VA_ARGS__>::call( name ## ptr_val, args... ); } \ - typedef retn(*name ## actual)(__VA_ARGS__); name ## actual name = &name ## impl; \ - pd2hook::SignatureSearch name ## search(const_cast(reinterpret_cast(&name ## ptr_val)), signature, mask, offset); + name ## ptr name = NULL; \ + SignatureSearch name ## search(#name, &name, signature, mask, offset); #define CREATE_NORMAL_CALLABLE_SIGNATURE(name, retn, signature, mask, offset, ...) \ typedef retn(*name ## ptr)(__VA_ARGS__); \ name ## ptr name = NULL; \ - pd2hook::SignatureSearch name ## search(&name, signature, mask, offset); + SignatureSearch name ## search(#name, &name, signature, mask, offset); #define CREATE_CALLABLE_CLASS_SIGNATURE(name, retn, signature, mask, offset, ...) \ typedef retn(__thiscall *name ## ptr)(void*, __VA_ARGS__); \ name ## ptr name = NULL; \ - pd2hook::SignatureSearch name ## search(const_cast(reinterpret_cast(&name)), signature, mask, offset); + SignatureSearch name ## search(#name, &name, signature, mask, offset); #define CREATE_LUA_FUNCTION(lua_func, name) \ lua_pushcclosure(L, lua_func, 0); \ - lua_setfield(L, LUA_GLOBALSINDEX, name) + _asm add esp, 4 \ + lua_setfield(L, LUA_GLOBALSINDEX, name); \ + _asm add esp, 4 -namespace pd2hook -{ struct SignatureF { + const char* funcname; const char* signature; const char* mask; int offset; - const void** address; + void* address; }; class SignatureSearch { public: - SignatureSearch(const void** address, const char* signature, const char* mask, int offset); + SignatureSearch(const char* funcname, void* address, const char* signature, const char* mask, int offset); static void Search(); }; @@ -157,10 +43,12 @@ class FuncDetour { public: FuncDetour(void** oldF, void* newF); ~FuncDetour(); + void Attach(); + void Detach(); protected: void** oldFunction; void* newFunction; }; -} + #endif // __SIGNATURE_HEADER__ \ No newline at end of file diff --git a/src/util/files.cpp b/src/util/files.cpp index 8d726f8..82f6295 100644 --- a/src/util/files.cpp +++ b/src/util/files.cpp @@ -3,7 +3,7 @@ #include "util/util.h" #include -#include +#include #include #include @@ -49,7 +49,7 @@ const char *IOException::exceptionName() const } do{ bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - + if ((dirs && isDir) || (!dirs && !isDir)){ files.push_back(ffd.cFileName); } @@ -64,14 +64,14 @@ const char *IOException::exceptionName() const } string GetFileContents(const string& filename){ - ifstream t(filename); + ifstream t(filename, std::ifstream::binary); string str; t.seekg(0, std::ios::end); str.reserve(static_cast(t.tellg())); t.seekg(0, std::ios::beg); str.assign((std::istreambuf_iterator(t)), std::istreambuf_iterator()); - + return str; } @@ -120,4 +120,4 @@ const char *IOException::exceptionName() const } } -} \ No newline at end of file +} diff --git a/src/util/logging.cpp b/src/util/logging.cpp index 96c736e..17bc529 100644 --- a/src/util/logging.cpp +++ b/src/util/logging.cpp @@ -175,12 +175,18 @@ LogWriter::LogWriter(LogType msgType) LogWriter::LogWriter(const char *file, int line, LogType msgType) { - *this << LogTime << msgType << " (" << file; - if (line) + if (line && line > 0) { - *this << ':' << line; + *this << LogTime << msgType << " (" << file << ':' << line << ") "; + } + else if (file) + { + *this << LogTime << msgType << " (" << file << ") "; + } + else + { + *this << LogTime << msgType; } - *this << ") "; } } } \ No newline at end of file diff --git a/src/util/util.cpp b/src/util/util.cpp index d3cb240..7aa3660 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -1,35 +1,106 @@ #include "util.h" +#include +#include +#include +#include +#include +#include +#include namespace pd2hook { -namespace Util -{ -Exception::Exception(const char *file, int line) : - mFile(file), mLine(line) -{} + namespace Util + { -Exception::Exception(std::string msg, const char *file, int line) : - mFile(file), mLine(line), mMsg(std::move(msg)) -{} + Exception::Exception(const char *file, int line) : + mFile(file), mLine(line) + {} -const char *Exception::what() const -{ - if (!mMsg.empty()) - { - return mMsg.c_str(); - } + Exception::Exception(std::string msg, const char *file, int line) : + mFile(file), mLine(line), mMsg(std::move(msg)) + {} - return std::exception::what(); -} + const char *Exception::what() const + { + if (!mMsg.empty()) + { + return mMsg.c_str(); + } -const char *Exception::exceptionName() const -{ - return "An exception"; -} + return std::exception::what(); + } -void Exception::writeToStream(std::ostream& os) const -{ - os << exceptionName() << " occurred @ (" << mFile << ':' << mLine << "). " << what(); -} + const char *Exception::exceptionName() const + { + return "An exception"; + } + + void Exception::writeToStream(std::ostream& os) const + { + os << exceptionName() << " occurred @ (" << mFile << ':' << mLine << "). " << what(); + } + + std::string sha256(const std::string str) + { + unsigned char hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, str.c_str(), str.size()); + SHA256_Final(hash, &sha256); + std::stringstream ss; + for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i]; + } + return ss.str(); + } + + void RecurseDirectoryPaths(std::vector& paths, std::string directory) { + std::vector dirs = pd2hook::Util::GetDirectoryContents(directory, true); + std::vector files = pd2hook::Util::GetDirectoryContents(directory); + for (auto it = files.begin(); it < files.end(); it++) { + std::string fpath = directory + *it; + std::transform(fpath.begin(), fpath.end(), fpath.begin(), ::tolower); + paths.push_back(fpath); + } + for (auto it = dirs.begin(); it < dirs.end(); it++) { + if (*it == "." || *it == "..") continue; + RecurseDirectoryPaths(paths, directory + *it + "\\"); + } + } + + std::string GetDirectoryHash(std::string directory) + { + std::vector paths; + RecurseDirectoryPaths(paths, directory); + std::sort(paths.begin(), paths.end()); + + std::string hashconcat; + + for (auto it = paths.begin(); it < paths.end(); it++) { + std::string hashstr = sha256(pd2hook::Util::GetFileContents(*it)); + hashconcat += hashstr; + } + + return sha256(hashconcat); + } + + std::string GetFileHash(std::string file) + { + // This has to be hashed twice otherwise it won't be the same hash if we're checking against a file uploaded to the server + std::string hash = sha256(pd2hook::Util::GetFileContents(file)); + return sha256(hash); + } + + bool MoveDirectory(const std::string & path, const std::string & destination) + { + bool success = MoveFileEx(path.c_str(), destination.c_str(), MOVEFILE_WRITE_THROUGH); + if (!success) + { + PD2HOOK_LOG_LOG("MoveFileEx failed with error " << GetLastError()); + } + return success; + } + + } } -} \ No newline at end of file diff --git a/src/util/util.h b/src/util/util.h index 5d65533..696053e 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace pd2hook { @@ -19,6 +20,9 @@ namespace Util { // String split from http://stackoverflow.com/a/236803 void SplitString(const std::string &s, char delim, std::vector &elems); std::vector SplitString(const std::string &s, char delim); + std::string GetDirectoryHash(std::string directory); + std::string GetFileHash(std::string filename); + bool MoveDirectory(const std::string & path, const std::string & destination); class Exception : public std::exception { @@ -30,7 +34,7 @@ namespace Util { virtual const char *exceptionName() const; virtual void writeToStream(std::ostream& os) const; - + private: const char * const mFile; const int mLine; @@ -129,19 +133,24 @@ bool ExtractZIPArchive(const std::string& path, const std::string& extractPath); #define PD2HOOK_TRACE_FUNC_MSG(msg) #endif -#define PD2HOOK_LOG_LEVEL(msg, level, file, line) do { \ +#define PD2HOOK_LOG_LEVEL(msg, level, file, line, color) do { \ auto& logger = pd2hook::Logging::Logger::Instance(); \ if(level >= logger.getLoggingLevel()) { \ + if (color > 0x0000) \ + { \ + HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); \ + SetConsoleTextAttribute(hStdout, color); \ + } \ pd2hook::Logging::LogWriter writer(file, line, level); \ writer << msg; \ writer.write(logger); \ }} while (false); -#define PD2HOOK_LOG_FUNC(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_FUNC, __FILE__, 0) -#define PD2HOOK_LOG_LOG(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_LOG, __FILE__, __LINE__) -#define PD2HOOK_LOG_LUA(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_LUA, __FILE__, __LINE__) -#define PD2HOOK_LOG_WARN(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_WARN, __FILE__, __LINE__) -#define PD2HOOK_LOG_ERROR(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_ERROR, __FILE__, __LINE__) +#define PD2HOOK_LOG_FUNC(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_FUNC, __FILE__, 0, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY) +#define PD2HOOK_LOG_LOG(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_LOG, __FILE__, __LINE__, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY) +#define PD2HOOK_LOG_LUA(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_LUA, NULL, -1, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY) +#define PD2HOOK_LOG_WARN(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_WARN, __FILE__, __LINE__, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY) +#define PD2HOOK_LOG_ERROR(msg) PD2HOOK_LOG_LEVEL(msg, pd2hook::Logging::LogType::LOGGING_ERROR, __FILE__, __LINE__, FOREGROUND_RED | FOREGROUND_INTENSITY) #define PD2HOOK_LOG_EXCEPTION(e) PD2HOOK_LOG_WARN(e) #define PD2HOOK_DEBUG_CHECKPOINT PD2HOOK_LOG_LOG("Checkpoint") @@ -153,12 +162,12 @@ namespace Logging inline FunctionLogger::FunctionLogger(const char *funcName, const char *file) : mFile(file), mFuncName(funcName) { - PD2HOOK_LOG_LEVEL(">>> " << mFuncName, LogType::LOGGING_FUNC, mFile, 0); + PD2HOOK_LOG_LEVEL(">>> " << mFuncName, LogType::LOGGING_FUNC, mFile, 0, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN); } inline FunctionLogger::~FunctionLogger() { - PD2HOOK_LOG_LEVEL("<<< " << mFuncName, LogType::LOGGING_FUNC, mFile, 0); + PD2HOOK_LOG_LEVEL("<<< " << mFuncName, LogType::LOGGING_FUNC, mFile, 0, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN); } } }