diff --git a/EmuAPI/EmuAPI.vcproj b/EmuAPI/EmuAPI.vcproj index 35eab8a..ba67e44 100644 --- a/EmuAPI/EmuAPI.vcproj +++ b/EmuAPI/EmuAPI.vcproj @@ -204,6 +204,10 @@ RelativePath=".\emu\EmuRegister.cpp" > + + + + #include "ZooState.h" #include "RegZooState.h" +#include +#include "EmuScriptMgr.h" bool IsConsoleRunning = false; bool IsConsoleHiding = false; bool HasConsoleOpenedOnce = false; // to avoid conflicts when console has not been opened yet HWND consoleWindow; // contains console window handle +#define fs std::filesystem +#define B EmuBase +#define ZS ZooState + DWORD WINAPI ZooConsole(LPVOID lpParameter) { EmuConsole console; // new console object. needed to keep token state persistent. FILE* file_s; + HasConsoleOpenedOnce = true; @@ -65,113 +72,102 @@ DWORD WINAPI ZooConsole(LPVOID lpParameter) return 1; } -DWORD WINAPI RunEmu(LPVOID lpParameter) -{ +DWORD WINAPI RunEmu(LPVOID lpParameter) { + //------ Variable and object initialization bool ctrlMPressed = false; - lua_State *lua = luaL_newstate(); // Open Lua - int iErr = 0; - if (!lua) - { - std::cerr << "Failed to create Lua state." << std::endl; - return 0; - } + //------ Timestamp for logging + std::time_t t = std::time(0); + char timestamp[80]; // timestamp buffer + std::strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", std::localtime(&t)); + + //------ Open log file in append mode + std::ofstream f; + f.open("out.log", std::ios_base::app); - RegZooState::register_zoo_state(lua); - luaL_openlibs (lua); // Load io library + //------ Find/load script file directories with script manager + EmuScriptMgr sm(f, timestamp); + sm.findScripts(); + sm.storeScripts(); // main loop - while (true) - { + + bool renaming_done = false; + while (true) { // CTRL + J - if (EmuBase::DoubleKey(0x11, 0x4A) == true && IsConsoleRunning == false && HasConsoleOpenedOnce == false) - { + if (B::DoubleKey(0x11, 0x4A) == true && IsConsoleRunning == false && HasConsoleOpenedOnce == false) { IsConsoleRunning = true; HANDLE thread = CreateThread(NULL, 0, &ZooConsole, NULL, 0, NULL); CloseHandle(thread); - } - else if (EmuBase::DoubleKey(0x11, 0x4A) == true && IsConsoleHiding == true && HasConsoleOpenedOnce == true) - { + } else if (B::DoubleKey(0x11, 0x4A) == true && IsConsoleHiding == true && HasConsoleOpenedOnce == true) { ShowWindow(consoleWindow, SW_SHOW); IsConsoleHiding = false; } // CTRL + M - if (EmuBase::DoubleKey(0x11, 0x4D) == true && !ctrlMPressed) - { + if (B::DoubleKey(0x11, 0x4D) == true && !ctrlMPressed) { ctrlMPressed = true; // Set the flag float mo_money = 1000000.00f; - ZooState::AddToZooBudget(mo_money); + ZS::AddToZooBudget(mo_money); } - else if (EmuBase::DoubleKey(0x11, 0x4D) == false) - { + else if (B::DoubleKey(0x11, 0x4D) == false) { ctrlMPressed = false; // Reset the flag when the key is released } + - - if ((((int)ZooState::object_ptr(0x0)) > 0) && (iErr = luaL_loadfile (lua, "playground.emu")) == 0) - { - if (ZooState::IsZooLoaded() == true) - { - // Call main... - if ((iErr = lua_pcall (lua, 0, LUA_MULTRET, 0)) == 0) - { - lua_pcall(lua, 0, LUA_MULTRET, 0); - - // Push the function name onto the stack - lua_getglobal(lua, "emu_run"); - - lua_pcall(lua, 0, 0, 0); - - } + + // only run scripts while zoo is loaded and not in main menu + if ((int)ZS::object_ptr(0x0) > 0) { + if (ZS::IsZooLoaded() == true) { + sm.executeScripts(); } } - - + Sleep(0); } - lua_close (lua); + f.close(); return 1; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, - LPVOID lpReserved) -{ - std::ofstream f; - f.open("out.log"); + LPVOID lpReserved) { + // std::ofstream f; + // f.open("out.log", std::ios_base::app); + // std::time_t t = std::time(0); + // char timestamp[80]; // timestamp buffer + // std::strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", std::localtime(&t)); // Get the thread ID of the current thread - DWORD mainThreadId = GetCurrentThreadId(); - f << mainThreadId << "\nCurrent thread ID: " << std::setfill('0') << std::setw(8) << std::hex << EmuBase::base << std::endl; + // DWORD mainThreadId = GetCurrentThreadId(); + // f << std::endl << std::endl << "[" << timestamp << "] " << "\nMain thread ID: " << mainThreadId << "\nCurrent thread ID: " << std::setfill('0') << std::setw(8) << std::hex << B::base << std::endl; // dll attachment status - f << "Status: "; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { - f << "DLL attached!\n"; + //f << "[" << timestamp << "] " << "DLL attached!\n"; HANDLE thread = CreateThread(NULL, 0, &RunEmu, NULL, 0, NULL); CloseHandle(thread); } break; case DLL_PROCESS_DETACH: - f << "DLL detached!\n"; + //f << "[" << timestamp << "] " << "DLL detached!\n"; break; case DLL_THREAD_ATTACH: - f << "Thread attached!\n"; + //f << "[" << timestamp << "] " << "Thread attached!\n"; break; case DLL_THREAD_DETACH: - f << "Thread detached!\n"; + //f << "[" << timestamp << "] " << "Thread detached!\n"; break; default: - f << "DLL was not attached to thread or process!\n"; + //f << "[" << timestamp << "] " << "DLL was not attached to thread or process!\n"; return FALSE; } - f.close(); + // f.close(); return TRUE; } \ No newline at end of file diff --git a/EmuAPI/emu/EmuConsole.cpp b/EmuAPI/emu/EmuConsole.cpp index b6888fd..d420a51 100644 --- a/EmuAPI/emu/EmuConsole.cpp +++ b/EmuAPI/emu/EmuConsole.cpp @@ -1,4 +1,3 @@ - #include "EmuConsole.h" #include @@ -17,33 +16,25 @@ void EmuConsole::tokenize() { std::string token = ""; // TODO: fix try/catch. Crashes if error found. - try + if (!std::getline(std::cin, token)) { + return; + } + + if (token.size() > 100) { - - std::getline(std::cin, token); + // this limit will be increased, it's just a pre-emptive measure + std::cout << "100 char limit in buffer. Please try again." << std::endl; + // std::cin.ignore(32767, '\n'); + } else { + std::istringstream iss(token); - if (token.size() > 100) + while (std::getline(iss, token, ' ')) { - // this limit will be increased, it's just a pre-emptive measure - std::cout << "100 char limit in buffer. Please try again." << std::endl; - throw; + tokens.push_back(token); } } - catch(const std::exception& e) - { - std::cout << e.what() << std::endl; - token = ""; - std::cin.clear(); - std::getline(std::cin, token); - - } - - std::istringstream iss(token); - while (std::getline(iss, token, ' ')) - { - tokens.push_back(token); - } + } /// @@ -213,7 +204,7 @@ void EmuConsole::processInput(bool& IsConsoleRunning) // } else { - std::cout << "Err: No such command exists." << std::endl; + std::cout << "Err: Command <" << tokens[0] << "> does not exist." << std::endl; } } else diff --git a/EmuAPI/emu/EmuScriptMgr.cpp b/EmuAPI/emu/EmuScriptMgr.cpp new file mode 100644 index 0000000..51f4622 --- /dev/null +++ b/EmuAPI/emu/EmuScriptMgr.cpp @@ -0,0 +1,154 @@ +#include "EmuScriptMgr.h" + +// EmuScriptMgr::EmuScriptMgr() : lua(NULL), f(std::ofstream("out.log", std::ios_base::app)), timestamp(*new char) { +// // do not use default constructor +// } + +EmuScriptMgr::EmuScriptMgr(std::ofstream& fs, char* ts) : f(fs), timestamp(ts) { + //------ Initializing Lua + lua = luaL_newstate(); // Open Lua + int iErr = 0; + if (!lua) { + f << "[" << timestamp << "] " << "Failed to create Lua state." << std::endl; + } + + //------ Register API functions to Lua + RegZooState::register_zoo_state(lua); + + //------ Load Lua libraries + luaL_openlibs (lua); +} + +EmuScriptMgr::~EmuScriptMgr() { +} + +/// @brief Executes all emu scripts in a directory. +int EmuScriptMgr::executeScripts() { + + for (int i = 0; i < scripts.size(); i++) { + lua = luaL_newstate(); // Open Lua + + int iErr = 0; + if (!lua) { + f << "[" << timestamp << "] " << "Failed to create Lua state." << std::endl; + } + //------ Register API functions to Lua + RegZooState::register_zoo_state(lua); + luaL_openlibs (lua); + if (luaL_loadstring(lua, scripts[i].c_str()) == 0) { + if (lua_pcall(lua, 0, LUA_MULTRET, 0) == 0) + { + // script is loaded, now load specific function + lua_getglobal(lua, "emu_run"); + // check if we can call function + if (lua_isfunction(lua, -1)) { + if (lua_pcall(lua, 0, LUA_MULTRET, 0) != 0) { + // errors if can't execute script + const char* error_message = lua_tostring(lua, -1); + f << "Error executing Lua function: " << error_message << std::endl; + lua_close (lua); + return 1; + } + } else { + f << "[" << timestamp << "] " << "Function 'emu_run' not found or not callable" << std::endl; + lua_close (lua); + return 1; + } + lua_pop(lua, 1); + } else { + // error handling + const char* error_message = lua_tostring(lua, -1); + f << "[" << timestamp << "] " << "Error executing Lua script " << i << ": " << error_message << std::endl; + + // remove err from stack + lua_pop(lua, 1); + lua_close (lua); + return 1; + } + } else { + // error handling + const char* error_message = lua_tostring(lua, -1); + f << "[" << timestamp << "] " << "Error loading Lua script " << i << ": " << error_message << std::endl; + + // remove err from stack + lua_pop(lua, 1); + lua_close (lua); + return 1; + } + lua_close (lua); + } + + return 0; +} + +/// @brief Stores all emu scripts in a directory in memory. +void EmuScriptMgr::storeScripts() { + + for (int i = 0; i < files.size(); i++) { + //------ Read script file + std::ifstream file(files[i].c_str()); + std::string script; + char c; + while (file.get(c)) { + script += c; + } + scripts.push_back(script); + file.close(); + //------ Compile script + if ((luaL_loadstring(lua, script.c_str())) == 0) { + lua_CFunction script_funct = lua_tocfunction(lua, -1); + compiled_scripts.push_back(script_funct); + lua_pop(lua, 1); + } else { + f << "[" << timestamp << "] " << "Error loading script: " << files[i].substr(0, files[i].size() - (int)(files[i].size() * 0.30)) << " [..]" << std::endl; + } + } + lua_close (lua); + +} + +/// @brief Finds all scripts within the /scripts directory and stores their location in memory. +void EmuScriptMgr::findScripts() { + + //------ Open log file in append mode + std::ofstream f; + f.open("out.log", std::ios_base::app); + + //------ Get working directory + wchar_t buffer[MAX_PATH]; // wide char buffer for GetCurrentDirectory + DWORD len = GetCurrentDirectory(MAX_PATH, buffer); // TODO: add error handling for this + std::wstring wpath(buffer, len);//std::wstring(buffer.begin(), buffer.end()); // conv path to wide string (project uses multi-byte char set) + wpath += L"\\scripts"; + + //------ Convert wide string to narrow string, + std::string path(wpath.begin(), wpath.end()); + + //------ Find first .emu file in directory + WIN32_FIND_DATA find_emu_file; + HANDLE hFind = FindFirstFile((wpath + L"\\*.emu").c_str(), &find_emu_file); // wide string used here + + wpath = find_emu_file.cFileName; + std::string file_found(wpath.begin(), wpath.end()); + + //------ If no .emu files found, log error + if (hFind == INVALID_HANDLE_VALUE) { + f << "[" << timestamp << "] " << "Error finding path: " << file_found << std::endl; + } else { + //------ Find the rest of the .emu files in the directory + do { + if (find_emu_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + // skip directories (for now) + } else { // we only care about files (for now) + std::wstring nFound = find_emu_file.cFileName; // conv file found back to narrow string + std::string file_name(nFound.begin(), nFound.end()); + std::string file = path + "\\" + file_name; + + files.push_back(file); + file_names.push_back(file_name); + } + } while (FindNextFile(hFind, &find_emu_file) != 0); // find the next file + FindClose(hFind); + } + +} + diff --git a/EmuAPI/emu/EmuScriptMgr.h b/EmuAPI/emu/EmuScriptMgr.h new file mode 100644 index 0000000..7877c9a --- /dev/null +++ b/EmuAPI/emu/EmuScriptMgr.h @@ -0,0 +1,38 @@ +#ifndef EMUSCRIPTMGR_H +#define EMUSCRIPTMGR_H + +#include +#include +#include +#include +#include +#include +#include +#include "lua.hpp" +#include +#include +#include "RegZooState.h" + +class EmuScriptMgr +{ +public: + EmuScriptMgr(); + EmuScriptMgr(std::ofstream&, char*); // overloaded constructor + ~EmuScriptMgr(); + void findScripts(); + void storeScripts(); + int executeScripts(); + +private: + std::vector files; + std::vector file_names; + std::vector compiled_scripts; + std::vector scripts; + static int writer(const void*, size_t, void*); + + char* timestamp; + lua_State *lua; + std::ofstream& f; +}; + +#endif \ No newline at end of file diff --git a/EmuAPI/emu/emu_run_wrapper.emu b/EmuAPI/emu/emu_run_wrapper.emu new file mode 100644 index 0000000..9bb7239 --- /dev/null +++ b/EmuAPI/emu/emu_run_wrapper.emu @@ -0,0 +1,21 @@ +-- Wrapper script +function emu_run_wrapper() + -- Define a function counter + local counter = 1 + + -- Iterate through the emu_run functions + while true do + -- Generate the function name + local func_name = "emu_run" .. counter + + -- Call the function with the generated name + local func = _G[func_name] + if func then + func() + counter = counter + 1 + else + break -- Exit the loop when no more functions are found + end + end +end + diff --git a/EmuAPI/emu/emu_test_script.lua b/EmuAPI/emu/emu_test_script.lua index 604d8c6..450e424 100644 --- a/EmuAPI/emu/emu_test_script.lua +++ b/EmuAPI/emu/emu_test_script.lua @@ -1,4 +1,3 @@ function emu_run() - local zs = ZooState() - io.write(zs:GetZooBudget() + "Hello world from inside Lua running inside EMU running inside Zoo Tycoon!") + io.write(GetZooBudget() + "Hello world from inside Lua running inside EMU running inside Zoo Tycoon!") end \ No newline at end of file