29 return Unbound(
"empty pattern");
33 if (!
match.Module.Base)
39 return Unbound(
"pattern not found");
43 return Unbound(
"pattern matched more than once");
49template <
class TEntry>
59 return Unbound(std::format(
"{} {} is negative",
what, *value));
76 for (
const auto&
key :
section | std::views::keys)
79 entry.Sections.push_back(name);
91 if (
entry.Sections.size() > 1)
103 if (!
entry.Reason.empty())
121 const auto it = _entries.find(
key);
122 return it != _entries.end() ? &
it->second :
nullptr;
127 Log::Info(
"GameData: {} (server {}, verified {}): {} functions, {} globals, {} vtables, {} offsets.", path,
135 std::ranges::count_if(_file.
vtables, [](
const auto&
each) { return each.second.Script.empty(); });
137 std::ranges::count_if(_file.
offsets, [](
const auto&
each) { return each.second.Base.empty(); });
138 Log::Warn(
"GameData: verified on server {}, running {}: {} vtable indices and {} offsets are unchecked.",
153 return FindSlot(
key).transform(
154 [](VirtualSlot
slot) {
return Bound{.Address =
slot.Table, .Value =
slot.Index}; });
156 return FindOffset(
key).transform([](
int value) {
return Bound{.Value = value}; });
158 return Unbound(
"in no section");
161void GameDataScanner::Fail(std::string_view
key, GameDataEntry&
entry,
const Error&
error)
164 _failures.push_back(std::format(
"{}: {}",
key,
entry.Reason));
170 if (!
entry.Script.empty())
172 return FindScriptFunction(
key,
entry);
184 return std::unexpected(
match.error());
188 return match->Address;
191Result<void*> GameDataScanner::FindScriptFunction(
const std::string&
key,
const GameDataDocument::Function&
entry)
193 const auto module = _symbols.Module(entry.Module);
196 return std::unexpected(
module.error());
201 return std::unexpected(
table.error());
206 return std::unexpected(
target.error());
208 if (!(*module)->Contains(
target->Address))
210 return Unbound(std::format(
"VScript '{}' is virtual or outside '{}'",
entry.Script,
entry.Module));
229 return std::unexpected(
match.error());
238 std::format(
"the rel32 at +{} does not point at readable memory in '{}'",
column->rel32At,
entry.Module));
252 return std::unexpected(index.error());
255 const auto module = _symbols.Module(entry.Module);
258 return std::unexpected(
module.error());
263 return std::unexpected(
table.error());
265 if (!
entry.Script.empty())
270 return std::unexpected(index.error());
275 void**
slot =
static_cast<void**
>(*table) + *index;
280 return Unbound(std::format(
"{}::[{}] does not hold code",
owner, *index));
284 const Image&
loaded = **
module;
287 return VirtualSlot{.Index = *index, .Table = *
table};
294 return Unbound(
"no VScript lookup");
296 return _scriptOf(
table, name);
299Result<int> GameDataScanner::ScriptSlot(
void*
table,
const std::string& name)
const
304 return std::unexpected(
target.error());
308 return Unbound(std::format(
"VScript '{}' is not virtual", name));
324Result<int> GameDataScanner::FindBaseOffset(
const GameDataDocument::Offset&
entry)
326 if (
entry.Class.empty())
328 return Unbound(std::format(
"base '{}' names no class",
entry.Base));
330 return _symbols.
Base(
entry.Module,
entry.Class,
entry.Base).transform([](
const BaseSubobject& base) {
GameDataScanner(const GameDataDocument &file, const OriginalSlotLookup &originalOf, const ScriptBindingLookup &scriptOf)
void LogSummary(std::string_view path) const
const GameDataEntry * Find(std::string_view key) const
Result< BaseSubobject > Base(const std::string &moduleName, const std::string &className, const std::string &baseName)
Result< void * > Table(const std::string &moduleName, const std::string &className, const std::string &baseName={})
void Info(std::format_string< Args... > fmt, Args &&... args)
void Warn(std::format_string< Args... > fmt, Args &&... args)
ScanResult FindPatternEx(std::string_view moduleName, const std::string &pattern)
std::function< Result< ScriptTarget >(void *classTable, std::string_view name)> ScriptBindingLookup
static const void * OriginalSlot(void *table, int index, const OriginalSlotLookup &originalOf)
static std::string ReadFile(const std::filesystem::path &path)
bool IsExecutableAddress(const void *address)
bool IsReadableAddress(const void *address, size_t bytes)
const auto & PlatformColumn(const TEntry &entry)
uintptr_t ResolveRelativeAddress(const Image &module, uintptr_t matchAddress, int ripOffset, int ripSize)
static std::unexpected< Error > Unbound(std::string reason)
std::string_view GameBuild()
std::function< const void *(void **vtable, int index)> OriginalSlotLookup
static uint64_t Rva(const Image &module, const void *address)
static Result< int > ColumnValue(const TEntry &entry, std::string_view what)
std::expected< T, Error > Result
constexpr std::string_view PlatformName
static Result< ScanResult > Scan(std::string_view moduleName, const std::string &pattern)
static Error NotFound(std::string detail)
std::string server
steam.inf ServerVersion the entries were verified on.
std::string verified
YYYY-MM-DD of the last full review.
std::map< std::string, Function > functions
std::map< std::string, Global > globals
std::map< std::string, VTable > vtables
std::map< std::string, Offset > offsets
std::map< std::string, Location > Functions
std::map< std::string, Location > Globals
std::map< std::string, int > Offsets
std::map< std::string, Slot > VTables