#include #include "hashes.hpp" #include "gsc_funcs.hpp" #include "definitions/variables.hpp" #include "loader/component_loader.hpp" #include namespace hashes { namespace { bool enabled = true; std::unordered_map& hash_storage() { static std::unordered_map storage{}; return storage; } const char* hff_names[] { "COMMON", "STRING", "SERIOUS_COMPILER" }; bool is_valid_h32(const char* str) { const char* s = str; while (*s) { char c = *(s++); if (!( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')) { return false; } } return true; // [A-Za-z0-9_]+ } } const char* lookup(uint64_t hash) { auto& hs = hash_storage(); auto it = hs.find(hash); if (it == hs.end()) { return nullptr; } return it->second.c_str(); } const char* lookup_tmp(const char* type, uint64_t hash) { static char tmp_buffer[0x50]; const char* val = lookup(hash); if (val) { return val; } sprintf_s(tmp_buffer, "%s_%llx", type, hash); return tmp_buffer; } void add_hash(uint64_t hash, const char* value) { if (!enabled) { return; } hash_storage()[hash] = value; } const char* get_format_name(hashes_file_format format) { if (format >= 0 && format < HFF_COUNT) { return hff_names[format]; } return ""; } hashes_file_format get_format_idx(const char* name) { for (size_t i = 0; i < HFF_COUNT; i++) { if (!_strcmpi(name, hff_names[i])) { return (hashes_file_format)i; } } return HFF_COUNT; } bool load_file(std::filesystem::path& file, hashes_file_format format) { if (!enabled) { return true; } logger::write(logger::LOG_TYPE_DEBUG, std::format("loading hash file {}", file.string())); switch (format) { case HFF_STRING: { // basic format, each line is a string, allows fast updates std::ifstream s(file); if (!s) { logger::write(logger::LOG_TYPE_ERROR, std::format("can't read hash file {}", file.string())); return false; // nothing to read } std::string line; while (s.good() && std::getline(s, line)) { const char* str = line.c_str(); if (is_valid_h32(str)) { add_hash(gsc_funcs::canon_hash(str), str); } add_hash(fnv1a::generate_hash(str), str); } s.close(); return true; } case HFF_COMMON: { // common precomputed format used by greyhound index and other tools // each line: , std::ifstream s(file); if (!s) { logger::write(logger::LOG_TYPE_ERROR, std::format("can't read hash file {}", file.string())); return false; // nothing to read } std::string line; while (s.good() && std::getline(s, line)) { auto idx = line.rfind(", ", 18); if (idx == std::string::npos) { continue; // bad line } char value[18] = {}; const char* str = line.c_str(); memcpy(value, str, idx); add_hash(std::strtoull(value, nullptr, 16), str + idx + 1); } s.close(); return false; } case HFF_SERIOUS_COMPILER: { // precomputed format generated by the serious compiler // each line: 0x, std::ifstream s(file); if (!s) { logger::write(logger::LOG_TYPE_ERROR, std::format("can't read hash file {}", file.string())); return false; // nothing to read } std::string line; while (s.good() && std::getline(s, line)) { if (!line.starts_with("0x")) { continue; // bad line } auto idx = line.rfind(", ", 18); if (idx == std::string::npos) { continue; // bad line } char value[18] = {}; const char* str = line.c_str(); memcpy(value, str + 2, idx - 2); add_hash(std::strtoull(value, nullptr, 16), str + idx + 2); } s.close(); return true; } default: logger::write(logger::LOG_TYPE_ERROR, std::format("bad format type {} for file {}", (int)format, file.string())); return false; } } class component final : public component_interface { public: void pre_start() override { enabled = utilities::json_config::ReadBoolean("gsc", "hashes", true); if (!enabled) { return; } // load default files std::filesystem::path default_file_name_str = "project-bo4/strings.txt"; if (std::filesystem::exists(default_file_name_str)) { load_file(default_file_name_str, HFF_STRING); } std::filesystem::path default_file_name_common = "project-bo4/hashes.csv"; if (std::filesystem::exists(default_file_name_common)) { load_file(default_file_name_common, HFF_COMMON); } } }; } REGISTER_COMPONENT(hashes::component)