mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
Initial commit for GitHub migration based on spice2x-25-03-03
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
#include "automap.h"
|
||||
#include <fstream>
|
||||
#include "external/tinyxml2/tinyxml2.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "./core.h"
|
||||
|
||||
// macro for easy hooking
|
||||
#define AUTOMAP_HOOK(s) detour::iat_try_proc(avs::core::DLL_NAME.c_str(), avs::core::s, s)
|
||||
|
||||
namespace avs::automap {
|
||||
|
||||
// state
|
||||
bool ENABLED = false;
|
||||
bool DUMP = false;
|
||||
bool PATCH = false;
|
||||
bool JSON = false;
|
||||
bool RESTRICT_NETWORK = false;
|
||||
std::string DUMP_FILENAME = "";
|
||||
|
||||
// logging
|
||||
static std::ofstream LOGFILE;
|
||||
static std::vector<std::pair<AutomapHook_t, void*>> HOOKS;
|
||||
|
||||
|
||||
static bool property_is_network(avs::core::property_ptr prop, avs::core::node_ptr node = nullptr) {
|
||||
avs::core::node_ptr root_node = nullptr;
|
||||
|
||||
/*
|
||||
* on failure to detect, we must return true
|
||||
* most of the time, no prop is specified
|
||||
*/
|
||||
|
||||
// get root node
|
||||
if (prop) {
|
||||
|
||||
// when prop is specified, this is easy
|
||||
root_node = avs::core::property_search(prop, nullptr, "/");
|
||||
|
||||
} else if (node) {
|
||||
|
||||
|
||||
// otherwise we need to traverse
|
||||
root_node = node;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
auto parent_node = avs::core::property_node_traversal(node, avs::core::TRAVERSE_PARENT);
|
||||
if (parent_node == root_node || parent_node == nullptr) {
|
||||
break;
|
||||
} else {
|
||||
root_node = parent_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// verify root node
|
||||
if (!root_node) {
|
||||
if (prop != nullptr) {
|
||||
avs::core::property_clear_error(prop);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// check name
|
||||
char root_name[256] {};
|
||||
if (avs::core::property_node_name(root_node, root_name, sizeof(root_name)) >= 0) {
|
||||
return !strcmp(root_name, "call") || !strcmp(root_name, "response");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool property_patches_enabled(avs::core::property_ptr prop, avs::core::node_ptr node = nullptr,
|
||||
bool force = false) {
|
||||
if (!ENABLED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if patches are enabled first
|
||||
if (!PATCH) {
|
||||
return false;
|
||||
} else if (force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if restricted to network
|
||||
if (RESTRICT_NETWORK) {
|
||||
return property_is_network(prop, node);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool property_dump_enabled(avs::core::property_ptr prop, avs::core::node_ptr node = nullptr) {
|
||||
if (!ENABLED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if dumps are enabled first
|
||||
if (!DUMP && HOOKS.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if restricted to network
|
||||
if (RESTRICT_NETWORK) {
|
||||
return property_is_network(prop, node);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t property_get_error(avs::core::property_ptr prop) {
|
||||
auto error = avs::core::property_get_error(prop);
|
||||
if (error != 0 && prop != nullptr) {
|
||||
log_misc("automap", "detected error: {}", error);
|
||||
avs::core::property_clear_error(prop);
|
||||
return avs::core::property_get_error(prop);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
avs::core::node_ptr property_search(avs::core::property_ptr prop, avs::core::node_ptr node, const char *path) {
|
||||
|
||||
// call original
|
||||
auto result = avs::core::property_search(prop, node, path);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
} else if (!property_patches_enabled(prop, node)) {
|
||||
log_misc("automap", "property_search error: {}", path ? path : "?");
|
||||
return result;
|
||||
}
|
||||
|
||||
// create node
|
||||
log_misc("automap", "property_search error: {}", path ? path : "?");
|
||||
static const uint8_t zero_buffer[64] {};
|
||||
avs::core::property_node_create(prop, node, avs::core::NODE_TYPE_node, path, zero_buffer);
|
||||
|
||||
// clear error
|
||||
if (prop) {
|
||||
avs::core::property_clear_error(prop);
|
||||
}
|
||||
|
||||
// now try again
|
||||
return avs::core::property_search(prop, node, path);
|
||||
}
|
||||
|
||||
int property_node_refer(avs::core::property_ptr prop, avs::core::node_ptr node, const char *path,
|
||||
avs::core::node_type type, void *data, uint32_t data_size) {
|
||||
auto result = avs::core::property_node_refer(prop, node, path, type, data, data_size);
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
} else if (!property_patches_enabled(prop, node)) {
|
||||
log_misc("automap", "property_node_refer error: {}", path ? path : "?");
|
||||
return result;
|
||||
}
|
||||
|
||||
// check if accessed by path
|
||||
if (!path) {
|
||||
|
||||
// print log without path
|
||||
log_misc("automap", "node refer error: {}", result);
|
||||
|
||||
} else {
|
||||
std::string path_str(path);
|
||||
|
||||
// prevent infinite loop in cardmng
|
||||
if (string_begins_with(path_str, "/card_allow/card")) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// print log with path
|
||||
log_misc("automap", "node refer error ({}): {}", path, result);
|
||||
if (path_str == "/") {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// maybe it *does* exist, but with the wrong type
|
||||
auto find_node = avs::core::property_search(prop, node, path);
|
||||
if (find_node != nullptr) {
|
||||
|
||||
// now get rid of it
|
||||
avs::core::property_node_remove(find_node);
|
||||
}
|
||||
|
||||
// fake buffer if required
|
||||
uint8_t *buffer = reinterpret_cast<uint8_t *>(data);
|
||||
auto buffer_size = data_size;
|
||||
if (buffer == nullptr || buffer_size == 0) {
|
||||
static uint8_t zero_buffer[64];
|
||||
buffer = zero_buffer;
|
||||
buffer_size = sizeof(zero_buffer);
|
||||
}
|
||||
|
||||
// initialize field
|
||||
memset(buffer, 0, buffer_size);
|
||||
if ((type == avs::core::NODE_TYPE_attr || type == avs::core::NODE_TYPE_str) && buffer_size > 1) {
|
||||
|
||||
// init attributes and strings with "0" by default - useful for "status" or "error" fields
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '\x00';
|
||||
}
|
||||
if (path) {
|
||||
|
||||
// for numbers and counts we fake a 1 to get a single entry
|
||||
if (string_ends_with(path, "num") || string_ends_with(path, "count")) {
|
||||
if ((type == avs::core::NODE_TYPE_attr || type == avs::core::NODE_TYPE_str) && buffer_size > 1) {
|
||||
buffer[0] = '1';
|
||||
buffer[1] = '\x00';
|
||||
} else if (buffer_size > 0) {
|
||||
buffer[0] = 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add to property
|
||||
avs::core::property_node_create(prop, node, type, path, buffer);
|
||||
|
||||
// clear error
|
||||
if (prop) {
|
||||
avs::core::property_clear_error(prop);
|
||||
}
|
||||
|
||||
// try again
|
||||
return avs::core::property_node_refer(prop, node, path, type, data, data_size);
|
||||
}
|
||||
|
||||
avs::core::avs_error_t property_get_attribute_u32(
|
||||
avs::core::property_ptr prop,
|
||||
avs::core::node_ptr node,
|
||||
const char *path,
|
||||
uint32_t *value)
|
||||
{
|
||||
auto result = avs::core::property_get_attribute_u32(prop, node, path, value);
|
||||
if (result < 0 && property_patches_enabled(prop, node, true)) {
|
||||
log_warning("automap", "property_get_attribute_u32 failed: {}", path ? path : "?");
|
||||
const char *data = "0";
|
||||
avs::core::property_node_create(prop, node, avs::core::NODE_TYPE_attr, path, data);
|
||||
return avs::core::property_get_attribute_u32(prop, node, path, value);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
avs::core::avs_error_t property_get_attribute_s32(
|
||||
avs::core::property_ptr prop,
|
||||
avs::core::node_ptr node,
|
||||
const char *path,
|
||||
int32_t *value)
|
||||
{
|
||||
auto result = avs::core::property_get_attribute_s32(prop, node, path, value);
|
||||
if (result < 0 && property_patches_enabled(prop, node, true)) {
|
||||
log_warning("automap", "property_get_attribute_s32 failed: {}", path ? path : "?");
|
||||
const char *data = "0";
|
||||
avs::core::property_node_create(prop, node, avs::core::NODE_TYPE_attr, path, data);
|
||||
return avs::core::property_get_attribute_s32(prop, node, path, value);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int property_node_read(avs::core::node_ptr node, avs::core::node_type type, void *data, uint32_t data_size) {
|
||||
auto result = avs::core::property_node_read(node, type, data, data_size);
|
||||
if (result < 0 && property_patches_enabled(nullptr, node)) {
|
||||
log_warning("automap", "property_node_read failed ({}): {}", type, result);
|
||||
|
||||
// probably wrong type
|
||||
memset(data, 0, data_size);
|
||||
if ((type == avs::core::NODE_TYPE_attr || type == avs::core::NODE_TYPE_str) && data_size > 1) {
|
||||
((char *) data)[0] = '0';
|
||||
((char *) data)[1] = '\x00';
|
||||
data_size = 2;
|
||||
}
|
||||
|
||||
// recreate the node with the correct type (probably invalidates the pointer, how to fix?)
|
||||
auto parent_node = avs::core::property_node_traversal(node, avs::core::TRAVERSE_PARENT);
|
||||
if (parent_node && parent_node != node) {
|
||||
char name_buf[256] {};
|
||||
if (avs::core::property_node_name(node, name_buf, sizeof(name_buf)) >= 0) {
|
||||
log_warning("automap", "property_node_read replace type ({}): {}", type, result);
|
||||
avs::core::property_node_remove(node);
|
||||
avs::core::property_node_create(nullptr, parent_node, type, name_buf, data);
|
||||
}
|
||||
}
|
||||
|
||||
// return input data size
|
||||
return data_size;
|
||||
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool property_psmap_export(avs::core::property_ptr prop, avs::core::node_ptr node, uint8_t *data,
|
||||
avs::core::psmap_data_ptr psmap) {
|
||||
|
||||
// check if enabled
|
||||
if (property_patches_enabled(prop, node)) {
|
||||
|
||||
// iterate psmap
|
||||
for (size_t i = 0; psmap[i].type != 0xFF; i++) {
|
||||
auto &cur_entry = psmap[i];
|
||||
|
||||
// determine node type
|
||||
avs::core::node_type node_type;
|
||||
switch (cur_entry.type) {
|
||||
case avs::core::PSMAP_TYPE_s8:
|
||||
node_type = avs::core::NODE_TYPE_s8;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_u8:
|
||||
node_type = avs::core::NODE_TYPE_u8;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_s16:
|
||||
node_type = avs::core::NODE_TYPE_s16;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_u16:
|
||||
node_type = avs::core::NODE_TYPE_u16;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_s32:
|
||||
node_type = avs::core::NODE_TYPE_s32;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_u32:
|
||||
node_type = avs::core::NODE_TYPE_u32;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_s64:
|
||||
node_type = avs::core::NODE_TYPE_s64;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_u64:
|
||||
node_type = avs::core::NODE_TYPE_u64;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_str:
|
||||
case avs::core::PSMAP_TYPE_str2:
|
||||
node_type = avs::core::NODE_TYPE_str;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_attr:
|
||||
node_type = avs::core::NODE_TYPE_attr;
|
||||
break;
|
||||
case avs::core::PSMAP_TYPE_bool:
|
||||
node_type = avs::core::NODE_TYPE_bool;
|
||||
break;
|
||||
default:
|
||||
node_type = (avs::core::node_type) 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// check if node type was found
|
||||
if (node_type != 0) {
|
||||
|
||||
// get rid of existing nodes with type mismatch
|
||||
auto node_search = avs::core::property_search(prop, node, cur_entry.path);
|
||||
if (node_search) {
|
||||
auto node_search_type = avs::core::property_node_type(node_search);
|
||||
if (node_type != node_search_type) {
|
||||
log_misc("automap", "psmap type mismatch, replacing node: {}", cur_entry.path);
|
||||
avs::core::property_node_remove(node_search);
|
||||
} else {
|
||||
|
||||
// this one seems to be fine actually!
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// generate node
|
||||
log_misc("automap", "generating psmap node: {}", cur_entry.path);
|
||||
uint8_t node_data[64]{};
|
||||
if (node_type == avs::core::NODE_TYPE_attr || node_type == avs::core::NODE_TYPE_str) {
|
||||
node_data[0] = '0';
|
||||
node_data[1] = '\x00';
|
||||
}
|
||||
avs::core::property_node_create(prop, node, node_type, cur_entry.path, node_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now we call the original
|
||||
return avs::core::property_psmap_export(prop, node, data, psmap);
|
||||
}
|
||||
|
||||
avs::core::avs_error_t property_destroy(avs::core::property_ptr prop) {
|
||||
|
||||
// we definitely need a property for this to work
|
||||
if (prop == NULL) {
|
||||
log_warning("automap", "property_destroy called on NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check if dump is enabled
|
||||
if (property_dump_enabled(prop)) {
|
||||
|
||||
// convert to XML
|
||||
avs::core::property_set_flag(prop, avs::core::PROP_XML, avs::core::PROP_BINARY);
|
||||
|
||||
// optionally reconvert to JSON
|
||||
if (JSON) {
|
||||
avs::core::property_set_flag(prop, avs::core::PROP_JSON, avs::core::PROP_XML);
|
||||
}
|
||||
|
||||
// query size
|
||||
auto size = avs::core::property_query_size(prop);
|
||||
if (size < 0) {
|
||||
log_warning("automap", "couldn't query property size");
|
||||
} else {
|
||||
log_misc("automap", "writing property to file: {} bytes", size);
|
||||
|
||||
// get XML
|
||||
std::vector<uint8_t> data(size);
|
||||
if (avs::core::property_mem_write(prop, &data[0], data.size()) >= 0) {
|
||||
|
||||
// initialize log
|
||||
if (DUMP && !LOGFILE.is_open()) {
|
||||
|
||||
// try filenames with IDs starting at 0
|
||||
for (int i = 0; i < 10000 && !LOGFILE.is_open(); i++) {
|
||||
std::string path = "automap_" + to_string(i) + ".xml";
|
||||
|
||||
// check if this one is available to use
|
||||
if (!fileutils::file_exists(path)) {
|
||||
|
||||
// try creating the file
|
||||
LOGFILE.open(path, std::ios::out | std::ios::binary);
|
||||
if (LOGFILE.is_open()) {
|
||||
DUMP_FILENAME = path;
|
||||
log_info("automap", "using logfile: {}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prettify
|
||||
tinyxml2::XMLDocument document;
|
||||
if (!JSON && document.Parse((const char*) &data[0],
|
||||
data.size()) == tinyxml2::XMLError::XML_SUCCESS) {
|
||||
|
||||
// write pretty output to log
|
||||
tinyxml2::XMLPrinter xml_printer;
|
||||
document.Print(&xml_printer);
|
||||
for (auto &hook : HOOKS) {
|
||||
hook.first(hook.second, xml_printer.CStr());
|
||||
}
|
||||
if (DUMP && LOGFILE.is_open()) {
|
||||
LOGFILE << xml_printer.CStr() << std::endl;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// write avs output to log
|
||||
for (auto &hook : HOOKS) {
|
||||
hook.first(hook.second, std::string((const char *) &data[0], data.size()).c_str());
|
||||
}
|
||||
if (DUMP && LOGFILE.is_open()) {
|
||||
LOGFILE << std::string((const char *) &data[0], data.size());
|
||||
}
|
||||
}
|
||||
|
||||
// flush
|
||||
LOGFILE.flush();
|
||||
|
||||
} else {
|
||||
log_warning("automap", "couldn't write property to memory");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// kill it with fire
|
||||
return avs::core::property_destroy(prop);
|
||||
}
|
||||
|
||||
void enable() {
|
||||
log_info("automap", "enable");
|
||||
ENABLED = true;
|
||||
|
||||
// check if optional imports are supported for this avs version
|
||||
if (!avs::core::property_node_read || !avs::core::property_get_error) {
|
||||
log_fatal("automap", "missing optional avs imports which are required for this module to work");
|
||||
}
|
||||
|
||||
// apply hooks
|
||||
AUTOMAP_HOOK(property_get_error);
|
||||
AUTOMAP_HOOK(property_search);
|
||||
AUTOMAP_HOOK(property_node_refer);
|
||||
AUTOMAP_HOOK(property_get_attribute_u32);
|
||||
AUTOMAP_HOOK(property_get_attribute_s32);
|
||||
AUTOMAP_HOOK(property_node_read);
|
||||
AUTOMAP_HOOK(property_psmap_export);
|
||||
AUTOMAP_HOOK(property_destroy);
|
||||
}
|
||||
|
||||
void disable() {
|
||||
log_info("automap", "disable");
|
||||
ENABLED = false;
|
||||
}
|
||||
|
||||
void hook_add(AutomapHook_t hook, void *user) {
|
||||
HOOKS.push_back(std::pair(hook, user));
|
||||
}
|
||||
|
||||
void hook_remove(AutomapHook_t hook, void *user) {
|
||||
HOOKS.erase(std::remove(HOOKS.begin(), HOOKS.end(), std::pair(hook, user)), HOOKS.end());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user