diff --git a/logger.hpp b/logger.hpp index ca50272..d0753b2 100644 --- a/logger.hpp +++ b/logger.hpp @@ -9,8 +9,7 @@ #include "utils.hpp" -enum class log_level : std::uint32_t -{ +enum class log_level : std::uint32_t { LOG_FATAL, LOG_ERROR, LOG_WARN, @@ -20,37 +19,33 @@ enum class log_level : std::uint32_t LOG_NOPREFIX, // keep this last }; -class logger -{ +class logger { public: - logger( const std::wstring_view& title_name = {} ) - { + logger(const std::wstring_view &title_name = {}) { + AllocConsole(); - AttachConsole( GetCurrentProcessId() ); + AttachConsole(GetCurrentProcessId()); - if ( !title_name.empty() ) - { - SetConsoleTitle( title_name.data() ); + if (!title_name.empty()) { + SetConsoleTitle(title_name.data()); } - FILE* in = nullptr; - FILE* out = nullptr; + FILE *in = nullptr; + FILE *out = nullptr; - freopen_s( &in, "conin$", "r", stdin ); - freopen_s( &out, "conout$", "w", stdout ); - freopen_s( &out, "conout$", "w", stderr ); + freopen_s(&in, "conin$", "r", stdin); + freopen_s(&out, "conout$", "w", stdout); + freopen_s(&out, "conout$", "w", stderr); - console_handle = GetStdHandle( STD_OUTPUT_HANDLE ); + console_handle = GetStdHandle(STD_OUTPUT_HANDLE); } - ~logger() - { + ~logger() { FreeConsole(); } - enum class console_colors : std::uint8_t - { + enum class console_colors : std::uint8_t { BLACK, DARK_BLUE, DARK_GREEN, @@ -69,8 +64,8 @@ class logger WHITE, }; - struct log_type_info_t - { + struct log_type_info_t { + std::string prefix{}; console_colors fg = console_colors::WHITE; console_colors bg = console_colors::BLACK; @@ -79,17 +74,17 @@ class logger }; template - void print_colored( console_colors fg, console_colors bg, bool newline, std::string_view fmt, arg ... args ) - { - std::unique_lock< decltype( m )> lock( m ); + void print_colored(console_colors fg, console_colors bg, bool newline, std::string_view fmt, arg ... args) { - const auto txt = utils::format_string( fmt, args... ); + std::unique_lock< decltype(m)> lock(m); - set_console_color( fg, bg ); + const auto txt = utils::format_string(fmt, args...); + + set_console_color(fg, bg); std::cout << txt.c_str(); - set_console_color( console_colors::WHITE, console_colors::BLACK ); + set_console_color(console_colors::WHITE, console_colors::BLACK); if (newline) { std::cout << std::endl; @@ -97,74 +92,72 @@ class logger } template< typename ... arg > - void print( log_level level, std::string_view fmt, arg ... args ) { - std::unique_lock< decltype( m )> lock( m ); + void print(log_level level, std::string_view fmt, arg ... args) { + + std::unique_lock< decltype(m)> lock(m); const auto &info = console_type_info[level]; - const auto txt = utils::format_string( fmt, args... ); + const auto txt = utils::format_string(fmt, args...); - set_console_color( info.fg, info.bg ); + set_console_color(info.fg, info.bg); - if ( level < log_level::LOG_NOPREFIX ) { + if (level < log_level::LOG_NOPREFIX) { std::cout << info.prefix; } std::cout << txt.c_str(); - set_console_color( console_colors::WHITE, console_colors::BLACK ); + set_console_color(console_colors::WHITE, console_colors::BLACK); std::cout << std::endl; } template< typename ... arg > - void print_with_func( log_level level, std::string_view func_name, std::string_view fmt, arg ... args ) - { - std::unique_lock< decltype( m )> lock( m ); + void print_with_func(log_level level, std::string_view func_name, std::string_view fmt, arg ... args) { - const auto &info = console_type_info[ level ]; - const auto txt = utils::format_string( fmt, args... ); + std::unique_lock< decltype(m)> lock(m); - set_console_color( info.fg, info.bg ); + const auto &info = console_type_info[level]; + const auto txt = utils::format_string(fmt, args...); + + set_console_color(info.fg, info.bg); - if ( level < log_level::LOG_NOPREFIX ) - { + if (level < log_level::LOG_NOPREFIX) { std::cout << info.prefix; } std::cout << "[ " << func_name.data() << " ] " << txt.c_str(); - set_console_color( console_colors::WHITE, console_colors::BLACK ); + set_console_color(console_colors::WHITE, console_colors::BLACK); std::cout << std::endl; } private: - inline bool set_console_color( const console_colors fg, const console_colors bg ) - { - if ( console_handle != INVALID_HANDLE_VALUE ) - { - WORD color = (uint8_t)fg | ( (uint8_t)bg << 4 ); - return SetConsoleTextAttribute( console_handle, color ); + inline bool set_console_color(const console_colors fg, const console_colors bg) { + + if (console_handle != INVALID_HANDLE_VALUE) { + WORD color = (uint8_t)fg | ((uint8_t)bg << 4); + return SetConsoleTextAttribute(console_handle, color); } return false; } - std::unordered_map< log_level, log_type_info_t > console_type_info = - { - { log_level::LOG_FATAL, { "[ ! ] ", console_colors::RED, console_colors::WHITE } }, - { log_level::LOG_ERROR, { "[ - ] ", console_colors::RED, console_colors::BLACK } }, - { log_level::LOG_WARN, { "[ # ] ", console_colors::BLACK, console_colors::YELLOW } }, - { log_level::LOG_OK, { "[ + ] ", console_colors::GREEN, console_colors::BLACK } }, - { log_level::LOG_INFO, { "[ ~ ] ", console_colors::WHITE, console_colors::BLACK } }, - { log_level::LOG_DEBUG, { " ", console_colors::DARK_GRAY, console_colors::BLACK } }, + std::unordered_map< log_level, log_type_info_t > console_type_info = { + {log_level::LOG_FATAL, {"[ ! ] ", console_colors::RED, console_colors::WHITE}}, + {log_level::LOG_ERROR, {"[ - ] ", console_colors::RED, console_colors::BLACK}}, + {log_level::LOG_WARN, {"[ # ] ", console_colors::BLACK, console_colors::YELLOW}}, + {log_level::LOG_OK, {"[ + ] ", console_colors::GREEN, console_colors::BLACK}}, + {log_level::LOG_INFO, {"[ ~ ] ", console_colors::WHITE, console_colors::BLACK}}, + {log_level::LOG_DEBUG, {" ", console_colors::DARK_GRAY, console_colors::BLACK}}, }; std::mutex m; HANDLE console_handle = INVALID_HANDLE_VALUE; }; -inline auto g_logger = std::make_unique< logger >( L"~ rpgmaker scraper by nit ~" ); +inline auto g_logger = std::make_unique< logger >(L"~ rpgmaker scraper by nit ~"); #define log_colored_nnl(fg, bg, ...) g_logger->print_colored(fg, bg, false, __VA_ARGS__) #define log_colored( fg, bg, ... ) g_logger->print_colored( fg, bg, true, __VA_ARGS__ ) #define _log(log_type, ...) g_logger->print( log_type, __VA_ARGS__ ) diff --git a/main.cpp b/main.cpp index ed22301..8b292c1 100644 --- a/main.cpp +++ b/main.cpp @@ -5,71 +5,71 @@ int main(int argc, const char *argv[]) { - constexpr uint32_t expected_minimum_argc = 3; - - constexpr const char* search_type_variables = "-v"; - constexpr const char* search_type_switches = "-s"; - - const auto print_usage = []() { - log_err(R"(Usage: RPGMakerScraper -v 143 test_output.txt)"); - }; - - // check the argument count - if (argc < expected_minimum_argc) { - print_usage(); - return 1; - } - - const std::string search_type{ argv[1] }; - const std::string id_str{ argv[2] }; - - const bool output_to_file = argc == 4; - - // check search types - if (search_type != search_type_variables) { - log_err(R"(invalid search type. Only '-v' for variables is supported at the moment.")"); - print_usage(); - return 1; - } - - // search variable ids - if (search_type == search_type_variables) { - // make sure this id is actually a number - if (!std::all_of(id_str.begin(), id_str.end(), isdigit)) { - log_err(R"(invalid variable id. Please provide a number.")"); - print_usage(); - return 1; - } - - const uint32_t variable_id = static_cast(std::stoul(id_str)); - - try { - RPGMakerScraper scraper{ ScrapeMode::VARIABLES, variable_id }; - scraper.scrape_variables(); - - if (output_to_file) { - log_info(R"(writing results to %s...)", argv[3]); - const std::string file_name{ argv[3] }; - std::ofstream file(file_name, std::ios_base::out); - - if (!file.is_open() || !file.good()) { - throw std::invalid_argument(R"(unable to create output file)"); - } - - file << scraper; - file.close(); - - log_ok(R"(results wrote successfully.)"); - } - - } catch (const std::exception &e) { - log_err(R"(exception caught: %s)", e.what()); - } - } - - log_nopre("\n"); - log_ok(R"(press any key to close the program...)"); - - std::cin.get(); - return 0; + constexpr uint32_t expected_minimum_argc = 3; + + constexpr const char *search_type_variables = "-v"; + constexpr const char *search_type_switches = "-s"; + + const auto print_usage = []() { + log_err(R"(Usage: RPGMakerScraper -v 143 test_output.txt)"); + }; + + // check the argument count + if (argc < expected_minimum_argc) { + print_usage(); + return 1; + } + + const std::string search_type{argv[1]}; + const std::string id_str{argv[2]}; + + const bool output_to_file = argc == 4; + + // check search types + if (search_type != search_type_variables) { + log_err(R"(invalid search type. Only '-v' for variables is supported at the moment.")"); + print_usage(); + return 1; + } + + // search variable ids + if (search_type == search_type_variables) { + // make sure this id is actually a number + if (!std::all_of(id_str.begin(), id_str.end(), isdigit)) { + log_err(R"(invalid variable id. Please provide a number.")"); + print_usage(); + return 1; + } + + const uint32_t variable_id = static_cast(std::stoul(id_str)); + + try { + RPGMakerScraper scraper{ScrapeMode::VARIABLES, variable_id}; + scraper.scrape_variables(); + + if (output_to_file) { + log_info(R"(writing results to %s...)", argv[3]); + const std::string file_name{argv[3]}; + std::ofstream file(file_name, std::ios_base::out); + + if (!file.is_open() || !file.good()) { + throw std::invalid_argument(R"(unable to create output file)"); + } + + file << scraper; + file.close(); + + log_ok(R"(results wrote successfully.)"); + } + + } catch (const std::exception &e) { + log_err(R"(exception caught: %s)", e.what()); + } + } + + log_nopre("\n"); + log_ok(R"(press any key to close the program...)"); + + std::cin.get(); + return 0; } diff --git a/rpgmaker_scraper.cpp b/rpgmaker_scraper.cpp index d6748ba..f34118f 100644 --- a/rpgmaker_scraper.cpp +++ b/rpgmaker_scraper.cpp @@ -10,583 +10,597 @@ using colors = logger::console_colors; void RPGMakerScraper::load() { - log_info(R"(verifying we're in the proper path...)"); - if (!setup_directory()) { - throw std::logic_error("invalid root directory"); - } + log_info(R"(verifying we're in the proper path...)"); - log_info(R"(populating all the map names...)"); + if (!setup_directory()) { + throw std::logic_error("invalid root directory"); + } - // grab all the map names for later use - if (!populate_map_names()) { - throw std::logic_error("unable to populate map names"); - } + log_info(R"(populating all the map names...)"); - // handle verifying ids - if (mode == ScrapeMode::VARIABLES) { - log_info(R"(populating all the variable names...)"); + // grab all the map names for later use + if (!populate_map_names()) { + throw std::logic_error("unable to populate map names"); + } - // grab all the variable names for later use - if (!populate_variable_names()) { - throw std::logic_error("unable to populate variable names"); - } + // handle verifying ids + if (mode == ScrapeMode::VARIABLES) { + log_info(R"(populating all the variable names...)"); - log_info(R"(verifying variable id...)"); + // grab all the variable names for later use + if (!populate_variable_names()) { + throw std::logic_error("unable to populate variable names"); + } - if (!get_variable_name(variable_id)) { - log_err(R"(variable #%03d doesn't exist as a predefined variable in this game!)", variable_id); - throw std::invalid_argument("invalid variable id"); - } + log_info(R"(verifying variable id...)"); - // setup variable name - variable_name = *get_variable_name(variable_id); - } else { - log_err(R"(only mode supported at the moment is variables)"); - throw std::invalid_argument("unsupported scrape mode"); - } + if (!get_variable_name(variable_id)) { + log_err(R"(variable #%03d doesn't exist as a predefined variable in this game!)", variable_id); + throw std::invalid_argument("invalid variable id"); + } - // scrape all maps - log_info(R"(scraping maps...)"); + // setup variable name + variable_name = *get_variable_name(variable_id); + } else { + log_err(R"(only mode supported at the moment is variables)"); + throw std::invalid_argument("unsupported scrape mode"); + } - scrape_maps(); + // scrape all maps + log_info(R"(scraping maps...)"); + + scrape_maps(); } void RPGMakerScraper::scrape_maps() { - for (const auto& [map_id, name] : map_info_names) { - // give hacky visual progress - progress_status = utils::format_string(R"(scraping Map%03d...)", map_id); - log_colored_nnl(colors::WHITE, colors::BLACK, "%s", progress_status.data()); - log_colored_nnl(colors::WHITE, colors::BLACK, "%s", std::string(progress_status.length(), '\b').data()); - - // check if the current map we're scraping exists - std::filesystem::path map_file_path = root_data_path / format_map_name(map_id); - if (!std::filesystem::exists(map_file_path)) { - log_nopre("\n"); - log_warn(R"(map id: %03d indicates there's supposed to be a file called: '%s' but it couldn't be found!)", map_id, map_file_path.string().data()); - continue; - } - - // open the file - std::ifstream map_file(map_file_path); - if (!map_file.is_open() || !map_file.good()) { - log_nopre("\n"); - log_err(R"(unable to read '%s')", map_file_path.string().data()); - continue; - } - - // extract the json content - json map_json; - map_file >> map_json; - map_file.close(); - - // verify that it contains 'events' - if (!map_json.contains("events")) { - log_nopre("\n"); - log_warn(R"('%s' doesn't contain events!)", map_file_path.string().data()); - continue; - } - - // scrape the events - for (const auto& event : map_json["events"]) { - if (event.empty() || event.is_null()) { - continue; - } - - all_events[map_id].emplace_back(RPGMaker::Event{ event }); - } - } + + for (const auto &[map_id, name] : map_info_names) { + // give hacky visual progress + progress_status = + utils::format_string(R"(scraping Map%03d...)", map_id); + log_colored_nnl(colors::WHITE, colors::BLACK, "%s", progress_status.data()); + + log_colored_nnl(colors::WHITE, colors::BLACK, "%s", + std::string(progress_status.length(), '\b').data()); + + // check if the current map we're scraping exists + std::filesystem::path map_file_path = root_data_path / format_map_name(map_id); + if (!std::filesystem::exists(map_file_path)) { + log_nopre("\n"); + log_warn(R"(map id: %03d indicates there's supposed to be a file called: '%s' but it couldn't be found!)", map_id, map_file_path.string().data()); + continue; + } + + // open the file + std::ifstream map_file(map_file_path); + if (!map_file.is_open() || !map_file.good()) { + log_nopre("\n"); + log_err(R"(unable to read '%s')", map_file_path.string().data()); + continue; + } + + // extract the json content + json map_json; + map_file >> map_json; + map_file.close(); + + // verify that it contains 'events' + if (!map_json.contains("events")) { + log_nopre("\n"); + log_warn(R"('%s' doesn't contain events!)", map_file_path.string().data()); + continue; + } + + // scrape the events + for (const auto &event : map_json["events"]) { + if (event.empty() || event.is_null()) { + continue; + } + + all_events[map_id].emplace_back(RPGMaker::Event{event}); + } + } } void RPGMakerScraper::scrape_variables() { - // go over every map - for (const auto& [map_id, events] : all_events) { - // go over every event - for (const auto& event : events) { - // go over event page in each event - for (size_t page_num = 0, page_count = event.pages.size(); page_num < page_count; ++page_num) { - const auto& page = event.pages[page_num]; - - ResultInformation result_info{}; - result_info.event_page = static_cast(page_num) + 1; - result_info.event_info = event; - - if (scrape_event_page_condition(result_info, page)) { - results[map_id].push_back(result_info); - } - - // now we'll take a look at each command and scrape accordingly - for (size_t line_num = 0, line_count = page.list.size(); line_num < line_count; ++line_num) { - const auto& line = page.list[line_num]; - - if (line.is_if_statement()) { - if (scrape_command_if_statement(result_info, line)) { - result_info.line_number = static_cast(line_num) + 1; - results[map_id].push_back(result_info); - continue; - } - } - - if (line.is_control_variable()) { - if (scrape_command_control_variable(result_info, line)) { - result_info.line_number = static_cast(line_num) + 1; - results[map_id].push_back(result_info); - continue; - } - } - - if (line.is_script()) { - if (scrape_command_script(result_info, line)) { - result_info.line_number = static_cast(line_num) + 1; - results[map_id].push_back(result_info); - continue; - } - } - } - } - } - } - - print_results(); + + // go over every map + for (const auto &[map_id, events] : all_events) { + // go over every event + for (const auto &event : events) { + // go over event page in each event + for (size_t page_num = 0, page_count = event.pages.size(); page_num < page_count; ++page_num) { + const auto &page = event.pages[page_num]; + + ResultInformation result_info{}; + result_info.event_page = static_cast(page_num) + 1; + result_info.event_info = event; + + if (scrape_event_page_condition(result_info, page)) { + results[map_id].push_back(result_info); + } + + // now we'll take a look at each command and scrape accordingly + for (size_t line_num = 0, line_count = page.list.size(); line_num < line_count; ++line_num) { + const auto &line = page.list[line_num]; + + if (line.is_if_statement()) { + if (scrape_command_if_statement(result_info, line)) { + result_info.line_number = static_cast(line_num) + 1; + results[map_id].push_back(result_info); + continue; + } + } + + if (line.is_control_variable()) { + if (scrape_command_control_variable(result_info, line)) { + result_info.line_number = static_cast(line_num) + 1; + results[map_id].push_back(result_info); + continue; + } + } + + if (line.is_script()) { + if (scrape_command_script(result_info, line)) { + result_info.line_number = static_cast(line_num) + 1; + results[map_id].push_back(result_info); + continue; + } + } + } + } + } + } + + print_results(); } bool RPGMakerScraper::setup_directory() { - // setup our working directory in the 'data' folder of the application - root_data_path = std::filesystem::current_path() / "data"; - // check if the 'data/' folder exists. - if (!std::filesystem::exists(root_data_path)) { - log_err(R"('data/' folder doesn't exist. Please drop this executable in the root directory of your RPG Maker project.)"); - return false; - } + // setup our working directory in the 'data' folder of the application + root_data_path = std::filesystem::current_path() / "data"; - return true; + // check if the 'data/' folder exists. + if (!std::filesystem::exists(root_data_path)) { + log_err(R"('data/' folder doesn't exist. Please drop this executable in the root directory of your RPG Maker project.)"); + return false; + } + + return true; } bool RPGMakerScraper::populate_map_names() { - constexpr const char* map_infos_file_str = "MapInfos.json"; - const std::filesystem::path map_infos_path = root_data_path / map_infos_file_str; + constexpr const char *map_infos_file_str = "MapInfos.json"; + const std::filesystem::path map_infos_path = root_data_path / map_infos_file_str; - if (!std::filesystem::exists(map_infos_path)) { - log_err(R"(MapInfos.json doesn't exist inside data/. Please make sure you're in the proper folder.)"); - return false; - } + if (!std::filesystem::exists(map_infos_path)) { + log_err(R"(MapInfos.json doesn't exist inside data/. Please make sure you're in the proper folder.)"); + return false; + } - std::ifstream mapInfos_file(map_infos_path); - if (mapInfos_file.bad()) { - log_err(R"(Unable to open the mapinfos file.)"); - return false; - } + std::ifstream mapInfos_file(map_infos_path); + if (mapInfos_file.bad()) { + log_err(R"(Unable to open the mapinfos file.)"); + return false; + } - json map_info_json; - mapInfos_file >> map_info_json; + json map_info_json; + mapInfos_file >> map_info_json; - for (const auto& group : map_info_json) { - if (group.empty() || - !group.contains("id") || !group["id"].is_number_integer() || - !group.contains("name") || !group["name"].is_string()) { - continue; - } + for (const auto &group : map_info_json) { + if (group.empty() || + !group.contains("id") || !group["id"].is_number_integer() || + !group.contains("name") || !group["name"].is_string()) { + continue; + } - const uint32_t map_id = group["id"].get(); - const std::string_view map_name = group["name"].get(); + const uint32_t map_id = group["id"].get(); + const std::string_view map_name = group["name"].get(); - map_info_names[map_id] = map_name; - } + map_info_names[map_id] = map_name; + } - mapInfos_file.close(); + mapInfos_file.close(); - return true; + return true; } bool RPGMakerScraper::populate_variable_names() { - constexpr const char* system_file_str = "System.json"; - const std::filesystem::path system_file_path = root_data_path / system_file_str; - - if (!std::filesystem::exists(system_file_path)) { - log_err(R"(System.json doesn't exist inside data/. Please make sure you're in the proper folder.)"); - return false; - } - - std::ifstream system_file(system_file_path); - if (system_file.bad()) { - log_err(R"(Unable to open the System file.)"); - return false; - } - - json system_json; - system_file >> system_json; - - if (!system_json.contains("variables")) { - log_err(R"(System.json doesn't contain variables!")"); - system_file.close(); - return false; - } - - for (size_t var_id = 0, size = system_json["variables"].size(); var_id < size; ++var_id) { - if (system_json["variables"][var_id].is_null()) { - continue; - } - variable_names[static_cast(var_id)] = system_json["variables"][var_id].get(); - } - - system_file.close(); - - return true; + + constexpr const char *system_file_str = "System.json"; + const std::filesystem::path system_file_path = root_data_path / system_file_str; + + if (!std::filesystem::exists(system_file_path)) { + log_err(R"(System.json doesn't exist inside data/. Please make sure you're in the proper folder.)"); + return false; + } + + std::ifstream system_file(system_file_path); + if (system_file.bad()) { + log_err(R"(Unable to open the System file.)"); + return false; + } + + json system_json; + system_file >> system_json; + + if (!system_json.contains("variables")) { + log_err(R"(System.json doesn't contain variables!")"); + system_file.close(); + return false; + } + + for (size_t var_id = 0, size = system_json["variables"].size(); var_id < size; ++var_id) { + if (system_json["variables"][var_id].is_null()) { + continue; + } + variable_names[static_cast(var_id)] = system_json["variables"][var_id].get(); + } + + system_file.close(); + + return true; } std::string RPGMakerScraper::format_map_name(uint32_t id) const { - return utils::format_string("Map%03d.json", id); + + return utils::format_string("Map%03d.json", id); } -std::string RPGMakerScraper::format_event_page_condition(const Condition& condition) { - return utils::format_string("IF {%s} >= %d:", variable_name.data(), condition.variable_value); +std::string RPGMakerScraper::format_event_page_condition(const Condition &condition) { + + return utils::format_string("IF {%s} >= %d:", variable_name.data(), condition.variable_value); } -bool RPGMakerScraper::scrape_event_page_condition(ResultInformation &result_info, const EventPage& event_page) { - if (event_page.conditions.variable_id != variable_id) { - return false; - } +bool RPGMakerScraper::scrape_event_page_condition(ResultInformation &result_info, const EventPage &event_page) { + + if (event_page.conditions.variable_id != variable_id) { + return false; + } - // hacky check since RPGMaker's default id is '1'. - // so to prevent possible wrong results, we're going to ignore the ones that are 'off' - if (variable_id == 1 && !event_page.conditions.variable_valid) { - return false; - } + // hacky check since RPGMaker's default id is '1'. + // so to prevent possible wrong results, we're going to ignore the ones that are 'off' + if (variable_id == 1 && !event_page.conditions.variable_valid) { + return false; + } - result_info.access_type = AccessType::READ; - result_info.active = event_page.conditions.variable_valid; - result_info.formatted_action = format_event_page_condition(event_page.conditions); + result_info.access_type = AccessType::READ; + result_info.active = event_page.conditions.variable_valid; + result_info.formatted_action = format_event_page_condition(event_page.conditions); - return true; + return true; } -std::string RPGMakerScraper::format_command_if_statement(const std::vector& parameters) { - static const std::unordered_map operator_strs = { - { 0, "=" }, - { 1, ">=" }, - { 2, "<=" }, - { 3, ">" }, - { 4, "<" }, - { 5, "!=" }, - }; - - const auto oper = std::get(parameters[4]); - - if (oper >= operator_strs.size()) { - log_warn("Operator was out of range!"); - return "malformed operator"; - } - - const bool being_compared_against = - (std::get(parameters[2]) == static_cast(IfStatement::IDType::VARIABLE) && - std::get(parameters[3]) == variable_id); - - std::string if_statement = "IF "; - - if (!being_compared_against) { - if_statement += - utils::format_string("{%s} %s %d:", get_variable_name(variable_id)->data(), - operator_strs.at(oper).data(), std::get(parameters[3])); - } else { - if_statement += - utils::format_string("{#%d} %s {%s}:", std::get(parameters[1]), - operator_strs.at(oper).data(), get_variable_name(variable_id)->data()); - } - return if_statement; +std::string RPGMakerScraper::format_command_if_statement(const std::vector ¶meters) { + + static const std::unordered_map operator_strs = { + {0, "="}, + {1, ">="}, + {2, "<="}, + {3, ">"}, + {4, "<"}, + {5, "!="}, + }; + + const auto oper = std::get(parameters[4]); + + if (oper >= operator_strs.size()) { + log_warn("Operator was out of range!"); + return "malformed operator"; + } + + const bool being_compared_against = + (std::get(parameters[2]) == static_cast(IfStatement::IDType::VARIABLE) && + std::get(parameters[3]) == variable_id); + + std::string if_statement = "IF "; + + if (!being_compared_against) { + if_statement += + utils::format_string("{%s} %s %d:", get_variable_name(variable_id)->data(), + operator_strs.at(oper).data(), std::get(parameters[3])); + } else { + if_statement += + utils::format_string("{#%d} %s {%s}:", std::get(parameters[1]), + operator_strs.at(oper).data(), get_variable_name(variable_id)->data()); + } + return if_statement; } -bool RPGMakerScraper::scrape_command_if_statement(ResultInformation& result_info, const Command& command) { - constexpr uint32_t expected_param_count = 5; - - if (command.parameters.size() != expected_param_count) { - return false; - } - - const auto id_type = static_cast(std::get(command.parameters[0])); - const auto id = std::get(command.parameters[1]); - const auto compare_type = static_cast(std::get(command.parameters[2])); - const auto compared_id = std::get(command.parameters[3]); - - // check if we're in the right mode - if (mode == ScrapeMode::VARIABLES && id_type != IfStatement::IDType::VARIABLE) { - return false; - } - if (mode == ScrapeMode::SWITCHES && id_type != IfStatement::IDType::SWITCH) { - return false; - } - - // check if our id is used - if (compare_type == IfStatement::CompareType::CONSTANT && id != variable_id) { - return false; - } - if (compare_type == IfStatement::CompareType::VARIABLE && (id != variable_id || compared_id != variable_id)) { - return false; - } - - result_info.access_type = AccessType::READ; - result_info.active = true; - result_info.formatted_action = format_command_if_statement(command.parameters); - - return true; +bool RPGMakerScraper::scrape_command_if_statement(ResultInformation &result_info, const Command &command) { + + constexpr uint32_t expected_param_count = 5; + + if (command.parameters.size() != expected_param_count) { + return false; + } + + const auto id_type = static_cast(std::get(command.parameters[0])); + const auto id = std::get(command.parameters[1]); + const auto compare_type = static_cast(std::get(command.parameters[2])); + const auto compared_id = std::get(command.parameters[3]); + + // check if we're in the right mode + if (mode == ScrapeMode::VARIABLES && id_type != IfStatement::IDType::VARIABLE) { + return false; + } + if (mode == ScrapeMode::SWITCHES && id_type != IfStatement::IDType::SWITCH) { + return false; + } + + // check if our id is used + if (compare_type == IfStatement::CompareType::CONSTANT && id != variable_id) { + return false; + } + if (compare_type == IfStatement::CompareType::VARIABLE && (id != variable_id || compared_id != variable_id)) { + return false; + } + + result_info.access_type = AccessType::READ; + result_info.active = true; + result_info.formatted_action = format_command_if_statement(command.parameters); + + return true; } -bool RPGMakerScraper::scrape_command_control_variable(ResultInformation& result_info, const Command& command) { - constexpr uint32_t expected_param_count_for_constant = 5; - constexpr uint32_t expected_param_count_for_variable = 5; - constexpr uint32_t expected_param_count_for_random = 6; - - const auto operand = static_cast(std::get(command.parameters[3])); - - // verify operands match their parameter counts - if (operand == ControlVariable::Operand::CONSTANT && command.parameters.size() != expected_param_count_for_constant) { - return false; - } - if (operand == ControlVariable::Operand::VARIABLE && command.parameters.size() != expected_param_count_for_variable) { - return false; - } - if (operand == ControlVariable::Operand::RANDOM && command.parameters.size() != expected_param_count_for_random) { - return false; - } - - const auto variable_id_start = std::get(command.parameters[0]); - const auto variable_id_end = std::get(command.parameters[1]); - - const bool is_range = variable_id_start != variable_id_end; - - // check if this command is mutating our variable - if (operand == ControlVariable::Operand::CONSTANT && variable_id_start != variable_id) { - return false; - } - if (operand == ControlVariable::Operand::VARIABLE && - (variable_id_start != variable_id && variable_id_end != variable_id)) { - return false; - } - if (operand == ControlVariable::Operand::RANDOM && - (variable_id_start != variable_id) || (is_range && variable_id_end != variable_id)){ - return false; - } - if (operand == ControlVariable::Operand::SCRIPT) { - const auto &script_line = std::get(command.parameters[4]); - - return determine_access_from_script(result_info, script_line); - } - if (operand == ControlVariable::Operand::GAME_DATA) { - // data doesn't pertain to variables or switches, so we don't care - return false; - } - - // access depends on where the variable is being used - - // if we're setting a value to our variable - if (operand == ControlVariable::Operand::CONSTANT || operand == ControlVariable::Operand::RANDOM) { - result_info.access_type = AccessType::WRITE; - } - else if (operand == ControlVariable::Operand::VARIABLE) { - // if the thing we're setting another variable to is ours - if (std::get(command.parameters[4]) == variable_id) { - result_info.access_type = AccessType::READ; - } - // if we're setting our variable to another variable in a range or not - else if (variable_id_start == variable_id || ( is_range && variable_id_end == variable_id)) { - result_info.access_type = AccessType::WRITE; - } - } - - result_info.active = true; - result_info.formatted_action = format_command_control_variable(command.parameters); - - return true; +bool RPGMakerScraper::scrape_command_control_variable(ResultInformation &result_info, const Command &command) { + + constexpr uint32_t expected_param_count_for_constant = 5; + constexpr uint32_t expected_param_count_for_variable = 5; + constexpr uint32_t expected_param_count_for_random = 6; + + const auto operand = static_cast(std::get(command.parameters[3])); + + // verify operands match their parameter counts + if (operand == ControlVariable::Operand::CONSTANT && command.parameters.size() != expected_param_count_for_constant) { + return false; + } + if (operand == ControlVariable::Operand::VARIABLE && command.parameters.size() != expected_param_count_for_variable) { + return false; + } + if (operand == ControlVariable::Operand::RANDOM && command.parameters.size() != expected_param_count_for_random) { + return false; + } + + const auto variable_id_start = std::get(command.parameters[0]); + const auto variable_id_end = std::get(command.parameters[1]); + + const bool is_range = variable_id_start != variable_id_end; + + // check if this command is mutating our variable + if (operand == ControlVariable::Operand::CONSTANT && variable_id_start != variable_id) { + return false; + } + if (operand == ControlVariable::Operand::VARIABLE && + (variable_id_start != variable_id && variable_id_end != variable_id)) { + return false; + } + if (operand == ControlVariable::Operand::RANDOM && + (variable_id_start != variable_id) || (is_range && variable_id_end != variable_id)) { + return false; + } + if (operand == ControlVariable::Operand::SCRIPT) { + const auto &script_line = std::get(command.parameters[4]); + + return determine_access_from_script(result_info, script_line); + } + if (operand == ControlVariable::Operand::GAME_DATA) { + // data doesn't pertain to variables or switches, so we don't care + return false; + } + + // access depends on where the variable is being used + + // if we're setting a value to our variable + if (operand == ControlVariable::Operand::CONSTANT || operand == ControlVariable::Operand::RANDOM) { + result_info.access_type = AccessType::WRITE; + } else if (operand == ControlVariable::Operand::VARIABLE) { + // if the thing we're setting another variable to is ours + if (std::get(command.parameters[4]) == variable_id) { + result_info.access_type = AccessType::READ; + } + // if we're setting our variable to another variable in a range or not + else if (variable_id_start == variable_id || (is_range && variable_id_end == variable_id)) { + result_info.access_type = AccessType::WRITE; + } + } + + result_info.active = true; + result_info.formatted_action = format_command_control_variable(command.parameters); + + return true; } -std::string RPGMakerScraper::format_command_control_variable(const std::vector& parameters) { - static const std::unordered_map operation_strs = { - { 0, "=" }, - { 1, "+=" }, - { 2, "-=" }, - { 3, "*=" }, - { 4, "/=" }, - { 5, "%=" }, - }; +std::string RPGMakerScraper::format_command_control_variable(const std::vector ¶meters) { - const auto variable_id_start = std::get(parameters[0]); - const auto variable_id_end = std::get(parameters[1]); + static const std::unordered_map operation_strs = { + {0, "="}, + {1, "+="}, + {2, "-="}, + {3, "*="}, + {4, "/="}, + {5, "%="}, + }; - const bool is_range = variable_id_start != variable_id_end; + const auto variable_id_start = std::get(parameters[0]); + const auto variable_id_end = std::get(parameters[1]); - const auto operation = std::get(parameters[3]); - if (operation >= operation_strs.size()) { - log_warn("Operation was out of range!"); - return "malformed operation"; - } + const bool is_range = variable_id_start != variable_id_end; - const auto var_prefix = is_range ? - utils::format_string("{%s} .. {%s}", get_variable_name(variable_id_start)->data(), get_variable_name(variable_id_end)->data()) : - utils::format_string("{%s}", get_variable_name(variable_id_start)->data()); + const auto operation = std::get(parameters[3]); + if (operation >= operation_strs.size()) { + log_warn("Operation was out of range!"); + return "malformed operation"; + } - const auto operand = static_cast(std::get(parameters[3])); + const auto var_prefix = is_range ? + utils::format_string("{%s} .. {%s}", get_variable_name(variable_id_start)->data(), get_variable_name(variable_id_end)->data()) : + utils::format_string("{%s}", get_variable_name(variable_id_start)->data()); - if (operand == ControlVariable::Operand::VARIABLE) { - const auto variable = std::get(parameters[4]); + const auto operand = static_cast(std::get(parameters[3])); - return utils::format_string("%s %s {%s}", var_prefix.data(), operation_strs.at(operation).data(), get_variable_name(variable)->data()); - } - else if (operand == ControlVariable::Operand::CONSTANT) { - const auto constant = std::get(parameters[4]); + if (operand == ControlVariable::Operand::VARIABLE) { + const auto variable = std::get(parameters[4]); - return utils::format_string("%s %s %d", var_prefix.data(), operation_strs.at(operation).data(), constant); - } - else if (operand == ControlVariable::Operand::RANDOM) { - const auto min = std::get(parameters[4]); - const auto max = std::get(parameters[5]); + return utils::format_string("%s %s {%s}", var_prefix.data(), operation_strs.at(operation).data(), get_variable_name(variable)->data()); + } else if (operand == ControlVariable::Operand::CONSTANT) { + const auto constant = std::get(parameters[4]); - return utils::format_string("%s = Random %d .. %d", var_prefix.data(), min, max); - } + return utils::format_string("%s %s %d", var_prefix.data(), operation_strs.at(operation).data(), constant); + } else if (operand == ControlVariable::Operand::RANDOM) { + const auto min = std::get(parameters[4]); + const auto max = std::get(parameters[5]); - return "unsupported"; + return utils::format_string("%s = Random %d .. %d", var_prefix.data(), min, max); + } + + return "unsupported"; } -bool RPGMakerScraper::scrape_command_script(ResultInformation& result_info, const Command& command) { - constexpr uint32_t expected_param_count = 1; +bool RPGMakerScraper::scrape_command_script(ResultInformation &result_info, const Command &command) { + + constexpr uint32_t expected_param_count = 1; - if (!command.parameters.size() != expected_param_count) { - return false; - } + if (!command.parameters.size() != expected_param_count) { + return false; + } - return determine_access_from_script(result_info, std::get(command.parameters[0])); + return determine_access_from_script(result_info, std::get(command.parameters[0])); } -bool RPGMakerScraper::determine_access_from_script(ResultInformation& result_info, std::string_view script_line) { - const std::string script_read_value = utils::format_string("$gameVariables.value(%d)", variable_id); - if (script_line.find(script_read_value) != std::string::npos) { - result_info.access_type = AccessType::READ; - result_info.active = true; - result_info.formatted_action = script_line; - return true; - } - const std::string script_write_value = utils::format_string("$gameVariables.setValue(%d", variable_id); - if (script_line.find(script_write_value) != std::string::npos) { - result_info.access_type = AccessType::WRITE; - result_info.active = true; - result_info.formatted_action = script_line; - return true; - } - - return false; +bool RPGMakerScraper::determine_access_from_script(ResultInformation &result_info, std::string_view script_line) { + + const std::string script_read_value = utils::format_string("$gameVariables.value(%d)", variable_id); + if (script_line.find(script_read_value) != std::string::npos) { + result_info.access_type = AccessType::READ; + result_info.active = true; + result_info.formatted_action = script_line; + return true; + } + const std::string script_write_value = utils::format_string("$gameVariables.setValue(%d", variable_id); + if (script_line.find(script_write_value) != std::string::npos) { + result_info.access_type = AccessType::WRITE; + result_info.active = true; + result_info.formatted_action = script_line; + return true; + } + + return false; } uint32_t RPGMakerScraper::calculate_instances() const { - uint32_t count = 0; - for (const auto& pair : results) { - count += static_cast(pair.second.size()); - } - return count; + + uint32_t count = 0; + for (const auto &pair : results) { + count += static_cast(pair.second.size()); + } + return count; } void RPGMakerScraper::print_results() { - if (results.empty()) { - log_colored(logger::console_colors::RED, logger::console_colors::BLACK, "Couldn't locate maps using RPGMaker Variable #%03d", variable_id); - return; - } - - log_nopre("========================================="); - - log_colored_nnl(colors::WHITE, colors::BLACK, "Found "); - log_colored_nnl(colors::GREEN, colors::BLACK, "%d %s", results.size(), (results.size() > 1 ? "maps" : "map")); - log_colored_nnl(colors::WHITE, colors::BLACK, " yielding "); - log_colored_nnl(colors::GREEN, colors::BLACK, "%d separate %s ", calculate_instances(), (calculate_instances() > 1 ? "instances" : "instance")); - - if (mode == ScrapeMode::VARIABLES) { - log_colored(colors::WHITE, colors::BLACK, "using variable #%03d (\'%s\')", variable_id, variable_name.data()); - } - - log_nopre("========================================="); - - for (const auto& [map_id, hits] : results) { - log_colored(colors::CYAN, colors::BLACK, "\n%s ('%s')", format_map_name(map_id).data(), get_map_name(map_id)->data()); - log_colored(colors::WHITE, colors::BLACK, "--------------------------------------------------\n"); - for (const auto& hit : hits) { - const auto& event_info = hit.event_info; - - // group similar events cleanly - static auto latest_event_id = event_info.id; - if (latest_event_id != event_info.id) { - latest_event_id = event_info.id; - if (hit != *hits.begin()) { - log_nopre("\n"); - } - } - - log_colored_nnl((hit.active ? colors::DARK_GREEN : colors::DARK_GRAY), colors::BLACK, "%s", - (hit.active ? "ON" : "OFF")); - log_colored((hit.access_type == AccessType::READ ? colors::BLUE : colors::RED), colors::BLACK, " [%s]", (hit.access_type == AccessType::READ ? "READ" : "WRITE")); - - log_nopre("\t@ [%d, %d] on Event #%03d ('%s') on Event Page #%02d:", event_info.x, event_info.y, - event_info.id, event_info.name.data(), hit.event_page); - - if (hit.line_number) { - log_colored_nnl(colors::DARK_GRAY, colors::BLACK, "\t\tLine %03d", *hit.line_number); - log_colored(colors::WHITE, colors::BLACK, " | %s", hit.formatted_action.data()); - } else { - log_colored(colors::WHITE, colors::BLACK, "\t\t%s", hit.formatted_action.data()); - } - } - } - - log_nopre("========================================="); + if (results.empty()) { + log_colored(logger::console_colors::RED, logger::console_colors::BLACK, "Couldn't locate maps using RPGMaker Variable #%03d", variable_id); + return; + } + + log_nopre("========================================="); + + log_colored_nnl(colors::WHITE, colors::BLACK, "Found "); + log_colored_nnl(colors::GREEN, colors::BLACK, "%d %s", results.size(), (results.size() > 1 ? "maps" : "map")); + log_colored_nnl(colors::WHITE, colors::BLACK, " yielding "); + log_colored_nnl(colors::GREEN, colors::BLACK, "%d separate %s ", calculate_instances(), (calculate_instances() > 1 ? "instances" : "instance")); + + if (mode == ScrapeMode::VARIABLES) { + log_colored(colors::WHITE, colors::BLACK, "using variable #%03d (\'%s\')", variable_id, variable_name.data()); + } + + log_nopre("========================================="); + + for (const auto &[map_id, hits] : results) { + log_colored(colors::CYAN, colors::BLACK, "\n%s ('%s')", format_map_name(map_id).data(), get_map_name(map_id)->data()); + log_colored(colors::WHITE, colors::BLACK, "--------------------------------------------------\n"); + for (const auto &hit : hits) { + const auto &event_info = hit.event_info; + + // group similar events cleanly + static auto latest_event_id = event_info.id; + if (latest_event_id != event_info.id) { + latest_event_id = event_info.id; + if (hit != *hits.begin()) { + log_nopre("\n"); + } + } + + log_colored_nnl((hit.active ? colors::DARK_GREEN : colors::DARK_GRAY), colors::BLACK, "%s", + (hit.active ? "ON" : "OFF")); + log_colored((hit.access_type == AccessType::READ ? colors::BLUE : colors::RED), colors::BLACK, " [%s]", (hit.access_type == AccessType::READ ? "READ" : "WRITE")); + + log_nopre("\t@ [%d, %d] on Event #%03d ('%s') on Event Page #%02d:", event_info.x, event_info.y, + event_info.id, event_info.name.data(), hit.event_page); + + if (hit.line_number) { + log_colored_nnl(colors::DARK_GRAY, colors::BLACK, "\t\tLine %03d", *hit.line_number); + log_colored(colors::WHITE, colors::BLACK, " | %s", hit.formatted_action.data()); + } else { + log_colored(colors::WHITE, colors::BLACK, "\t\t%s", hit.formatted_action.data()); + } + } + } + + log_nopre("========================================="); } -std::ostream& operator<<(std::ostream& os, const RPGMakerScraper& scraper) -{ - if (scraper.results.empty()) { - return os; - } - - os << "=========================================" << std::endl; - - os << utils::format_string("Found %d %s ", scraper.results.size(), (scraper.results.size() > 1 ? "maps" : "map")).data() << - utils::format_string("yielding %d %s ", scraper.calculate_instances(), (scraper.calculate_instances() > 1 ? "instances" : "instance")).data(); - - if (scraper.mode == ScrapeMode::VARIABLES) { - os << utils::format_string("using variable #%03d (\'%s\')", scraper.variable_id, scraper.variable_name.data()).data(); - } - - os << std::endl << "=========================================" << std::endl; - - for (const auto& [map_id, hits] : scraper.results) { - os << std::endl; - os << utils::format_string("%s (\'%s\')", scraper.format_map_name(map_id).data(), scraper.get_map_name(map_id)->data()) << std::endl; - os << "--------------------------------------------------" << std::endl; - for (const auto& hit : hits) { - const auto& event_info = hit.event_info; - - // group similar events cleanly - static auto latest_event_id = event_info.id; - if (latest_event_id != event_info.id) { - latest_event_id = event_info.id; - if (hit != *hits.begin()) { - os << std::endl; - } - } - - os << utils::format_string("%s", (hit.active ? "ON" : "OFF")).data() << - utils::format_string(" [%s]", (hit.access_type == AccessType::READ ? "READ" : "WRITE")) << std::endl; - - os << utils::format_string("\t@ [%d, %d] on Event #%03d (\'%s\') on Event Page #%02d:", event_info.x, event_info.y, - event_info.id, event_info.name.data(), hit.event_page) << std::endl; - - if (hit.line_number) { - os << utils::format_string("\t\tLine %03d | %s", *hit.line_number, hit.formatted_action.data()) << std::endl; - } - else { - os << "\t\t" << hit.formatted_action.data() << std::endl; - } - } - } - - os << "=========================================" << std::endl; - - return os; +std::ostream &operator<<(std::ostream &os, const RPGMakerScraper &scraper) { + + if (scraper.results.empty()) { + return os; + } + + os << "=========================================" << std::endl; + + os << utils::format_string("Found %d %s ", scraper.results.size(), (scraper.results.size() > 1 ? "maps" : "map")).data() << + utils::format_string("yielding %d %s ", scraper.calculate_instances(), (scraper.calculate_instances() > 1 ? "instances" : "instance")).data(); + + if (scraper.mode == ScrapeMode::VARIABLES) { + os << utils::format_string("using variable #%03d (\'%s\')", scraper.variable_id, scraper.variable_name.data()).data(); + } + + os << std::endl << "=========================================" << std::endl; + + for (const auto &[map_id, hits] : scraper.results) { + os << std::endl; + os << utils::format_string("%s (\'%s\')", scraper.format_map_name(map_id).data(), scraper.get_map_name(map_id)->data()) << std::endl; + os << "--------------------------------------------------" << std::endl; + for (const auto &hit : hits) { + const auto &event_info = hit.event_info; + + // group similar events cleanly + static auto latest_event_id = event_info.id; + if (latest_event_id != event_info.id) { + latest_event_id = event_info.id; + if (hit != *hits.begin()) { + os << std::endl; + } + } + + os << utils::format_string("%s", (hit.active ? "ON" : "OFF")).data() << + utils::format_string(" [%s]", (hit.access_type == AccessType::READ ? "READ" : "WRITE")) << std::endl; + + os << utils::format_string("\t@ [%d, %d] on Event #%03d (\'%s\') on Event Page #%02d:", event_info.x, event_info.y, + event_info.id, event_info.name.data(), hit.event_page) << std::endl; + + if (hit.line_number) { + os << utils::format_string("\t\tLine %03d | %s", *hit.line_number, hit.formatted_action.data()) << std::endl; + } else { + os << "\t\t" << hit.formatted_action.data() << std::endl; + } + } + } + + os << "=========================================" << std::endl; + + return os; } diff --git a/rpgmaker_scraper.hpp b/rpgmaker_scraper.hpp index 012a85f..8d40409 100644 --- a/rpgmaker_scraper.hpp +++ b/rpgmaker_scraper.hpp @@ -17,74 +17,41 @@ using json = nlohmann::json; using namespace RPGMaker; enum class AccessType : uint32_t { - READ, - WRITE, + READ, + WRITE, }; enum class ScrapeMode : uint32_t { - VARIABLES, - SWITCHES, -}; - -class EventInformation { -public: - - EventInformation() = default; - EventInformation(const json &event_json); - - std::string_view get_name() const { - return name; - }; - - uint32_t get_id() const { - return id; - } - - uint32_t get_x() const { - return x; - } - - uint32_t get_y() const { - return y; - } - -private: - // Name of the event - std::string name; - // ID of the event - uint32_t id; - // X Position on the map where the event rests - uint32_t x; - // Y position on the map where the event rests - uint32_t y; + VARIABLES, + SWITCHES, }; struct ResultInformation { - - bool operator==(const ResultInformation other) const { - return (other.access_type == access_type && - other.active == active && - other.event_page == event_page && - other.formatted_action == formatted_action && - other.line_number == line_number); - } - - bool operator!=(const ResultInformation other) const { - return !operator==(other); - } - - // Is this an accessor or mutator? - AccessType access_type; - // The event information it belongs to - Event event_info; - // Is this actually active in-game code - bool active; - // What event page this is present on - uint32_t event_page; - // If this is a conditional in script, what line it appears on - std::optional line_number; - // information parsed from json describing where the variable is used - std::string formatted_action; + bool operator==(const ResultInformation other) const { + + return (other.access_type == access_type && + other.active == active && + other.event_page == event_page && + other.formatted_action == formatted_action && + other.line_number == line_number); + } + + bool operator!=(const ResultInformation other) const { + return !operator==(other); + } + + // Is this an accessor or mutator? + AccessType access_type; + // The event information it belongs to + Event event_info; + // Is this actually active in-game code + bool active; + // What event page this is present on + uint32_t event_page; + // If this is a conditional in script, what line it appears on + std::optional line_number; + // information parsed from json describing where the variable is used + std::string formatted_action; }; using MapIdToName = std::map; @@ -94,131 +61,133 @@ using EventMap = std::map>; class RPGMakerScraper { public: - RPGMakerScraper() = default; - RPGMakerScraper(ScrapeMode _mode, uint32_t _id) : variable_id(_id), mode(_mode) { load(); } - ~RPGMakerScraper() = default; + RPGMakerScraper() = default; + RPGMakerScraper(ScrapeMode _mode, uint32_t _id) : variable_id(_id), mode(_mode) { load(); } + ~RPGMakerScraper() = default; - MapIdToName get_map_info_names() const { - return map_info_names; - } + MapIdToName get_map_info_names() const { + return map_info_names; + } - VariableIdToName get_variable_names() const { - return variable_names; - } + VariableIdToName get_variable_names() const { + return variable_names; + } - std::optional get_map_name(uint32_t id) const { - if (map_info_names.empty() || map_info_names.find(id) == map_info_names.end()) { - return std::nullopt; - } - return map_info_names.at(id); - } + std::optional get_map_name(uint32_t id) const { - std::optional get_variable_name(uint32_t id) const { - if (id == 0 || variable_names.empty() || variable_names.find(id) == variable_names.end()) { - return std::nullopt; - } + if (map_info_names.empty() || map_info_names.find(id) == map_info_names.end()) { + return std::nullopt; + } + return map_info_names.at(id); + } - const std::string &name = variable_names.at(id); + std::optional get_variable_name(uint32_t id) const { - if (name.empty()) { - return std::string("#") + std::to_string(id); - } + if (id == 0 || variable_names.empty() || variable_names.find(id) == variable_names.end()) { + return std::nullopt; + } - return name; - } + const std::string &name = variable_names.at(id); - // loads all the necessary functions to setup and verify input - // throws several types of exceptions - void load(); + if (name.empty()) { + return std::string("#") + std::to_string(id); + } - // scrape all information exclusive to RPGMaker variables into results - void scrape_variables(); + return name; + } - // overload operator for ostream to print information to a file - friend std::ostream& operator<<(std::ostream& os, const RPGMakerScraper &scraper); + // loads all the necessary functions to setup and verify input + // throws several types of exceptions + void load(); + + // scrape all information exclusive to RPGMaker variables into results + void scrape_variables(); + + // overload operator for ostream to print information to a file + friend std::ostream &operator<<(std::ostream &os, const RPGMakerScraper &scraper); private: - // Path to the root folder we're searching - std::filesystem::path root_data_path; + // Path to the root folder we're searching + std::filesystem::path root_data_path; + + // All the map names mapped via map id + MapIdToName map_info_names{}; - // All the map names mapped via map id - MapIdToName map_info_names{}; + // All the variable names mapped via variable id + VariableIdToName variable_names{}; - // All the variable names mapped via variable id - VariableIdToName variable_names{}; + // The variable ID we're interested in + uint32_t variable_id = 0; - // The variable ID we're interested in - uint32_t variable_id = 0; + // The name of the variable we're interested in + std::string variable_name{}; - // The name of the variable we're interested in - std::string variable_name{}; + // All of our results via map id + ResultMap results; - // All of our results via map id - ResultMap results; + // What mode the scraper is currently in + ScrapeMode mode; - // What mode the scraper is currently in - ScrapeMode mode; + // All the events already parsed via map id + EventMap all_events; - // All the events already parsed via map id - EventMap all_events; + // Progress status + std::string progress_status{}; - // Progress status - std::string progress_status{}; + // populate all the map names into map_info_names + // returns true if successful, otherwise false + bool populate_map_names(); - // populate all the map names into map_info_names - // returns true if successful, otherwise false - bool populate_map_names(); + // populate all the variable names into variable_names + // returns true if successful, otherwise false + bool populate_variable_names(); - // populate all the variable names into variable_names - // returns true if successful, otherwise false - bool populate_variable_names(); + // check if the root directory exists and setup root_data_path + // returns true if valid, otherwise false + bool setup_directory(); - // check if the root directory exists and setup root_data_path - // returns true if valid, otherwise false - bool setup_directory(); + // scrape all the existing maps and their events into all_events + void scrape_maps(); - // scrape all the existing maps and their events into all_events - void scrape_maps(); + // translate a map id into the name of the .json file associated + std::string format_map_name(uint32_t id) const; - // translate a map id into the name of the .json file associated - std::string format_map_name(uint32_t id) const; + // output the string showing the reference to a wanted id inside a + // RPGMaker event page condition + std::string format_event_page_condition(const Condition &condition); - // output the string showing the reference to a wanted id inside a - // RPGMaker event page condition - std::string format_event_page_condition(const Condition& condition); + // scrape RPGMaker event page conditions and modify result_info accordingly + // returns true if valid, otherwise false + bool scrape_event_page_condition(ResultInformation &result_info, const EventPage &event_page); - // scrape RPGMaker event page conditions and modify result_info accordingly - // returns true if valid, otherwise false - bool scrape_event_page_condition(ResultInformation &result_info, const EventPage& event_page); - - // output the string showing the reference to a wanted id inside a - // 'If Statement' command on an event page - std::string format_command_if_statement(const std::vector& parameters); + // output the string showing the reference to a wanted id inside a + // 'If Statement' command on an event page + std::string format_command_if_statement(const std::vector ¶meters); - // scrape RPGMaker command 'If Statement' and modify result_info accordingly - // returns true if valid, otherwise false - bool scrape_command_if_statement(ResultInformation& result_info, const Command& command); + // scrape RPGMaker command 'If Statement' and modify result_info accordingly + // returns true if valid, otherwise false + bool scrape_command_if_statement(ResultInformation &result_info, const Command &command); - // output the string showing the reference to a wanted id inside a - // 'Control Variable' command on an event page - std::string format_command_control_variable(const std::vector& parameters); + // output the string showing the reference to a wanted id inside a + // 'Control Variable' command on an event page + std::string format_command_control_variable(const std::vector ¶meters); - // scrape RPGMaker command 'Control Variable' and modify result_info accordingly - // returns true if valid, otherwise false - bool scrape_command_control_variable(ResultInformation& result_info, const Command& command); + // scrape RPGMaker command 'Control Variable' and modify result_info accordingly + // returns true if valid, otherwise false + bool scrape_command_control_variable(ResultInformation &result_info, const Command &command); - // scrape a line of 'script' and modify result_info accordingly - // returns true if successful, otherwise false - bool scrape_command_script(ResultInformation& result_info, const Command& command); + // scrape a line of 'script' and modify result_info accordingly + // returns true if successful, otherwise false + bool scrape_command_script(ResultInformation &result_info, const Command &command); - // determines based on the found text inside the script line if it's read or write access - // returns true if successful, otherwise false - bool determine_access_from_script(ResultInformation& result_info, std::string_view script_line); + // determines based on the found text inside the script line if it's read or write access + // returns true if successful, otherwise false + bool determine_access_from_script(ResultInformation &result_info, std::string_view script_line); - // calculates total of all results found - uint32_t calculate_instances() const; + // calculates total of all results found + uint32_t calculate_instances() const; - // print all the found results in a pretty, colored and neat fashion - // if file_output exists, the file is output with the same information without colors - void print_results(); + // print all the found results in a pretty, colored and neat fashion + // if file_output exists, the file is output with the same information without colors + void print_results(); }; \ No newline at end of file diff --git a/rpgmaker_types.cpp b/rpgmaker_types.cpp index ce4b864..bc9a013 100644 --- a/rpgmaker_types.cpp +++ b/rpgmaker_types.cpp @@ -4,165 +4,165 @@ using namespace RPGMaker; -bool Command::is_valid(const json& command_json) const { - if (!command_json.contains("code") || !command_json["code"].is_number_integer()) { - log_err(R"(Command doesn't have a code or it's not an integer!")"); - return false; - } - if (!command_json.contains("parameters")) { - log_err(R"(Command doesn't have parameters!)"); - return false; - } - - return true; +bool Command::is_valid(const json &command_json) const { + if (!command_json.contains("code") || !command_json["code"].is_number_integer()) { + log_err(R"(Command doesn't have a code or it's not an integer!")"); + return false; + } + if (!command_json.contains("parameters")) { + log_err(R"(Command doesn't have parameters!)"); + return false; + } + + return true; } -Command::Command(const json& command_json) { - if (!is_valid(command_json)) { - return; - } - - code = static_cast(command_json["code"].get()); - - for (const auto& parameter : command_json["parameters"]) { - if (parameter.is_number_integer()) { - parameters.emplace_back(parameter.get()); - continue; - } - if (parameter.is_number_float()) { - parameters.emplace_back(parameter.get()); - continue; - } - if (parameter.is_boolean()) { - parameters.emplace_back(parameter.get()); - continue; - } - if (parameter.is_string() && parameter.get().length() == 1) { - parameters.emplace_back(parameter.get()[0]); - continue; - } - if (parameter.is_string()) { - parameters.emplace_back(parameter.get()); - continue; - } - } +Command::Command(const json &command_json) { + if (!is_valid(command_json)) { + return; + } + + code = static_cast(command_json["code"].get()); + + for (const auto ¶meter : command_json["parameters"]) { + if (parameter.is_number_integer()) { + parameters.emplace_back(parameter.get()); + continue; + } + if (parameter.is_number_float()) { + parameters.emplace_back(parameter.get()); + continue; + } + if (parameter.is_boolean()) { + parameters.emplace_back(parameter.get()); + continue; + } + if (parameter.is_string() && parameter.get().length() == 1) { + parameters.emplace_back(parameter.get()[0]); + continue; + } + if (parameter.is_string()) { + parameters.emplace_back(parameter.get()); + continue; + } + } } -bool Condition::is_valid(const json& condition_json) const { - if (!condition_json.contains("switch1Id") || !condition_json["switch1Id"].is_number_integer()) { - log_err(R"(This condition doesn't have a switch 1 id or it's not an integer!)"); - return false; - } - if (!condition_json.contains("switch1Valid") || !condition_json["switch1Valid"].is_boolean()) { - log_err(R"(This condition doesn't have a bool to define if switch1Id is active or it's not a boolean!)"); - return false; - } - if (!condition_json.contains("switch2Id") || !condition_json["switch2Id"].is_number_integer()) { - log_err(R"(This condition doesn't have a switch 2 id or it's not an integer!)"); - return false; - } - if (!condition_json.contains("switch2Valid") || !condition_json["switch2Valid"].is_boolean()) { - log_err(R"(This condition doesn't have a bool to define if switch2Id is active or it's not a boolean!)"); - return false; - } - if (!condition_json.contains("variableId") || !condition_json["variableId"].is_number_integer()) { - log_err(R"(This condition doesn't have a variable id or it's not an integer!)"); - return false; - } - if (!condition_json.contains("variableValid") || !condition_json["variableValid"].is_boolean()) { - log_err(R"(This condition doesn't have a bool to define if variableId is active or it's not a boolean!)"); - return false; - } - if (!condition_json.contains("variableValue") || !condition_json["variableValue"].is_number_integer()) { - log_err(R"(This condition doesn't have a value to compare against or it's not an integer!)"); - return false; - } - - return true; +bool Condition::is_valid(const json &condition_json) const { + if (!condition_json.contains("switch1Id") || !condition_json["switch1Id"].is_number_integer()) { + log_err(R"(This condition doesn't have a switch 1 id or it's not an integer!)"); + return false; + } + if (!condition_json.contains("switch1Valid") || !condition_json["switch1Valid"].is_boolean()) { + log_err(R"(This condition doesn't have a bool to define if switch1Id is active or it's not a boolean!)"); + return false; + } + if (!condition_json.contains("switch2Id") || !condition_json["switch2Id"].is_number_integer()) { + log_err(R"(This condition doesn't have a switch 2 id or it's not an integer!)"); + return false; + } + if (!condition_json.contains("switch2Valid") || !condition_json["switch2Valid"].is_boolean()) { + log_err(R"(This condition doesn't have a bool to define if switch2Id is active or it's not a boolean!)"); + return false; + } + if (!condition_json.contains("variableId") || !condition_json["variableId"].is_number_integer()) { + log_err(R"(This condition doesn't have a variable id or it's not an integer!)"); + return false; + } + if (!condition_json.contains("variableValid") || !condition_json["variableValid"].is_boolean()) { + log_err(R"(This condition doesn't have a bool to define if variableId is active or it's not a boolean!)"); + return false; + } + if (!condition_json.contains("variableValue") || !condition_json["variableValue"].is_number_integer()) { + log_err(R"(This condition doesn't have a value to compare against or it's not an integer!)"); + return false; + } + + return true; } -Condition::Condition(const json& condition_json) { - if (!is_valid(condition_json)) { - return; - } - - switch1_id = condition_json["switch1Id"].get(); - switch1_valid = condition_json["switch1Valid"].get(); - switch2_id = condition_json["switch2Id"].get(); - switch2_valid = condition_json["switch2Valid"].get(); - variable_id = condition_json["variableId"].get(); - variable_valid = condition_json["variableValid"].get(); - variable_value = condition_json["variableValue"].get(); +Condition::Condition(const json &condition_json) { + if (!is_valid(condition_json)) { + return; + } + + switch1_id = condition_json["switch1Id"].get(); + switch1_valid = condition_json["switch1Valid"].get(); + switch2_id = condition_json["switch2Id"].get(); + switch2_valid = condition_json["switch2Valid"].get(); + variable_id = condition_json["variableId"].get(); + variable_valid = condition_json["variableValid"].get(); + variable_value = condition_json["variableValue"].get(); } -bool EventPage::is_valid(const json& event_page_json) const { - if (!event_page_json.contains("conditions")) { - log_err(R"(This event page doesn't have conditions!)"); - return false; - } - if (!event_page_json.contains("list")) { - log_err(R"(This event page doesn't have commands!)"); - return false; - } - - return true; +bool EventPage::is_valid(const json &event_page_json) const { + if (!event_page_json.contains("conditions")) { + log_err(R"(This event page doesn't have conditions!)"); + return false; + } + if (!event_page_json.contains("list")) { + log_err(R"(This event page doesn't have commands!)"); + return false; + } + + return true; } -EventPage::EventPage(const json& event_page_json) { - if (!is_valid(event_page_json)) { - return; - } +EventPage::EventPage(const json &event_page_json) { + if (!is_valid(event_page_json)) { + return; + } - conditions = Condition(event_page_json["conditions"]); + conditions = Condition(event_page_json["conditions"]); - const auto& command_list = event_page_json["list"]; - list.reserve(command_list.size()); + const auto &command_list = event_page_json["list"]; + list.reserve(command_list.size()); - for (size_t line = 0, last_line = command_list.size(); line < last_line; ++line) { - list.emplace_back(Command(command_list[line])); - } + for (size_t line = 0, last_line = command_list.size(); line < last_line; ++line) { + list.emplace_back(Command(command_list[line])); + } } bool Event::is_valid(const json &event_json) const { - if (!event_json.contains("x") || !event_json["x"].is_number_integer()) { - log_err(R"(Event doesn't have a x position or it's not an integer!)"); - return false; - } - if (!event_json.contains("y") || !event_json["y"].is_number_integer()) { - log_err(R"(Event doesn't have a y position or it's not an integer!)"); - return false; - } - if (!event_json.contains("name") || !event_json["name"].is_string()) { - log_err(R"(Event doesn't have a name or it's not a string!)"); - return false; - } - if (!event_json.contains("id") || !event_json["id"].is_number_integer()) { - log_err(R"(Event doesn't have an id or it's not an integer!)"); - return false; - } - if (!event_json.contains("pages")) { - log_err(R"(Event doesn't have pages!)"); - return false; - } - - return true; + if (!event_json.contains("x") || !event_json["x"].is_number_integer()) { + log_err(R"(Event doesn't have a x position or it's not an integer!)"); + return false; + } + if (!event_json.contains("y") || !event_json["y"].is_number_integer()) { + log_err(R"(Event doesn't have a y position or it's not an integer!)"); + return false; + } + if (!event_json.contains("name") || !event_json["name"].is_string()) { + log_err(R"(Event doesn't have a name or it's not a string!)"); + return false; + } + if (!event_json.contains("id") || !event_json["id"].is_number_integer()) { + log_err(R"(Event doesn't have an id or it's not an integer!)"); + return false; + } + if (!event_json.contains("pages")) { + log_err(R"(Event doesn't have pages!)"); + return false; + } + + return true; } -Event::Event(const json& event_json) { - if (!is_valid(event_json)) { - return; - } +Event::Event(const json &event_json) { + if (!is_valid(event_json)) { + return; + } - x = event_json["x"].get(); - y = event_json["y"].get(); - id = event_json["id"].get(); - name = event_json["name"].get(); - note = event_json["note"].get(); + x = event_json["x"].get(); + y = event_json["y"].get(); + id = event_json["id"].get(); + name = event_json["name"].get(); + note = event_json["note"].get(); - const auto page_count = event_json["pages"].size(); - pages.reserve(page_count); + const auto page_count = event_json["pages"].size(); + pages.reserve(page_count); - for (size_t page_num = 0; page_num < page_count; ++page_num) { - pages.emplace_back(EventPage(event_json["pages"][page_num])); - } + for (size_t page_num = 0; page_num < page_count; ++page_num) { + pages.emplace_back(EventPage(event_json["pages"][page_num])); + } } \ No newline at end of file diff --git a/rpgmaker_types.hpp b/rpgmaker_types.hpp index 431ff86..eb1bec1 100644 --- a/rpgmaker_types.hpp +++ b/rpgmaker_types.hpp @@ -8,102 +8,107 @@ #include "json.hpp" using json = nlohmann::json; -using variable_element = std::variant; +using variable_element = + std::variant; namespace RPGMaker { - enum class CommandCode : uint32_t { - IF_STATEMENT = 111, - CONTROL_VARIABLE = 122, - SCRIPT_SINGLE_LINE = 355, - SCRIPT_MULTI_LINE = 655, - }; - - namespace IfStatement { - // parameter 0 - enum class IDType : uint32_t { - SWITCH, - VARIABLE - }; - // parameter 2 - enum class CompareType : uint32_t { - CONSTANT, - VARIABLE, - }; - }; // IfStatement - - namespace ControlVariable { - // parameter 3 - enum class Operand : uint32_t { - CONSTANT, - VARIABLE, - RANDOM, - GAME_DATA, - SCRIPT - }; - }; // ControlVariable - - struct Command { - Command() = default; - Command(const json& command); - - bool is_valid(const json& command) const; - - bool is_script() const { - return code == CommandCode::SCRIPT_SINGLE_LINE || - code == CommandCode::SCRIPT_MULTI_LINE; - } - bool is_if_statement() const { - return code == CommandCode::IF_STATEMENT; - } - - bool is_control_variable() const { - return code == CommandCode::CONTROL_VARIABLE; - } - - CommandCode code{}; - std::vector parameters{}; - }; - - struct Condition { - - Condition() = default; - Condition(const json& condition_json); - - bool is_valid(const json &condition_json) const; - - uint32_t switch1_id{}; - bool switch1_valid{}; - uint32_t switch2_id{}; - bool switch2_valid{}; - uint32_t variable_id{}; - bool variable_valid{}; - uint32_t variable_value{}; - }; - - struct EventPage { - - EventPage() = default; - EventPage(const json& event_page_json); - - bool is_valid(const json& event_page_json) const; - - Condition conditions{}; - std::vector list{}; - }; - - struct Event { - - Event() = default; - Event(const json& event_json); - - bool is_valid(const json &event_json) const; - - uint32_t id{}; - std::string name{}; - std::string note{}; - std::vector pages{}; - uint32_t x{}; - uint32_t y{}; - }; + enum class CommandCode : uint32_t { + + IF_STATEMENT = 111, + CONTROL_VARIABLE = 122, + SCRIPT_SINGLE_LINE = 355, + SCRIPT_MULTI_LINE = 655, + }; + + namespace IfStatement { + + // parameter 0 + enum class IDType : uint32_t { + SWITCH, + VARIABLE + }; + // parameter 2 + enum class CompareType : uint32_t { + CONSTANT, + VARIABLE, + }; + }; // IfStatement + + namespace ControlVariable { + + // parameter 3 + enum class Operand : uint32_t { + CONSTANT, + VARIABLE, + RANDOM, + GAME_DATA, + SCRIPT + }; + }; // ControlVariable + + struct Command { + + Command() = default; + Command(const json &command); + + bool is_valid(const json &command) const; + + bool is_script() const { + return code == CommandCode::SCRIPT_SINGLE_LINE || + code == CommandCode::SCRIPT_MULTI_LINE; + } + bool is_if_statement() const { + return code == CommandCode::IF_STATEMENT; + } + + bool is_control_variable() const { + return code == CommandCode::CONTROL_VARIABLE; + } + + CommandCode code{}; + std::vector parameters{}; + }; + + struct Condition { + + Condition() = default; + Condition(const json &condition_json); + + bool is_valid(const json &condition_json) const; + + uint32_t switch1_id{}; + bool switch1_valid{}; + uint32_t switch2_id{}; + bool switch2_valid{}; + uint32_t variable_id{}; + bool variable_valid{}; + uint32_t variable_value{}; + }; + + struct EventPage { + + EventPage() = default; + EventPage(const json &event_page_json); + + bool is_valid(const json &event_page_json) const; + + Condition conditions{}; + std::vector list{}; + }; + + struct Event { + + Event() = default; + Event(const json &event_json); + + bool is_valid(const json &event_json) const; + + uint32_t id{}; + std::string name{}; + std::string note{}; + std::vector pages{}; + uint32_t x{}; + uint32_t y{}; + }; }; //RPGMaker diff --git a/utils.hpp b/utils.hpp index 71953d9..c2af23b 100644 --- a/utils.hpp +++ b/utils.hpp @@ -6,9 +6,8 @@ namespace utils { - template - static std::string format_string( std::string_view fmt, arg ... args ) - { + template + static std::string format_string(std::string_view fmt, arg ... args) { const int size = std::snprintf(nullptr, NULL, fmt.data(), args ...) + 1; const auto buf = std::make_unique(size); std::snprintf(buf.get(), size, fmt.data(), args ...);