#include #include #include #include #include #include namespace fs = std::filesystem; #define INIT_SCRIPTS_NAME "__init_scripts" struct Registry { std::string m_name; std::string m_macroIdentifier; std::string m_suffix; std::vector m_values; }; std::vector registries = { // do NOT change this without also refactoring the macro in ClientDetours.h { "CLIENT_DETOUR" ,"__detour_name" ,"__Result" } // do NOT change this without also refactoring the macro in ClientLua.h , { "LUA_FUNCTION" , "__lua_function_name", "__Result" } // do NOT change this without also refactoring the macro in ClientNetwork.h , { "ON_CUSTOM_PACKET" , "__listener_name", "__Result" } }; std::vector cppSourceFiles = { ".c", ".cc", ".cpp", ".cxx", ".cp", ".c++" }; static bool endsWith(const std::string& str, const std::string& suffix) { return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); } int main() { std::vector luaFiles; for (auto& p : fs::recursive_directory_iterator(fs::current_path())) { std::string path = p.path().string(); auto itr = std::find_if(cppSourceFiles.begin(), cppSourceFiles.end(), [&path](auto cur) { return endsWith(path, cur); }); // Generate cpp scripts if (itr != cppSourceFiles.end()) { std::string sourceFile = (std::stringstream() << std::ifstream(path).rdbuf()).str(); std::vector> invalidRanges; enum class State { IN_COMMENT, IN_ML_COMMENT, IN_STRING, STRING_ESCAPE, OUTSIDE }; State cur = State::OUTSIDE; size_t curStart = 0; for (size_t i = 0; i < sourceFile.size(); ++i) { switch (cur) { case State::OUTSIDE: if (i < sourceFile.size() - 1 && sourceFile[i] == '/' && sourceFile[i + 1] == '/') { cur = State::IN_COMMENT; curStart = i; } else if (i < sourceFile.size() - 1 && sourceFile[i] == '/' && sourceFile[i + 1] == '*') { cur = State::IN_ML_COMMENT; curStart = i; } else if (sourceFile[i] == '"') { cur = State::IN_STRING; curStart = i; } break; case State::STRING_ESCAPE: cur = State::IN_STRING; break; case State::IN_STRING: if (sourceFile[i] == '\\') { cur = State::STRING_ESCAPE; } else if (sourceFile[i] == '"') { invalidRanges.push_back({ curStart,i }); cur = State::OUTSIDE; } break; case State::IN_COMMENT: if (sourceFile[i] == '\n') { invalidRanges.push_back({ curStart, i }); cur = State::OUTSIDE; } break; case State::IN_ML_COMMENT: if (i < sourceFile.size() - 1 && sourceFile[i] == '*' && sourceFile[i + 1] == '/') { invalidRanges.push_back({ curStart, i }); cur = State::OUTSIDE; } break; } } for (Registry& reg : registries) { std::string cur = sourceFile; std::regex exp(reg.m_name + "[ \t\n\r]*\\([ \t\n\r]*(.+?)[ \t\n\r]*,"); std::smatch res; size_t totalPos = 0; while (std::regex_search(cur, res, exp)) { std::string r1 = res[1]; auto find = std::find_if(invalidRanges.begin(), invalidRanges.end(), [&](std::pair const& v) { return v.first <= totalPos+res.position() && v.second >= totalPos+res.position(); }); if (find == invalidRanges.end()) { if (r1.compare(reg.m_macroIdentifier)) { reg.m_values.push_back(r1 + reg.m_suffix); } } // i just gave up, position() just won't work with iterators size_t offset = res.position() + res[0].length(); totalPos += offset; cur = cur.substr(offset); } } } // Generate lua scripts if (endsWith(path, ".lua")) { luaFiles.push_back(path); std::ifstream luafile = std::ifstream(path); } } std::sort(luaFiles.begin(), luaFiles.end(), [](auto const& a, auto const& b) { size_t aDirs = std::count( a.begin() , a.end(), '\\') + std::count(a.begin(), a.end(), '/' ); size_t bDirs = std::count( b.begin() , b.end(), '\\') + std::count(b.begin(), b.end(), '/' ); return (aDirs != bDirs) ? aDirs < bDirs : a < b; }); std::ofstream luafilesGenerated("luafiles.generated.h"); luafilesGenerated << "#include \n"; luafilesGenerated << "std::vector> LUA_FILES = {\n"; for (std::string const& path : luaFiles) { std::ifstream luafile = std::ifstream(path); luafilesGenerated << " {\""; auto relPath = std::filesystem::relative (path,std::filesystem::path(__FILE__).parent_path()).string(); for (char c : relPath) { if (c == '\\') { luafilesGenerated << "\\\\"; } else { luafilesGenerated << c; } } luafilesGenerated << "\", {"; char byte; while (luafile.get(byte)) { luafilesGenerated << "0x" << std::hex << uint32_t(byte) << ","; } luafilesGenerated << "}},\n"; } luafilesGenerated << "};"; // Generate scripts header std::ofstream scriptsGenerated("scripts.generated.h"); scriptsGenerated << "#include \n"; for (Registry const& reg : registries) { for (std::string const& id : reg.m_values) { scriptsGenerated << "extern int " << id << ";\n"; } } // there's likely a better way to force the compiler to not optimize this, // but this should be very safe. scriptsGenerated << "inline void " INIT_SCRIPTS_NAME "()\n{\n"; for (Registry const& reg : registries) { if (reg.m_values.size() == 0) continue; scriptsGenerated << " std::cout << \" Generated \" << ("; for (size_t i = 0; i < reg.m_values.size(); ++i) { scriptsGenerated << reg.m_values[i]; if (i < reg.m_values.size() - 1) { scriptsGenerated << " + "; } } scriptsGenerated << ") << \"" << reg.m_name << "s\\n\";\n"; } scriptsGenerated << "}"; return 0; }