62 lines
3.3 KiB
C++
62 lines
3.3 KiB
C++
// Hook descriptor for Mars::GlobalConsts::LoadFile(const char* file, GlobalConstMap* consts)
|
|
// -- the file-level read of the flat KEY/value constant tables (M1).
|
|
//
|
|
// Side-effect model: the map handed to LoadFile lists every constant registered for that
|
|
// file (key -> {storage word, parser, file}). The hook walks the map before the original
|
|
// runs and declares one region per constant, named by its key, as wide as its parser's
|
|
// type (int / float / scaled float = 4 bytes, colour = float[4] = 16 bytes), with a struct
|
|
// describer so a diff names the field ("v", or "r"/"g"/"b"/"a"). In compare mode `ours`
|
|
// (game::config::apply over the same file bytes, read through the game's own gobio) fills
|
|
// the scratch copies of those words; the tracer diffs them against the original's.
|
|
//
|
|
// The map itself is a temporary of LoadAll (the original erases consumed keys from it); it
|
|
// is not declared as a region and `ours` never touches it.
|
|
//
|
|
// LoadFile is cdecl with a verified prototype, so the Hook<> template applies directly.
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
#include "shim/trace/hook.h"
|
|
|
|
namespace shim::hooks {
|
|
|
|
struct GlobalConstsLoadFileHook {
|
|
static constexpr const char* name = "Mars::GlobalConsts::LoadFile";
|
|
static constexpr trace::CallConv conv = trace::CallConv::Cdecl;
|
|
using Ret = void;
|
|
using Args = std::tuple<const char*, void*>;
|
|
|
|
static void describe_args(std::vector<trace::Tv>& out, const char* file, void* consts);
|
|
static void regions(std::vector<trace::Region>& out, const char* file, void* consts);
|
|
static Args rebind(trace::Scratch& s, const char* file, void* consts);
|
|
static void ours(const char* file, void* consts);
|
|
static trace::HookPolicy policy() { return trace::HookPolicy{}; }
|
|
static void coverage(trace::Coverage& c) {
|
|
c.unmodelled("erases each consumed key from the caller's std::map",
|
|
trace::Risk::Medium,
|
|
"the map is a LoadAll temporary; declaring a red-black tree as a region is "
|
|
"not possible before the call. First-occurrence-wins is reproduced in "
|
|
"game::config::apply instead, so the *effect* is modelled, the container is "
|
|
"not",
|
|
"LoadAll's post-state would have to be hooked to see it");
|
|
c.unmodelled("writes three kinds of line to the game log (unrecognised key, applied "
|
|
"key, expected-but-not-found)",
|
|
trace::Risk::Low, "log text is not part of the simulation state");
|
|
c.unmodelled("opens the file through the VFS and allocates/releases a refcounted buffer",
|
|
trace::Risk::Low,
|
|
"ours performs the same two calls, so allocation behaviour matches by "
|
|
"construction rather than by comparison");
|
|
c.unmodelled("String slots assign through the engine's own std::string, leaking one heap "
|
|
"block per long string in compare mode",
|
|
trace::Risk::Low, "start-up only; documented in docs/M1.md");
|
|
}
|
|
};
|
|
|
|
// Process facts the hook needs: the exe's load address (parser identification, the scale
|
|
// constant, gobio::ReadFile) and a line logger (shim.log). Call once before installing.
|
|
void init_global_consts(std::uintptr_t exe_base, void (*log_line)(const char* line));
|
|
|
|
} // namespace shim::hooks
|