WIP: feat: bemanitools 6, wip (TBD needs splitting and reviewing) #305

Draft
icex2 wants to merge 90 commits from bemanitools-6-alpha into master
37 changed files with 4917 additions and 943 deletions
Showing only changes of commit 35ca49fe7e - Show all commits
+15 -3
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 \
+773
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;
}
+57
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
-72
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);
}
-20
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
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
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
+548
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");
}
+139
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 */
+347
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();
}
}
+32
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
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);
}
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef LAUNCHER_DEBUG_H
#define LAUNCHER_DEBUG_H
void debug_remote_debugger_trap();
#endif
-190
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);
}
-46
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
+104
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);
}
+37
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
+125
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);
}
+23
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
+25
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");
}
+9
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
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
View File
@@ -0,0 +1,6 @@
#ifndef LAUNCHER_HOOK_H
#define LAUNCHER_HOOK_H
void hook_load_dll(const char *path);
#endif
+371
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);
}
}
+56
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
+587
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);
}
+8
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
+4 -319
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;
}
}
+144 -15
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");
}
+10 -8
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
+98 -108
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);
}
}
+44 -16
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);
+891
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;
}
+99
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
-135
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);
}
-10
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
+6 -1
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));
}