1
0
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:
icex2 2024-02-25 09:36:53 +01:00
parent cd798ebe77
commit a243791d5c
37 changed files with 4917 additions and 943 deletions

View File

@ -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 \

View 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;
}

View 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

View File

@ -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);
}

View File

@ -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
View 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
View 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

View 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");
}

View 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 */

View 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();
}
}

View 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
View 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);
}
}

View File

@ -0,0 +1,6 @@
#ifndef LAUNCHER_DEBUG_H
#define LAUNCHER_DEBUG_H
void debug_remote_debugger_trap();
#endif

View File

@ -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);
}

View File

@ -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

View 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);
}

View 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

View 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);
}

View 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

View 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");
}

View 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
View 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
View File

@ -0,0 +1,6 @@
#ifndef LAUNCHER_HOOK_H
#define LAUNCHER_HOOK_H
void hook_load_dll(const char *path);
#endif

View 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);
}
}

View 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

View 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);
}

View 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

View File

@ -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;
}
}

View File

@ -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");
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View 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;
}

View 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

View File

@ -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);
}

View File

@ -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

View File

@ -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));
}