mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2024-12-04 19:07:54 +01:00
refactor(launcher): Major re-work of launcher
Kudos to Shiz for providing the groundwork for this. Fundamentally re-think how launcher operates and bootstrapping the games is managed and configured. This brings it significantly closer to how the original bootstrap is doing the job: launcher now utilizes the data (structures) provided by the bootstrap.xml configuration file. This creates compatibility with vanilla data dumps and original stock images. Note that bemanitools does not include any code or means to run DRM'd data, only decrypted. But, this allows users to keep decrypted dumps as stock as possible which means: * No copying around of property files anymore * Keep the modules/ folder with the binaries * Have bemanitools binaries separate in the data * No need to edit/customize the original configuration files A list of key features of the "new" launcher: * Boostrap games by following the configuration provided by stock game's bootstrap.xml files * Custom launcher.xml configuration file that adds further launcher configurable features, composability of bootstrap.xml configuration(s) as well as configuration overriding/stacking of selected types of configurations, e.g. eamuse config, avs-config. The latter eliminates the need for modifying stock config files in the prop/ folder * Unified logging system: launcher and AVS logging uses the same logger, all output can now be in a single file * Original features such as various hook types still available Due to the significant architectural changes, this also breaks with any backwards compatibility to existing launcher setups. Thus, users need to migrate by re-applying the new configuration format and migrating their config parameters accordingly. Further migration instructions and updated documentation will be provided upon release. Co-authored-by: Shiz <hi@shiz.me>
This commit is contained in:
parent
cd798ebe77
commit
a243791d5c
@ -3,22 +3,34 @@ rc_launcher := launcher.rc
|
||||
|
||||
ldflags_launcher := \
|
||||
-mconsole \
|
||||
-ldbghelp \
|
||||
|
||||
deplibs_launcher := \
|
||||
avs \
|
||||
avs-ea3 \
|
||||
|
||||
libs_launcher := \
|
||||
avs-util \
|
||||
core \
|
||||
hook \
|
||||
util \
|
||||
|
||||
src_launcher := \
|
||||
avs-context.c \
|
||||
ea3-config.c \
|
||||
avs-config.c \
|
||||
avs.c \
|
||||
bootstrap-config.c \
|
||||
bootstrap.c \
|
||||
debug.c \
|
||||
ea3-ident-config.c \
|
||||
eamuse-config.c \
|
||||
eamuse.c \
|
||||
hook.c \
|
||||
launcher-config.c \
|
||||
launcher.c \
|
||||
main.c \
|
||||
module.c \
|
||||
options.c \
|
||||
property.c \
|
||||
property-util.c \
|
||||
stubs.c \
|
||||
version.c \
|
||||
|
||||
|
773
src/main/launcher/avs-config.c
Normal file
773
src/main/launcher/avs-config.c
Normal file
@ -0,0 +1,773 @@
|
||||
#define LOG_MODULE "avs-config"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs-util/error.h"
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/str.h"
|
||||
|
||||
#define AVS_CONFIG_ROOT_NODE "/config"
|
||||
|
||||
static const char *_avs_config_property_mounttable_path =
|
||||
"/config/fs/mounttable";
|
||||
|
||||
static void _avs_config_node_vfs_copy(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source)
|
||||
{
|
||||
// Use max path size to fit dst and src fs paths
|
||||
char data[MAX_PATH];
|
||||
|
||||
// Remark: Using property_node_clone doesn't work here
|
||||
// Cloning non-deep only clones the vfs node. Cloning deep doesn't seem
|
||||
// to work with arbitrary attributes that don't follow the general
|
||||
// design of a property structure. This seems to require clear typing for
|
||||
// nodes in order to allow property_node_clone to work
|
||||
|
||||
// Ignore errors and default to empty
|
||||
memset(data, 0, sizeof(data));
|
||||
property_node_refer(
|
||||
NULL, source, "name@", PROPERTY_TYPE_ATTR, data, sizeof(data));
|
||||
property_util_node_attribute_replace(
|
||||
parent_property, parent, "name@", data);
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
property_node_refer(
|
||||
NULL, source, "fstype@", PROPERTY_TYPE_ATTR, data, sizeof(data));
|
||||
property_util_node_attribute_replace(
|
||||
parent_property, parent, "fstype@", data);
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
property_node_refer(
|
||||
NULL, source, "src@", PROPERTY_TYPE_ATTR, data, sizeof(data));
|
||||
property_util_node_attribute_replace(parent_property, parent, "src@", data);
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
property_node_refer(
|
||||
NULL, source, "dst@", PROPERTY_TYPE_ATTR, data, sizeof(data));
|
||||
property_util_node_attribute_replace(parent_property, parent, "dst@", data);
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
property_node_refer(
|
||||
NULL, source, "opt@", PROPERTY_TYPE_ATTR, data, sizeof(data));
|
||||
property_util_node_attribute_replace(parent_property, parent, "opt@", data);
|
||||
}
|
||||
|
||||
static bool _avs_config_mounttable_vfs_nodes_merge_strategy_do(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source,
|
||||
void *ctx,
|
||||
property_util_node_merge_recursion_do_t node_merge_recursion_do)
|
||||
{
|
||||
struct property_node *parent_child;
|
||||
struct property_node *source_child;
|
||||
|
||||
char parent_child_name[PROPERTY_NODE_NAME_SIZE_MAX];
|
||||
char name_parent[PROPERTY_NODE_ATTR_NAME_SIZE_MAX];
|
||||
char dst_parent[PROPERTY_NODE_ATTR_NAME_SIZE_MAX];
|
||||
|
||||
char source_child_name[PROPERTY_NODE_NAME_SIZE_MAX];
|
||||
char name_source[PROPERTY_NODE_ATTR_NAME_SIZE_MAX];
|
||||
char dst_source[PROPERTY_NODE_ATTR_NAME_SIZE_MAX];
|
||||
|
||||
bool node_consumed;
|
||||
bool found_parent;
|
||||
|
||||
source_child = property_node_traversal(source, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
node_consumed = false;
|
||||
|
||||
while (source_child) {
|
||||
property_node_name(
|
||||
source_child, source_child_name, sizeof(source_child_name));
|
||||
|
||||
if (str_eq(source_child_name, "vfs")) {
|
||||
node_consumed = true;
|
||||
|
||||
parent_child =
|
||||
property_node_traversal(parent, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
found_parent = false;
|
||||
|
||||
while (parent_child) {
|
||||
property_node_name(
|
||||
parent_child, parent_child_name, sizeof(parent_child_name));
|
||||
|
||||
if (str_eq(parent_child_name, "vfs")) {
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
source_child,
|
||||
"name@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
name_source,
|
||||
sizeof(name_source)))) {
|
||||
log_fatal(
|
||||
"Missing 'name' attribute on avs config mounttable "
|
||||
"vfs source node");
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
source_child,
|
||||
"dst@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
dst_source,
|
||||
sizeof(dst_source)))) {
|
||||
log_fatal(
|
||||
"Missing 'dst' attribute on avs config mounttable "
|
||||
"vfs source node");
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
parent_child,
|
||||
"name@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
name_parent,
|
||||
sizeof(name_parent)))) {
|
||||
log_fatal(
|
||||
"Missing 'name' attribute on avs config mounttable "
|
||||
"vfs parent node");
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
parent_child,
|
||||
"dst@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
dst_parent,
|
||||
sizeof(dst_parent)))) {
|
||||
log_fatal(
|
||||
"Missing 'dst' attribute on avs config mounttable "
|
||||
"vfs parent node");
|
||||
}
|
||||
|
||||
// Found existing matching node on parent, replace it
|
||||
if (str_eq(name_source, name_parent) &&
|
||||
str_eq(dst_source, dst_parent)) {
|
||||
_avs_config_node_vfs_copy(
|
||||
parent_property, parent_child, source_child);
|
||||
|
||||
found_parent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parent_child = property_node_traversal(
|
||||
parent_child, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
|
||||
// Not found an existing node that got replaced, insert/merge new
|
||||
// data
|
||||
if (!found_parent) {
|
||||
parent_child = property_node_create(
|
||||
parent_property, parent, PROPERTY_TYPE_VOID, "vfs");
|
||||
|
||||
_avs_config_node_vfs_copy(
|
||||
parent_property, parent_child, source_child);
|
||||
}
|
||||
}
|
||||
|
||||
source_child =
|
||||
property_node_traversal(source_child, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
|
||||
return node_consumed;
|
||||
}
|
||||
|
||||
struct property *avs_config_load(const char *filepath)
|
||||
{
|
||||
struct property *property;
|
||||
|
||||
log_assert(filepath);
|
||||
|
||||
log_info("Loading from file path: %s", filepath);
|
||||
|
||||
property = property_util_load(filepath);
|
||||
|
||||
// Check if root node exists, call already errors if not
|
||||
avs_config_root_get(property);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
struct property_node *avs_config_root_get(struct property *property)
|
||||
{
|
||||
struct property_node *node;
|
||||
|
||||
log_assert(property);
|
||||
|
||||
node = property_search(property, 0, AVS_CONFIG_ROOT_NODE);
|
||||
|
||||
if (node == NULL) {
|
||||
log_fatal("Root node " AVS_CONFIG_ROOT_NODE " in AVS config missing");
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct property *
|
||||
avs_config_property_merge(struct property *parent, struct property *source)
|
||||
{
|
||||
struct property_util_node_merge_strategies strategies;
|
||||
|
||||
log_assert(parent);
|
||||
log_assert(source);
|
||||
|
||||
strategies.num = 2;
|
||||
|
||||
strategies.entry[0].path = _avs_config_property_mounttable_path;
|
||||
strategies.entry[0].merge_strategy_do =
|
||||
_avs_config_mounttable_vfs_nodes_merge_strategy_do;
|
||||
|
||||
strategies.entry[1].path = "";
|
||||
strategies.entry[1].merge_strategy_do =
|
||||
property_util_node_merge_default_strategy_do;
|
||||
|
||||
return property_util_merge_with_strategies(parent, source, &strategies);
|
||||
}
|
||||
|
||||
void avs_config_fs_root_device_get(
|
||||
struct property_node *node, char *buffer, size_t size)
|
||||
{
|
||||
struct property_node *device_node;
|
||||
avs_error error;
|
||||
|
||||
log_assert(node);
|
||||
|
||||
device_node = property_search(NULL, node, "fs/root/device");
|
||||
|
||||
if (device_node == NULL) {
|
||||
log_fatal("Could not find node fs/root/device AVS config");
|
||||
}
|
||||
|
||||
error = property_node_read(device_node, PROPERTY_TYPE_STR, buffer, size);
|
||||
|
||||
if (AVS_IS_ERROR(error)) {
|
||||
log_fatal(
|
||||
"fs/root/device, property read failed: %s",
|
||||
avs_util_error_str(error));
|
||||
}
|
||||
}
|
||||
|
||||
void avs_config_mode_product_set(struct property_node *node, bool enable)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(NULL, node, "mode/product", enable ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "mode/product", enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_net_raw_set(struct property_node *node, bool enable)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(NULL, node, "net/enable_raw", enable ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "net/enable_raw", enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_net_eaudp_set(struct property_node *node, bool enable)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(
|
||||
NULL, node, "net/eaudp/enable", enable ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "net/eaudp/enable", enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_sntp_ea_set(struct property_node *node, bool on)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(NULL, node, "sntp/ea_on", on ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "sntp/ea_on", on);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_level_set(struct property_node *node, const char *level)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(level);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
uint32_t level_value;
|
||||
|
||||
if (str_eq(level, "fatal")) {
|
||||
level_value = 1;
|
||||
} else if (str_eq(level, "warning")) {
|
||||
level_value = 2;
|
||||
} else if (str_eq(level, "info")) {
|
||||
level_value = 3;
|
||||
} else if (str_eq(level, "misc")) {
|
||||
level_value = 4;
|
||||
} else if (str_eq(level, "all")) {
|
||||
level_value = 4;
|
||||
} else if (str_eq(level, "disable")) {
|
||||
level_value = 0;
|
||||
} else if (str_eq(level, "default")) {
|
||||
level_value = 4;
|
||||
} else {
|
||||
log_fatal("Unknown log level string %s", level);
|
||||
}
|
||||
|
||||
property_util_node_u32_replace(NULL, node, "log/level", level_value);
|
||||
#else
|
||||
property_util_node_str_replace(NULL, node, "log/level", level);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_name_set(struct property_node *node, const char *name)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(name);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "log/name", name);
|
||||
}
|
||||
|
||||
void avs_config_log_file_set(struct property_node *node, const char *file)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(file);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "log/file", file);
|
||||
}
|
||||
|
||||
void avs_config_log_buffer_size_set(struct property_node *node, uint32_t size)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
property_util_node_u32_replace(NULL, node, "log/sz_buf", size);
|
||||
}
|
||||
|
||||
void avs_config_log_output_delay_set(
|
||||
struct property_node *node, uint16_t delay_ms)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
property_util_node_u16_replace(NULL, node, "log/output_delay", delay_ms);
|
||||
}
|
||||
|
||||
void avs_config_log_enable_console_set(struct property_node *node, bool enable)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(
|
||||
NULL, node, "log/enable_console", enable ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "log/enable_console", enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_enable_sci_set(struct property_node *node, bool enable)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(
|
||||
NULL, node, "log/enable_netsci", enable ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "log/enable_netsci", enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_enable_net_set(struct property_node *node, bool enable)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(
|
||||
NULL, node, "log/enable_netlog", enable ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "log/enable_netlog", enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_enable_file_set(struct property_node *node, bool enable)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(
|
||||
NULL, node, "log/enable_file", enable ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "log/enable_file", enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_rotate_set(struct property_node *node, bool rotate)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(NULL, node, "log/rotate", rotate ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "log/rotate", rotate);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_append_set(struct property_node *node, bool append)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
#if AVS_VERSION <= 1306
|
||||
property_util_node_u8_replace(NULL, node, "log/append", append ? 1 : 0);
|
||||
#else
|
||||
property_util_node_bool_replace(NULL, node, "log/append", append);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_config_log_count_set(struct property_node *node, uint16_t count)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
property_util_node_u16_replace(NULL, node, "log/gen", count);
|
||||
}
|
||||
|
||||
void avs_config_set_log_level(
|
||||
struct property_node *node, enum core_log_bt_log_level loglevel)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
log_assert(node);
|
||||
|
||||
switch (loglevel) {
|
||||
case CORE_LOG_BT_LOG_LEVEL_OFF:
|
||||
str = "disable";
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_FATAL:
|
||||
str = "fatal";
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_WARNING:
|
||||
str = "warn";
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_INFO:
|
||||
str = "info";
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_MISC:
|
||||
str = "misc";
|
||||
break;
|
||||
|
||||
default:
|
||||
log_fatal("Unsupported log level: %d", loglevel);
|
||||
break;
|
||||
}
|
||||
|
||||
avs_config_log_level_set(node, str);
|
||||
}
|
||||
|
||||
void avs_config_local_fs_path_dev_nvram_and_raw_set(
|
||||
struct property_node *node, const char *dev_nvram_raw_path)
|
||||
{
|
||||
char path_dev_raw[MAX_PATH];
|
||||
char path_dev_nvram[MAX_PATH];
|
||||
|
||||
struct property_node *fs_node;
|
||||
struct property_node *mounttable_node;
|
||||
struct property_node *vfs_node;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(dev_nvram_raw_path);
|
||||
|
||||
str_cpy(path_dev_raw, sizeof(path_dev_raw), dev_nvram_raw_path);
|
||||
str_cat(path_dev_raw, sizeof(path_dev_raw), "/dev/raw");
|
||||
|
||||
str_cpy(path_dev_nvram, sizeof(path_dev_nvram), dev_nvram_raw_path);
|
||||
str_cat(path_dev_nvram, sizeof(path_dev_nvram), "/dev/nvram");
|
||||
|
||||
fs_node = property_search(NULL, node, "fs");
|
||||
|
||||
if (!fs_node) {
|
||||
log_fatal("Cannot find 'fs' node in avs config");
|
||||
}
|
||||
|
||||
// Check if "new" mounttable config is used for dev/nvram and dev/raw or
|
||||
// legacy config
|
||||
if (property_search(NULL, fs_node, "mounttable")) {
|
||||
property_remove(NULL, fs_node, "mounttable");
|
||||
|
||||
mounttable_node = property_node_create(
|
||||
NULL, fs_node, PROPERTY_TYPE_VOID, "mounttable");
|
||||
|
||||
vfs_node = property_node_create(
|
||||
NULL, mounttable_node, PROPERTY_TYPE_VOID, "vfs");
|
||||
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot");
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs");
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_raw);
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/raw");
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1");
|
||||
|
||||
vfs_node = property_node_create(
|
||||
NULL, mounttable_node, PROPERTY_TYPE_VOID, "vfs");
|
||||
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot");
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs");
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_nvram);
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/nvram");
|
||||
property_node_create(
|
||||
NULL, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1");
|
||||
} else {
|
||||
property_util_node_str_replace(
|
||||
NULL, fs_node, "nvram/device", path_dev_raw);
|
||||
property_util_node_str_replace(NULL, fs_node, "nvram/fstype", "fs");
|
||||
property_util_node_str_replace(
|
||||
NULL, fs_node, "nvram/option", "vf=1,posix=1");
|
||||
|
||||
property_util_node_str_replace(
|
||||
NULL, fs_node, "raw/device", path_dev_nvram);
|
||||
property_util_node_str_replace(NULL, fs_node, "raw/fstype", "fs");
|
||||
property_util_node_str_replace(
|
||||
NULL, fs_node, "raw/option", "vf=1,posix=1");
|
||||
}
|
||||
}
|
||||
|
||||
void avs_config_vfs_mounttable_get(
|
||||
struct property_node *node, struct avs_config_vfs_mounttable *mounttable)
|
||||
{
|
||||
struct property_node *fs_node;
|
||||
struct property_node *mounttable_node;
|
||||
struct property_node *cur;
|
||||
char mounttable_selector[128];
|
||||
char name[128];
|
||||
uint8_t pos;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(mounttable);
|
||||
|
||||
fs_node = property_search(NULL, node, "fs");
|
||||
|
||||
if (!fs_node) {
|
||||
log_fatal("Cannot find 'fs' node in avs config");
|
||||
}
|
||||
|
||||
// Check if new mounttable config is used for dev/nvram and dev/raw or
|
||||
// legacy config
|
||||
mounttable_node = property_search(NULL, fs_node, "mounttable");
|
||||
|
||||
memset(mounttable, 0, sizeof(*mounttable));
|
||||
pos = 0;
|
||||
|
||||
if (mounttable_node) {
|
||||
cur = property_search(NULL, fs_node, "mounttable_selector");
|
||||
|
||||
if (!cur) {
|
||||
log_fatal("Missing 'mounttable_selector' on mounttable");
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_read(
|
||||
cur,
|
||||
PROPERTY_TYPE_STR,
|
||||
mounttable_selector,
|
||||
sizeof(mounttable_selector)))) {
|
||||
log_fatal("Reading 'mounttable_selector' failed");
|
||||
}
|
||||
|
||||
log_misc("Mounttable selector: %s", mounttable_selector);
|
||||
|
||||
cur = property_node_traversal(mounttable_node, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
while (cur) {
|
||||
property_node_name(cur, name, sizeof(name));
|
||||
|
||||
if (str_eq(name, "vfs")) {
|
||||
if (pos >= AVS_CONFIG_MOUNTTABLE_MAX_ENTRIES) {
|
||||
log_warning(
|
||||
"Exceeding max number of supported mounttable entries "
|
||||
"(%d), ignoring remaining",
|
||||
pos);
|
||||
break;
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"name@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
name,
|
||||
sizeof(name)))) {
|
||||
log_fatal("Missing 'name' attribute on vfs node");
|
||||
}
|
||||
|
||||
if (str_eq(name, mounttable_selector)) {
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"fstype@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
mounttable->entry[pos].fstype,
|
||||
sizeof(mounttable->entry[pos].fstype)))) {
|
||||
// default
|
||||
str_cpy(
|
||||
mounttable->entry[pos].fstype,
|
||||
sizeof(mounttable->entry[pos].fstype),
|
||||
"fs");
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"src@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
mounttable->entry[pos].src,
|
||||
sizeof(mounttable->entry[pos].src)))) {
|
||||
log_fatal(
|
||||
"Missing 'src' attribute on vfs node, name: %s",
|
||||
name);
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"dst@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
mounttable->entry[pos].dst,
|
||||
sizeof(mounttable->entry[pos].dst)))) {
|
||||
log_fatal(
|
||||
"Missing 'dst' attribute on vfs node, name: %s",
|
||||
name);
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"opt@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
mounttable->entry[pos].opt,
|
||||
sizeof(mounttable->entry[pos].opt)))) {
|
||||
// optional
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
cur = property_node_traversal(cur, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
} else {
|
||||
cur = property_search(NULL, fs_node, "nvram");
|
||||
|
||||
if (cur) {
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"fstype",
|
||||
PROPERTY_TYPE_STR,
|
||||
mounttable->entry[pos].fstype,
|
||||
sizeof(mounttable->entry[pos].fstype)))) {
|
||||
// default
|
||||
str_cpy(
|
||||
mounttable->entry[pos].fstype,
|
||||
sizeof(mounttable->entry[pos].fstype),
|
||||
"fs");
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"device",
|
||||
PROPERTY_TYPE_STR,
|
||||
mounttable->entry[pos].src,
|
||||
sizeof(mounttable->entry[pos].src)))) {
|
||||
log_fatal("Missing 'device' attribute on nvram node");
|
||||
}
|
||||
|
||||
str_cpy(
|
||||
mounttable->entry[pos].dst,
|
||||
sizeof(mounttable->entry[pos].dst),
|
||||
"/dev/nvram");
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"opt",
|
||||
PROPERTY_TYPE_STR,
|
||||
mounttable->entry[pos].opt,
|
||||
sizeof(mounttable->entry[pos].opt)))) {
|
||||
// optional
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
cur = property_search(NULL, fs_node, "raw");
|
||||
|
||||
if (cur) {
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"fstype",
|
||||
PROPERTY_TYPE_STR,
|
||||
mounttable->entry[pos].fstype,
|
||||
sizeof(mounttable->entry[pos].fstype)))) {
|
||||
// default
|
||||
str_cpy(
|
||||
mounttable->entry[pos].fstype,
|
||||
sizeof(mounttable->entry[pos].fstype),
|
||||
"fs");
|
||||
}
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"device",
|
||||
PROPERTY_TYPE_STR,
|
||||
mounttable->entry[pos].src,
|
||||
sizeof(mounttable->entry[pos].src)))) {
|
||||
log_fatal("Missing 'device' attribute on raw node");
|
||||
}
|
||||
|
||||
str_cpy(
|
||||
mounttable->entry[pos].dst,
|
||||
sizeof(mounttable->entry[pos].dst),
|
||||
"/dev/raw");
|
||||
|
||||
if (AVS_IS_ERROR(property_node_refer(
|
||||
NULL,
|
||||
cur,
|
||||
"opt",
|
||||
PROPERTY_TYPE_STR,
|
||||
mounttable->entry[pos].opt,
|
||||
sizeof(mounttable->entry[pos].opt)))) {
|
||||
// optional
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
mounttable->num_entries = pos;
|
||||
}
|
57
src/main/launcher/avs-config.h
Normal file
57
src/main/launcher/avs-config.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef LAUNCHER_AVS_CONFIG_H
|
||||
#define LAUNCHER_AVS_CONFIG_H
|
||||
|
||||
#include "core/log-bt.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/bootstrap-config.h"
|
||||
|
||||
#define AVS_CONFIG_MOUNTTABLE_MAX_ENTRIES 16
|
||||
|
||||
struct avs_config_vfs_mounttable {
|
||||
struct {
|
||||
char fstype[64];
|
||||
char src[512];
|
||||
char dst[512];
|
||||
char opt[256];
|
||||
} entry[AVS_CONFIG_MOUNTTABLE_MAX_ENTRIES];
|
||||
|
||||
uint8_t num_entries;
|
||||
};
|
||||
|
||||
struct property *avs_config_load(const char *filepath);
|
||||
struct property_node *avs_config_root_get(struct property *property);
|
||||
struct property *
|
||||
avs_config_property_merge(struct property *parent, struct property *source);
|
||||
|
||||
void avs_config_fs_root_device_get(
|
||||
struct property_node *node, char *buffer, size_t size);
|
||||
|
||||
void avs_config_mode_product_set(struct property_node *node, bool enable);
|
||||
void avs_config_net_raw_set(struct property_node *node, bool enable);
|
||||
void avs_config_net_eaudp_set(struct property_node *node, bool enable);
|
||||
void avs_config_sntp_ea_set(struct property_node *node, bool on);
|
||||
void avs_config_log_level_set(struct property_node *node, const char *level);
|
||||
void avs_config_log_name_set(struct property_node *node, const char *name);
|
||||
void avs_config_log_file_set(struct property_node *node, const char *file);
|
||||
void avs_config_log_buffer_size_set(struct property_node *node, uint32_t size);
|
||||
void avs_config_log_output_delay_set(
|
||||
struct property_node *node, uint16_t delay_ms);
|
||||
void avs_config_log_enable_console_set(struct property_node *node, bool enable);
|
||||
void avs_config_log_enable_sci_set(struct property_node *node, bool enable);
|
||||
void avs_config_log_enable_net_set(struct property_node *node, bool enable);
|
||||
void avs_config_log_enable_file_set(struct property_node *node, bool enable);
|
||||
void avs_config_log_rotate_set(struct property_node *node, bool rotate);
|
||||
void avs_config_log_append_set(struct property_node *node, bool append);
|
||||
void avs_config_log_count_set(struct property_node *node, uint16_t count);
|
||||
|
||||
void avs_config_set_log_level(
|
||||
struct property_node *node, enum core_log_bt_log_level loglevel);
|
||||
void avs_config_local_fs_path_dev_nvram_and_raw_set(
|
||||
struct property_node *node, const char *dev_nvram_raw_path);
|
||||
|
||||
void avs_config_vfs_mounttable_get(
|
||||
struct property_node *node, struct avs_config_vfs_mounttable *mounttable);
|
||||
|
||||
#endif
|
@ -1,72 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-context.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static void *avs_heap;
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
static void *std_heap;
|
||||
#endif
|
||||
|
||||
void avs_context_init(
|
||||
struct property_node *config,
|
||||
uint32_t avs_heap_size,
|
||||
uint32_t std_heap_size,
|
||||
avs_log_writer_t log_writer,
|
||||
void *log_writer_ctx)
|
||||
{
|
||||
avs_heap = VirtualAlloc(
|
||||
NULL, avs_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
if (avs_heap == NULL) {
|
||||
log_fatal(
|
||||
"Failed to VirtualAlloc %d byte AVS heap: %08x",
|
||||
avs_heap_size,
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
std_heap = VirtualAlloc(
|
||||
NULL, std_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
if (std_heap == NULL) {
|
||||
log_fatal(
|
||||
"Failed to VirtualAlloc %d byte \"std\" heap: %08x",
|
||||
std_heap_size,
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
avs_boot(
|
||||
config,
|
||||
std_heap,
|
||||
std_heap_size,
|
||||
avs_heap,
|
||||
avs_heap_size,
|
||||
log_writer,
|
||||
log_writer_ctx);
|
||||
#else
|
||||
/* AVS v2.16.xx and I suppose onward uses a unified heap */
|
||||
avs_boot(config, avs_heap, avs_heap_size, NULL, log_writer, log_writer_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_context_fini(void)
|
||||
{
|
||||
avs_shutdown();
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
VirtualFree(std_heap, 0, MEM_RELEASE);
|
||||
#endif
|
||||
|
||||
VirtualFree(avs_heap, 0, MEM_RELEASE);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#ifndef LAUNCHER_AVS_CONTEXT_H
|
||||
#define LAUNCHER_AVS_CONTEXT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#if AVS_VERSION < 1600
|
||||
#define AVS_HAS_STD_HEAP
|
||||
#endif
|
||||
|
||||
void avs_context_init(
|
||||
struct property_node *config,
|
||||
uint32_t avs_heap_size,
|
||||
uint32_t std_heap_size,
|
||||
avs_log_writer_t log_writer,
|
||||
void *log_writer_ctx);
|
||||
void avs_context_fini(void);
|
||||
|
||||
#endif
|
270
src/main/launcher/avs.c
Normal file
270
src/main/launcher/avs.c
Normal file
@ -0,0 +1,270 @@
|
||||
#define LOG_MODULE "avs"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "core/log-bt.h"
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-config.h"
|
||||
#include "launcher/avs.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/codepage.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#if AVS_VERSION < 1600
|
||||
#define AVS_HAS_STD_HEAP
|
||||
#endif
|
||||
|
||||
static void *avs_heap;
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
static void *std_heap;
|
||||
#endif
|
||||
|
||||
/* Gratuitous API changes orz */
|
||||
static AVS_LOG_WRITER(_avs_context_log_writer, chars, nchars, ctx)
|
||||
{
|
||||
wchar_t *utf16;
|
||||
char *utf8;
|
||||
int utf16_len;
|
||||
int utf8_len;
|
||||
int result;
|
||||
|
||||
/* Ignore existing NUL terminator */
|
||||
|
||||
nchars--;
|
||||
|
||||
/* Transcode shit_jis to UTF-8 */
|
||||
|
||||
utf16_len = MultiByteToWideChar(CP_SHIFT_JIS, 0, chars, nchars, NULL, 0);
|
||||
|
||||
if (utf16_len == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
utf16 = xmalloc(sizeof(*utf16) * utf16_len);
|
||||
result =
|
||||
MultiByteToWideChar(CP_SHIFT_JIS, 0, chars, nchars, utf16, utf16_len);
|
||||
|
||||
if (result == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
utf8_len =
|
||||
WideCharToMultiByte(CP_UTF8, 0, utf16, utf16_len, NULL, 0, NULL, NULL);
|
||||
|
||||
if (utf8_len == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
utf8 = xmalloc(utf8_len + 3);
|
||||
result = WideCharToMultiByte(
|
||||
CP_UTF8, 0, utf16, utf16_len, utf8, utf8_len, NULL, NULL);
|
||||
|
||||
if (result == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
#if AVS_VERSION >= 1500
|
||||
utf8[utf8_len + 0] = '\r';
|
||||
utf8[utf8_len + 1] = '\n';
|
||||
|
||||
utf8_len += 2;
|
||||
#endif
|
||||
|
||||
// Clean string terminate
|
||||
utf8[utf8_len] = '\0';
|
||||
|
||||
// Write to launcher's dedicated logging backend
|
||||
core_log_bt_direct_sink_write(utf8, utf8_len);
|
||||
|
||||
/* Clean up */
|
||||
|
||||
free(utf8);
|
||||
free(utf16);
|
||||
}
|
||||
|
||||
static void _avs_switch_log_engine()
|
||||
{
|
||||
// Switch the logging backend now that AVS is booted to use a single logging
|
||||
// engine which avoids concurrency issues as AVS runs it's own async logger
|
||||
// thread
|
||||
core_log_impl_set(
|
||||
log_body_misc, log_body_info, log_body_warning, log_body_fatal);
|
||||
|
||||
log_misc("Switched logging engine to AVS");
|
||||
}
|
||||
|
||||
void avs_fs_assert_root_device_exists(struct property_node *node)
|
||||
{
|
||||
char root_device_path[PATH_MAX];
|
||||
char cwd_path[PATH_MAX];
|
||||
|
||||
avs_config_fs_root_device_get(
|
||||
node, root_device_path, sizeof(root_device_path));
|
||||
getcwd(cwd_path, sizeof(cwd_path));
|
||||
|
||||
if (!path_exists(root_device_path)) {
|
||||
log_fatal(
|
||||
"Root device path '%s' does not exist in current working dir '%s'",
|
||||
root_device_path,
|
||||
cwd_path);
|
||||
}
|
||||
}
|
||||
|
||||
void avs_fs_mountpoints_fs_dirs_create(struct property_node *node)
|
||||
{
|
||||
struct avs_config_vfs_mounttable mounttable;
|
||||
uint8_t i;
|
||||
|
||||
avs_config_vfs_mounttable_get(node, &mounttable);
|
||||
|
||||
if (mounttable.num_entries == 0) {
|
||||
log_warning("No mountpoints found in mounttable");
|
||||
}
|
||||
|
||||
for (i = 0; i < mounttable.num_entries; i++) {
|
||||
if (str_eq(mounttable.entry[i].fstype, "fs")) {
|
||||
log_misc(
|
||||
"Creating avs fs directory '%s' for destination/device '%s'...",
|
||||
mounttable.entry[i].src,
|
||||
mounttable.entry[i].dst);
|
||||
|
||||
if (!path_exists(mounttable.entry[i].src)) {
|
||||
if (!path_mkdir(mounttable.entry[i].src)) {
|
||||
log_fatal(
|
||||
"Creating fs directory %s failed",
|
||||
mounttable.entry[i].src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void avs_init(
|
||||
struct property_node *node, uint32_t avs_heap_size, uint32_t std_heap_size)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(avs_heap_size > 0);
|
||||
// Modern games don't have a separate std heap anymore
|
||||
log_assert(std_heap_size >= 0);
|
||||
|
||||
log_info("init");
|
||||
|
||||
log_misc("Allocating avs heap: %d", avs_heap_size);
|
||||
|
||||
avs_heap = VirtualAlloc(
|
||||
NULL, avs_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
if (avs_heap == NULL) {
|
||||
log_fatal(
|
||||
"Failed to VirtualAlloc %d byte AVS heap: %08x",
|
||||
avs_heap_size,
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
log_misc("Allocating std heap: %d", std_heap_size);
|
||||
|
||||
std_heap = VirtualAlloc(
|
||||
NULL, std_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
if (std_heap == NULL) {
|
||||
log_fatal(
|
||||
"Failed to VirtualAlloc %d byte \"std\" heap: %08x",
|
||||
std_heap_size,
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
#endif
|
||||
|
||||
log_info("Calling avs_boot");
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
avs_boot(
|
||||
node,
|
||||
std_heap,
|
||||
std_heap_size,
|
||||
avs_heap,
|
||||
avs_heap_size,
|
||||
_avs_context_log_writer,
|
||||
NULL);
|
||||
#else
|
||||
/* AVS v2.16.xx and I suppose onward uses a unified heap */
|
||||
avs_boot(
|
||||
node, avs_heap, avs_heap_size, NULL, _avs_context_log_writer, NULL);
|
||||
#endif
|
||||
|
||||
_avs_switch_log_engine();
|
||||
|
||||
log_misc("init done");
|
||||
}
|
||||
|
||||
void avs_fs_file_copy(const char *src, const char *dst)
|
||||
{
|
||||
struct avs_stat st;
|
||||
|
||||
log_assert(src);
|
||||
log_assert(dst);
|
||||
|
||||
log_misc("Copying %s to %s...", src, dst);
|
||||
|
||||
if (!avs_fs_lstat(src, &st)) {
|
||||
log_fatal("File source %s does not exist or is not accessible", src);
|
||||
}
|
||||
|
||||
if (avs_fs_copy(src, dst) < 0) {
|
||||
log_fatal("Failed copying file %s to %s", src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
void avs_fs_dir_log(const char *path)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
log_assert(path);
|
||||
|
||||
avs_desc dir = avs_fs_opendir(path);
|
||||
|
||||
if (dir < 0) {
|
||||
log_warning(
|
||||
"Opening avs dir %s failed, skipping logging contents", path);
|
||||
}
|
||||
|
||||
log_misc("Contents of %s:", path);
|
||||
|
||||
do {
|
||||
name = avs_fs_readdir(dir);
|
||||
|
||||
if (name == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
log_misc("%s", name);
|
||||
} while (name != NULL);
|
||||
|
||||
avs_fs_closedir(dir);
|
||||
}
|
||||
|
||||
void avs_fini(void)
|
||||
{
|
||||
log_info("fini");
|
||||
|
||||
avs_shutdown();
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
VirtualFree(std_heap, 0, MEM_RELEASE);
|
||||
#endif
|
||||
|
||||
VirtualFree(avs_heap, 0, MEM_RELEASE);
|
||||
|
||||
log_misc("fini done");
|
||||
}
|
16
src/main/launcher/avs.h
Normal file
16
src/main/launcher/avs.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef LAUNCHER_AVS_H
|
||||
#define LAUNCHER_AVS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
void avs_fs_assert_root_device_exists(struct property_node *node);
|
||||
void avs_fs_mountpoints_fs_dirs_create(struct property_node *node);
|
||||
void avs_init(
|
||||
struct property_node *node, uint32_t avs_heap_size, uint32_t std_heap_size);
|
||||
void avs_fs_file_copy(const char *src, const char *dst);
|
||||
void avs_fs_dir_log(const char *path);
|
||||
void avs_fini(void);
|
||||
|
||||
#endif
|
548
src/main/launcher/bootstrap-config.c
Normal file
548
src/main/launcher/bootstrap-config.c
Normal file
@ -0,0 +1,548 @@
|
||||
#define LOG_MODULE "bootstrap-config"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-config.h"
|
||||
#include "launcher/bootstrap-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/str.h"
|
||||
|
||||
// clang-format off
|
||||
PSMAP_BEGIN(bootstrap_startup_boot_psmap)
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_boot_config, config_file,
|
||||
"boot/file")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_U32, struct bootstrap_boot_config, avs_heap_size,
|
||||
"boot/heap_avs")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_boot_config, std_heap_size,
|
||||
"boot/heap_std", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_boot_config, mount_table_selector,
|
||||
"boot/mounttable_selector", "boot")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_boot_config, watcher_enable,
|
||||
"boot/watcher", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_boot_config, timemachine_enable,
|
||||
"boot/timemachine", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_boot_config, launch_config_file,
|
||||
"boot/launch_path", "/dev/raw/launch.xml")
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_log_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_log_config, level,
|
||||
"log/level", "all")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_log_config, name,
|
||||
"log/name", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_log_config, file,
|
||||
"log/file", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_log_config, bufsz,
|
||||
"log/sz_buf", 4096)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_log_config, output_delay_ms,
|
||||
"log/output_delay", 10)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_console,
|
||||
"log/enable_console", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_sci,
|
||||
"log/enable_netsci", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_net,
|
||||
"log/enable_netlog", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_file,
|
||||
"log/enable_file", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, rotate,
|
||||
"log/rotate", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, append,
|
||||
"log/append", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_log_config, count,
|
||||
"log/gen", 10)
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_minidump_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_minidump_config, count,
|
||||
"minidump/gen", 10)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_minidump_config, continue_,
|
||||
"minidump/cont_debug", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_minidump_config, log,
|
||||
"minidump/echo_log", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_minidump_config, type,
|
||||
"minidump/dump_type", 2)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_minidump_config, path,
|
||||
"minidump/path", "/dev/raw/minidump")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_minidump_config, symbufsz,
|
||||
"minidump/sz_symbuf", 32768)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_minidump_config, search_path,
|
||||
"minidump/search", ".")
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_module_psmap)
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_module_config, file,
|
||||
"component/file")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_module_config, load_type,
|
||||
"component/load_type", "MEMORY")
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_dlm_psmap)
|
||||
/* disabled until we implement PSMAP_TYPE_BIN
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, ntdll_digest,
|
||||
"dlml/ntdll/hash", "")
|
||||
*/
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, size,
|
||||
"dlml/ntdll/size", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, ift_table,
|
||||
"dlml/ntdll/ift_table", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, ift_insert,
|
||||
"dlml/ntdll/insert_ift", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, ift_remove,
|
||||
"dlml/ntdll/remove_ift", 0)
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_shield_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_shield_config, enable,
|
||||
"shield/enable", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_shield_config, verbose,
|
||||
"shield/verbose", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_shield_config, use_loadlibrary,
|
||||
"shield/use_loadlibrary", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_shield_config, logger,
|
||||
"shield/logger", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, sleep_min,
|
||||
"shield/sleepmin", 10)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, sleep_blur,
|
||||
"shield/sleepblur", 90)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_shield_config, whitelist_file,
|
||||
"shield/whitelist", "prop/whitelist.csv")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, tick_sleep,
|
||||
"shield/ticksleep", 100)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, tick_error,
|
||||
"shield/tickerror", 1000)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_shield_config, overwork_threshold,
|
||||
"shield/overwork_threshold", 50)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, overwork_delay,
|
||||
"shield/overwork_delay", 100)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, pause_delay,
|
||||
"shield/pause_delay", 1000)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_shield_config, unlimited_key,
|
||||
"shield/unlimited_key", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_shield_config, killer_port,
|
||||
"shield_killer/port", 5001)
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_dongle_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_dongle_config, license_cn,
|
||||
"dongle/license", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_dongle_config, account_cn,
|
||||
"dongle/account", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_dongle_config, driver_dll,
|
||||
"dongle/pkcs11_driver", "eTPKCS11.dll")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_dongle_config, disable_gc,
|
||||
"dongle/disable_gc", 0)
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_drm_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, dll,
|
||||
"drm/dll", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, fstype,
|
||||
"drm/fstype", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, device,
|
||||
"drm/device", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, mount,
|
||||
"drm/dst", "/")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, options,
|
||||
"drm/option", "")
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_lte_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_lte_config, enable,
|
||||
"lte/enable", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_lte_config, config_file,
|
||||
"lte/file", "/dev/nvram/lte-config.xml")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_lte_config, unlimited_key,
|
||||
"lte/unlimited_key", "")
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_ssl_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_ssl_config, options,
|
||||
"ssl/option", "")
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_esign_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_esign_config, enable,
|
||||
"esign/enable", 0)
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_startup_eamuse_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_eamuse_config, enable,
|
||||
"eamuse/enable", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_eamuse_config, sync,
|
||||
"eamuse/sync", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_eamuse_config, enable_model,
|
||||
"eamuse/enable_model", 0)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_eamuse_config, config_file,
|
||||
"eamuse/file", "/dev/nvram/ea3-config.xml")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_eamuse_config, updatecert_enable,
|
||||
"eamuse/updatecert_enable", 1)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_eamuse_config, updatecert_interval,
|
||||
"eamuse/updatecert_interval", 0)
|
||||
PSMAP_END
|
||||
|
||||
PSMAP_BEGIN(bootstrap_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_config, release_code, "/release_code", "")
|
||||
PSMAP_END
|
||||
// clang-format on
|
||||
|
||||
#define ROOT_NODE "/config"
|
||||
#define MODULE_PATH_PREFIX "modules/"
|
||||
|
||||
#define NODE_MISSING_FATAL(subnode) \
|
||||
log_fatal("%s/%s: Node missing", ROOT_NODE, subnode);
|
||||
#define NODE_STARTUP_MISSING_FATAL(profile) \
|
||||
log_fatal("%s/startup/%s: Node missing", ROOT_NODE, profile);
|
||||
#define NODE_PROFILE_MISSING_FATAL(profile, subnode) \
|
||||
log_fatal("%s/%s/%s: Node missing", ROOT_NODE, profile, subnode);
|
||||
#define NODE_PROFILE_LOADING_FATAL(profile, subnode) \
|
||||
log_fatal("%s/startup/%s/%s: Node loading", ROOT_NODE, profile, subnode);
|
||||
|
||||
#define DEFAULT_HEAP_SIZE 16777216
|
||||
|
||||
const char *const inherited_nodes[] = {
|
||||
"develop",
|
||||
"default",
|
||||
"log",
|
||||
"minidump",
|
||||
"boot",
|
||||
"drm",
|
||||
"ssl",
|
||||
"eamuse",
|
||||
"shield",
|
||||
"esign",
|
||||
"dongle",
|
||||
"lte",
|
||||
};
|
||||
|
||||
static void _bootstrap_config_profile_node_verify(
|
||||
struct property_node *node, const char *profile)
|
||||
{
|
||||
struct property_node *profile_node;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(profile);
|
||||
|
||||
profile_node = property_search(NULL, node, profile);
|
||||
|
||||
if (!profile_node) {
|
||||
NODE_STARTUP_MISSING_FATAL(profile);
|
||||
}
|
||||
}
|
||||
|
||||
static struct property_node *
|
||||
_bootstrap_config_root_node_get(struct property *property)
|
||||
{
|
||||
struct property_node *root_node;
|
||||
|
||||
log_assert(property);
|
||||
|
||||
root_node = property_search(property, NULL, ROOT_NODE);
|
||||
|
||||
if (!root_node) {
|
||||
NODE_MISSING_FATAL("");
|
||||
}
|
||||
|
||||
return root_node;
|
||||
}
|
||||
|
||||
static struct property_node *
|
||||
_bootstrap_config_startup_node_get(struct property_node *node)
|
||||
{
|
||||
struct property_node *startup_node;
|
||||
|
||||
log_assert(node);
|
||||
|
||||
startup_node = property_search(NULL, node, "startup");
|
||||
|
||||
if (!startup_node) {
|
||||
NODE_MISSING_FATAL("startup");
|
||||
}
|
||||
|
||||
return startup_node;
|
||||
}
|
||||
|
||||
static void _bootstrap_config_inheritance_resolve(
|
||||
struct property_node *startup_node, const char *profile_name)
|
||||
{
|
||||
struct property_node *startup_parent_node;
|
||||
struct property_node *startup_profile_node;
|
||||
struct property_node *tmp_node;
|
||||
|
||||
char inherit_name[64];
|
||||
avs_error error;
|
||||
struct property_node *result;
|
||||
|
||||
startup_profile_node = property_search(NULL, startup_node, profile_name);
|
||||
|
||||
if (!startup_profile_node) {
|
||||
log_fatal(ROOT_NODE "/startup/%s: missing", profile_name);
|
||||
}
|
||||
|
||||
startup_parent_node = startup_profile_node;
|
||||
|
||||
for (;;) {
|
||||
error = property_node_refer(
|
||||
NULL,
|
||||
startup_parent_node,
|
||||
"inherit@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
inherit_name,
|
||||
sizeof(inherit_name));
|
||||
|
||||
if (AVS_IS_ERROR(error)) {
|
||||
break;
|
||||
}
|
||||
|
||||
startup_parent_node = property_search(NULL, startup_node, inherit_name);
|
||||
|
||||
if (!startup_parent_node) {
|
||||
NODE_STARTUP_MISSING_FATAL(inherit_name);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _countof(inherited_nodes); i++) {
|
||||
if (property_search(NULL, startup_node, inherited_nodes[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp_node =
|
||||
property_search(NULL, startup_parent_node, inherited_nodes[i]);
|
||||
|
||||
if (tmp_node) {
|
||||
log_misc(
|
||||
ROOT_NODE "/startup/%s: merging %s...",
|
||||
inherit_name,
|
||||
inherited_nodes[i]);
|
||||
|
||||
result = property_node_clone(
|
||||
NULL, startup_profile_node, tmp_node, true);
|
||||
|
||||
if (!result) {
|
||||
log_fatal(
|
||||
"Merging '%s' into '%s' failed",
|
||||
inherited_nodes[i],
|
||||
inherit_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _bootstrap_config_load_bootstrap_module_app_config(
|
||||
struct property_node *profile_node, struct bootstrap_module_config *config)
|
||||
{
|
||||
struct property_node *app_node;
|
||||
|
||||
log_assert(profile_node);
|
||||
log_assert(config);
|
||||
|
||||
app_node = property_search(NULL, profile_node, "component/param");
|
||||
|
||||
config->app_config = property_util_node_extract(app_node);
|
||||
}
|
||||
|
||||
static void _bootstrap_config_load_bootstrap_default_files_config(
|
||||
const char *profile_name,
|
||||
struct property_node *profile_node,
|
||||
struct bootstrap_default_file_config *config)
|
||||
{
|
||||
int i;
|
||||
int result;
|
||||
struct property_node *child;
|
||||
|
||||
log_assert(profile_node);
|
||||
log_assert(config);
|
||||
|
||||
child = property_search(NULL, profile_node, "default/file");
|
||||
i = 0;
|
||||
|
||||
while (child) {
|
||||
if (i >= DEFAULT_FILE_MAX) {
|
||||
log_warning(
|
||||
"Currently not supporting more than %d default files, skipping "
|
||||
"remaining",
|
||||
i);
|
||||
break;
|
||||
}
|
||||
|
||||
result = property_node_refer(
|
||||
NULL,
|
||||
child,
|
||||
"src@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
&config->file[i].src,
|
||||
sizeof(config->file[i].src));
|
||||
|
||||
if (result < 0) {
|
||||
log_fatal(
|
||||
"Missing src attribute on default file node of profile %s",
|
||||
profile_name);
|
||||
}
|
||||
|
||||
result = property_node_refer(
|
||||
NULL,
|
||||
child,
|
||||
"dst@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
&config->file[i].dst,
|
||||
sizeof(config->file[i].dst));
|
||||
|
||||
if (result < 0) {
|
||||
log_fatal(
|
||||
"Missing dst attribute on default file node of profile %s",
|
||||
profile_name);
|
||||
}
|
||||
|
||||
child = property_node_traversal(child, TRAVERSE_NEXT_SEARCH_RESULT);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void _bootstrap_config_load_bootstrap(
|
||||
struct property_node *startup_node,
|
||||
const char *profile,
|
||||
struct bootstrap_startup_config *config)
|
||||
{
|
||||
struct property_node *profile_node;
|
||||
|
||||
profile_node = property_search(NULL, startup_node, profile);
|
||||
|
||||
if (!profile_node) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "");
|
||||
}
|
||||
|
||||
_bootstrap_config_load_bootstrap_default_files_config(
|
||||
profile, profile_node, &config->default_file);
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL, profile_node, &config->boot, bootstrap_startup_boot_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "boot");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL, profile_node, &config->log, bootstrap_startup_log_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "log");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL,
|
||||
profile_node,
|
||||
&config->minidump,
|
||||
bootstrap_startup_minidump_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "minidump");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL,
|
||||
profile_node,
|
||||
&config->module,
|
||||
bootstrap_startup_module_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "component");
|
||||
}
|
||||
|
||||
_bootstrap_config_load_bootstrap_module_app_config(
|
||||
profile_node, &config->module);
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL,
|
||||
profile_node,
|
||||
&config->dlm_ntdll,
|
||||
bootstrap_startup_dlm_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "dlm/ntdll");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL,
|
||||
profile_node,
|
||||
&config->shield,
|
||||
bootstrap_startup_shield_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "shield");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL,
|
||||
profile_node,
|
||||
&config->dongle,
|
||||
bootstrap_startup_dongle_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "dongle");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL, profile_node, &config->drm, bootstrap_startup_drm_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "drm");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL, profile_node, &config->lte, bootstrap_startup_lte_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "lte");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL, profile_node, &config->ssl, bootstrap_startup_ssl_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "ssl");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL,
|
||||
profile_node,
|
||||
&config->esign,
|
||||
bootstrap_startup_esign_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "esign");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
NULL,
|
||||
profile_node,
|
||||
&config->eamuse,
|
||||
bootstrap_startup_eamuse_psmap)) {
|
||||
NODE_PROFILE_LOADING_FATAL(profile, "eamuse");
|
||||
}
|
||||
}
|
||||
|
||||
void bootstrap_config_init(struct bootstrap_config *config)
|
||||
{
|
||||
log_assert(config);
|
||||
|
||||
memset(config, 0, sizeof(*config));
|
||||
}
|
||||
|
||||
void bootstrap_config_load(
|
||||
struct property *property,
|
||||
const char *profile,
|
||||
struct bootstrap_config *config)
|
||||
{
|
||||
struct property_node *root_node;
|
||||
struct property_node *startup_node;
|
||||
|
||||
log_assert(property);
|
||||
log_assert(profile);
|
||||
log_assert(config);
|
||||
|
||||
log_info(ROOT_NODE ": loading...");
|
||||
|
||||
root_node = _bootstrap_config_root_node_get(property);
|
||||
|
||||
if (!property_psmap_import(NULL, root_node, config, bootstrap_psmap)) {
|
||||
log_fatal(ROOT_NODE ": loading failed");
|
||||
}
|
||||
|
||||
startup_node = _bootstrap_config_startup_node_get(root_node);
|
||||
|
||||
_bootstrap_config_profile_node_verify(startup_node, profile);
|
||||
|
||||
_bootstrap_config_inheritance_resolve(startup_node, profile);
|
||||
|
||||
log_misc(ROOT_NODE "/startup/%s: loading merged result...", profile);
|
||||
|
||||
property_util_node_log(startup_node);
|
||||
|
||||
_bootstrap_config_load_bootstrap(startup_node, profile, &config->startup);
|
||||
|
||||
log_misc("Loading finished");
|
||||
}
|
139
src/main/launcher/bootstrap-config.h
Normal file
139
src/main/launcher/bootstrap-config.h
Normal file
@ -0,0 +1,139 @@
|
||||
#ifndef LAUNCHER_BOOTSTRAP_CONFIG_H
|
||||
#define LAUNCHER_BOOTSTRAP_CONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
// should be enough for a while
|
||||
#define DEFAULT_FILE_MAX 16
|
||||
|
||||
struct bootstrap_startup_config {
|
||||
struct bootstrap_default_file_config {
|
||||
struct bootstrap_default_file {
|
||||
char src[64];
|
||||
char dst[64];
|
||||
} file[DEFAULT_FILE_MAX];
|
||||
} default_file;
|
||||
|
||||
struct bootstrap_boot_config {
|
||||
char config_file[64];
|
||||
uint32_t avs_heap_size;
|
||||
uint32_t std_heap_size;
|
||||
char launch_config_file[64];
|
||||
char mount_table_selector[16];
|
||||
bool watcher_enable;
|
||||
bool timemachine_enable;
|
||||
} boot;
|
||||
|
||||
struct bootstrap_log_config {
|
||||
char level[8];
|
||||
char name[64];
|
||||
char file[64];
|
||||
uint32_t bufsz;
|
||||
uint16_t output_delay_ms;
|
||||
bool enable_console;
|
||||
bool enable_sci;
|
||||
bool enable_net;
|
||||
bool enable_file;
|
||||
bool rotate;
|
||||
bool append;
|
||||
uint16_t count;
|
||||
} log;
|
||||
|
||||
struct bootstrap_minidump_config {
|
||||
uint8_t count;
|
||||
bool continue_;
|
||||
bool log;
|
||||
uint8_t type;
|
||||
char path[64];
|
||||
uint32_t symbufsz;
|
||||
char search_path[64];
|
||||
} minidump;
|
||||
|
||||
struct bootstrap_module_config {
|
||||
char file[64];
|
||||
char load_type[64];
|
||||
struct property *app_config;
|
||||
} module;
|
||||
|
||||
struct bootstrap_dlm_config {
|
||||
char digest[16];
|
||||
uint32_t size;
|
||||
uint32_t ift_table;
|
||||
uint32_t ift_insert;
|
||||
uint32_t ift_remove;
|
||||
};
|
||||
|
||||
struct bootstrap_dlm_config dlm_ntdll;
|
||||
|
||||
struct bootstrap_shield_config {
|
||||
bool enable;
|
||||
bool verbose;
|
||||
bool use_loadlibrary;
|
||||
char logger[64];
|
||||
uint32_t sleep_min;
|
||||
uint32_t sleep_blur;
|
||||
uint32_t tick_sleep;
|
||||
uint32_t tick_error;
|
||||
uint8_t overwork_threshold;
|
||||
uint32_t overwork_delay;
|
||||
uint32_t pause_delay;
|
||||
char whitelist_file[64];
|
||||
char unlimited_key[10];
|
||||
uint16_t killer_port;
|
||||
} shield;
|
||||
|
||||
struct bootstrap_dongle_config {
|
||||
char license_cn[32];
|
||||
char account_cn[32];
|
||||
char driver_dll[16];
|
||||
bool disable_gc;
|
||||
} dongle;
|
||||
|
||||
struct bootstrap_drm_config {
|
||||
char dll[64];
|
||||
char device[64];
|
||||
char mount[64];
|
||||
char fstype[64];
|
||||
char options[64];
|
||||
} drm;
|
||||
|
||||
struct bootstrap_lte_config {
|
||||
bool enable;
|
||||
char config_file[64];
|
||||
char unlimited_key[10];
|
||||
} lte;
|
||||
|
||||
struct bootstrap_ssl_config {
|
||||
char options[64];
|
||||
} ssl;
|
||||
|
||||
struct bootstrap_esign_config {
|
||||
bool enable;
|
||||
} esign;
|
||||
|
||||
struct bootstrap_eamuse_config {
|
||||
bool enable;
|
||||
bool sync;
|
||||
bool enable_model;
|
||||
char config_file[64];
|
||||
bool updatecert_enable;
|
||||
uint32_t updatecert_interval;
|
||||
} eamuse;
|
||||
};
|
||||
|
||||
struct bootstrap_config {
|
||||
char release_code[16];
|
||||
struct bootstrap_startup_config startup;
|
||||
};
|
||||
|
||||
void bootstrap_config_init(struct bootstrap_config *config);
|
||||
|
||||
void bootstrap_config_load(
|
||||
struct property *property,
|
||||
const char *profile,
|
||||
struct bootstrap_config *config);
|
||||
|
||||
#endif /* LAUNCHER_BOOTSTRAP_CONFIG_H */
|
347
src/main/launcher/bootstrap.c
Normal file
347
src/main/launcher/bootstrap.c
Normal file
@ -0,0 +1,347 @@
|
||||
#define LOG_MODULE "bootstrap"
|
||||
|
||||
#include "core/log-bt.h"
|
||||
#include "core/log-sink-file.h"
|
||||
#include "core/log-sink-list.h"
|
||||
#include "core/log-sink-null.h"
|
||||
#include "core/log-sink-std.h"
|
||||
#include "core/log.h"
|
||||
|
||||
#include "launcher/avs-config.h"
|
||||
#include "launcher/avs.h"
|
||||
#include "launcher/bootstrap-config.h"
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
#include "launcher/eamuse-config.h"
|
||||
#include "launcher/eamuse.h"
|
||||
#include "launcher/launcher-config.h"
|
||||
#include "launcher/module.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/str.h"
|
||||
|
||||
static bool _bootstrap_log_property_configs;
|
||||
static struct module_context _bootstrap_module_context;
|
||||
|
||||
static void _bootstrap_eamuse_ea3_ident_config_inject(
|
||||
struct property_node *node, const struct ea3_ident_config *ea3_ident_config)
|
||||
{
|
||||
eamuse_config_id_softid_set(node, ea3_ident_config->softid);
|
||||
eamuse_config_id_hardid_set(node, ea3_ident_config->hardid);
|
||||
eamuse_config_id_pcbid_set(node, ea3_ident_config->pcbid);
|
||||
eamuse_config_soft_model_set(node, ea3_ident_config->model);
|
||||
eamuse_config_soft_dest_set(node, ea3_ident_config->dest);
|
||||
eamuse_config_soft_spec_set(node, ea3_ident_config->spec);
|
||||
eamuse_config_soft_rev_set(node, ea3_ident_config->rev);
|
||||
eamuse_config_soft_ext_set(node, ea3_ident_config->ext);
|
||||
}
|
||||
|
||||
static void
|
||||
_bootstrap_avs_config_force_overrides_apply(struct property_node *node)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
avs_config_mode_product_set(node, true);
|
||||
avs_config_net_raw_set(node, true);
|
||||
avs_config_net_eaudp_set(node, true);
|
||||
avs_config_sntp_ea_set(node, true);
|
||||
}
|
||||
|
||||
static void _bootstrap_avs_config_log_overrides_apply(
|
||||
struct property_node *node, const struct bootstrap_log_config *log_config)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(log_config);
|
||||
|
||||
avs_config_log_level_set(node, log_config->level);
|
||||
avs_config_log_name_set(node, log_config->name);
|
||||
avs_config_log_file_set(node, log_config->file);
|
||||
avs_config_log_buffer_size_set(node, log_config->bufsz);
|
||||
avs_config_log_output_delay_set(node, log_config->output_delay_ms);
|
||||
avs_config_log_enable_console_set(node, log_config->enable_console);
|
||||
avs_config_log_enable_sci_set(node, log_config->enable_sci);
|
||||
avs_config_log_enable_net_set(node, log_config->enable_net);
|
||||
avs_config_log_enable_file_set(node, log_config->enable_file);
|
||||
avs_config_log_rotate_set(node, log_config->rotate);
|
||||
avs_config_log_append_set(node, log_config->append);
|
||||
avs_config_log_count_set(node, log_config->count);
|
||||
}
|
||||
|
||||
static enum core_log_bt_log_level _bootstrap_log_map_level(const char *level)
|
||||
{
|
||||
if (str_eq(level, "fatal")) {
|
||||
return CORE_LOG_BT_LOG_LEVEL_FATAL;
|
||||
} else if (str_eq(level, "warning")) {
|
||||
return CORE_LOG_BT_LOG_LEVEL_WARNING;
|
||||
} else if (str_eq(level, "info")) {
|
||||
return CORE_LOG_BT_LOG_LEVEL_INFO;
|
||||
} else if (str_eq(level, "misc")) {
|
||||
return CORE_LOG_BT_LOG_LEVEL_MISC;
|
||||
} else if (str_eq(level, "all")) {
|
||||
return CORE_LOG_BT_LOG_LEVEL_MISC;
|
||||
} else if (str_eq(level, "disable")) {
|
||||
return CORE_LOG_BT_LOG_LEVEL_OFF;
|
||||
} else if (str_eq(level, "default")) {
|
||||
return CORE_LOG_BT_LOG_LEVEL_WARNING;
|
||||
} else {
|
||||
log_fatal("Unknown log level string %s", level);
|
||||
}
|
||||
}
|
||||
|
||||
void bootstrap_init(bool log_property_configs)
|
||||
{
|
||||
log_info("init");
|
||||
|
||||
_bootstrap_log_property_configs = log_property_configs;
|
||||
|
||||
log_misc("init done");
|
||||
}
|
||||
|
||||
void bootstrap_log_init(const struct bootstrap_log_config *config)
|
||||
{
|
||||
struct core_log_sink sinks[2];
|
||||
struct core_log_sink sink_composed;
|
||||
enum core_log_bt_log_level level;
|
||||
|
||||
log_assert(config);
|
||||
|
||||
log_info("log init");
|
||||
|
||||
// Shutdown old setup
|
||||
core_log_bt_fini();
|
||||
|
||||
if (config->enable_file && strlen(config->file) > 0 &&
|
||||
config->enable_console) {
|
||||
core_log_sink_std_out_open(true, &sinks[0]);
|
||||
core_log_sink_file_open(
|
||||
config->file,
|
||||
config->append,
|
||||
config->rotate,
|
||||
config->count,
|
||||
&sinks[1]);
|
||||
core_log_sink_list_open(sinks, 2, &sink_composed);
|
||||
} else if (config->enable_file && strlen(config->file) > 0) {
|
||||
core_log_sink_file_open(
|
||||
config->file,
|
||||
config->append,
|
||||
config->rotate,
|
||||
config->count,
|
||||
&sink_composed);
|
||||
} else if (config->enable_console) {
|
||||
core_log_sink_std_out_open(true, &sink_composed);
|
||||
} else {
|
||||
core_log_sink_null_open(&sink_composed);
|
||||
}
|
||||
|
||||
core_log_bt_init(&sink_composed);
|
||||
|
||||
level = _bootstrap_log_map_level(config->level);
|
||||
core_log_bt_level_set(level);
|
||||
|
||||
log_misc("log init done");
|
||||
}
|
||||
|
||||
void bootstrap_default_files_create(
|
||||
const struct bootstrap_default_file_config *config)
|
||||
{
|
||||
log_assert(config);
|
||||
|
||||
log_info("default files create");
|
||||
|
||||
for (int i = 0; i < DEFAULT_FILE_MAX; i++) {
|
||||
if (strlen(config->file[i].src) > 0 &&
|
||||
strlen(config->file[i].dst) > 0) {
|
||||
avs_fs_file_copy(config->file[i].src, config->file[i].dst);
|
||||
}
|
||||
}
|
||||
|
||||
log_misc("default files create done");
|
||||
}
|
||||
|
||||
void bootstrap_avs_init(
|
||||
const struct bootstrap_boot_config *config,
|
||||
const struct bootstrap_log_config *log_config,
|
||||
struct property *override_property)
|
||||
{
|
||||
struct property *file_property;
|
||||
struct property *merged_property;
|
||||
struct property_node *root_node;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(log_config);
|
||||
log_assert(override_property);
|
||||
|
||||
log_info("avs init");
|
||||
|
||||
file_property = avs_config_load(config->config_file);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("avs-config from file: %s", config->config_file);
|
||||
property_util_log(file_property);
|
||||
}
|
||||
|
||||
merged_property =
|
||||
avs_config_property_merge(file_property, override_property);
|
||||
|
||||
property_util_free(file_property);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("avs-config merged with overrides");
|
||||
property_util_log(merged_property);
|
||||
}
|
||||
|
||||
root_node = avs_config_root_get(merged_property);
|
||||
|
||||
_bootstrap_avs_config_force_overrides_apply(root_node);
|
||||
_bootstrap_avs_config_log_overrides_apply(root_node, log_config);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("avs-config final");
|
||||
property_util_log(merged_property);
|
||||
}
|
||||
|
||||
avs_fs_assert_root_device_exists(root_node);
|
||||
|
||||
log_misc("Creating AVS file system directories...");
|
||||
|
||||
avs_fs_mountpoints_fs_dirs_create(root_node);
|
||||
|
||||
avs_init(root_node, config->avs_heap_size, config->std_heap_size);
|
||||
|
||||
property_util_free(merged_property);
|
||||
|
||||
log_misc("avs init done");
|
||||
}
|
||||
|
||||
void bootstrap_eamuse_init(
|
||||
const struct bootstrap_eamuse_config *config,
|
||||
const struct ea3_ident_config *ea3_ident_config,
|
||||
struct property *override_property)
|
||||
{
|
||||
struct property *file_property;
|
||||
struct property *merged_property;
|
||||
struct property_node *root_node;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(ea3_ident_config);
|
||||
log_assert(override_property);
|
||||
|
||||
log_info("eamuse init");
|
||||
|
||||
if (config->enable) {
|
||||
file_property = eamuse_config_avs_load(config->config_file);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("eamuse-config from file: %s", config->config_file);
|
||||
property_util_log(file_property);
|
||||
}
|
||||
|
||||
merged_property = property_util_merge(file_property, override_property);
|
||||
|
||||
property_util_free(file_property);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("eamuse-config merged with overrides");
|
||||
property_util_log(merged_property);
|
||||
}
|
||||
|
||||
root_node = eamuse_config_root_get(merged_property);
|
||||
|
||||
_bootstrap_eamuse_ea3_ident_config_inject(root_node, ea3_ident_config);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("eamuse-config final");
|
||||
property_util_log(merged_property);
|
||||
}
|
||||
|
||||
eamuse_init(root_node);
|
||||
|
||||
property_util_free(merged_property);
|
||||
} else {
|
||||
log_warning("Eamuse disabled");
|
||||
}
|
||||
|
||||
log_misc("eamuse init done");
|
||||
}
|
||||
|
||||
void bootstrap_module_init(
|
||||
const struct bootstrap_module_config *module_config,
|
||||
const struct array *iat_hook_dlls)
|
||||
{
|
||||
log_assert(module_config);
|
||||
log_assert(iat_hook_dlls);
|
||||
|
||||
log_info("module init");
|
||||
|
||||
if (iat_hook_dlls->nitems > 0) {
|
||||
log_info(
|
||||
"Load game DLL with IAT hooks (%d): %s",
|
||||
(uint32_t) iat_hook_dlls->nitems,
|
||||
module_config->file);
|
||||
|
||||
module_with_iat_hooks_init(
|
||||
&_bootstrap_module_context, module_config->file, iat_hook_dlls);
|
||||
} else {
|
||||
log_info("Load game DLL: %s", module_config->file);
|
||||
|
||||
module_init(&_bootstrap_module_context, module_config->file);
|
||||
}
|
||||
|
||||
log_misc("module init done");
|
||||
}
|
||||
|
||||
void bootstrap_module_game_init(
|
||||
const struct bootstrap_module_config *module_config,
|
||||
struct ea3_ident_config *ea3_ident_config)
|
||||
{
|
||||
struct property_node *node;
|
||||
|
||||
log_assert(module_config);
|
||||
log_assert(ea3_ident_config);
|
||||
|
||||
log_info("module game init");
|
||||
|
||||
node = property_search(module_config->app_config, NULL, "/param");
|
||||
|
||||
if (!node) {
|
||||
log_fatal("Missing param node on app-config");
|
||||
}
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("app-config");
|
||||
property_util_node_log(node);
|
||||
}
|
||||
|
||||
module_init_invoke(&_bootstrap_module_context, ea3_ident_config, node);
|
||||
|
||||
log_misc("module game init done");
|
||||
}
|
||||
|
||||
void bootstrap_module_game_run()
|
||||
{
|
||||
log_info("module game run");
|
||||
|
||||
module_main_invoke(&_bootstrap_module_context);
|
||||
}
|
||||
|
||||
void bootstrap_module_game_fini()
|
||||
{
|
||||
log_info("module game fini");
|
||||
|
||||
module_fini(&_bootstrap_module_context);
|
||||
}
|
||||
|
||||
void bootstrap_avs_fini()
|
||||
{
|
||||
log_info("avs fini");
|
||||
|
||||
avs_fini();
|
||||
}
|
||||
|
||||
void bootstrap_eamuse_fini(const struct bootstrap_eamuse_config *config)
|
||||
{
|
||||
log_info("eamuse fini");
|
||||
|
||||
if (config->enable) {
|
||||
eamuse_fini();
|
||||
}
|
||||
}
|
32
src/main/launcher/bootstrap.h
Normal file
32
src/main/launcher/bootstrap.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef LAUNCHER_BOOTSTRAP_H
|
||||
#define LAUNCHER_BOOTSTRAP_H
|
||||
|
||||
#include "launcher/bootstrap-config.h"
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
|
||||
#include "util/array.h"
|
||||
|
||||
void bootstrap_init(bool log_property_configs);
|
||||
void bootstrap_log_init(const struct bootstrap_log_config *config);
|
||||
void bootstrap_default_files_create(
|
||||
const struct bootstrap_default_file_config *config);
|
||||
void bootstrap_avs_init(
|
||||
const struct bootstrap_boot_config *config,
|
||||
const struct bootstrap_log_config *log_config,
|
||||
struct property *override_property);
|
||||
void bootstrap_eamuse_init(
|
||||
const struct bootstrap_eamuse_config *config,
|
||||
const struct ea3_ident_config *ea3_ident_config,
|
||||
struct property *override_property);
|
||||
void bootstrap_module_init(
|
||||
const struct bootstrap_module_config *module_config,
|
||||
const struct array *iat_hook_dlls);
|
||||
void bootstrap_module_game_init(
|
||||
const struct bootstrap_module_config *module_config,
|
||||
struct ea3_ident_config *ea3_ident_config);
|
||||
void bootstrap_module_game_run();
|
||||
void bootstrap_module_game_fini();
|
||||
void bootstrap_avs_fini();
|
||||
void bootstrap_eamuse_fini(const struct bootstrap_eamuse_config *config);
|
||||
|
||||
#endif
|
33
src/main/launcher/debug.c
Normal file
33
src/main/launcher/debug.c
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
#define LOG_MODULE "debug"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "launcher/debug.h"
|
||||
|
||||
void debug_remote_debugger_trap()
|
||||
{
|
||||
BOOL res;
|
||||
|
||||
log_info("Waiting until debugger attaches to remote process...");
|
||||
|
||||
while (true) {
|
||||
res = FALSE;
|
||||
|
||||
if (!CheckRemoteDebuggerPresent(GetCurrentProcess(), &res)) {
|
||||
log_fatal(
|
||||
"CheckRemoteDebuggerPresent failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
if (res) {
|
||||
log_info("Debugger attached, resuming");
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
6
src/main/launcher/debug.h
Normal file
6
src/main/launcher/debug.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef LAUNCHER_DEBUG_H
|
||||
#define LAUNCHER_DEBUG_H
|
||||
|
||||
void debug_remote_debugger_trap();
|
||||
|
||||
#endif
|
@ -1,190 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/ea3-config.h"
|
||||
#include "launcher/module.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
PSMAP_BEGIN(ea3_ident_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, softid, "/ea3/id/softid", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, hardid, "/ea3/id/hardid", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, pcbid, "/ea3/id/pcbid", "")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, model, "/ea3/soft/model")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, dest, "/ea3/soft/dest")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, spec, "/ea3/soft/spec")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, rev, "/ea3/soft/rev")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, ext, "/ea3/soft/ext")
|
||||
PSMAP_END
|
||||
|
||||
void ea3_ident_init(struct ea3_ident *ident)
|
||||
{
|
||||
memset(ident, 0, sizeof(*ident));
|
||||
}
|
||||
|
||||
bool ea3_ident_from_property(
|
||||
struct ea3_ident *ident, struct property *ea3_config)
|
||||
{
|
||||
return property_psmap_import(ea3_config, NULL, ident, ea3_ident_psmap);
|
||||
}
|
||||
|
||||
void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident)
|
||||
{
|
||||
struct avs_net_interface netif;
|
||||
int result;
|
||||
|
||||
result = avs_net_ctrl(1, &netif, sizeof(netif));
|
||||
|
||||
if (result < 0) {
|
||||
log_fatal(
|
||||
"avs_net_ctrl call to get MAC address returned error: %d", result);
|
||||
}
|
||||
|
||||
ident->hardid[0] = '0';
|
||||
ident->hardid[1] = '1';
|
||||
ident->hardid[2] = '0';
|
||||
ident->hardid[3] = '0';
|
||||
|
||||
hex_encode_uc(
|
||||
netif.mac_addr,
|
||||
sizeof(netif.mac_addr),
|
||||
ident->hardid + 4,
|
||||
sizeof(ident->hardid) - 4);
|
||||
}
|
||||
|
||||
bool ea3_ident_invoke_module_init(
|
||||
struct ea3_ident *ident,
|
||||
const struct module_context *module,
|
||||
struct property_node *app_config)
|
||||
{
|
||||
char sidcode_short[17];
|
||||
char sidcode_long[21];
|
||||
char security_code[9];
|
||||
bool ok;
|
||||
|
||||
/* Set up security env vars */
|
||||
|
||||
str_format(
|
||||
security_code,
|
||||
lengthof(security_code),
|
||||
"G*%s%s%s%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev);
|
||||
|
||||
std_setenv("/env/boot/version", "0.0.0");
|
||||
std_setenv("/env/profile/security_code", security_code);
|
||||
std_setenv("/env/profile/system_id", ident->pcbid);
|
||||
std_setenv("/env/profile/account_id", ident->pcbid);
|
||||
std_setenv("/env/profile/license_id", ident->softid);
|
||||
std_setenv("/env/profile/software_id", ident->softid);
|
||||
std_setenv("/env/profile/hardware_id", ident->hardid);
|
||||
|
||||
/* Set up the short sidcode string, let dll_entry_init mangle it */
|
||||
|
||||
str_format(
|
||||
sidcode_short,
|
||||
lengthof(sidcode_short),
|
||||
"%s%s%s%s%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev,
|
||||
ident->ext);
|
||||
|
||||
/* Set up long-form sidcode env var */
|
||||
|
||||
str_format(
|
||||
sidcode_long,
|
||||
lengthof(sidcode_long),
|
||||
"%s:%s:%s:%s:%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev,
|
||||
ident->ext);
|
||||
|
||||
/* Set this up beforehand, as certain games require it in dll_entry_init */
|
||||
|
||||
std_setenv("/env/profile/soft_id_code", sidcode_long);
|
||||
|
||||
ok = module_context_invoke_init(module, sidcode_short, app_config);
|
||||
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Back-propagate sidcode, as some games modify it during init */
|
||||
|
||||
memcpy(ident->model, sidcode_short + 0, sizeof(ident->model) - 1);
|
||||
ident->dest[0] = sidcode_short[3];
|
||||
ident->spec[0] = sidcode_short[4];
|
||||
ident->rev[0] = sidcode_short[5];
|
||||
memcpy(ident->ext, sidcode_short + 6, sizeof(ident->ext));
|
||||
|
||||
/* Set up long-form sidcode env var again */
|
||||
|
||||
str_format(
|
||||
sidcode_long,
|
||||
lengthof(sidcode_long),
|
||||
"%s:%s:%s:%s:%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev,
|
||||
ident->ext);
|
||||
|
||||
std_setenv("/env/profile/soft_id_code", sidcode_long);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ea3_ident_to_property(
|
||||
const struct ea3_ident *ident, struct property *ea3_config)
|
||||
{
|
||||
struct property_node *node;
|
||||
int i;
|
||||
|
||||
for (i = 0; ea3_ident_psmap[i].type != 0xFF; i++) {
|
||||
node = property_search(ea3_config, 0, ea3_ident_psmap[i].path);
|
||||
|
||||
if (node != NULL) {
|
||||
property_node_remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
property_psmap_export(ea3_config, NULL, ident, ea3_ident_psmap);
|
||||
}
|
||||
|
||||
void ea3_ident_replace_property_bool(
|
||||
struct property_node *node, const char *name, uint8_t val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
tmp = property_search(NULL, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
property_node_create(NULL, node, PROPERTY_TYPE_BOOL, name, val);
|
||||
}
|
||||
|
||||
void ea3_ident_replace_property_str(
|
||||
struct property_node *node, const char *name, const char *val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
tmp = property_search(NULL, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(NULL, node, PROPERTY_TYPE_STR, name, val);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#ifndef LAUNCHER_EA3_CONFIG_H
|
||||
#define LAUNCHER_EA3_CONFIG_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/module.h"
|
||||
|
||||
/* N.B. even though this might look like a Konami ABI, this is purely an
|
||||
internal data structure. */
|
||||
|
||||
struct ea3_ident {
|
||||
/* psmapped structure offset can't be zero for some stupid reason */
|
||||
|
||||
uint32_t dummy;
|
||||
|
||||
/* Initialized from ea3-config.xml, then fed back from sidcode_short */
|
||||
|
||||
char model[4];
|
||||
char dest[4];
|
||||
char spec[4];
|
||||
char rev[4];
|
||||
char ext[11];
|
||||
|
||||
/* Initialized from ea3-config.xml (hardware_id defaults to MAC addr) */
|
||||
|
||||
char softid[24];
|
||||
char hardid[24];
|
||||
char pcbid[24];
|
||||
};
|
||||
|
||||
void ea3_ident_init(struct ea3_ident *ident);
|
||||
bool ea3_ident_from_property(
|
||||
struct ea3_ident *ident, struct property *ea3_config);
|
||||
void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident);
|
||||
bool ea3_ident_invoke_module_init(
|
||||
struct ea3_ident *ident,
|
||||
const struct module_context *module,
|
||||
struct property_node *app_config);
|
||||
void ea3_ident_to_property(
|
||||
const struct ea3_ident *ident, struct property *ea3_config);
|
||||
void ea3_ident_replace_property_bool(
|
||||
struct property_node *node, const char *name, uint8_t val);
|
||||
void ea3_ident_replace_property_str(
|
||||
struct property_node *node, const char *name, const char *val);
|
||||
|
||||
#endif
|
104
src/main/launcher/ea3-ident-config.c
Normal file
104
src/main/launcher/ea3-ident-config.c
Normal file
@ -0,0 +1,104 @@
|
||||
#define LOG_MODULE "ea3-ident-config"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define ROOT_NODE "/ea3_conf"
|
||||
|
||||
PSMAP_BEGIN(ea3_ident_config_psmap)
|
||||
PSMAP_OPTIONAL(
|
||||
PSMAP_TYPE_STR, struct ea3_ident_config, softid, "/id/softid", "")
|
||||
PSMAP_OPTIONAL(
|
||||
PSMAP_TYPE_STR, struct ea3_ident_config, hardid, "/id/hardid", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident_config, pcbid, "/id/pcbid", "")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident_config, model, "/soft/model")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident_config, dest, "/soft/dest")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident_config, spec, "/soft/spec")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident_config, rev, "/soft/rev")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident_config, ext, "/soft/ext")
|
||||
PSMAP_END
|
||||
|
||||
void ea3_ident_config_init(struct ea3_ident_config *config)
|
||||
{
|
||||
memset(config, 0, sizeof(*config));
|
||||
}
|
||||
|
||||
void ea3_ident_config_from_file_load(
|
||||
const char *path, struct ea3_ident_config *config)
|
||||
{
|
||||
struct property *property;
|
||||
|
||||
log_assert(path);
|
||||
log_assert(config);
|
||||
|
||||
log_info("Loading from file path: %s", path);
|
||||
|
||||
property = property_util_load(path);
|
||||
|
||||
ea3_ident_config_load(property, config);
|
||||
|
||||
property_util_free(property);
|
||||
}
|
||||
|
||||
void ea3_ident_config_load(
|
||||
struct property *property, struct ea3_ident_config *config)
|
||||
{
|
||||
struct property_node *node;
|
||||
|
||||
log_assert(property);
|
||||
log_assert(config);
|
||||
|
||||
node = property_search(property, NULL, ROOT_NODE);
|
||||
|
||||
if (node == NULL) {
|
||||
log_fatal("Root node '" ROOT_NODE "' missing");
|
||||
}
|
||||
|
||||
if (!property_psmap_import(
|
||||
property, node, config, ea3_ident_config_psmap)) {
|
||||
log_fatal("Error reading config file");
|
||||
}
|
||||
}
|
||||
|
||||
bool ea3_ident_config_hardid_is_defined(struct ea3_ident_config *config)
|
||||
{
|
||||
log_assert(config);
|
||||
|
||||
return strlen(config->hardid) > 0;
|
||||
}
|
||||
|
||||
void ea3_ident_config_hardid_from_ethernet_set(struct ea3_ident_config *config)
|
||||
{
|
||||
struct avs_net_interface netif;
|
||||
int result;
|
||||
|
||||
log_assert(config);
|
||||
|
||||
result = avs_net_ctrl(1, &netif, sizeof(netif));
|
||||
|
||||
if (result < 0) {
|
||||
log_fatal(
|
||||
"avs_net_ctrl call to get MAC address returned error: %d", result);
|
||||
}
|
||||
|
||||
config->hardid[0] = '0';
|
||||
config->hardid[1] = '1';
|
||||
config->hardid[2] = '0';
|
||||
config->hardid[3] = '0';
|
||||
|
||||
hex_encode_uc(
|
||||
netif.mac_addr,
|
||||
sizeof(netif.mac_addr),
|
||||
config->hardid + 4,
|
||||
sizeof(config->hardid) - 4);
|
||||
}
|
37
src/main/launcher/ea3-ident-config.h
Normal file
37
src/main/launcher/ea3-ident-config.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef LAUNCHER_EA3_IDENT_CONFIG_H
|
||||
#define LAUNCHER_EA3_IDENT_CONFIG_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
/* N.B. even though this might look like a Konami ABI, this is purely an
|
||||
internal data structure. */
|
||||
|
||||
struct ea3_ident_config {
|
||||
/* psmapped structure offset can't be zero for some stupid reason */
|
||||
|
||||
uint32_t dummy;
|
||||
|
||||
/* Initialized from ea3-config.xml, then fed back from sidcode_short */
|
||||
|
||||
char model[4];
|
||||
char dest[4];
|
||||
char spec[4];
|
||||
char rev[4];
|
||||
char ext[11];
|
||||
|
||||
/* Initialized from ea3-config.xml (hardware_id defaults to MAC addr) */
|
||||
|
||||
char softid[24];
|
||||
char hardid[24];
|
||||
char pcbid[24];
|
||||
};
|
||||
|
||||
void ea3_ident_config_init(struct ea3_ident_config *config);
|
||||
void ea3_ident_config_from_file_load(
|
||||
const char *path, struct ea3_ident_config *config);
|
||||
void ea3_ident_config_load(
|
||||
struct property *property, struct ea3_ident_config *config);
|
||||
bool ea3_ident_config_hardid_is_defined(struct ea3_ident_config *config);
|
||||
void ea3_ident_config_hardid_from_ethernet_set(struct ea3_ident_config *config);
|
||||
|
||||
#endif
|
125
src/main/launcher/eamuse-config.c
Normal file
125
src/main/launcher/eamuse-config.c
Normal file
@ -0,0 +1,125 @@
|
||||
#define LOG_MODULE "eamuse-config"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
#include "launcher/eamuse-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#define EAMUSE_CONFIG_ROOT_NODE "/ea3"
|
||||
|
||||
struct property *eamuse_config_avs_load(const char *path)
|
||||
{
|
||||
struct property *property;
|
||||
|
||||
log_assert(path);
|
||||
|
||||
log_misc("Loading from avs path: %s", path);
|
||||
|
||||
property = property_util_avs_fs_load(path);
|
||||
|
||||
// Check if root node exists, call already errors if not
|
||||
eamuse_config_root_get(property);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
struct property_node *eamuse_config_root_get(struct property *property)
|
||||
{
|
||||
struct property_node *node;
|
||||
|
||||
log_assert(property);
|
||||
|
||||
node = property_search(property, 0, EAMUSE_CONFIG_ROOT_NODE);
|
||||
|
||||
if (node == NULL) {
|
||||
log_fatal("Root node " EAMUSE_CONFIG_ROOT_NODE
|
||||
" in eamuse config missing");
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void eamuse_config_id_softid_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "id/softid", value);
|
||||
}
|
||||
|
||||
void eamuse_config_id_hardid_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "id/hardid", value);
|
||||
}
|
||||
|
||||
void eamuse_config_id_pcbid_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "id/pcbid", value);
|
||||
}
|
||||
|
||||
void eamuse_config_soft_model_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "soft/model", value);
|
||||
}
|
||||
|
||||
void eamuse_config_soft_dest_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "soft/dest", value);
|
||||
}
|
||||
|
||||
void eamuse_config_soft_spec_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "soft/spec", value);
|
||||
}
|
||||
|
||||
void eamuse_config_soft_rev_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "soft/rev", value);
|
||||
}
|
||||
|
||||
void eamuse_config_soft_ext_set(struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "soft/ext", value);
|
||||
}
|
||||
|
||||
void eamuse_config_network_url_slash_set(struct property_node *node, bool value)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
property_util_node_bool_replace(NULL, node, "network/url_slash", value);
|
||||
}
|
||||
|
||||
void eamuse_config_network_service_url_set(
|
||||
struct property_node *node, const char *value)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(value);
|
||||
|
||||
property_util_node_str_replace(NULL, node, "network/services", value);
|
||||
}
|
23
src/main/launcher/eamuse-config.h
Normal file
23
src/main/launcher/eamuse-config.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef LAUNCHER_EAMUSE_CONFIG_H
|
||||
#define LAUNCHER_EAMUSE_CONFIG_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
struct property *eamuse_config_avs_load(const char *path);
|
||||
struct property_node *eamuse_config_root_get(struct property *property);
|
||||
|
||||
void eamuse_config_id_softid_set(struct property_node *node, const char *value);
|
||||
void eamuse_config_id_hardid_set(struct property_node *node, const char *value);
|
||||
void eamuse_config_id_pcbid_set(struct property_node *node, const char *value);
|
||||
void eamuse_config_soft_model_set(
|
||||
struct property_node *node, const char *value);
|
||||
void eamuse_config_soft_dest_set(struct property_node *node, const char *value);
|
||||
void eamuse_config_soft_spec_set(struct property_node *node, const char *value);
|
||||
void eamuse_config_soft_rev_set(struct property_node *node, const char *value);
|
||||
void eamuse_config_soft_ext_set(struct property_node *node, const char *value);
|
||||
void eamuse_config_network_url_slash_set(
|
||||
struct property_node *node, bool value);
|
||||
void eamuse_config_network_service_url_set(
|
||||
struct property_node *node, const char *value);
|
||||
|
||||
#endif
|
25
src/main/launcher/eamuse.c
Normal file
25
src/main/launcher/eamuse.c
Normal file
@ -0,0 +1,25 @@
|
||||
#define LOG_MODULE "eamuse"
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs-ea3.h"
|
||||
|
||||
void eamuse_init(struct property_node *node)
|
||||
{
|
||||
log_assert(node);
|
||||
|
||||
log_info("init");
|
||||
|
||||
ea3_boot(node);
|
||||
|
||||
log_misc("init done");
|
||||
}
|
||||
|
||||
void eamuse_fini()
|
||||
{
|
||||
log_info("fini");
|
||||
|
||||
ea3_shutdown();
|
||||
|
||||
log_misc("fini done");
|
||||
}
|
9
src/main/launcher/eamuse.h
Normal file
9
src/main/launcher/eamuse.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef LAUNCHER_EAMUSE_H
|
||||
#define LAUNCHER_EAMUSE_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
void eamuse_init(struct property_node *node);
|
||||
void eamuse_fini();
|
||||
|
||||
#endif
|
34
src/main/launcher/hook.c
Normal file
34
src/main/launcher/hook.c
Normal file
@ -0,0 +1,34 @@
|
||||
#define LOG_MODULE "hook"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "launcher/hook.h"
|
||||
|
||||
void hook_load_dll(const char *path)
|
||||
{
|
||||
log_assert(path);
|
||||
|
||||
log_info("Load hook dll: %s", path);
|
||||
|
||||
if (LoadLibraryA(path) == NULL) {
|
||||
LPSTR buffer;
|
||||
|
||||
FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR) &buffer,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
log_fatal("%s: Failed to load hook DLL: %s", path, buffer);
|
||||
|
||||
LocalFree(buffer);
|
||||
}
|
||||
|
||||
log_misc("Load hook dll done");
|
||||
}
|
6
src/main/launcher/hook.h
Normal file
6
src/main/launcher/hook.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef LAUNCHER_HOOK_H
|
||||
#define LAUNCHER_HOOK_H
|
||||
|
||||
void hook_load_dll(const char *path);
|
||||
|
||||
#endif
|
371
src/main/launcher/launcher-config.c
Normal file
371
src/main/launcher/launcher-config.c
Normal file
@ -0,0 +1,371 @@
|
||||
#define LOG_MODULE "launcher-config"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/launcher-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
// clang-format off
|
||||
PSMAP_BEGIN(launcher_debug_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct launcher_debug_config, remote_debugger,
|
||||
"debug/remote_debugger", false)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct launcher_debug_config, log_property_configs,
|
||||
"debug/log_property_configs", false)
|
||||
PSMAP_END
|
||||
// clang-format on
|
||||
|
||||
#define ROOT_NODE "/launcher"
|
||||
#define MAX_LAYER_CONFIG_NODES 8
|
||||
|
||||
#define NODE_MISSING_FATAL(subnode) \
|
||||
log_fatal("%s/%s: Node missing", ROOT_NODE, subnode);
|
||||
#define NODE_LOADING_FATAL(subnode) \
|
||||
log_fatal("%s/%s: Node loading", ROOT_NODE, subnode);
|
||||
|
||||
static struct property *
|
||||
_launcher_config_layered_config_nodes_load(struct property_node *node)
|
||||
{
|
||||
char kind[64];
|
||||
char file[MAX_PATH];
|
||||
int res;
|
||||
int cnt;
|
||||
|
||||
struct property_node *cur;
|
||||
struct property *config_property[MAX_LAYER_CONFIG_NODES];
|
||||
struct property *merged_property;
|
||||
|
||||
log_assert(node);
|
||||
|
||||
cnt = 0;
|
||||
cur = property_search(NULL, node, "config");
|
||||
|
||||
while (cur) {
|
||||
if (cnt >= MAX_LAYER_CONFIG_NODES) {
|
||||
log_fatal(
|
||||
"Exceeding max supported config nodes for layering, max is %d",
|
||||
MAX_LAYER_CONFIG_NODES);
|
||||
}
|
||||
|
||||
res = property_node_refer(
|
||||
NULL, cur, "kind@", PROPERTY_TYPE_ATTR, kind, sizeof(kind));
|
||||
|
||||
if (res < 0) {
|
||||
log_fatal("Failed reading 'kind' attribute value of config node");
|
||||
}
|
||||
|
||||
if (!strcmp(kind, "file")) {
|
||||
property_node_read(cur, PROPERTY_TYPE_STR, file, sizeof(file));
|
||||
|
||||
config_property[cnt] = property_util_load(file);
|
||||
} else if (!strcmp(kind, "inline")) {
|
||||
// The nested child is the actual root of the inline, not the outer
|
||||
// <config> node
|
||||
cur = property_node_traversal(cur, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
config_property[cnt] = property_util_node_extract(cur);
|
||||
} else {
|
||||
log_fatal(
|
||||
"Unsupported 'kind' attribute value '%s' of config node", kind);
|
||||
}
|
||||
|
||||
cnt++;
|
||||
cur = property_node_traversal(cur, TRAVERSE_NEXT_SEARCH_RESULT);
|
||||
}
|
||||
|
||||
if (cnt == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
merged_property = property_util_many_merge(config_property, cnt);
|
||||
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
property_util_free(config_property[i]);
|
||||
}
|
||||
|
||||
return merged_property;
|
||||
}
|
||||
|
||||
static void _launcher_config_hook_dlls_parse(
|
||||
struct property_node *node,
|
||||
const char *node_path,
|
||||
char dlls[LAUNCHER_CONFIG_MAX_HOOK_DLL][MAX_PATH])
|
||||
{
|
||||
int cnt;
|
||||
struct property_node *cur;
|
||||
|
||||
cnt = 0;
|
||||
cur = property_search(NULL, node, node_path);
|
||||
|
||||
while (cur) {
|
||||
if (cnt >= LAUNCHER_CONFIG_MAX_HOOK_DLL) {
|
||||
log_warning(
|
||||
"Currently not supporting more than %d dlls, skipping "
|
||||
"remaining",
|
||||
cnt);
|
||||
break;
|
||||
}
|
||||
|
||||
property_node_read(cur, PROPERTY_TYPE_STR, dlls[cnt], MAX_PATH);
|
||||
|
||||
cnt++;
|
||||
cur = property_node_traversal(cur, TRAVERSE_NEXT_SEARCH_RESULT);
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_config_bootstrap_load(
|
||||
struct property_node *node, struct launcher_bootstrap_config *config)
|
||||
{
|
||||
int res;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(config);
|
||||
|
||||
res = property_node_refer(
|
||||
NULL,
|
||||
node,
|
||||
"selector",
|
||||
PROPERTY_TYPE_STR,
|
||||
config->selector,
|
||||
sizeof(config->selector));
|
||||
|
||||
if (res < 0) {
|
||||
NODE_MISSING_FATAL("bootstrap/selector");
|
||||
}
|
||||
|
||||
config->property = _launcher_config_layered_config_nodes_load(node);
|
||||
|
||||
if (config->property == NULL) {
|
||||
NODE_MISSING_FATAL("bootstrap/config");
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_config_hook_load(
|
||||
struct property_node *node, struct launcher_hook_config *config)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(config);
|
||||
|
||||
_launcher_config_hook_dlls_parse(node, "hook_dlls/dll", config->hook_dlls);
|
||||
_launcher_config_hook_dlls_parse(
|
||||
node, "before_hook_dlls/dll", config->before_hook_dlls);
|
||||
_launcher_config_hook_dlls_parse(
|
||||
node, "iat_hook_dlls/dll", config->iat_hook_dlls);
|
||||
}
|
||||
|
||||
static void _launcher_config_debug_load(
|
||||
struct property_node *node, struct launcher_debug_config *config)
|
||||
{
|
||||
log_assert(node);
|
||||
log_assert(config);
|
||||
|
||||
if (!property_psmap_import(NULL, node, config, launcher_debug_psmap)) {
|
||||
NODE_LOADING_FATAL("debug");
|
||||
}
|
||||
}
|
||||
|
||||
void launcher_config_init(struct launcher_config *config)
|
||||
{
|
||||
log_assert(config);
|
||||
|
||||
memset(config->bootstrap.selector, 0, sizeof(config->bootstrap.selector));
|
||||
config->bootstrap.property = NULL;
|
||||
|
||||
config->avs.property = NULL;
|
||||
|
||||
config->ea3_ident.property = NULL;
|
||||
|
||||
config->eamuse.property = NULL;
|
||||
|
||||
memset(config->hook.hook_dlls, 0, sizeof(config->hook.hook_dlls));
|
||||
memset(
|
||||
config->hook.before_hook_dlls,
|
||||
0,
|
||||
sizeof(config->hook.before_hook_dlls));
|
||||
memset(config->hook.iat_hook_dlls, 0, sizeof(config->hook.iat_hook_dlls));
|
||||
|
||||
config->debug.remote_debugger = false;
|
||||
config->debug.log_property_configs = false;
|
||||
}
|
||||
|
||||
void launcher_config_load(
|
||||
struct property *property, struct launcher_config *config)
|
||||
{
|
||||
struct property_node *root_node;
|
||||
struct property_node *node;
|
||||
avs_error error;
|
||||
|
||||
log_assert(property);
|
||||
log_assert(config);
|
||||
|
||||
root_node = property_search(property, NULL, ROOT_NODE);
|
||||
|
||||
if (root_node == NULL) {
|
||||
NODE_MISSING_FATAL("");
|
||||
}
|
||||
|
||||
error = property_node_refer(
|
||||
NULL,
|
||||
root_node,
|
||||
"version@",
|
||||
PROPERTY_TYPE_ATTR,
|
||||
&config->version,
|
||||
sizeof(uint32_t));
|
||||
|
||||
if (AVS_IS_ERROR(error)) {
|
||||
log_fatal("Missing version attribute on root node");
|
||||
}
|
||||
|
||||
// if (config->version != 1) {
|
||||
// log_fatal("Unsupported version of launcher configuration: %d",
|
||||
// config->version);
|
||||
// }
|
||||
|
||||
node = property_search(NULL, root_node, "bootstrap");
|
||||
|
||||
if (node == NULL) {
|
||||
NODE_MISSING_FATAL("bootstrap");
|
||||
}
|
||||
|
||||
_launcher_config_bootstrap_load(node, &config->bootstrap);
|
||||
|
||||
node = property_search(NULL, root_node, "avs");
|
||||
|
||||
if (node) {
|
||||
config->avs.property = _launcher_config_layered_config_nodes_load(node);
|
||||
}
|
||||
|
||||
node = property_search(NULL, root_node, "ea3_ident");
|
||||
|
||||
if (node) {
|
||||
config->ea3_ident.property =
|
||||
_launcher_config_layered_config_nodes_load(node);
|
||||
}
|
||||
|
||||
node = property_search(NULL, root_node, "eamuse");
|
||||
|
||||
if (node) {
|
||||
config->eamuse.property =
|
||||
_launcher_config_layered_config_nodes_load(node);
|
||||
}
|
||||
|
||||
node = property_search(NULL, root_node, "hook");
|
||||
|
||||
if (node) {
|
||||
_launcher_config_hook_load(node, &config->hook);
|
||||
}
|
||||
|
||||
_launcher_config_debug_load(root_node, &config->debug);
|
||||
}
|
||||
|
||||
bool launcher_config_add_hook_dll(
|
||||
struct launcher_config *config, const char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(path);
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < LAUNCHER_CONFIG_MAX_HOOK_DLL) {
|
||||
if (strlen(config->hook.hook_dlls[i]) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= LAUNCHER_CONFIG_MAX_HOOK_DLL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
str_cpy(config->hook.hook_dlls[i], sizeof(config->hook.hook_dlls[i]), path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool launcher_config_add_before_hook_dll(
|
||||
struct launcher_config *config, const char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(path);
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < LAUNCHER_CONFIG_MAX_HOOK_DLL) {
|
||||
if (strlen(config->hook.before_hook_dlls[i]) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= LAUNCHER_CONFIG_MAX_HOOK_DLL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
str_cpy(
|
||||
config->hook.before_hook_dlls[i],
|
||||
sizeof(config->hook.before_hook_dlls[i]),
|
||||
path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool launcher_config_add_iat_hook_dll(
|
||||
struct launcher_config *config, const char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(path);
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < LAUNCHER_CONFIG_MAX_HOOK_DLL) {
|
||||
if (strlen(config->hook.iat_hook_dlls[i]) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= LAUNCHER_CONFIG_MAX_HOOK_DLL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
str_cpy(
|
||||
config->hook.iat_hook_dlls[i],
|
||||
sizeof(config->hook.iat_hook_dlls[i]),
|
||||
path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void launcher_config_fini(struct launcher_config *config)
|
||||
{
|
||||
log_assert(config);
|
||||
|
||||
property_util_free(config->bootstrap.property);
|
||||
|
||||
if (config->avs.property) {
|
||||
property_util_free(config->avs.property);
|
||||
}
|
||||
|
||||
if (config->ea3_ident.property) {
|
||||
property_util_free(config->ea3_ident.property);
|
||||
}
|
||||
|
||||
if (config->eamuse.property) {
|
||||
property_util_free(config->eamuse.property);
|
||||
}
|
||||
}
|
56
src/main/launcher/launcher-config.h
Normal file
56
src/main/launcher/launcher-config.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef LAUNCHER_CONFIG_H
|
||||
#define LAUNCHER_CONFIG_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "util/array.h"
|
||||
|
||||
#define LAUNCHER_CONFIG_MAX_HOOK_DLL 16
|
||||
|
||||
struct launcher_config {
|
||||
uint32_t version;
|
||||
|
||||
struct launcher_bootstrap_config {
|
||||
char selector[128];
|
||||
struct property *property;
|
||||
} bootstrap;
|
||||
|
||||
struct launcher_avs_config {
|
||||
struct property *property;
|
||||
} avs;
|
||||
|
||||
struct launcher_ea3_ident_config {
|
||||
struct property *property;
|
||||
} ea3_ident;
|
||||
|
||||
struct launcher_eamuse_config {
|
||||
struct property *property;
|
||||
} eamuse;
|
||||
|
||||
struct launcher_hook_config {
|
||||
char hook_dlls[LAUNCHER_CONFIG_MAX_HOOK_DLL][MAX_PATH];
|
||||
char before_hook_dlls[LAUNCHER_CONFIG_MAX_HOOK_DLL][MAX_PATH];
|
||||
char iat_hook_dlls[LAUNCHER_CONFIG_MAX_HOOK_DLL][MAX_PATH];
|
||||
} hook;
|
||||
|
||||
struct launcher_debug_config {
|
||||
bool remote_debugger;
|
||||
bool log_property_configs;
|
||||
} debug;
|
||||
};
|
||||
|
||||
void launcher_config_init(struct launcher_config *config);
|
||||
|
||||
void launcher_config_load(
|
||||
struct property *property, struct launcher_config *config);
|
||||
|
||||
bool launcher_config_add_hook_dll(
|
||||
struct launcher_config *config, const char *path);
|
||||
bool launcher_config_add_before_hook_dll(
|
||||
struct launcher_config *config, const char *path);
|
||||
bool launcher_config_add_iat_hook_dll(
|
||||
struct launcher_config *config, const char *path);
|
||||
|
||||
void launcher_config_fini(struct launcher_config *config);
|
||||
|
||||
#endif
|
587
src/main/launcher/launcher.c
Normal file
587
src/main/launcher/launcher.c
Normal file
@ -0,0 +1,587 @@
|
||||
#define LOG_MODULE "launcher"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "core/log-bt-ext.h"
|
||||
#include "core/log-bt.h"
|
||||
#include "core/log-sink-file.h"
|
||||
#include "core/log-sink-list.h"
|
||||
#include "core/log-sink-std.h"
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs-ea3.h"
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-config.h"
|
||||
#include "launcher/avs.h"
|
||||
#include "launcher/bootstrap-config.h"
|
||||
#include "launcher/bootstrap.h"
|
||||
#include "launcher/debug.h"
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
#include "launcher/eamuse-config.h"
|
||||
#include "launcher/eamuse.h"
|
||||
#include "launcher/hook.h"
|
||||
#include "launcher/launcher-config.h"
|
||||
#include "launcher/module.h"
|
||||
#include "launcher/options.h"
|
||||
#include "launcher/property-util.h"
|
||||
#include "launcher/stubs.h"
|
||||
#include "launcher/version.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/os.h"
|
||||
#include "util/proc.h"
|
||||
#include "util/signal.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static void _launcher_log_header()
|
||||
{
|
||||
log_info(
|
||||
"\n"
|
||||
" .__ .__ \n"
|
||||
" | | _____ __ __ ____ ____ | |__ ___________ \n"
|
||||
" | | \\__ \\ | | \\/ \\_/ ___\\| | \\_/ __ \\_ __ \\ \n"
|
||||
" | |__/ __ \\| | / | \\ \\___| Y \\ ___/| | \\/ \n"
|
||||
" |____(____ /____/|___| /\\___ >___| /\\___ >__| \n"
|
||||
" \\/ \\/ \\/ \\/ \\/ ");
|
||||
|
||||
log_info(
|
||||
"launcher build date %s, gitrev %s",
|
||||
launcher_build_date,
|
||||
launcher_gitrev);
|
||||
}
|
||||
|
||||
void _launcher_log_init(
|
||||
const char *log_file_path, enum core_log_bt_log_level level)
|
||||
{
|
||||
struct core_log_sink sinks[2];
|
||||
struct core_log_sink sink_composed;
|
||||
|
||||
core_log_bt_ext_impl_set();
|
||||
|
||||
if (log_file_path) {
|
||||
core_log_sink_std_out_open(true, &sinks[0]);
|
||||
core_log_sink_file_open(log_file_path, false, true, 10, &sinks[1]);
|
||||
core_log_sink_list_open(sinks, 2, &sink_composed);
|
||||
} else {
|
||||
core_log_sink_std_out_open(true, &sink_composed);
|
||||
}
|
||||
|
||||
core_log_bt_init(&sink_composed);
|
||||
core_log_bt_level_set(level);
|
||||
}
|
||||
|
||||
static void _launcher_signal_shutdown_handler()
|
||||
{
|
||||
core_log_bt_fini();
|
||||
ExitProcess(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void _launcher_env_game_dir_verify()
|
||||
{
|
||||
char cwd[MAX_PATH];
|
||||
char modules_dir[MAX_PATH];
|
||||
char prop_dir[MAX_PATH];
|
||||
|
||||
getcwd(cwd, sizeof(cwd));
|
||||
|
||||
log_info("Current working directory: %s", cwd);
|
||||
|
||||
str_cpy(modules_dir, sizeof(modules_dir), cwd);
|
||||
str_cpy(prop_dir, sizeof(prop_dir), cwd);
|
||||
|
||||
str_cat(modules_dir, sizeof(modules_dir), "/modules");
|
||||
str_cat(prop_dir, sizeof(prop_dir), "/prop");
|
||||
|
||||
if (!path_exists(modules_dir)) {
|
||||
log_fatal(
|
||||
"Cannot find 'modules' directory in current working directory: %s",
|
||||
cwd);
|
||||
}
|
||||
|
||||
if (!path_exists(prop_dir)) {
|
||||
log_fatal(
|
||||
"Cannot find 'prop' directory in current working directory: %s",
|
||||
cwd);
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_bootstrap_config_options_override(
|
||||
struct launcher_bootstrap_config *config,
|
||||
const struct options_bootstrap *options)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(options);
|
||||
|
||||
if (options->config_path) {
|
||||
log_misc(
|
||||
"Command line override bootstrap configuration from file: %s",
|
||||
options->config_path);
|
||||
|
||||
property_util_free(config->property);
|
||||
config->property = property_util_load(options->config_path);
|
||||
}
|
||||
|
||||
if (options->selector) {
|
||||
log_misc(
|
||||
"Command line override bootstrap selector: %s", options->selector);
|
||||
|
||||
str_cpy(config->selector, sizeof(config->selector), options->selector);
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_ea3_ident_config_options_override(
|
||||
struct ea3_ident_config *config, const struct options_eamuse *options)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(options);
|
||||
|
||||
if (options->softid) {
|
||||
str_cpy(config->softid, sizeof(config->softid), options->softid);
|
||||
}
|
||||
|
||||
if (options->pcbid) {
|
||||
str_cpy(config->pcbid, sizeof(config->pcbid), options->pcbid);
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_hook_config_options_override(
|
||||
struct launcher_config *config, const struct options_hook *options)
|
||||
{
|
||||
size_t i;
|
||||
const char *dll;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(options);
|
||||
|
||||
for (i = 0; i < options->hook_dlls.nitems; i++) {
|
||||
dll = *array_item(const char *, &options->hook_dlls, i);
|
||||
|
||||
if (!launcher_config_add_hook_dll(config, dll)) {
|
||||
log_warning(
|
||||
"Adding override hook dll '%s' failed (max supported limit "
|
||||
"exceeded), ignored",
|
||||
dll);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < options->before_hook_dlls.nitems; i++) {
|
||||
dll = *array_item(const char *, &options->before_hook_dlls, i);
|
||||
|
||||
if (!launcher_config_add_before_hook_dll(config, dll)) {
|
||||
log_warning(
|
||||
"Adding override before hook dll '%s' failed (max supported "
|
||||
"limit exceeded), ignored",
|
||||
dll);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < options->iat_hook_dlls.nitems; i++) {
|
||||
dll = *array_item(const char *, &options->iat_hook_dlls, i);
|
||||
|
||||
if (!launcher_config_add_iat_hook_dll(config, dll)) {
|
||||
log_warning(
|
||||
"Adding override iat hook dll '%s' failed (max supported limit "
|
||||
"exceeded), ignored",
|
||||
dll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_debug_config_options_override(
|
||||
struct launcher_debug_config *config, const struct options_debug *options)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(options);
|
||||
|
||||
if (options->remote_debugger) {
|
||||
log_misc("Command line override, enable remote debugger");
|
||||
|
||||
config->remote_debugger = true;
|
||||
}
|
||||
|
||||
if (options->log_property_configs) {
|
||||
log_misc("Command line override, log property configs");
|
||||
|
||||
config->log_property_configs = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_config_options_override(
|
||||
struct launcher_config *config, const struct options *options)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(options);
|
||||
|
||||
// Apply command line overrides on all launcher owned configuration
|
||||
// parameters
|
||||
_launcher_bootstrap_config_options_override(
|
||||
&config->bootstrap, &options->bootstrap);
|
||||
_launcher_hook_config_options_override(config, &options->hook);
|
||||
_launcher_debug_config_options_override(&config->debug, &options->debug);
|
||||
}
|
||||
|
||||
static void
|
||||
_launcher_config_full_resolved_log(const struct launcher_config *config)
|
||||
{
|
||||
if (config->debug.log_property_configs) {
|
||||
log_misc("launcher-config resolved properties");
|
||||
log_misc("bootstrap-config");
|
||||
property_util_log(config->bootstrap.property);
|
||||
|
||||
log_misc("avs-config");
|
||||
property_util_log(config->avs.property);
|
||||
|
||||
log_misc("ea3-ident-config");
|
||||
property_util_log(config->ea3_ident.property);
|
||||
|
||||
log_misc("eamuse-config");
|
||||
property_util_log(config->eamuse.property);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_launcher_remote_debugger_trap(const struct launcher_debug_config *config)
|
||||
{
|
||||
log_assert(config);
|
||||
|
||||
/* If enabled, wait for a remote debugger to attach as early as possible.
|
||||
Spawning launcher with a debugger crashes it for some reason
|
||||
(e.g. on jubeat08). However, starting the launcher separately and
|
||||
attaching a remote debugger works */
|
||||
|
||||
if (config->remote_debugger) {
|
||||
debug_remote_debugger_trap();
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_bootstrap_config_load(
|
||||
const struct launcher_bootstrap_config *launcher_bootstrap_config,
|
||||
struct bootstrap_config *config)
|
||||
{
|
||||
bootstrap_config_init(config);
|
||||
|
||||
bootstrap_config_load(
|
||||
launcher_bootstrap_config->property,
|
||||
launcher_bootstrap_config->selector,
|
||||
config);
|
||||
}
|
||||
|
||||
static void _launcher_bootstrap_log_config_options_override(
|
||||
struct bootstrap_log_config *config, const struct options_log *options)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(options);
|
||||
|
||||
if (options->level) {
|
||||
log_misc(
|
||||
"Command line override bootstrap log level: %d", *(options->level));
|
||||
|
||||
switch (*(options->level)) {
|
||||
case CORE_LOG_BT_LOG_LEVEL_OFF:
|
||||
str_cpy(config->level, sizeof(config->level), "disable");
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_FATAL:
|
||||
str_cpy(config->level, sizeof(config->level), "fatal");
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_WARNING:
|
||||
str_cpy(config->level, sizeof(config->level), "warn");
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_INFO:
|
||||
str_cpy(config->level, sizeof(config->level), "info");
|
||||
break;
|
||||
|
||||
case CORE_LOG_BT_LOG_LEVEL_MISC:
|
||||
str_cpy(config->level, sizeof(config->level), "misc");
|
||||
break;
|
||||
|
||||
default:
|
||||
log_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (options->file_path) {
|
||||
log_misc(
|
||||
"Command line override bootstrap log file: %s", options->file_path);
|
||||
str_cpy(config->file, sizeof(config->file), options->file_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_bootstrap_log_config_verify(
|
||||
const struct launcher_config *launcher_config,
|
||||
const struct bootstrap_config *bootstrap_config)
|
||||
{
|
||||
log_assert(launcher_config);
|
||||
log_assert(bootstrap_config);
|
||||
|
||||
if (!str_eq(bootstrap_config->startup.log.level, "misc")) {
|
||||
if (launcher_config->debug.log_property_configs) {
|
||||
log_warning(
|
||||
"Logging of property configs enabled, but requires misc log "
|
||||
"level, current log level: %s",
|
||||
bootstrap_config->startup.log.level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_launcher_before_hook_dlls_load(const struct launcher_hook_config *config)
|
||||
{
|
||||
int i;
|
||||
|
||||
log_assert(config);
|
||||
|
||||
log_misc("Loading before hook dlls...");
|
||||
|
||||
for (i = 0; i < LAUNCHER_CONFIG_MAX_HOOK_DLL; i++) {
|
||||
if (strlen(config->before_hook_dlls[i]) > 0) {
|
||||
hook_load_dll(config->before_hook_dlls[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_ea3_ident_config_load(
|
||||
const struct launcher_ea3_ident_config *launcher_config,
|
||||
struct ea3_ident_config *config,
|
||||
bool log_property_configs)
|
||||
{
|
||||
log_assert(launcher_config);
|
||||
log_assert(config);
|
||||
|
||||
ea3_ident_config_init(config);
|
||||
ea3_ident_config_load(launcher_config->property, config);
|
||||
|
||||
if (log_property_configs) {
|
||||
log_misc("Property ea3-ident-config");
|
||||
|
||||
property_util_log(launcher_config->property);
|
||||
}
|
||||
|
||||
if (!ea3_ident_config_hardid_is_defined(config)) {
|
||||
log_misc(
|
||||
"No no hardid defined in ea3-ident-config, derive from ethernet");
|
||||
|
||||
ea3_ident_config_hardid_from_ethernet_set(config);
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_bootstrap_module_init(
|
||||
const struct bootstrap_module_config *module_config,
|
||||
const struct launcher_hook_config *hook_config)
|
||||
{
|
||||
int i;
|
||||
struct array iat_hook_dlls;
|
||||
|
||||
log_assert(module_config);
|
||||
log_assert(hook_config);
|
||||
|
||||
array_init(&iat_hook_dlls);
|
||||
|
||||
for (i = 0; i < LAUNCHER_CONFIG_MAX_HOOK_DLL; i++) {
|
||||
if (strlen(hook_config->before_hook_dlls[i]) > 0) {
|
||||
*array_append(const char *, &iat_hook_dlls) =
|
||||
(const char *) &hook_config->before_hook_dlls[i];
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap_module_init(module_config, &iat_hook_dlls);
|
||||
|
||||
array_fini(&iat_hook_dlls);
|
||||
}
|
||||
|
||||
static void _launcher_hook_dlls_load(const struct launcher_hook_config *config)
|
||||
{
|
||||
int i;
|
||||
|
||||
log_assert(config);
|
||||
|
||||
log_misc("Loading hook dlls...");
|
||||
|
||||
for (i = 0; i < LAUNCHER_CONFIG_MAX_HOOK_DLL; i++) {
|
||||
if (strlen(config->hook_dlls[i]) > 0) {
|
||||
hook_load_dll(config->hook_dlls[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _launcher_dongle_stubs_init()
|
||||
{
|
||||
stubs_init();
|
||||
}
|
||||
|
||||
static void _launcher_debugger_break()
|
||||
{
|
||||
/* Opportunity for breakpoint setup etc */
|
||||
if (IsDebuggerPresent()) {
|
||||
DebugBreak();
|
||||
}
|
||||
}
|
||||
|
||||
void _launcher_log_reinit()
|
||||
{
|
||||
core_log_bt_ext_impl_set();
|
||||
}
|
||||
|
||||
void _launcher_init(
|
||||
const struct options *options,
|
||||
struct launcher_config *launcher_config,
|
||||
struct bootstrap_config *bootstrap_config,
|
||||
struct ea3_ident_config *ea3_ident_config)
|
||||
{
|
||||
struct property *launcher_property;
|
||||
|
||||
log_assert(options);
|
||||
log_assert(launcher_config);
|
||||
log_assert(bootstrap_config);
|
||||
log_assert(ea3_ident_config);
|
||||
|
||||
// Early logging pre AVS setup depend entirely on command args
|
||||
// We don't even have the bootstrap configuration loaded at this point
|
||||
_launcher_log_init(options->log.file_path, *(options->log.level));
|
||||
_launcher_log_header();
|
||||
|
||||
signal_exception_handler_init(_launcher_signal_shutdown_handler);
|
||||
signal_register_shutdown_handler(&_launcher_signal_shutdown_handler);
|
||||
|
||||
os_version_log();
|
||||
_launcher_env_game_dir_verify();
|
||||
|
||||
if (proc_is_running_as_admin_user()) {
|
||||
log_warning(
|
||||
"Not running as admin user. Launcher and games require elevated "
|
||||
"privileges to run correctly");
|
||||
}
|
||||
|
||||
launcher_config_init(launcher_config);
|
||||
|
||||
if (options->launcher.config_path) {
|
||||
log_info(
|
||||
"Loading launcher configuration from file: %s",
|
||||
options->launcher.config_path);
|
||||
|
||||
launcher_property = property_util_load(options->launcher.config_path);
|
||||
launcher_config_load(launcher_property, launcher_config);
|
||||
|
||||
_launcher_config_options_override(launcher_config, options);
|
||||
|
||||
if (launcher_config->debug.log_property_configs) {
|
||||
log_misc("launcher-config");
|
||||
property_util_log(launcher_property);
|
||||
}
|
||||
|
||||
property_util_free(launcher_property);
|
||||
} else {
|
||||
_launcher_config_options_override(launcher_config, options);
|
||||
}
|
||||
|
||||
// Not really fully resolved, but have an early debug dump because there are
|
||||
// still several more steps that can fail before having the entire
|
||||
// configuration resolved
|
||||
_launcher_config_full_resolved_log(launcher_config);
|
||||
|
||||
_launcher_remote_debugger_trap(&launcher_config->debug);
|
||||
|
||||
_launcher_bootstrap_config_load(
|
||||
&launcher_config->bootstrap, bootstrap_config);
|
||||
_launcher_bootstrap_log_config_options_override(
|
||||
&bootstrap_config->startup.log, &options->log);
|
||||
_launcher_bootstrap_log_config_verify(launcher_config, bootstrap_config);
|
||||
|
||||
bootstrap_init(launcher_config->debug.log_property_configs);
|
||||
bootstrap_log_init(&bootstrap_config->startup.log);
|
||||
|
||||
_launcher_before_hook_dlls_load(&launcher_config->hook);
|
||||
|
||||
bootstrap_avs_init(
|
||||
&bootstrap_config->startup.boot,
|
||||
&bootstrap_config->startup.log,
|
||||
launcher_config->avs.property);
|
||||
bootstrap_default_files_create(&bootstrap_config->startup.default_file);
|
||||
|
||||
_launcher_ea3_ident_config_load(
|
||||
&launcher_config->ea3_ident,
|
||||
ea3_ident_config,
|
||||
launcher_config->debug.log_property_configs);
|
||||
_launcher_ea3_ident_config_options_override(
|
||||
ea3_ident_config, &options->eamuse);
|
||||
|
||||
// Execute another one which is now actually final. No more configuration
|
||||
// changes from this point on
|
||||
_launcher_config_full_resolved_log(launcher_config);
|
||||
}
|
||||
|
||||
void _launcher_run(
|
||||
const struct launcher_config *launcher_config,
|
||||
const struct bootstrap_config *bootstrap_config,
|
||||
struct ea3_ident_config *ea3_ident_config)
|
||||
{
|
||||
log_assert(launcher_config);
|
||||
log_assert(bootstrap_config);
|
||||
log_assert(ea3_ident_config);
|
||||
|
||||
_launcher_bootstrap_module_init(
|
||||
&bootstrap_config->startup.module, &launcher_config->hook);
|
||||
|
||||
_launcher_hook_dlls_load(&launcher_config->hook);
|
||||
|
||||
_launcher_dongle_stubs_init();
|
||||
|
||||
_launcher_debugger_break();
|
||||
|
||||
bootstrap_module_game_init(
|
||||
&bootstrap_config->startup.module, ea3_ident_config);
|
||||
|
||||
bootstrap_eamuse_init(
|
||||
&bootstrap_config->startup.eamuse,
|
||||
ea3_ident_config,
|
||||
launcher_config->eamuse.property);
|
||||
|
||||
bootstrap_module_game_run();
|
||||
}
|
||||
|
||||
void _launcher_fini(
|
||||
struct launcher_config *launcher_config,
|
||||
const struct bootstrap_config *bootstrap_config)
|
||||
{
|
||||
log_assert(launcher_config);
|
||||
log_assert(bootstrap_config);
|
||||
|
||||
bootstrap_eamuse_fini(&bootstrap_config->startup.eamuse);
|
||||
|
||||
bootstrap_avs_fini();
|
||||
|
||||
_launcher_log_reinit();
|
||||
|
||||
bootstrap_module_game_fini();
|
||||
|
||||
launcher_config_fini(launcher_config);
|
||||
|
||||
log_info("Shutdown complete");
|
||||
|
||||
core_log_bt_fini();
|
||||
}
|
||||
|
||||
void launcher_main(const struct options *options)
|
||||
{
|
||||
struct launcher_config launcher_config;
|
||||
struct bootstrap_config bootstrap_config;
|
||||
struct ea3_ident_config ea3_ident_config;
|
||||
|
||||
log_assert(options);
|
||||
|
||||
_launcher_init(
|
||||
options, &launcher_config, &bootstrap_config, &ea3_ident_config);
|
||||
|
||||
_launcher_run(&launcher_config, &bootstrap_config, &ea3_ident_config);
|
||||
|
||||
_launcher_fini(&launcher_config, &bootstrap_config);
|
||||
}
|
8
src/main/launcher/launcher.h
Normal file
8
src/main/launcher/launcher.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LAUNCHER_LAUNCHER_H
|
||||
#define LAUNCHER_LAUNCHER_H
|
||||
|
||||
#include "launcher/options.h"
|
||||
|
||||
void launcher_main(const struct options *options);
|
||||
|
||||
#endif
|
@ -1,338 +1,23 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs-ea3.h"
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-context.h"
|
||||
#include "launcher/ea3-config.h"
|
||||
#include "launcher/module.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "launcher/options.h"
|
||||
#include "launcher/property.h"
|
||||
#include "launcher/stubs.h"
|
||||
#include "launcher/version.h"
|
||||
|
||||
#include "util/codepage.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/os.h"
|
||||
#include "util/str.h"
|
||||
|
||||
/* Gratuitous API changes orz */
|
||||
static AVS_LOG_WRITER(log_callback, chars, nchars, ctx)
|
||||
{
|
||||
wchar_t *utf16;
|
||||
char *utf8;
|
||||
int utf16_len;
|
||||
int utf8_len;
|
||||
int result;
|
||||
DWORD nwritten;
|
||||
HANDLE console;
|
||||
HANDLE file;
|
||||
|
||||
/* Ignore existing NUL terminator */
|
||||
|
||||
nchars--;
|
||||
|
||||
/* Transcode shit_jis to UTF-8 */
|
||||
|
||||
utf16_len = MultiByteToWideChar(CP_SHIFT_JIS, 0, chars, nchars, NULL, 0);
|
||||
|
||||
if (utf16_len == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
utf16 = xmalloc(sizeof(*utf16) * utf16_len);
|
||||
result =
|
||||
MultiByteToWideChar(CP_SHIFT_JIS, 0, chars, nchars, utf16, utf16_len);
|
||||
|
||||
if (result == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
utf8_len =
|
||||
WideCharToMultiByte(CP_UTF8, 0, utf16, utf16_len, NULL, 0, NULL, NULL);
|
||||
|
||||
if (utf8_len == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
utf8 = xmalloc(utf8_len + 2);
|
||||
result = WideCharToMultiByte(
|
||||
CP_UTF8, 0, utf16, utf16_len, utf8, utf8_len, NULL, NULL);
|
||||
|
||||
if (result == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
#if AVS_VERSION >= 1500
|
||||
utf8[utf8_len + 0] = '\r';
|
||||
utf8[utf8_len + 1] = '\n';
|
||||
|
||||
utf8_len += 2;
|
||||
#endif
|
||||
|
||||
/* Write to console and log file */
|
||||
|
||||
file = (HANDLE) ctx;
|
||||
console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (ctx != INVALID_HANDLE_VALUE) {
|
||||
WriteFile(file, utf8, utf8_len, &nwritten, NULL);
|
||||
}
|
||||
|
||||
WriteFile(console, utf8, utf8_len, &nwritten, NULL);
|
||||
|
||||
/* Clean up */
|
||||
|
||||
free(utf8);
|
||||
free(utf16);
|
||||
}
|
||||
|
||||
static void load_hook_dlls(struct array *hook_dlls)
|
||||
{
|
||||
const char *hook_dll;
|
||||
|
||||
for (size_t i = 0; i < hook_dlls->nitems; i++) {
|
||||
hook_dll = *array_item(char *, hook_dlls, i);
|
||||
|
||||
if (LoadLibraryA(hook_dll) == NULL) {
|
||||
LPSTR buffer;
|
||||
|
||||
FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR) &buffer,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
log_fatal("%s: Failed to load hook DLL: %s", hook_dll, buffer);
|
||||
|
||||
LocalFree(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
bool ok;
|
||||
HANDLE logfile;
|
||||
|
||||
struct ea3_ident ea3;
|
||||
struct module_context module;
|
||||
struct options options;
|
||||
|
||||
struct property *app_config;
|
||||
struct property *avs_config;
|
||||
struct property *ea3_config;
|
||||
|
||||
struct property_node *app_config_root;
|
||||
struct property_node *avs_config_root;
|
||||
struct property_node *ea3_config_root;
|
||||
|
||||
log_to_writer(log_writer_file, stdout);
|
||||
log_info(
|
||||
"launcher build date %s, gitrev %s",
|
||||
launcher_build_date,
|
||||
launcher_gitrev);
|
||||
|
||||
/* Read command line */
|
||||
|
||||
options_init(&options);
|
||||
|
||||
if (!options_read_cmdline(&options, argc, argv)) {
|
||||
options_print_usage();
|
||||
|
||||
return EXIT_FAILURE;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* If enabled, wait for a remote debugger to attach. Spawning launcher
|
||||
with a debugger crashes it for some reason (e.g. on jubeat08). However,
|
||||
starting the launcher separately and attaching a remote debugger works */
|
||||
launcher_main(&options);
|
||||
|
||||
if (options.remote_debugger) {
|
||||
log_info("Waiting until debugger attaches to remote process...");
|
||||
|
||||
while (true) {
|
||||
BOOL res = FALSE;
|
||||
if (!CheckRemoteDebuggerPresent(GetCurrentProcess(), &res)) {
|
||||
log_fatal(
|
||||
"CheckRemoteDebuggerPresent failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
if (res) {
|
||||
log_info("Debugger attached, resuming");
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/* Start up AVS */
|
||||
|
||||
if (options.logfile != NULL) {
|
||||
logfile = CreateFileA(
|
||||
options.logfile,
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
CREATE_ALWAYS,
|
||||
0,
|
||||
NULL);
|
||||
} else {
|
||||
logfile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
avs_config = boot_property_load(options.avs_config_path);
|
||||
avs_config_root = property_search(avs_config, 0, "/config");
|
||||
|
||||
if (avs_config_root == NULL) {
|
||||
log_fatal("%s: /config missing", options.avs_config_path);
|
||||
}
|
||||
|
||||
load_hook_dlls(&options.before_hook_dlls);
|
||||
|
||||
avs_context_init(
|
||||
avs_config_root,
|
||||
options.avs_heap_size,
|
||||
options.std_heap_size,
|
||||
log_callback,
|
||||
logfile);
|
||||
|
||||
boot_property_free(avs_config);
|
||||
|
||||
log_to_external(
|
||||
log_body_misc, log_body_info, log_body_warning, log_body_fatal);
|
||||
|
||||
os_version_log();
|
||||
|
||||
/* Load game DLL */
|
||||
|
||||
if (options.iat_hook_dlls.nitems > 0) {
|
||||
module_context_init_with_iat_hooks(
|
||||
&module, options.module, &options.iat_hook_dlls);
|
||||
} else {
|
||||
module_context_init(&module, options.module);
|
||||
}
|
||||
|
||||
/* Load hook DLLs */
|
||||
|
||||
load_hook_dlls(&options.hook_dlls);
|
||||
|
||||
/* Inject GetModuleHandle hooks */
|
||||
|
||||
stubs_init();
|
||||
|
||||
/* Prepare ea3 config */
|
||||
|
||||
ea3_config = boot_property_load(options.ea3_config_path);
|
||||
ea3_config_root = property_search(ea3_config, 0, "/ea3");
|
||||
|
||||
if (ea3_config_root == NULL) {
|
||||
log_fatal("%s: /ea3 missing", options.ea3_config_path);
|
||||
}
|
||||
|
||||
ea3_ident_init(&ea3);
|
||||
|
||||
if (!ea3_ident_from_property(&ea3, ea3_config)) {
|
||||
log_fatal(
|
||||
"%s: Error reading IDs from config file", options.ea3_config_path);
|
||||
}
|
||||
|
||||
if (options.softid != NULL) {
|
||||
str_cpy(ea3.softid, lengthof(ea3.softid), options.softid);
|
||||
}
|
||||
|
||||
if (options.pcbid != NULL) {
|
||||
str_cpy(ea3.pcbid, lengthof(ea3.pcbid), options.pcbid);
|
||||
}
|
||||
|
||||
if (!ea3.hardid[0]) {
|
||||
ea3_ident_hardid_from_ethernet(&ea3);
|
||||
}
|
||||
|
||||
/* Invoke dll_entry_init */
|
||||
|
||||
if (path_exists(options.app_config_path)) {
|
||||
app_config = boot_property_load(options.app_config_path);
|
||||
} else {
|
||||
log_warning(
|
||||
"%s: app config file missing, using empty",
|
||||
options.app_config_path);
|
||||
app_config = boot_property_load_cstring("<param>dummy</param>");
|
||||
}
|
||||
|
||||
app_config_root = property_search(app_config, 0, "/param");
|
||||
|
||||
if (app_config_root == NULL) {
|
||||
log_fatal("%s: /param missing", options.app_config_path);
|
||||
}
|
||||
|
||||
if (IsDebuggerPresent()) {
|
||||
/* Opportunity for breakpoint setup etc */
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
ok = ea3_ident_invoke_module_init(&ea3, &module, app_config_root);
|
||||
|
||||
if (!ok) {
|
||||
log_fatal("%s: dll_module_init() returned failure", options.module);
|
||||
}
|
||||
|
||||
boot_property_free(app_config);
|
||||
|
||||
ea3_ident_to_property(&ea3, ea3_config);
|
||||
|
||||
if (options.override_urlslash_enabled) {
|
||||
log_info(
|
||||
"Overriding url_slash to: %d", options.override_urlslash_value);
|
||||
|
||||
ea3_ident_replace_property_bool(
|
||||
ea3_config_root,
|
||||
"/network/url_slash",
|
||||
options.override_urlslash_value);
|
||||
}
|
||||
|
||||
if (options.override_service != NULL) {
|
||||
log_info("Overriding service url to: %s", options.override_service);
|
||||
|
||||
ea3_ident_replace_property_str(
|
||||
ea3_config_root, "/network/services", options.override_service);
|
||||
}
|
||||
|
||||
/* Start up e-Amusement client */
|
||||
|
||||
ea3_boot(ea3_config_root);
|
||||
boot_property_free(ea3_config);
|
||||
|
||||
/* Run application */
|
||||
|
||||
module_context_invoke_main(&module);
|
||||
|
||||
/* Shut down */
|
||||
|
||||
ea3_shutdown();
|
||||
|
||||
log_to_writer(log_writer_file, stdout);
|
||||
avs_context_fini();
|
||||
|
||||
if (logfile != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(logfile);
|
||||
}
|
||||
|
||||
module_context_fini(&module);
|
||||
options_fini(&options);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
@ -1,22 +1,29 @@
|
||||
#define LOG_MODULE "module"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "hook/pe.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
#include "imports/eapki.h"
|
||||
|
||||
#include "launcher/module.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define MM_ALLOCATION_GRANULARITY 0x10000
|
||||
|
||||
static bool module_replace_dll_iat(HMODULE hModule, struct array *iat_hook_dlls)
|
||||
static bool
|
||||
module_replace_dll_iat(HMODULE hModule, const struct array *iat_hook_dlls)
|
||||
{
|
||||
log_assert(hModule);
|
||||
log_assert(iat_hook_dlls);
|
||||
|
||||
log_misc("replace dll iat: %p", hModule);
|
||||
|
||||
if (iat_hook_dlls->nitems == 0)
|
||||
return true;
|
||||
|
||||
@ -127,11 +134,13 @@ inject_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
void module_context_init(struct module_context *module, const char *path)
|
||||
void module_init(struct module_context *module, const char *path)
|
||||
{
|
||||
log_assert(module != NULL);
|
||||
log_assert(path != NULL);
|
||||
|
||||
log_info("init: %s", path);
|
||||
|
||||
module->dll = LoadLibrary(path);
|
||||
|
||||
if (module->dll == NULL) {
|
||||
@ -159,16 +168,20 @@ void module_context_init(struct module_context *module, const char *path)
|
||||
}
|
||||
|
||||
module->path = str_dup(path);
|
||||
|
||||
log_misc("init done");
|
||||
}
|
||||
|
||||
void module_context_init_with_iat_hooks(
|
||||
void module_with_iat_hooks_init(
|
||||
struct module_context *module,
|
||||
const char *path,
|
||||
struct array *iat_hook_dlls)
|
||||
const struct array *iat_hook_dlls)
|
||||
{
|
||||
log_assert(module != NULL);
|
||||
log_assert(path != NULL);
|
||||
|
||||
log_info("init iat hooks: %s", path);
|
||||
|
||||
module->dll = LoadLibraryExA(path, NULL, DONT_RESOLVE_DLL_REFERENCES);
|
||||
|
||||
if (module->dll == NULL) {
|
||||
@ -211,16 +224,71 @@ void module_context_init_with_iat_hooks(
|
||||
module->path = str_dup(path);
|
||||
}
|
||||
|
||||
bool module_context_invoke_init(
|
||||
void module_init_invoke(
|
||||
const struct module_context *module,
|
||||
char *sidcode,
|
||||
struct property_node *app_config)
|
||||
struct ea3_ident_config *ea3_ident_config,
|
||||
struct property_node *app_params_node)
|
||||
{
|
||||
char sidcode_short[17];
|
||||
char sidcode_long[21];
|
||||
char security_code[9];
|
||||
dll_entry_init_t init;
|
||||
bool ok;
|
||||
|
||||
log_assert(module != NULL);
|
||||
log_assert(sidcode != NULL);
|
||||
log_assert(app_config != NULL);
|
||||
log_info("init invoke");
|
||||
|
||||
/* Set up security env vars */
|
||||
|
||||
str_format(
|
||||
security_code,
|
||||
lengthof(security_code),
|
||||
"G*%s%s%s%s",
|
||||
ea3_ident_config->model,
|
||||
ea3_ident_config->dest,
|
||||
ea3_ident_config->spec,
|
||||
ea3_ident_config->rev);
|
||||
|
||||
log_misc("security code: %s", security_code);
|
||||
|
||||
std_setenv("/env/boot/version", "0.0.0");
|
||||
std_setenv("/env/profile/security_code", security_code);
|
||||
std_setenv("/env/profile/system_id", ea3_ident_config->pcbid);
|
||||
std_setenv("/env/profile/account_id", ea3_ident_config->pcbid);
|
||||
std_setenv("/env/profile/license_id", ea3_ident_config->softid);
|
||||
std_setenv("/env/profile/software_id", ea3_ident_config->softid);
|
||||
std_setenv("/env/profile/hardware_id", ea3_ident_config->hardid);
|
||||
|
||||
/* Set up the short sidcode string, let dll_entry_init mangle it */
|
||||
|
||||
str_format(
|
||||
sidcode_short,
|
||||
lengthof(sidcode_short),
|
||||
"%s%s%s%s%s",
|
||||
ea3_ident_config->model,
|
||||
ea3_ident_config->dest,
|
||||
ea3_ident_config->spec,
|
||||
ea3_ident_config->rev,
|
||||
ea3_ident_config->ext);
|
||||
|
||||
log_misc("sidcode short: %s", sidcode_short);
|
||||
|
||||
/* Set up long-form sidcode env var */
|
||||
|
||||
str_format(
|
||||
sidcode_long,
|
||||
lengthof(sidcode_long),
|
||||
"%s:%s:%s:%s:%s",
|
||||
ea3_ident_config->model,
|
||||
ea3_ident_config->dest,
|
||||
ea3_ident_config->spec,
|
||||
ea3_ident_config->rev,
|
||||
ea3_ident_config->ext);
|
||||
|
||||
log_misc("sidecode long: %s", sidcode_long);
|
||||
|
||||
/* Set this up beforehand, as certain games require it in dll_entry_init */
|
||||
|
||||
std_setenv("/env/profile/soft_id_code", sidcode_long);
|
||||
|
||||
init = (void *) GetProcAddress(module->dll, "dll_entry_init");
|
||||
|
||||
@ -229,16 +297,67 @@ bool module_context_invoke_init(
|
||||
"%s: dll_entry_init not found. Is this a game DLL?", module->path);
|
||||
}
|
||||
|
||||
return init(sidcode, app_config);
|
||||
log_info("Invoking game init...");
|
||||
|
||||
struct property *prop =
|
||||
property_util_cstring_load("<param><io>p3io</io></param>");
|
||||
|
||||
struct property_node *prop_node = property_search(prop, NULL, "/param");
|
||||
|
||||
property_util_log(prop);
|
||||
property_util_node_log(prop_node);
|
||||
|
||||
ok = init(sidcode_short, prop_node);
|
||||
|
||||
if (!ok) {
|
||||
log_fatal("%s: dll_module_init() returned failure", module->path);
|
||||
} else {
|
||||
log_info("Game init done");
|
||||
}
|
||||
|
||||
/* Back-propagate sidcode, as some games modify it during init */
|
||||
|
||||
memcpy(
|
||||
ea3_ident_config->model,
|
||||
sidcode_short + 0,
|
||||
sizeof(ea3_ident_config->model) - 1);
|
||||
ea3_ident_config->dest[0] = sidcode_short[3];
|
||||
ea3_ident_config->spec[0] = sidcode_short[4];
|
||||
ea3_ident_config->rev[0] = sidcode_short[5];
|
||||
memcpy(
|
||||
ea3_ident_config->ext,
|
||||
sidcode_short + 6,
|
||||
sizeof(ea3_ident_config->ext));
|
||||
|
||||
/* Set up long-form sidcode env var again */
|
||||
|
||||
str_format(
|
||||
sidcode_long,
|
||||
lengthof(sidcode_long),
|
||||
"%s:%s:%s:%s:%s",
|
||||
ea3_ident_config->model,
|
||||
ea3_ident_config->dest,
|
||||
ea3_ident_config->spec,
|
||||
ea3_ident_config->rev,
|
||||
ea3_ident_config->ext);
|
||||
|
||||
std_setenv("/env/profile/soft_id_code", sidcode_long);
|
||||
|
||||
log_misc("back-propagated sidcode long: %s", sidcode_long);
|
||||
|
||||
log_misc("init invoke done");
|
||||
}
|
||||
|
||||
bool module_context_invoke_main(const struct module_context *module)
|
||||
bool module_main_invoke(const struct module_context *module)
|
||||
{
|
||||
/* GCC warns if you call a variable "main" */
|
||||
dll_entry_main_t main_;
|
||||
bool result;
|
||||
|
||||
log_assert(module != NULL);
|
||||
|
||||
log_info("main invoke");
|
||||
|
||||
main_ = (void *) GetProcAddress(module->dll, "dll_entry_main");
|
||||
|
||||
if (main_ == NULL) {
|
||||
@ -246,11 +365,19 @@ bool module_context_invoke_main(const struct module_context *module)
|
||||
"%s: dll_entry_main not found. Is this a game DLL?", module->path);
|
||||
}
|
||||
|
||||
return main_();
|
||||
log_info("Invoking game's main function...");
|
||||
|
||||
result = main_();
|
||||
|
||||
log_info("Main terminated, result: %d", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void module_context_fini(struct module_context *module)
|
||||
void module_fini(struct module_context *module)
|
||||
{
|
||||
log_info("fini");
|
||||
|
||||
if (module == NULL) {
|
||||
return;
|
||||
}
|
||||
@ -260,4 +387,6 @@ void module_context_fini(struct module_context *module)
|
||||
if (module->dll != NULL) {
|
||||
FreeLibrary(module->dll);
|
||||
}
|
||||
|
||||
log_misc("fini done");
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
|
||||
#include "util/array.h"
|
||||
|
||||
struct module_context {
|
||||
@ -12,16 +14,16 @@ struct module_context {
|
||||
char *path;
|
||||
};
|
||||
|
||||
void module_context_init(struct module_context *module, const char *path);
|
||||
void module_context_init_with_iat_hooks(
|
||||
void module_init(struct module_context *module, const char *path);
|
||||
void module_with_iat_hooks_init(
|
||||
struct module_context *module,
|
||||
const char *path,
|
||||
struct array *iat_hook_dlls);
|
||||
bool module_context_invoke_init(
|
||||
const struct array *iat_hook_dlls);
|
||||
void module_init_invoke(
|
||||
const struct module_context *module,
|
||||
char *sidcode,
|
||||
struct property_node *app_config);
|
||||
bool module_context_invoke_main(const struct module_context *module);
|
||||
void module_context_fini(struct module_context *module);
|
||||
struct ea3_ident_config *ea3_ident_config,
|
||||
struct property_node *app_params_node);
|
||||
bool module_main_invoke(const struct module_context *module);
|
||||
void module_fini(struct module_context *module);
|
||||
|
||||
#endif
|
||||
|
@ -1,3 +1,5 @@
|
||||
#define LOG_MODULE "options"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -6,62 +8,42 @@
|
||||
|
||||
#include "launcher/options.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define DEFAULT_HEAP_SIZE 16777216
|
||||
|
||||
void options_init(struct options *options)
|
||||
{
|
||||
options->std_heap_size = DEFAULT_HEAP_SIZE;
|
||||
options->avs_heap_size = DEFAULT_HEAP_SIZE;
|
||||
options->app_config_path = "prop/app-config.xml";
|
||||
options->avs_config_path = "prop/avs-config.xml";
|
||||
options->ea3_config_path = "prop/ea3-config.xml";
|
||||
options->softid = NULL;
|
||||
options->pcbid = NULL;
|
||||
options->module = NULL;
|
||||
options->logfile = NULL;
|
||||
options->remote_debugger = false;
|
||||
array_init(&options->hook_dlls);
|
||||
array_init(&options->before_hook_dlls);
|
||||
array_init(&options->iat_hook_dlls);
|
||||
memset(options, 0, sizeof(struct options));
|
||||
|
||||
options->override_service = NULL;
|
||||
options->override_urlslash_enabled = false;
|
||||
options->override_urlslash_value = false;
|
||||
array_init(&options->hook.hook_dlls);
|
||||
array_init(&options->hook.before_hook_dlls);
|
||||
array_init(&options->hook.iat_hook_dlls);
|
||||
}
|
||||
|
||||
bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
{
|
||||
bool got_launcher_config;
|
||||
|
||||
got_launcher_config = false;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
switch (argv[i][1]) {
|
||||
case 'A':
|
||||
case 'T':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->app_config_path = argv[++i];
|
||||
options->bootstrap.config_path = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
case 'Z':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->ea3_config_path = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->avs_config_path = argv[++i];
|
||||
options->bootstrap.selector = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
@ -70,7 +52,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
return false;
|
||||
}
|
||||
|
||||
options->pcbid = argv[++i];
|
||||
options->eamuse.pcbid = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
@ -79,7 +61,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
return false;
|
||||
}
|
||||
|
||||
options->softid = argv[++i];
|
||||
options->eamuse.softid = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
@ -88,37 +70,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
return false;
|
||||
}
|
||||
|
||||
options->avs_heap_size =
|
||||
(size_t) strtol(argv[++i], NULL, 0);
|
||||
|
||||
if (options->avs_heap_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
case 'T':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->std_heap_size =
|
||||
(size_t) strtol(argv[++i], NULL, 0);
|
||||
|
||||
if (options->std_heap_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'K':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*array_append(const char *, &options->hook_dlls) =
|
||||
*array_append(const char *, &options->hook.hook_dlls) =
|
||||
argv[++i];
|
||||
|
||||
break;
|
||||
@ -128,7 +80,8 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
return false;
|
||||
}
|
||||
|
||||
*array_append(const char *, &options->before_hook_dlls) =
|
||||
*array_append(
|
||||
const char *, &options->hook.before_hook_dlls) =
|
||||
argv[++i];
|
||||
|
||||
break;
|
||||
@ -141,17 +94,41 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
const char *dll = argv[++i];
|
||||
log_assert(strstr(dll, "=") != NULL);
|
||||
|
||||
*array_append(const char *, &options->iat_hook_dlls) = dll;
|
||||
*array_append(const char *, &options->hook.iat_hook_dlls) =
|
||||
dll;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'L':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long tmp = strtol(argv[++i], NULL, 0);
|
||||
|
||||
if (tmp < CORE_LOG_BT_LOG_LEVEL_OFF ||
|
||||
tmp > CORE_LOG_BT_LOG_LEVEL_MISC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->log.level =
|
||||
xmalloc(sizeof(enum core_log_bt_log_level));
|
||||
*(options->log.level) = (enum core_log_bt_log_level) tmp;
|
||||
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->logfile = argv[++i];
|
||||
options->log.file_path = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
options->debug.log_property_configs = true;
|
||||
|
||||
break;
|
||||
|
||||
@ -160,7 +137,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
return false;
|
||||
}
|
||||
|
||||
options->override_service = argv[++i];
|
||||
options->eamuse.service_url = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
@ -169,22 +146,23 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
return false;
|
||||
}
|
||||
|
||||
options->override_urlslash_enabled = true;
|
||||
options->eamuse.urlslash = xmalloc(sizeof(bool));
|
||||
|
||||
const char *urlslash_value = argv[++i];
|
||||
|
||||
options->override_urlslash_value = false;
|
||||
*(options->eamuse.urlslash) = false;
|
||||
|
||||
if (_stricmp(urlslash_value, "1") == 0) {
|
||||
options->override_urlslash_value = true;
|
||||
*(options->eamuse.urlslash) = true;
|
||||
}
|
||||
if (_stricmp(urlslash_value, "true") == 0) {
|
||||
options->override_urlslash_value = true;
|
||||
*(options->eamuse.urlslash) = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
options->remote_debugger = true;
|
||||
options->debug.remote_debugger = true;
|
||||
|
||||
break;
|
||||
|
||||
@ -192,59 +170,71 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!options->module) {
|
||||
options->module = argv[i];
|
||||
if (!got_launcher_config) {
|
||||
options->launcher.config_path = argv[i];
|
||||
got_launcher_config = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options->module) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return got_launcher_config;
|
||||
}
|
||||
|
||||
void options_print_usage(void)
|
||||
{
|
||||
fprintf(
|
||||
stderr,
|
||||
"Usage: launcher.exe [launcher options...] <app.dll> [hooks "
|
||||
"options...] \n"
|
||||
"Usage:\n"
|
||||
" launcher.exe [launcher options as overrides...] <path launcher.xml> "
|
||||
"[further options, e.g. for hook libraries to pick up...]\n"
|
||||
"\n"
|
||||
" The following options can be specified before the app DLL "
|
||||
"path:\n"
|
||||
" The following options can be specified before the launcher.xml "
|
||||
"configuration file:\n"
|
||||
"\n"
|
||||
" -A [filename] App configuration file (default: "
|
||||
"prop/app-config.xml)\n"
|
||||
" -V [filename] AVS configuration file (default: "
|
||||
"prop/avs-config.xml)\n"
|
||||
" -E [filename] ea3 configuration file (default: "
|
||||
"prop/ea3-config.xml)\n"
|
||||
" -H [bytes] AVS heap size (default: 16777216)\n"
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
" -T [bytes] 'std' heap size (default 16777216)\n"
|
||||
#endif
|
||||
" -P [pcbid] Specify PCBID (default: use ea3 config)\n"
|
||||
" -R [pcbid] Specify Soft ID (default: use ea3 config)\n"
|
||||
" -S [url] Specify service url (default: use ea3 config)\n"
|
||||
" -U [0/1] Specify url_slash (default: use ea3 config)\n"
|
||||
" -K [filename] Load hook DLL (can be specified multiple "
|
||||
" Bootstrap\n"
|
||||
" -T [filename] Bootstrap configuration file\n"
|
||||
" -Z [selector] Bootstrap selector used in configuration\n"
|
||||
"\n"
|
||||
" Eamuse\n"
|
||||
" -P [pcbid] Specify PCBID\n"
|
||||
" -R [softid] Specify Soft ID\n"
|
||||
" -S [url] Specify service url\n"
|
||||
" -U [0/1] Specify url_slash enabled/disabled\n"
|
||||
"\n"
|
||||
" Hook\n"
|
||||
" -H [filename] Load hook DLL (can be specified multiple "
|
||||
"times)\n"
|
||||
" -B [filename] Load pre-hook DLL loaded before avs boot "
|
||||
"(can be specified multiple times)\n"
|
||||
" -I [filename] Load pre-hook DLL that overrides IAT reference "
|
||||
"before execution"
|
||||
"(can be specified multiple times)\n"
|
||||
"before execution (can be specified multiple times)\n"
|
||||
"\n"
|
||||
" Logging\n"
|
||||
" -L [0/1/2/3] Log level for both console and file with "
|
||||
"increasing verbosity (0 = fatal, 1 = warn, 2 = info, 3 = misc)\n"
|
||||
" -Y [filename] Log to a file in addition to the console\n"
|
||||
"\n"
|
||||
" Debug\n"
|
||||
" -D Halt the launcher before bootstrapping AVS "
|
||||
"until a"
|
||||
" remote debugger is attached\n");
|
||||
"until a remote debugger is attached\n"
|
||||
" -C Log all loaded and final (property) "
|
||||
"configuration that launcher uses for bootstrapping. IMPORTANT: DO NOT "
|
||||
"ENABLE unless you know what you are doing. This prints sensitive data "
|
||||
"and credentials to the console and logfile. BE CAUTIOUS not to share "
|
||||
"this information before redaction.");
|
||||
}
|
||||
|
||||
void options_fini(struct options *options)
|
||||
{
|
||||
array_fini(&options->hook_dlls);
|
||||
array_fini(&options->before_hook_dlls);
|
||||
array_fini(&options->iat_hook_dlls);
|
||||
array_fini(&options->hook.hook_dlls);
|
||||
array_fini(&options->hook.before_hook_dlls);
|
||||
array_fini(&options->hook.iat_hook_dlls);
|
||||
|
||||
if (options->log.level) {
|
||||
free(options->log.level);
|
||||
}
|
||||
|
||||
if (options->eamuse.urlslash) {
|
||||
free(options->eamuse.urlslash);
|
||||
}
|
||||
}
|
||||
|
@ -4,25 +4,53 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core/log-bt.h"
|
||||
#include "core/log.h"
|
||||
|
||||
#include "launcher/bootstrap-config.h"
|
||||
|
||||
#include "util/array.h"
|
||||
|
||||
// Launcher options (cmd params) are limited to:
|
||||
// - Options to run a (vanilla) game without additional launcher features, e.g.
|
||||
// hooking
|
||||
// - Options that are handy to have for development/debugging purpose, e.g.
|
||||
// quickly switching on/off
|
||||
// logging levels
|
||||
//
|
||||
// Everything else is driven by a composable configuration file (launcher.xml)
|
||||
struct options {
|
||||
size_t std_heap_size;
|
||||
size_t avs_heap_size;
|
||||
const char *app_config_path;
|
||||
const char *avs_config_path;
|
||||
const char *ea3_config_path;
|
||||
const char *softid;
|
||||
const char *pcbid;
|
||||
const char *module;
|
||||
const char *logfile;
|
||||
struct array hook_dlls;
|
||||
struct array before_hook_dlls;
|
||||
struct array iat_hook_dlls;
|
||||
bool remote_debugger;
|
||||
const char *override_service;
|
||||
bool override_urlslash_enabled;
|
||||
bool override_urlslash_value;
|
||||
struct options_launcher {
|
||||
const char *config_path;
|
||||
} launcher;
|
||||
|
||||
struct options_bootstrap {
|
||||
const char *config_path;
|
||||
const char *selector;
|
||||
} bootstrap;
|
||||
|
||||
struct options_log {
|
||||
enum core_log_bt_log_level *level;
|
||||
const char *file_path;
|
||||
} log;
|
||||
|
||||
struct options_eamuse {
|
||||
const char *softid;
|
||||
const char *pcbid;
|
||||
const char *service_url;
|
||||
bool *urlslash;
|
||||
} eamuse;
|
||||
|
||||
struct options_hook {
|
||||
struct array hook_dlls;
|
||||
struct array before_hook_dlls;
|
||||
struct array iat_hook_dlls;
|
||||
} hook;
|
||||
|
||||
struct options_debug {
|
||||
bool remote_debugger;
|
||||
bool log_property_configs;
|
||||
} debug;
|
||||
};
|
||||
|
||||
void options_init(struct options *options);
|
||||
|
891
src/main/launcher/property-util.c
Normal file
891
src/main/launcher/property-util.c
Normal file
@ -0,0 +1,891 @@
|
||||
#define LOG_MODULE "property-util"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "avs-util/error.h"
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define PROPERTY_STRUCTURE_META_SIZE 576
|
||||
|
||||
typedef void (*rewinder)(uint32_t context);
|
||||
|
||||
struct cstring_read_handle {
|
||||
const char *buffer;
|
||||
size_t buffer_len;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
struct property_util_node_merge_ctx {
|
||||
const char *path;
|
||||
const struct property_util_node_merge_strategies *strategies;
|
||||
};
|
||||
|
||||
static struct property *property_util_do_load(
|
||||
avs_reader_t reader, rewinder rewinder, uint32_t context, const char *name)
|
||||
{
|
||||
struct property *prop;
|
||||
void *buffer;
|
||||
int nbytes;
|
||||
|
||||
nbytes = property_read_query_memsize(reader, context, 0, 0);
|
||||
|
||||
if (nbytes < 0) {
|
||||
log_fatal("%s: Error querying configuration file", name);
|
||||
}
|
||||
|
||||
buffer = xmalloc(nbytes);
|
||||
prop = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
nbytes);
|
||||
|
||||
if (!prop) {
|
||||
log_fatal(
|
||||
"%s: Creating property failed: %s",
|
||||
name,
|
||||
avs_util_property_error_get_and_clear(prop));
|
||||
}
|
||||
|
||||
rewinder(context);
|
||||
|
||||
if (!property_insert_read(prop, 0, reader, context)) {
|
||||
log_fatal(
|
||||
"%s: Error reading configuration file: %s",
|
||||
name,
|
||||
avs_util_property_error_get_and_clear(prop));
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
static int property_util_fread(uint32_t context, void *bytes, size_t nbytes)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = TlsGetValue(context);
|
||||
|
||||
return fread(bytes, 1, nbytes, f);
|
||||
}
|
||||
|
||||
static void property_util_frewind(uint32_t context)
|
||||
{
|
||||
FILE *f = TlsGetValue(context);
|
||||
rewind(f);
|
||||
}
|
||||
|
||||
static void property_util_log_node_tree_rec(
|
||||
struct property_node *parent_node, const char *parent_path)
|
||||
{
|
||||
char cur_path[PROPERTY_NODE_PATH_LEN_MAX];
|
||||
char cur_node_name[PROPERTY_NODE_NAME_SIZE_MAX];
|
||||
|
||||
struct property_node *child_node;
|
||||
enum property_type property_type;
|
||||
|
||||
int8_t value_s8;
|
||||
int16_t value_s16;
|
||||
int32_t value_s32;
|
||||
int64_t value_s64;
|
||||
uint8_t value_u8;
|
||||
uint16_t value_u16;
|
||||
uint32_t value_u32;
|
||||
uint64_t value_u64;
|
||||
char value_str[4096];
|
||||
bool value_bool;
|
||||
|
||||
avs_error error;
|
||||
|
||||
// Carry on the full root path down the node tree
|
||||
property_node_name(parent_node, cur_node_name, sizeof(cur_node_name));
|
||||
|
||||
str_cpy(cur_path, sizeof(cur_path), parent_path);
|
||||
str_cat(cur_path, sizeof(cur_path), "/");
|
||||
str_cat(cur_path, sizeof(cur_path), cur_node_name);
|
||||
|
||||
child_node = property_node_traversal(parent_node, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
// parent node is a leaf node, print all data of it
|
||||
if (child_node == NULL) {
|
||||
property_type = property_node_type(parent_node);
|
||||
|
||||
switch (property_type) {
|
||||
case PROPERTY_TYPE_VOID:
|
||||
log_misc("%s: <VOID>", cur_path);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_S8:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_s8, sizeof(value_s8));
|
||||
log_misc("%s: %" PRId8, cur_path, value_s8);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_S16:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_s16, sizeof(value_s16));
|
||||
log_misc("%s: %" PRId16, cur_path, value_s16);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_S32:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_s32, sizeof(value_s32));
|
||||
log_misc("%s: %" PRId32, cur_path, value_s32);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_S64:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_s64, sizeof(value_s64));
|
||||
log_misc("%s: %" PRId64, cur_path, value_s64);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_U8:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_u8, sizeof(value_u8));
|
||||
log_misc("%s: %" PRIu8, cur_path, value_u8);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_U16:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_u16, sizeof(value_u16));
|
||||
log_misc("%s: %" PRIu16, cur_path, value_u16);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_U32:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_u32, sizeof(value_u32));
|
||||
log_misc("%s: %" PRIu32, cur_path, value_u32);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_U64:
|
||||
property_node_read(
|
||||
parent_node, property_type, &value_u64, sizeof(value_u64));
|
||||
log_misc("%s: %" PRIu64, cur_path, value_u64);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_STR:
|
||||
property_node_read(
|
||||
parent_node, property_type, value_str, sizeof(value_str));
|
||||
log_misc("%s: %s", cur_path, value_str);
|
||||
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_BOOL:
|
||||
property_node_read(
|
||||
parent_node,
|
||||
property_type,
|
||||
&value_bool,
|
||||
sizeof(value_bool));
|
||||
log_misc("%s: %d", cur_path, value_bool);
|
||||
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_BIN:
|
||||
log_misc("%s: <BINARY>", cur_path);
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_ATTR:
|
||||
error = property_node_read(
|
||||
parent_node, property_type, value_str, sizeof(value_str));
|
||||
|
||||
if (AVS_IS_ERROR(error)) {
|
||||
log_fatal(
|
||||
"%s, property read failed: %s",
|
||||
cur_path,
|
||||
avs_util_error_str(error));
|
||||
}
|
||||
|
||||
log_misc("%s@: %s", cur_path, value_str);
|
||||
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_VOID_WITH_ATTRIBUTES:
|
||||
log_misc("%s: <VOID>", cur_path);
|
||||
|
||||
child_node =
|
||||
property_node_traversal(parent_node, TRAVERSE_FIRST_ATTR);
|
||||
|
||||
while (child_node) {
|
||||
property_util_log_node_tree_rec(child_node, cur_path);
|
||||
|
||||
child_node = property_node_traversal(
|
||||
child_node, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROPERTY_TYPE_STR_WITH_ATTRIBUTES:
|
||||
error = property_node_read(
|
||||
parent_node, property_type, value_str, sizeof(value_str));
|
||||
|
||||
if (AVS_IS_ERROR(error)) {
|
||||
log_fatal(
|
||||
"%s, property read failed: %s",
|
||||
cur_path,
|
||||
avs_util_error_str(error));
|
||||
}
|
||||
|
||||
log_misc("%s: %s", cur_path, value_str);
|
||||
|
||||
child_node =
|
||||
property_node_traversal(parent_node, TRAVERSE_FIRST_ATTR);
|
||||
|
||||
while (child_node) {
|
||||
property_util_log_node_tree_rec(child_node, cur_path);
|
||||
|
||||
child_node = property_node_traversal(
|
||||
child_node, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_misc("%s: <UNKNOWN TYPE> (%d)", cur_path, property_type);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
while (child_node) {
|
||||
property_util_log_node_tree_rec(child_node, cur_path);
|
||||
|
||||
child_node =
|
||||
property_node_traversal(child_node, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
property_util_cstring_read(uint32_t context, void *bytes, size_t nbytes)
|
||||
{
|
||||
int result = 0;
|
||||
struct cstring_read_handle *h = TlsGetValue(context);
|
||||
|
||||
if (h->offset < h->buffer_len) {
|
||||
result = min(nbytes, h->buffer_len - h->offset);
|
||||
memcpy(bytes, (const void *) (h->buffer + h->offset), result);
|
||||
h->offset += result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void property_util_cstring_rewind(uint32_t context)
|
||||
{
|
||||
struct cstring_read_handle *h = TlsGetValue(context);
|
||||
h->offset = 0;
|
||||
}
|
||||
|
||||
static int property_util_avs_read(uint32_t context, void *bytes, size_t nbytes)
|
||||
{
|
||||
avs_desc desc = (avs_desc) context;
|
||||
return avs_fs_read(desc, bytes, nbytes);
|
||||
}
|
||||
|
||||
static void property_util_avs_rewind(uint32_t context)
|
||||
{
|
||||
avs_desc desc = (avs_desc) context;
|
||||
avs_fs_lseek(desc, 0, AVS_SEEK_SET);
|
||||
}
|
||||
|
||||
static void _property_util_node_merge_recursive(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source,
|
||||
void *ctx)
|
||||
{
|
||||
uint8_t i;
|
||||
bool consumed;
|
||||
struct property_node *result;
|
||||
|
||||
const struct property_util_node_merge_ctx *ctx_;
|
||||
struct property_util_node_merge_ctx ctx_next;
|
||||
|
||||
char parent_name[PROPERTY_NODE_NAME_SIZE_MAX];
|
||||
char parent_path[PROPERTY_NODE_PATH_LEN_MAX];
|
||||
|
||||
log_assert(source);
|
||||
log_assert(ctx);
|
||||
|
||||
ctx_ = (const struct property_util_node_merge_ctx *) ctx;
|
||||
|
||||
log_assert(ctx_->path);
|
||||
log_assert(ctx_->strategies);
|
||||
log_assert(ctx_->strategies->num > 0);
|
||||
|
||||
// Default to copying to an empty node
|
||||
if (!parent) {
|
||||
result = property_node_clone(parent_property, NULL, source, true);
|
||||
|
||||
if (!result) {
|
||||
log_fatal("Copying '%s' into empty parent failed", ctx_->path);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
property_node_name(parent, parent_name, sizeof(parent_name));
|
||||
|
||||
str_cpy(parent_path, sizeof(parent_path), ctx_->path);
|
||||
str_cat(parent_path, sizeof(parent_path), "/");
|
||||
str_cat(parent_path, sizeof(parent_path), parent_name);
|
||||
|
||||
ctx_next.path = parent_path;
|
||||
ctx_next.strategies = ctx_->strategies;
|
||||
|
||||
consumed = false;
|
||||
|
||||
// Apply given strategies, one MUST consume
|
||||
for (i = 0; i < ctx_->strategies->num; i++) {
|
||||
log_assert(ctx_->strategies->entry[i].path);
|
||||
|
||||
// path == "" matches everything
|
||||
if (str_eq(ctx_->strategies->entry[i].path, "") ||
|
||||
str_eq(ctx_->strategies->entry[i].path, parent_path)) {
|
||||
|
||||
consumed = ctx_->strategies->entry[i].merge_strategy_do(
|
||||
parent_property,
|
||||
parent,
|
||||
source,
|
||||
&ctx_next,
|
||||
_property_util_node_merge_recursive);
|
||||
|
||||
log_misc(
|
||||
"Merge strategy for '%s' consumed: %d",
|
||||
ctx_->strategies->entry[i].path,
|
||||
consumed);
|
||||
|
||||
if (consumed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(consumed);
|
||||
}
|
||||
|
||||
void property_util_log(struct property *property)
|
||||
{
|
||||
property_util_log_node_tree_rec(property_search(property, NULL, "/"), "");
|
||||
}
|
||||
|
||||
void property_util_node_log(struct property_node *node)
|
||||
{
|
||||
property_util_log_node_tree_rec(node, "");
|
||||
}
|
||||
|
||||
struct property *property_util_load(const char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
uint32_t f_keyhole;
|
||||
struct property *prop;
|
||||
|
||||
log_assert(filename);
|
||||
|
||||
/* AVS callbacks are only given a 32-bit context parameter, even in 64-bit
|
||||
builds of AVS. We allocate a 32-bit TLS key and pass the context in this
|
||||
manner instead. Inefficient, but it works. */
|
||||
|
||||
f = fopen(filename, "r");
|
||||
|
||||
f_keyhole = TlsAlloc();
|
||||
TlsSetValue(f_keyhole, f);
|
||||
|
||||
if (f == NULL) {
|
||||
log_fatal("%s: Error opening configuration file", filename);
|
||||
}
|
||||
|
||||
prop = property_util_do_load(
|
||||
property_util_fread, property_util_frewind, f_keyhole, filename);
|
||||
|
||||
TlsFree(f_keyhole);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
struct property *property_util_avs_fs_load(const char *filename)
|
||||
{
|
||||
avs_desc desc;
|
||||
struct property *prop;
|
||||
|
||||
log_assert(filename);
|
||||
|
||||
desc = avs_fs_open(filename, AVS_FILE_READ, AVS_FILE_FLAG_SHARE_READ);
|
||||
|
||||
if (!desc) {
|
||||
log_fatal("%s: Error opening configuration file", filename);
|
||||
}
|
||||
|
||||
prop = property_util_do_load(
|
||||
property_util_avs_read, property_util_avs_rewind, desc, filename);
|
||||
|
||||
avs_fs_close(desc);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
struct property *property_util_cstring_load(const char *cstring)
|
||||
{
|
||||
uint32_t s_keyhole;
|
||||
struct property *prop;
|
||||
|
||||
log_assert(cstring);
|
||||
|
||||
// see above
|
||||
struct cstring_read_handle read_handle;
|
||||
read_handle.buffer = cstring;
|
||||
read_handle.buffer_len = strlen(cstring);
|
||||
read_handle.offset = 0;
|
||||
|
||||
s_keyhole = TlsAlloc();
|
||||
TlsSetValue(s_keyhole, &read_handle);
|
||||
|
||||
prop = property_util_do_load(
|
||||
property_util_cstring_read,
|
||||
property_util_cstring_rewind,
|
||||
s_keyhole,
|
||||
"<string>");
|
||||
|
||||
TlsFree(s_keyhole);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
struct property *property_util_clone(struct property *property)
|
||||
{
|
||||
struct property *clone;
|
||||
size_t size;
|
||||
void *buffer;
|
||||
|
||||
struct property_node *node_property;
|
||||
struct property_node *node_clone;
|
||||
|
||||
log_assert(property);
|
||||
|
||||
size = property_util_property_query_real_size(property);
|
||||
|
||||
buffer = xmalloc(size);
|
||||
|
||||
clone = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
size);
|
||||
|
||||
if (!clone) {
|
||||
log_fatal("Creating property failed");
|
||||
}
|
||||
|
||||
node_property = property_search(property, NULL, "/");
|
||||
node_clone = property_search(clone, NULL, "/");
|
||||
|
||||
if (!property_node_clone(clone, node_clone, node_property, true)) {
|
||||
log_fatal(
|
||||
"Cloning property data failed: %s",
|
||||
avs_util_property_error_get_and_clear(clone));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
void property_util_free(struct property *prop)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
buffer = property_desc_to_buffer(prop);
|
||||
property_destroy(prop);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
uint32_t property_util_property_query_real_size(struct property *property)
|
||||
{
|
||||
avs_error size;
|
||||
|
||||
log_assert(property);
|
||||
|
||||
// Returns the size of the actual data in the property structure only
|
||||
// Hence, using that size only, allocating another buffer for a copy
|
||||
// of this might fail or copying the data will fail because the buffer
|
||||
// is too small
|
||||
size = property_query_size(property);
|
||||
|
||||
if (AVS_IS_ERROR(size)) {
|
||||
log_fatal(
|
||||
"Querying property size failed: %s", avs_util_error_str(size));
|
||||
}
|
||||
|
||||
// Hack: *2 to have enough space and not cut off data when cloning/copying
|
||||
// property data because...reasons? I haven't figured this one out and
|
||||
// there doesn't seem to be an actual API call for that to return the
|
||||
// "true" size that allows the caller to figure out how much memory
|
||||
// they have to allocate to create a copy of the property structure
|
||||
// with property_create and
|
||||
return (PROPERTY_STRUCTURE_META_SIZE + size) * 2;
|
||||
}
|
||||
|
||||
void property_util_node_u8_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
uint8_t val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(name);
|
||||
|
||||
tmp = property_search(property, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(property, node, PROPERTY_TYPE_U8, name, val);
|
||||
|
||||
if (!tmp) {
|
||||
log_fatal(
|
||||
"Creating node '%s' failed: %s",
|
||||
name,
|
||||
property ? avs_util_property_error_get_and_clear(property) :
|
||||
"unknown");
|
||||
}
|
||||
}
|
||||
|
||||
void property_util_node_u16_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
uint16_t val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(name);
|
||||
|
||||
tmp = property_search(property, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(property, node, PROPERTY_TYPE_U16, name, val);
|
||||
|
||||
if (!tmp) {
|
||||
log_fatal(
|
||||
"Creating node '%s' failed: %s",
|
||||
name,
|
||||
property ? avs_util_property_error_get_and_clear(property) :
|
||||
"unknown");
|
||||
}
|
||||
}
|
||||
|
||||
void property_util_node_u32_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
uint32_t val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(name);
|
||||
|
||||
tmp = property_search(property, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(property, node, PROPERTY_TYPE_U32, name, val);
|
||||
|
||||
if (!tmp) {
|
||||
log_fatal(
|
||||
"Creating node '%s' failed: %s",
|
||||
name,
|
||||
property ? avs_util_property_error_get_and_clear(property) :
|
||||
"unknown");
|
||||
}
|
||||
}
|
||||
|
||||
void property_util_node_str_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
const char *val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(name);
|
||||
|
||||
tmp = property_search(property, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(property, node, PROPERTY_TYPE_STR, name, val);
|
||||
|
||||
if (!tmp) {
|
||||
log_fatal(
|
||||
"Creating node '%s' failed: %s",
|
||||
name,
|
||||
property ? avs_util_property_error_get_and_clear(property) :
|
||||
"unknown");
|
||||
}
|
||||
}
|
||||
|
||||
void property_util_node_bool_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
bool val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(name);
|
||||
|
||||
tmp = property_search(property, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(property, node, PROPERTY_TYPE_BOOL, name, val);
|
||||
|
||||
if (!tmp) {
|
||||
log_fatal(
|
||||
"Creating node '%s' failed: %s",
|
||||
name,
|
||||
property ? avs_util_property_error_get_and_clear(property) :
|
||||
"unknown");
|
||||
}
|
||||
}
|
||||
|
||||
void property_util_node_attribute_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
const char *val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
log_assert(node);
|
||||
log_assert(name);
|
||||
|
||||
tmp = property_search(property, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(property, node, PROPERTY_TYPE_ATTR, name, val);
|
||||
}
|
||||
|
||||
struct property *
|
||||
property_util_many_merge(struct property **properties, size_t count)
|
||||
{
|
||||
struct property *merged_property;
|
||||
struct property *tmp;
|
||||
int i;
|
||||
|
||||
log_assert(properties);
|
||||
log_assert(count > 0);
|
||||
|
||||
merged_property = property_util_clone(properties[0]);
|
||||
|
||||
if (count == 1) {
|
||||
return merged_property;
|
||||
}
|
||||
|
||||
for (i = 1; i < count; i++) {
|
||||
tmp = property_util_merge(merged_property, properties[i]);
|
||||
|
||||
property_util_free(merged_property);
|
||||
merged_property = tmp;
|
||||
}
|
||||
|
||||
return merged_property;
|
||||
}
|
||||
|
||||
struct property *property_util_node_extract(struct property_node *node)
|
||||
{
|
||||
struct property *property;
|
||||
struct property_node *root_node;
|
||||
uint32_t size;
|
||||
void *buffer;
|
||||
struct property_node *result;
|
||||
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Hack: Is it even possible to get the size of a (sub-) node without
|
||||
// the property? 256kb should be fine for now, even for larger
|
||||
// configurations. Obviously, this scales horribly and wastes a lot of
|
||||
// memory for most smaller sub-nodes
|
||||
size = 1024 * 256;
|
||||
|
||||
buffer = xmalloc(size);
|
||||
property = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
size);
|
||||
root_node = property_search(property, NULL, "");
|
||||
|
||||
result = property_node_clone(property, root_node, node, true);
|
||||
|
||||
if (!result) {
|
||||
log_fatal("Cloning node into empty property failed");
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
struct property *
|
||||
property_util_merge(struct property *parent, struct property *source)
|
||||
{
|
||||
struct property_util_node_merge_strategies strategies;
|
||||
|
||||
log_assert(parent);
|
||||
log_assert(source);
|
||||
|
||||
strategies.num = 1;
|
||||
|
||||
strategies.entry[0].path = "";
|
||||
strategies.entry[0].merge_strategy_do =
|
||||
property_util_node_merge_default_strategy_do;
|
||||
|
||||
return property_util_merge_with_strategies(parent, source, &strategies);
|
||||
}
|
||||
|
||||
struct property *property_util_merge_with_strategies(
|
||||
struct property *parent,
|
||||
struct property *source,
|
||||
const struct property_util_node_merge_strategies *strategies)
|
||||
{
|
||||
struct property_util_node_merge_ctx ctx;
|
||||
size_t total_size;
|
||||
void *buffer;
|
||||
struct property *merged;
|
||||
struct property_node *parent_node;
|
||||
struct property_node *source_node;
|
||||
|
||||
log_assert(parent);
|
||||
log_assert(source);
|
||||
log_assert(strategies);
|
||||
|
||||
// We can't estimate how these two are being merged as in how much new
|
||||
// data is being inserted from source into parent. Therefore, worse-case
|
||||
// estimate memory requirement for no overlap
|
||||
total_size = 0;
|
||||
total_size += property_util_property_query_real_size(parent);
|
||||
total_size += property_util_property_query_real_size(source);
|
||||
|
||||
buffer = xmalloc(total_size);
|
||||
|
||||
merged = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
total_size);
|
||||
|
||||
ctx.path = "";
|
||||
ctx.strategies = strategies;
|
||||
|
||||
parent_node = property_search(parent, NULL, "/");
|
||||
|
||||
if (!property_node_clone(merged, NULL, parent_node, true)) {
|
||||
log_fatal(
|
||||
"Copying parent base failed: %s",
|
||||
avs_util_property_error_get_and_clear(merged));
|
||||
}
|
||||
|
||||
// Grab parent_node from merged property which is the target one to merge
|
||||
// into
|
||||
parent_node = property_search(merged, NULL, "/");
|
||||
source_node = property_search(source, NULL, "/");
|
||||
|
||||
_property_util_node_merge_recursive(merged, parent_node, source_node, &ctx);
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
bool property_util_node_merge_default_strategy_do(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source,
|
||||
void *ctx,
|
||||
property_util_node_merge_recursion_do_t node_merge_recursion_do)
|
||||
{
|
||||
struct property_node *result;
|
||||
|
||||
struct property_node *parent_child;
|
||||
struct property_node *source_child;
|
||||
struct property_node *source_child_child;
|
||||
|
||||
char child_node_name[PROPERTY_NODE_NAME_SIZE_MAX];
|
||||
|
||||
log_assert(parent);
|
||||
log_assert(source);
|
||||
|
||||
source_child = property_node_traversal(source, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
while (source_child) {
|
||||
property_node_name(
|
||||
source_child, child_node_name, sizeof(child_node_name));
|
||||
|
||||
parent_child = property_search(NULL, parent, child_node_name);
|
||||
|
||||
if (parent_child) {
|
||||
source_child_child =
|
||||
property_node_traversal(source_child, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
if (source_child_child) {
|
||||
// Continue recursion if there are actually more children
|
||||
node_merge_recursion_do(
|
||||
parent_property, parent_child, source_child, ctx);
|
||||
} else {
|
||||
// Found identical leaf node, remove the matching parent's child
|
||||
// and copy the source child over to the parent and terminate
|
||||
// the recursion
|
||||
property_node_remove(parent_child);
|
||||
result = property_node_clone(
|
||||
parent_property, parent, source_child, true);
|
||||
|
||||
if (!result) {
|
||||
log_fatal(
|
||||
"Replacing leaf node '%s' failed", child_node_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Could not find an identical child on parent, copy source
|
||||
// recursively to parent
|
||||
result = property_node_clone(
|
||||
parent_property, parent, source_child, true);
|
||||
|
||||
if (!result) {
|
||||
log_fatal("Deep copying child '%s' failed", child_node_name);
|
||||
}
|
||||
}
|
||||
|
||||
source_child =
|
||||
property_node_traversal(source_child, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
|
||||
// Default strategy always consumes
|
||||
return true;
|
||||
}
|
99
src/main/launcher/property-util.h
Normal file
99
src/main/launcher/property-util.h
Normal file
@ -0,0 +1,99 @@
|
||||
#ifndef PROPERTY_UTIL_H
|
||||
#define PROPERTY_UTIL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
// Guestimate, should be long enough, I hope?
|
||||
#define PROPERTY_NODE_PATH_LEN_MAX 4096
|
||||
// 256 found in AVS code as size used on property_node_name
|
||||
#define PROPERTY_NODE_NAME_SIZE_MAX 256
|
||||
// Guestimate, should be enough, I hope?
|
||||
#define PROPERTY_NODE_ATTR_NAME_SIZE_MAX 128
|
||||
|
||||
#define PROPERTY_UTIL_MAX_NODE_NAME_RESOLVERS 4
|
||||
|
||||
typedef void (*property_util_node_merge_recursion_do_t)(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source,
|
||||
void *ctx);
|
||||
|
||||
typedef bool (*property_util_node_merge_strategy_do_t)(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source,
|
||||
void *ctx,
|
||||
property_util_node_merge_recursion_do_t node_merge_recursion_do);
|
||||
|
||||
struct property_util_node_merge_strategies {
|
||||
struct {
|
||||
const char *path;
|
||||
property_util_node_merge_strategy_do_t merge_strategy_do;
|
||||
} entry[PROPERTY_UTIL_MAX_NODE_NAME_RESOLVERS];
|
||||
uint8_t num;
|
||||
};
|
||||
|
||||
void property_util_log(struct property *property);
|
||||
void property_util_node_log(struct property_node *node);
|
||||
struct property *property_util_load(const char *filename);
|
||||
struct property *property_util_avs_fs_load(const char *filename);
|
||||
struct property *property_util_cstring_load(const char *cstring);
|
||||
struct property *property_util_clone(struct property *property);
|
||||
void property_util_free(struct property *prop);
|
||||
uint32_t property_util_property_query_real_size(struct property *property);
|
||||
void property_util_node_u8_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
uint8_t val);
|
||||
void property_util_node_u16_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
uint16_t val);
|
||||
void property_util_node_u32_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
uint32_t val);
|
||||
void property_util_node_str_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
const char *val);
|
||||
void property_util_node_bool_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
bool val);
|
||||
void property_util_node_attribute_replace(
|
||||
struct property *property,
|
||||
struct property_node *node,
|
||||
const char *name,
|
||||
const char *val);
|
||||
|
||||
struct property *
|
||||
property_util_many_merge(struct property **properties, size_t count);
|
||||
struct property *property_util_node_extract(struct property_node *node);
|
||||
|
||||
struct property *
|
||||
property_util_merge(struct property *parent, struct property *source);
|
||||
|
||||
// Strategies are applied in order and first consumer terminates
|
||||
// applying further strategies Typically, you want to include the default
|
||||
// strategy after your custom strategies for special cases
|
||||
struct property *property_util_merge_with_strategies(
|
||||
struct property *parent,
|
||||
struct property *source,
|
||||
const struct property_util_node_merge_strategies *strategies);
|
||||
|
||||
bool property_util_node_merge_default_strategy_do(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source,
|
||||
void *ctx,
|
||||
property_util_node_merge_recursion_do_t node_merge_recursion_do);
|
||||
|
||||
#endif
|
@ -1,135 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/property.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static int boot_property_fread(uint32_t context, void *bytes, size_t nbytes)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = TlsGetValue(context);
|
||||
|
||||
return fread(bytes, 1, nbytes, f);
|
||||
}
|
||||
|
||||
struct cstring_read_handle {
|
||||
const char *buffer;
|
||||
size_t buffer_len;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
static int
|
||||
boot_property_cstring_read(uint32_t context, void *bytes, size_t nbytes)
|
||||
{
|
||||
int result = 0;
|
||||
struct cstring_read_handle *h = TlsGetValue(context);
|
||||
|
||||
if (h->offset < h->buffer_len) {
|
||||
result = min(nbytes, h->buffer_len - h->offset);
|
||||
memcpy(bytes, (const void *) (h->buffer + h->offset), result);
|
||||
h->offset += result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct property *boot_property_load(const char *filename)
|
||||
{
|
||||
struct property *prop;
|
||||
void *buffer;
|
||||
int nbytes;
|
||||
FILE *f;
|
||||
uint32_t f_keyhole;
|
||||
|
||||
/* AVS callbacks are only given a 32-bit context parameter, even in 64-bit
|
||||
builds of AVS. We allocate a 32-bit TLS key and pass the context in this
|
||||
manner instead. Inefficient, but it works. */
|
||||
|
||||
f = fopen(filename, "r");
|
||||
|
||||
f_keyhole = TlsAlloc();
|
||||
TlsSetValue(f_keyhole, f);
|
||||
|
||||
if (f == NULL) {
|
||||
log_fatal("%s: Error opening configuration file", filename);
|
||||
}
|
||||
|
||||
nbytes = property_read_query_memsize(boot_property_fread, f_keyhole, 0, 0);
|
||||
|
||||
if (nbytes < 0) {
|
||||
log_fatal("%s: Error querying configuration file", filename);
|
||||
}
|
||||
|
||||
buffer = xmalloc(nbytes);
|
||||
prop = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
nbytes);
|
||||
rewind(f);
|
||||
|
||||
if (!property_insert_read(prop, 0, boot_property_fread, f_keyhole)) {
|
||||
log_fatal("%s: Error reading configuration file", filename);
|
||||
}
|
||||
|
||||
TlsFree(f_keyhole);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return prop;
|
||||
}
|
||||
struct property *boot_property_load_cstring(const char *cstring)
|
||||
{
|
||||
struct property *prop;
|
||||
void *buffer;
|
||||
int nbytes;
|
||||
uint32_t s_keyhole;
|
||||
|
||||
// see above
|
||||
struct cstring_read_handle read_handle;
|
||||
read_handle.buffer = cstring;
|
||||
read_handle.buffer_len = strlen(cstring);
|
||||
read_handle.offset = 0;
|
||||
|
||||
s_keyhole = TlsAlloc();
|
||||
TlsSetValue(s_keyhole, &read_handle);
|
||||
|
||||
nbytes = property_read_query_memsize(
|
||||
boot_property_cstring_read, s_keyhole, 0, 0);
|
||||
|
||||
if (nbytes < 0) {
|
||||
log_fatal("Error querying configuration string");
|
||||
}
|
||||
|
||||
buffer = xmalloc(nbytes);
|
||||
prop = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
nbytes);
|
||||
|
||||
read_handle.offset = 0;
|
||||
if (!property_insert_read(prop, 0, boot_property_cstring_read, s_keyhole)) {
|
||||
log_fatal("Error inserting configuration string");
|
||||
}
|
||||
|
||||
TlsFree(s_keyhole);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
void boot_property_free(struct property *prop)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
buffer = property_desc_to_buffer(prop);
|
||||
property_destroy(prop);
|
||||
free(buffer);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#ifndef LAUNCHER_PROPERTY_H
|
||||
#define LAUNCHER_PROPERTY_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
struct property *boot_property_load(const char *filename);
|
||||
struct property *boot_property_load_cstring(const char *cstring);
|
||||
void boot_property_free(struct property *prop);
|
||||
|
||||
#endif
|
@ -1,3 +1,5 @@
|
||||
#define LOG_MODULE "stubs"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
@ -5,12 +7,13 @@
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "launcher/stubs.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
struct ikey_status {
|
||||
uint32_t field_0;
|
||||
@ -116,6 +119,8 @@ static void *STDCALL my_GetProcAddress(HMODULE dll, const char *name)
|
||||
|
||||
void stubs_init(void)
|
||||
{
|
||||
log_info("Init");
|
||||
|
||||
hook_table_apply(
|
||||
NULL, "kernel32.dll", stub_hook_syms, lengthof(stub_hook_syms));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user