256 lines
4.7 KiB
C++
256 lines
4.7 KiB
C++
#include <std_include.hpp>
|
|
#include "hashes.hpp"
|
|
#include "gsc_funcs.hpp"
|
|
|
|
#include "definitions/variables.hpp"
|
|
#include "loader/component_loader.hpp"
|
|
#include <utilities/json_config.hpp>
|
|
|
|
namespace hashes
|
|
{
|
|
namespace
|
|
{
|
|
bool enabled = true;
|
|
std::unordered_map<uint64_t, std::string>& hash_storage()
|
|
{
|
|
static std::unordered_map<uint64_t, std::string> 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 "<invalid>";
|
|
}
|
|
|
|
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: <hash>,<string>
|
|
|
|
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<hash>, <string>
|
|
|
|
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) |