refactor(launcher): Major re-work of launcher
Kudos to Shiz for providing the groundwork for this. Fundamentally re-think how launcher operates and bootstrapping the games is managed and configured. This brings it significantly closer to how the original bootstrap is doing the job: launcher now utilizes the data (structures) provided by the bootstrap.xml configuration file. This creates compatibility with vanilla data dumps and original stock images. Note that bemanitools does not include any code or means to run DRM'd data, only decrypted. But, this allows users to keep decrypted dumps as stock as possible which means: * No copying around of property files anymore * Keep the modules/ folder with the binaries * Have bemanitools binaries separate in the data * No need to edit/customize the original configuration files A list of key features of the "new" launcher: * Boostrap games by following the configuration provided by stock game's bootstrap.xml files * Custom launcher.xml configuration file that adds further launcher configurable features, composability of bootstrap.xml configuration(s) as well as configuration overriding/stacking of selected types of configurations, e.g. eamuse config, avs-config. The latter eliminates the need for modifying stock config files in the prop/ folder * Unified logging system: launcher and AVS logging uses the same logger, all output can now be in a single file * Original features such as various hook types still available Due to the significant architectural changes, this also breaks with any backwards compatibility to existing launcher setups. Thus, users need to migrate by re-applying the new configuration format and migrating their config parameters accordingly. Further migration instructions and updated documentation will be provided upon release. Co-authored-by: Shiz <hi@shiz.me>
This commit is contained in:
@@ -3,22 +3,35 @@ rc_launcher := launcher.rc
|
||||
|
||||
ldflags_launcher := \
|
||||
-mconsole \
|
||||
-ldbghelp \
|
||||
|
||||
deplibs_launcher := \
|
||||
avs \
|
||||
avs-ea3 \
|
||||
|
||||
libs_launcher := \
|
||||
avs-util \
|
||||
hook \
|
||||
util \
|
||||
dwarfstack \
|
||||
|
||||
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 \
|
||||
logger.c \
|
||||
main.c \
|
||||
module.c \
|
||||
options.c \
|
||||
property.c \
|
||||
property-util.c \
|
||||
stubs.c \
|
||||
version.c \
|
||||
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
#define LOG_MODULE "avs-config"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs-util/error.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define AVS_CONFIG_ROOT_NODE "/config"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 log_level loglevel)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
log_assert(node);
|
||||
|
||||
switch (loglevel) {
|
||||
case LOG_LEVEL_FATAL:
|
||||
str = "fatal";
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_WARNING:
|
||||
str = "warn";
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_INFO:
|
||||
str = "info";
|
||||
break;
|
||||
|
||||
case 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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef LAUNCHER_AVS_CONFIG_H
|
||||
#define LAUNCHER_AVS_CONFIG_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/bootstrap-config.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
struct property *avs_config_load(const char *filepath);
|
||||
struct property_node *avs_config_root_get(struct property *property);
|
||||
|
||||
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 log_level loglevel);
|
||||
void avs_config_local_fs_path_dev_nvram_and_raw_set(
|
||||
struct property_node *node, const char *dev_nvram_raw_path);
|
||||
|
||||
#endif
|
||||
@@ -1,72 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-context.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static void *avs_heap;
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
static void *std_heap;
|
||||
#endif
|
||||
|
||||
void avs_context_init(
|
||||
struct property_node *config,
|
||||
uint32_t avs_heap_size,
|
||||
uint32_t std_heap_size,
|
||||
avs_log_writer_t log_writer,
|
||||
void *log_writer_ctx)
|
||||
{
|
||||
avs_heap = VirtualAlloc(
|
||||
NULL, avs_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
if (avs_heap == NULL) {
|
||||
log_fatal(
|
||||
"Failed to VirtualAlloc %d byte AVS heap: %08x",
|
||||
avs_heap_size,
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
std_heap = VirtualAlloc(
|
||||
NULL, std_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
if (std_heap == NULL) {
|
||||
log_fatal(
|
||||
"Failed to VirtualAlloc %d byte \"std\" heap: %08x",
|
||||
std_heap_size,
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
avs_boot(
|
||||
config,
|
||||
std_heap,
|
||||
std_heap_size,
|
||||
avs_heap,
|
||||
avs_heap_size,
|
||||
log_writer,
|
||||
log_writer_ctx);
|
||||
#else
|
||||
/* AVS v2.16.xx and I suppose onward uses a unified heap */
|
||||
avs_boot(config, avs_heap, avs_heap_size, NULL, log_writer, log_writer_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void avs_context_fini(void)
|
||||
{
|
||||
avs_shutdown();
|
||||
|
||||
#ifdef AVS_HAS_STD_HEAP
|
||||
VirtualFree(std_heap, 0, MEM_RELEASE);
|
||||
#endif
|
||||
|
||||
VirtualFree(avs_heap, 0, MEM_RELEASE);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef LAUNCHER_AVS_CONTEXT_H
|
||||
#define LAUNCHER_AVS_CONTEXT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#if AVS_VERSION < 1600
|
||||
#define AVS_HAS_STD_HEAP
|
||||
#endif
|
||||
|
||||
void avs_context_init(
|
||||
struct property_node *config,
|
||||
uint32_t avs_heap_size,
|
||||
uint32_t std_heap_size,
|
||||
avs_log_writer_t log_writer,
|
||||
void *log_writer_ctx);
|
||||
void avs_context_fini(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,307 @@
|
||||
#define LOG_MODULE "avs"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/avs-config.h"
|
||||
#include "launcher/avs.h"
|
||||
#include "launcher/logger.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/codepage.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/log.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
|
||||
logger_log_avs_log_message(utf8, utf8_len);
|
||||
|
||||
/* Clean up */
|
||||
|
||||
free(utf8);
|
||||
free(utf16);
|
||||
}
|
||||
|
||||
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_mountpoint_dir_create(
|
||||
struct property_node *node, const char *folder_name)
|
||||
{
|
||||
char fs_path[1024];
|
||||
char fs_type[255];
|
||||
char device_path[1024];
|
||||
struct property_node *fs_node;
|
||||
int res;
|
||||
|
||||
memset(fs_path, 0, sizeof(fs_path));
|
||||
memset(fs_type, 0, sizeof(fs_type));
|
||||
|
||||
str_cpy(fs_path, sizeof(fs_path), "/fs/");
|
||||
str_cat(fs_path, sizeof(fs_path), folder_name);
|
||||
|
||||
fs_node = property_search(NULL, node, fs_path);
|
||||
|
||||
if (!fs_node) {
|
||||
log_warning(
|
||||
"Could not find file system node %s in avs configuration", fs_path);
|
||||
return;
|
||||
}
|
||||
|
||||
res = property_node_refer(
|
||||
NULL,
|
||||
fs_node,
|
||||
"device",
|
||||
PROPERTY_TYPE_STR,
|
||||
device_path,
|
||||
sizeof(device_path));
|
||||
|
||||
if (res < 0) {
|
||||
log_fatal(
|
||||
"Getting 'device' attribute from avs config entry %s failed",
|
||||
fs_path);
|
||||
}
|
||||
|
||||
// 'fstype' attribute is optional and defaults to value 'fs'
|
||||
if (!property_search(NULL, fs_node, "fstype")) {
|
||||
if (path_exists(device_path)) {
|
||||
// skip if exists already
|
||||
return;
|
||||
}
|
||||
|
||||
log_misc("Creating avs directory %s", device_path);
|
||||
|
||||
if (!path_mkdir(device_path)) {
|
||||
log_fatal("Creating directory %s failed", device_path);
|
||||
}
|
||||
} else {
|
||||
res = property_node_refer(
|
||||
NULL,
|
||||
fs_node,
|
||||
"fstype",
|
||||
PROPERTY_TYPE_STR,
|
||||
fs_type,
|
||||
sizeof(fs_type));
|
||||
|
||||
if (res < 0) {
|
||||
log_fatal(
|
||||
"Getting 'fstype' attribute from avs config entry %s failed",
|
||||
fs_path);
|
||||
}
|
||||
|
||||
if (!strcmp(fs_type, "fs") || !strcmp(fs_type, "nvram")) {
|
||||
if (path_exists(device_path)) {
|
||||
// skip if exists already
|
||||
return;
|
||||
}
|
||||
|
||||
log_misc("Creating avs directory %s", device_path);
|
||||
|
||||
if (!path_mkdir(device_path)) {
|
||||
log_fatal("Creating directory %s failed", device_path);
|
||||
}
|
||||
} else {
|
||||
log_fatal(
|
||||
"Cannot create folders for unsupported file system type %s of "
|
||||
"path %s in avs config",
|
||||
fs_type,
|
||||
fs_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
log_assert(std_heap_size > 0);
|
||||
|
||||
log_info("init");
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#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_mountpoint_dir_create(
|
||||
struct property_node *node, const char *folder_name);
|
||||
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
|
||||
@@ -0,0 +1,538 @@
|
||||
#define LOG_MODULE "bootstrap-config"
|
||||
|
||||
#include <string.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/log.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;
|
||||
|
||||
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]);
|
||||
|
||||
property_node_clone(NULL, startup_profile_node, tmp_node, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_clone(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");
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,294 @@
|
||||
#define LOG_MODULE "bootstrap"
|
||||
|
||||
#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/logger.h"
|
||||
#include "launcher/module.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/log.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 logger_level _bootstrap_log_map_level(const char *level)
|
||||
{
|
||||
if (str_eq(level, "fatal")) {
|
||||
return LOGGER_LEVEL_FATAL;
|
||||
} else if (str_eq(level, "warning")) {
|
||||
return LOGGER_LEVEL_WARNING;
|
||||
} else if (str_eq(level, "info")) {
|
||||
return LOGGER_LEVEL_INFO;
|
||||
} else if (str_eq(level, "misc")) {
|
||||
return LOGGER_LEVEL_MISC;
|
||||
} else if (str_eq(level, "all")) {
|
||||
return LOGGER_LEVEL_ALL;
|
||||
} else if (str_eq(level, "disable")) {
|
||||
return LOGGER_LEVEL_OFF;
|
||||
} else if (str_eq(level, "default")) {
|
||||
return LOGGER_LEVEL_DEFAULT;
|
||||
} 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)
|
||||
{
|
||||
enum logger_level level;
|
||||
|
||||
log_assert(config);
|
||||
|
||||
log_info("log init");
|
||||
|
||||
logger_init(
|
||||
config->file,
|
||||
config->enable_console,
|
||||
config->enable_file,
|
||||
config->rotate,
|
||||
config->append,
|
||||
config->count);
|
||||
|
||||
level = _bootstrap_log_map_level(config->level);
|
||||
logger_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_node *override_node)
|
||||
{
|
||||
struct property *property;
|
||||
struct property_node *node;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(log_config);
|
||||
log_assert(override_node);
|
||||
|
||||
log_info("avs init");
|
||||
|
||||
property = avs_config_load(config->config_file);
|
||||
node = avs_config_root_get(property);
|
||||
|
||||
property_util_node_merge(property, node, override_node);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("avs-config");
|
||||
property_util_node_log(node);
|
||||
}
|
||||
|
||||
_bootstrap_avs_config_force_overrides_apply(node);
|
||||
_bootstrap_avs_config_log_overrides_apply(node, log_config);
|
||||
|
||||
avs_fs_assert_root_device_exists(node);
|
||||
|
||||
log_misc("Creating AVS file system directories for nvram and raw...");
|
||||
|
||||
avs_fs_mountpoint_dir_create(node, "nvram");
|
||||
avs_fs_mountpoint_dir_create(node, "raw");
|
||||
|
||||
avs_init(node, config->avs_heap_size, config->std_heap_size);
|
||||
|
||||
property_util_free(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_node *override_node)
|
||||
{
|
||||
struct property *property;
|
||||
struct property_node *node;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(ea3_ident_config);
|
||||
log_assert(override_node);
|
||||
|
||||
log_info("eamuse init");
|
||||
|
||||
if (config->enable) {
|
||||
property = eamuse_config_avs_load(config->config_file);
|
||||
node = eamuse_config_root_get(property);
|
||||
|
||||
property_util_node_merge(property, node, override_node);
|
||||
|
||||
_bootstrap_eamuse_ea3_ident_config_inject(node, ea3_ident_config);
|
||||
|
||||
property_util_node_log(node);
|
||||
|
||||
if (_bootstrap_log_property_configs) {
|
||||
log_misc("eamuse-config");
|
||||
property_util_node_log(node);
|
||||
}
|
||||
|
||||
eamuse_init(node);
|
||||
|
||||
property_util_free(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();
|
||||
}
|
||||
}
|
||||
@@ -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_node *override_node);
|
||||
void bootstrap_eamuse_init(
|
||||
const struct bootstrap_eamuse_config *config,
|
||||
const struct ea3_ident_config *ea3_ident_config,
|
||||
struct property_node *override_node);
|
||||
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
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
#define LOG_MODULE "debug"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "launcher/debug.h"
|
||||
|
||||
#include "util/log.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef LAUNCHER_DEBUG_H
|
||||
#define LAUNCHER_DEBUG_H
|
||||
|
||||
void debug_remote_debugger_trap();
|
||||
|
||||
#endif
|
||||
@@ -1,190 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/ea3-config.h"
|
||||
#include "launcher/module.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
PSMAP_BEGIN(ea3_ident_psmap)
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, softid, "/ea3/id/softid", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, hardid, "/ea3/id/hardid", "")
|
||||
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, pcbid, "/ea3/id/pcbid", "")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, model, "/ea3/soft/model")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, dest, "/ea3/soft/dest")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, spec, "/ea3/soft/spec")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, rev, "/ea3/soft/rev")
|
||||
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, ext, "/ea3/soft/ext")
|
||||
PSMAP_END
|
||||
|
||||
void ea3_ident_init(struct ea3_ident *ident)
|
||||
{
|
||||
memset(ident, 0, sizeof(*ident));
|
||||
}
|
||||
|
||||
bool ea3_ident_from_property(
|
||||
struct ea3_ident *ident, struct property *ea3_config)
|
||||
{
|
||||
return property_psmap_import(ea3_config, NULL, ident, ea3_ident_psmap);
|
||||
}
|
||||
|
||||
void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident)
|
||||
{
|
||||
struct avs_net_interface netif;
|
||||
int result;
|
||||
|
||||
result = avs_net_ctrl(1, &netif, sizeof(netif));
|
||||
|
||||
if (result < 0) {
|
||||
log_fatal(
|
||||
"avs_net_ctrl call to get MAC address returned error: %d", result);
|
||||
}
|
||||
|
||||
ident->hardid[0] = '0';
|
||||
ident->hardid[1] = '1';
|
||||
ident->hardid[2] = '0';
|
||||
ident->hardid[3] = '0';
|
||||
|
||||
hex_encode_uc(
|
||||
netif.mac_addr,
|
||||
sizeof(netif.mac_addr),
|
||||
ident->hardid + 4,
|
||||
sizeof(ident->hardid) - 4);
|
||||
}
|
||||
|
||||
bool ea3_ident_invoke_module_init(
|
||||
struct ea3_ident *ident,
|
||||
const struct module_context *module,
|
||||
struct property_node *app_config)
|
||||
{
|
||||
char sidcode_short[17];
|
||||
char sidcode_long[21];
|
||||
char security_code[9];
|
||||
bool ok;
|
||||
|
||||
/* Set up security env vars */
|
||||
|
||||
str_format(
|
||||
security_code,
|
||||
lengthof(security_code),
|
||||
"G*%s%s%s%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev);
|
||||
|
||||
std_setenv("/env/boot/version", "0.0.0");
|
||||
std_setenv("/env/profile/security_code", security_code);
|
||||
std_setenv("/env/profile/system_id", ident->pcbid);
|
||||
std_setenv("/env/profile/account_id", ident->pcbid);
|
||||
std_setenv("/env/profile/license_id", ident->softid);
|
||||
std_setenv("/env/profile/software_id", ident->softid);
|
||||
std_setenv("/env/profile/hardware_id", ident->hardid);
|
||||
|
||||
/* Set up the short sidcode string, let dll_entry_init mangle it */
|
||||
|
||||
str_format(
|
||||
sidcode_short,
|
||||
lengthof(sidcode_short),
|
||||
"%s%s%s%s%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev,
|
||||
ident->ext);
|
||||
|
||||
/* Set up long-form sidcode env var */
|
||||
|
||||
str_format(
|
||||
sidcode_long,
|
||||
lengthof(sidcode_long),
|
||||
"%s:%s:%s:%s:%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev,
|
||||
ident->ext);
|
||||
|
||||
/* Set this up beforehand, as certain games require it in dll_entry_init */
|
||||
|
||||
std_setenv("/env/profile/soft_id_code", sidcode_long);
|
||||
|
||||
ok = module_context_invoke_init(module, sidcode_short, app_config);
|
||||
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Back-propagate sidcode, as some games modify it during init */
|
||||
|
||||
memcpy(ident->model, sidcode_short + 0, sizeof(ident->model) - 1);
|
||||
ident->dest[0] = sidcode_short[3];
|
||||
ident->spec[0] = sidcode_short[4];
|
||||
ident->rev[0] = sidcode_short[5];
|
||||
memcpy(ident->ext, sidcode_short + 6, sizeof(ident->ext));
|
||||
|
||||
/* Set up long-form sidcode env var again */
|
||||
|
||||
str_format(
|
||||
sidcode_long,
|
||||
lengthof(sidcode_long),
|
||||
"%s:%s:%s:%s:%s",
|
||||
ident->model,
|
||||
ident->dest,
|
||||
ident->spec,
|
||||
ident->rev,
|
||||
ident->ext);
|
||||
|
||||
std_setenv("/env/profile/soft_id_code", sidcode_long);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ea3_ident_to_property(
|
||||
const struct ea3_ident *ident, struct property *ea3_config)
|
||||
{
|
||||
struct property_node *node;
|
||||
int i;
|
||||
|
||||
for (i = 0; ea3_ident_psmap[i].type != 0xFF; i++) {
|
||||
node = property_search(ea3_config, 0, ea3_ident_psmap[i].path);
|
||||
|
||||
if (node != NULL) {
|
||||
property_node_remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
property_psmap_export(ea3_config, NULL, ident, ea3_ident_psmap);
|
||||
}
|
||||
|
||||
void ea3_ident_replace_property_bool(
|
||||
struct property_node *node, const char *name, uint8_t val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
tmp = property_search(NULL, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
property_node_create(NULL, node, PROPERTY_TYPE_BOOL, name, val);
|
||||
}
|
||||
|
||||
void ea3_ident_replace_property_str(
|
||||
struct property_node *node, const char *name, const char *val)
|
||||
{
|
||||
struct property_node *tmp;
|
||||
|
||||
tmp = property_search(NULL, node, name);
|
||||
|
||||
if (tmp) {
|
||||
property_node_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = property_node_create(NULL, node, PROPERTY_TYPE_STR, name, val);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#ifndef LAUNCHER_EA3_CONFIG_H
|
||||
#define LAUNCHER_EA3_CONFIG_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/module.h"
|
||||
|
||||
/* N.B. even though this might look like a Konami ABI, this is purely an
|
||||
internal data structure. */
|
||||
|
||||
struct ea3_ident {
|
||||
/* psmapped structure offset can't be zero for some stupid reason */
|
||||
|
||||
uint32_t dummy;
|
||||
|
||||
/* Initialized from ea3-config.xml, then fed back from sidcode_short */
|
||||
|
||||
char model[4];
|
||||
char dest[4];
|
||||
char spec[4];
|
||||
char rev[4];
|
||||
char ext[11];
|
||||
|
||||
/* Initialized from ea3-config.xml (hardware_id defaults to MAC addr) */
|
||||
|
||||
char softid[24];
|
||||
char hardid[24];
|
||||
char pcbid[24];
|
||||
};
|
||||
|
||||
void ea3_ident_init(struct ea3_ident *ident);
|
||||
bool ea3_ident_from_property(
|
||||
struct ea3_ident *ident, struct property *ea3_config);
|
||||
void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident);
|
||||
bool ea3_ident_invoke_module_init(
|
||||
struct ea3_ident *ident,
|
||||
const struct module_context *module,
|
||||
struct property_node *app_config);
|
||||
void ea3_ident_to_property(
|
||||
const struct ea3_ident *ident, struct property *ea3_config);
|
||||
void ea3_ident_replace_property_bool(
|
||||
struct property_node *node, const char *name, uint8_t val);
|
||||
void ea3_ident_replace_property_str(
|
||||
struct property_node *node, const char *name, const char *val);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
#define LOG_MODULE "ea3-ident-config"
|
||||
|
||||
#include <string.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/log.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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,125 @@
|
||||
#define LOG_MODULE "eamuse-config"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
#include "launcher/eamuse-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/log.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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,25 @@
|
||||
#define LOG_MODULE "eamuse"
|
||||
|
||||
#include "imports/avs-ea3.h"
|
||||
|
||||
#include "util/log.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");
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,34 @@
|
||||
#define LOG_MODULE "hook"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "launcher/hook.h"
|
||||
|
||||
#include "util/log.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");
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef LAUNCHER_HOOK_H
|
||||
#define LAUNCHER_HOOK_H
|
||||
|
||||
void hook_load_dll(const char *path);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,371 @@
|
||||
#define LOG_MODULE "launcher-config"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/launcher-config.h"
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/log.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_clone(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_merge((struct property **) &config_property[0], 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,543 @@
|
||||
#define LOG_MODULE "launcher"
|
||||
|
||||
#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-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/logger.h"
|
||||
#include "launcher/module.h"
|
||||
#include "launcher/options.h"
|
||||
#include "launcher/property-util.h"
|
||||
#include "launcher/stubs.h"
|
||||
|
||||
#include "util/debug.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/os.h"
|
||||
#include "util/proc.h"
|
||||
#include "util/signal.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static void _launcher_signal_shutdown_handler()
|
||||
{
|
||||
logger_finit();
|
||||
ExitProcess(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void _launcher_logger_early_init(const struct options_log *options)
|
||||
{
|
||||
log_assert(options);
|
||||
|
||||
// Early logging pre AVS setup depend entirely on command args
|
||||
// We don't even have the bootstrap configuration loaded at this point
|
||||
logger_early_init(options->file_path);
|
||||
|
||||
if (options->level) {
|
||||
log_set_level(*(options->level));
|
||||
}
|
||||
}
|
||||
|
||||
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 LOG_LEVEL_FATAL:
|
||||
str_cpy(config->level, sizeof(config->level), "fatal");
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_WARNING:
|
||||
str_cpy(config->level, sizeof(config->level), "warn");
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_INFO:
|
||||
str_cpy(config->level, sizeof(config->level), "info");
|
||||
break;
|
||||
|
||||
case 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_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);
|
||||
|
||||
_launcher_logger_early_init(&options->log);
|
||||
|
||||
debug_init();
|
||||
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,
|
||||
avs_config_root_get(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,
|
||||
eamuse_config_root_get(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();
|
||||
|
||||
bootstrap_module_game_fini();
|
||||
|
||||
launcher_config_fini(launcher_config);
|
||||
|
||||
log_info("Shutdown complete");
|
||||
|
||||
logger_finit();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef LAUNCHER_LAUNCHER_H
|
||||
#define LAUNCHER_LAUNCHER_H
|
||||
|
||||
#include "launcher/options.h"
|
||||
|
||||
void launcher_main(const struct options *options);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,365 @@
|
||||
#define LOG_MODULE "launcher-logger"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "launcher/logger.h"
|
||||
#include "launcher/version.h"
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static FILE *log_file;
|
||||
static HANDLE log_mutex;
|
||||
|
||||
static const char *logger_get_formatted_timestamp(void)
|
||||
{
|
||||
static char buffer[64];
|
||||
time_t cur = 0;
|
||||
struct tm *tm = NULL;
|
||||
|
||||
cur = time(NULL);
|
||||
tm = localtime(&cur);
|
||||
|
||||
strftime(buffer, sizeof(buffer), "[%Y/%m/%d %H:%M:%S] ", tm);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static char logger_console_determine_color(const char *str)
|
||||
{
|
||||
log_assert(str);
|
||||
|
||||
/* Add some color to make spotting warnings/errors easier.
|
||||
Based on debug output level identifier. */
|
||||
|
||||
/* Avoids colored output on strings like "Windows" */
|
||||
if (str[1] != ':') {
|
||||
return 15;
|
||||
}
|
||||
|
||||
switch (str[0]) {
|
||||
/* green */
|
||||
case 'M':
|
||||
return 10;
|
||||
/* blue */
|
||||
case 'I':
|
||||
return 9;
|
||||
/* yellow */
|
||||
case 'W':
|
||||
return 14;
|
||||
/* red */
|
||||
case 'F':
|
||||
return 12;
|
||||
/* default console color */
|
||||
default:
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t logger_msg_coloring_len(const char *str)
|
||||
{
|
||||
// Expected format example: "I:boot: my log message"
|
||||
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
int colon_count;
|
||||
|
||||
ptr = str;
|
||||
len = 0;
|
||||
colon_count = 0;
|
||||
|
||||
while (true) {
|
||||
// End of string = invalid log format
|
||||
if (*ptr == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*ptr == ':') {
|
||||
colon_count++;
|
||||
}
|
||||
|
||||
if (colon_count == 2) {
|
||||
// Skip current colon, next char is a space
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
len++;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void logger_console(
|
||||
void *ctx, const char *chars, size_t nchars, const char *timestamp_str)
|
||||
{
|
||||
char color;
|
||||
size_t color_len;
|
||||
// See "util/log.c", has to align
|
||||
char buffer[65536];
|
||||
char tmp;
|
||||
|
||||
color_len = logger_msg_coloring_len(chars);
|
||||
|
||||
// Check if we could detect which part to color, otherwise just write the
|
||||
// whole log message without any coloring logic
|
||||
if (color_len > 0) {
|
||||
color = logger_console_determine_color(chars);
|
||||
|
||||
strcpy(buffer, chars);
|
||||
|
||||
// Mask start of log message for coloring
|
||||
tmp = buffer[color_len];
|
||||
buffer[color_len] = '\0';
|
||||
|
||||
printf("%s", timestamp_str);
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
|
||||
printf("%s", buffer);
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15);
|
||||
|
||||
// Write actual message non colored
|
||||
buffer[color_len] = tmp;
|
||||
printf("%s", buffer + color_len);
|
||||
} else {
|
||||
printf("%s", chars);
|
||||
}
|
||||
}
|
||||
|
||||
static void logger_console_avs(const char *chars)
|
||||
{
|
||||
char color;
|
||||
size_t color_len;
|
||||
// See "util/log.c", has to align
|
||||
char buffer[65536];
|
||||
char tmp;
|
||||
const char *timestamp;
|
||||
const char *msg;
|
||||
|
||||
static const size_t timestamp_len = strlen("[----/--/-- --:--:--]");
|
||||
|
||||
timestamp = chars;
|
||||
msg = timestamp + timestamp_len + 1; // +1 is the space
|
||||
|
||||
color_len = logger_msg_coloring_len(msg);
|
||||
|
||||
// Check if we could detect which part to color, otherwise just write the
|
||||
// whole log message without any coloring logic
|
||||
if (color_len > 0) {
|
||||
color = logger_console_determine_color(msg);
|
||||
|
||||
strcpy(buffer, msg);
|
||||
|
||||
// Mask start of log message for coloring
|
||||
tmp = buffer[color_len];
|
||||
buffer[color_len] = '\0';
|
||||
|
||||
printf("%.21s ", timestamp);
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
|
||||
printf("%s", buffer);
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15);
|
||||
|
||||
// Write actual message non colored
|
||||
buffer[color_len] = tmp;
|
||||
printf("%s\n", buffer + color_len);
|
||||
} else {
|
||||
printf("%s\n", chars);
|
||||
}
|
||||
}
|
||||
|
||||
static void logger_file(
|
||||
void *ctx, const char *chars, size_t nchars, const char *timestamp_str)
|
||||
{
|
||||
if (ctx) {
|
||||
fwrite(timestamp_str, 1, strlen(timestamp_str), (FILE *) ctx);
|
||||
fwrite(chars, 1, nchars, (FILE *) ctx);
|
||||
fflush((FILE *) ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void logger_file_avs(const char *chars, size_t len)
|
||||
{
|
||||
if (log_file) {
|
||||
fwrite(chars, 1, len, log_file);
|
||||
fflush(log_file);
|
||||
}
|
||||
}
|
||||
|
||||
static void logger_writer(void *ctx, const char *chars, size_t nchars)
|
||||
{
|
||||
const char *timestamp_str;
|
||||
|
||||
// Different threads logging the same destination, e.g. debugger thread,
|
||||
// main thread
|
||||
|
||||
WaitForSingleObject(log_mutex, INFINITE);
|
||||
|
||||
timestamp_str = logger_get_formatted_timestamp();
|
||||
|
||||
logger_console(ctx, chars, nchars, timestamp_str);
|
||||
logger_file(ctx, chars, nchars, timestamp_str);
|
||||
|
||||
ReleaseMutex(log_mutex);
|
||||
}
|
||||
|
||||
static void logger_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 logger_early_init(const char *log_file_path)
|
||||
{
|
||||
if (log_file_path) {
|
||||
log_file = fopen(log_file_path, "w+");
|
||||
} else {
|
||||
log_file = NULL;
|
||||
}
|
||||
|
||||
log_to_writer(logger_writer, log_file);
|
||||
|
||||
logger_log_header();
|
||||
|
||||
if (log_file_path) {
|
||||
log_info("Log file: %s", log_file_path);
|
||||
|
||||
if (!log_file) {
|
||||
log_fatal(
|
||||
"ERROR: Opening log file %s failed: %s",
|
||||
log_file_path,
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
log_mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
}
|
||||
|
||||
void logger_init(
|
||||
const char *filename,
|
||||
bool enable_console,
|
||||
bool enable_file,
|
||||
bool rotate_file,
|
||||
bool append_file,
|
||||
uint16_t count_file)
|
||||
{
|
||||
// Remark: very basic implementation for now, logger needs a proper cleanup
|
||||
// anyway before implementing more features such as rotation
|
||||
|
||||
if (enable_file) {
|
||||
if (log_file) {
|
||||
// Log file stitching of early log output
|
||||
fseek(log_file, 0, SEEK_END);
|
||||
size_t file_size = ftell(log_file);
|
||||
fseek(log_file, 0, SEEK_SET);
|
||||
|
||||
void *buffer = xmalloc(file_size);
|
||||
|
||||
fread(buffer, file_size, 1, log_file);
|
||||
|
||||
fclose(log_file);
|
||||
|
||||
log_file = fopen(filename, "w+");
|
||||
|
||||
fwrite(buffer, file_size, 1, log_file);
|
||||
fflush(log_file);
|
||||
free(buffer);
|
||||
}
|
||||
} else {
|
||||
if (log_file) {
|
||||
fclose(log_file);
|
||||
log_file = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logger_level_set(enum logger_level level)
|
||||
{
|
||||
enum log_level internal_level;
|
||||
|
||||
switch (level) {
|
||||
case LOGGER_LEVEL_OFF:
|
||||
case LOGGER_LEVEL_FATAL:
|
||||
internal_level = LOG_LEVEL_FATAL;
|
||||
break;
|
||||
|
||||
case LOGGER_LEVEL_WARNING:
|
||||
internal_level = LOG_LEVEL_WARNING;
|
||||
break;
|
||||
|
||||
case LOGGER_LEVEL_DEFAULT:
|
||||
case LOGGER_LEVEL_INFO:
|
||||
internal_level = LOG_LEVEL_INFO;
|
||||
break;
|
||||
|
||||
case LOGGER_LEVEL_MISC:
|
||||
case LOGGER_LEVEL_ALL:
|
||||
internal_level = LOG_LEVEL_MISC;
|
||||
break;
|
||||
|
||||
default:
|
||||
internal_level = LOG_LEVEL_FATAL;
|
||||
log_assert(false);
|
||||
}
|
||||
|
||||
log_set_level(internal_level);
|
||||
}
|
||||
|
||||
void logger_log_avs_log_message(char *str, size_t len)
|
||||
{
|
||||
bool use_crlf;
|
||||
|
||||
#if AVS_VERSION >= 1500
|
||||
use_crlf = true;
|
||||
#else
|
||||
use_crlf = false;
|
||||
#endif
|
||||
|
||||
// Different threads logging the same destination, e.g. debugger thread,
|
||||
// main thread
|
||||
|
||||
WaitForSingleObject(log_mutex, INFINITE);
|
||||
|
||||
// Write to file first, tokenizing trashes the string
|
||||
logger_file_avs(str, len);
|
||||
|
||||
// The character stream provided here can contain multiple lines as AVS
|
||||
// manages buffered writes to the logger backend. We need to split them
|
||||
// up for the logging backend to allow for further enhancements
|
||||
|
||||
char *line = strtok(str, use_crlf ? "\r\n" : "\n");
|
||||
|
||||
while (line) {
|
||||
logger_console_avs(line);
|
||||
line = strtok(NULL, use_crlf ? "\r\n" : "\n");
|
||||
}
|
||||
|
||||
ReleaseMutex(log_mutex);
|
||||
}
|
||||
|
||||
void logger_finit()
|
||||
{
|
||||
log_misc("Logger finit");
|
||||
|
||||
if (log_file) {
|
||||
fflush(log_file);
|
||||
fclose(log_file);
|
||||
}
|
||||
|
||||
CloseHandle(log_mutex);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef LAUNCHER_LOGGER_H
|
||||
#define LAUNCHER_LOGGER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum logger_level {
|
||||
LOGGER_LEVEL_OFF = 0,
|
||||
LOGGER_LEVEL_FATAL = 1,
|
||||
LOGGER_LEVEL_WARNING = 2,
|
||||
LOGGER_LEVEL_INFO = 3,
|
||||
LOGGER_LEVEL_MISC = 4,
|
||||
LOGGER_LEVEL_ALL = 5,
|
||||
LOGGER_LEVEL_DEFAULT = 6
|
||||
};
|
||||
|
||||
void logger_early_init(const char *log_file_path);
|
||||
|
||||
void logger_init(
|
||||
const char *filename,
|
||||
bool enable_console,
|
||||
bool enable_file,
|
||||
bool rotate_file,
|
||||
bool append_file,
|
||||
uint16_t count_file);
|
||||
|
||||
void logger_level_set(enum logger_level level);
|
||||
|
||||
/**
|
||||
* Write a log message from AVS to the logging backend.
|
||||
*
|
||||
* @param str String to log
|
||||
* @param len Total length of the log string
|
||||
*/
|
||||
void logger_log_avs_log_message(char *str, size_t len);
|
||||
|
||||
/**
|
||||
* Shutdown and cleanup the logging backend.
|
||||
*/
|
||||
void logger_finit();
|
||||
|
||||
#endif
|
||||
+4
-319
@@ -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;
|
||||
}
|
||||
}
|
||||
+142
-14
@@ -1,3 +1,5 @@
|
||||
#define LOG_MODULE "module"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/pe.h"
|
||||
@@ -6,17 +8,21 @@
|
||||
#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 +133,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 +167,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 +223,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 +296,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 +364,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 +386,6 @@ void module_context_fini(struct module_context *module)
|
||||
if (module->dll != NULL) {
|
||||
FreeLibrary(module->dll);
|
||||
}
|
||||
|
||||
log_misc("fini done");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/ea3-ident-config.h"
|
||||
|
||||
#include "util/array.h"
|
||||
|
||||
struct module_context {
|
||||
@@ -12,16 +14,16 @@ struct module_context {
|
||||
char *path;
|
||||
};
|
||||
|
||||
void module_context_init(struct module_context *module, const char *path);
|
||||
void module_context_init_with_iat_hooks(
|
||||
void module_init(struct module_context *module, const char *path);
|
||||
void module_with_iat_hooks_init(
|
||||
struct module_context *module,
|
||||
const char *path,
|
||||
struct array *iat_hook_dlls);
|
||||
bool module_context_invoke_init(
|
||||
const struct array *iat_hook_dlls);
|
||||
void module_init_invoke(
|
||||
const struct module_context *module,
|
||||
char *sidcode,
|
||||
struct property_node *app_config);
|
||||
bool module_context_invoke_main(const struct module_context *module);
|
||||
void module_context_fini(struct module_context *module);
|
||||
struct ea3_ident_config *ea3_ident_config,
|
||||
struct property_node *app_params_node);
|
||||
bool module_main_invoke(const struct module_context *module);
|
||||
void module_fini(struct module_context *module);
|
||||
|
||||
#endif
|
||||
|
||||
+96
-108
@@ -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,39 @@ 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 < LOG_LEVEL_FATAL || tmp > LOG_LEVEL_MISC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->log.level = xmalloc(sizeof(enum log_level));
|
||||
*(options->log.level) = (enum 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 +135,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 +144,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 +168,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);
|
||||
}
|
||||
}
|
||||
|
||||
+42
-16
@@ -5,24 +5,50 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#include "launcher/bootstrap-config.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 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);
|
||||
|
||||
@@ -0,0 +1,618 @@
|
||||
#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 "imports/avs.h"
|
||||
|
||||
#include "launcher/property-util.h"
|
||||
|
||||
#include "util/log.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;
|
||||
};
|
||||
|
||||
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);
|
||||
rewinder(context);
|
||||
|
||||
if (!property_insert_read(prop, 0, reader, context)) {
|
||||
log_fatal("%s: Error reading configuration file", name);
|
||||
}
|
||||
|
||||
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[4096];
|
||||
// 256 found in AVS code as size used on property_node_name
|
||||
char cur_node_name[256];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
property_node_create(property, node, PROPERTY_TYPE_U8, name, val);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
property_node_create(property, node, PROPERTY_TYPE_U16, name, val);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
property_node_create(property, node, PROPERTY_TYPE_U32, name, val);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
property_node_create(property, node, PROPERTY_TYPE_STR, name, val);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
property_node_create(property, node, PROPERTY_TYPE_BOOL, name, val);
|
||||
}
|
||||
|
||||
static void _property_util_node_merge_rec(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source)
|
||||
{
|
||||
char name[256];
|
||||
struct property_node *child;
|
||||
struct property_node *matching_child;
|
||||
|
||||
log_assert(parent_property);
|
||||
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parent) {
|
||||
property_node_clone(parent_property, parent, source, true);
|
||||
return;
|
||||
}
|
||||
|
||||
child = property_node_traversal(source, TRAVERSE_FIRST_CHILD);
|
||||
|
||||
while (child) {
|
||||
property_node_name(child, name, sizeof(name));
|
||||
|
||||
matching_child = property_search(parent_property, parent, name);
|
||||
|
||||
if (!matching_child) {
|
||||
property_node_clone(parent_property, parent, child, true);
|
||||
} else {
|
||||
_property_util_node_merge_rec(
|
||||
parent_property, matching_child, child);
|
||||
}
|
||||
|
||||
child = property_node_traversal(child, TRAVERSE_NEXT_SIBLING);
|
||||
}
|
||||
}
|
||||
|
||||
struct property *property_util_merge(struct property **properties, size_t count)
|
||||
{
|
||||
uint32_t total_size;
|
||||
void *buffer;
|
||||
struct property *merged_property;
|
||||
int i;
|
||||
struct property_node *parent_node;
|
||||
struct property_node *to_merge_node;
|
||||
|
||||
log_assert(properties);
|
||||
log_assert(count > 0);
|
||||
|
||||
total_size = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
total_size += property_util_property_query_real_size(properties[i]);
|
||||
}
|
||||
|
||||
buffer = xmalloc(total_size);
|
||||
|
||||
merged_property = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
total_size);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
parent_node = property_search(merged_property, NULL, "/");
|
||||
to_merge_node = property_search(properties[i], NULL, "/");
|
||||
|
||||
property_util_node_merge(merged_property, parent_node, to_merge_node);
|
||||
}
|
||||
|
||||
return merged_property;
|
||||
}
|
||||
|
||||
struct property *property_util_clone(struct property_node *node)
|
||||
{
|
||||
struct property *property;
|
||||
struct property_node *root_node;
|
||||
uint32_t size;
|
||||
void *buffer;
|
||||
|
||||
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, "");
|
||||
|
||||
property_node_clone(property, root_node, node, true);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
void property_util_node_merge(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent,
|
||||
struct property_node *source)
|
||||
{
|
||||
_property_util_node_merge_rec(parent_property, parent, source);
|
||||
}
|
||||
|
||||
void property_util_free(struct property *prop)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
buffer = property_desc_to_buffer(prop);
|
||||
property_destroy(prop);
|
||||
free(buffer);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef PROPERTY_UTIL_H
|
||||
#define PROPERTY_UTIL_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
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);
|
||||
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);
|
||||
struct property *property_util_clone(struct property_node *node);
|
||||
struct property *
|
||||
property_util_merge(struct property **properties, size_t count);
|
||||
void property_util_node_merge(
|
||||
struct property *parent_property,
|
||||
struct property_node *parent_node,
|
||||
struct property_node *source_node);
|
||||
void property_util_free(struct property *prop);
|
||||
|
||||
#endif
|
||||
@@ -1,135 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/property.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static int boot_property_fread(uint32_t context, void *bytes, size_t nbytes)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = TlsGetValue(context);
|
||||
|
||||
return fread(bytes, 1, nbytes, f);
|
||||
}
|
||||
|
||||
struct cstring_read_handle {
|
||||
const char *buffer;
|
||||
size_t buffer_len;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
static int
|
||||
boot_property_cstring_read(uint32_t context, void *bytes, size_t nbytes)
|
||||
{
|
||||
int result = 0;
|
||||
struct cstring_read_handle *h = TlsGetValue(context);
|
||||
|
||||
if (h->offset < h->buffer_len) {
|
||||
result = min(nbytes, h->buffer_len - h->offset);
|
||||
memcpy(bytes, (const void *) (h->buffer + h->offset), result);
|
||||
h->offset += result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct property *boot_property_load(const char *filename)
|
||||
{
|
||||
struct property *prop;
|
||||
void *buffer;
|
||||
int nbytes;
|
||||
FILE *f;
|
||||
uint32_t f_keyhole;
|
||||
|
||||
/* AVS callbacks are only given a 32-bit context parameter, even in 64-bit
|
||||
builds of AVS. We allocate a 32-bit TLS key and pass the context in this
|
||||
manner instead. Inefficient, but it works. */
|
||||
|
||||
f = fopen(filename, "r");
|
||||
|
||||
f_keyhole = TlsAlloc();
|
||||
TlsSetValue(f_keyhole, f);
|
||||
|
||||
if (f == NULL) {
|
||||
log_fatal("%s: Error opening configuration file", filename);
|
||||
}
|
||||
|
||||
nbytes = property_read_query_memsize(boot_property_fread, f_keyhole, 0, 0);
|
||||
|
||||
if (nbytes < 0) {
|
||||
log_fatal("%s: Error querying configuration file", filename);
|
||||
}
|
||||
|
||||
buffer = xmalloc(nbytes);
|
||||
prop = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
nbytes);
|
||||
rewind(f);
|
||||
|
||||
if (!property_insert_read(prop, 0, boot_property_fread, f_keyhole)) {
|
||||
log_fatal("%s: Error reading configuration file", filename);
|
||||
}
|
||||
|
||||
TlsFree(f_keyhole);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return prop;
|
||||
}
|
||||
struct property *boot_property_load_cstring(const char *cstring)
|
||||
{
|
||||
struct property *prop;
|
||||
void *buffer;
|
||||
int nbytes;
|
||||
uint32_t s_keyhole;
|
||||
|
||||
// see above
|
||||
struct cstring_read_handle read_handle;
|
||||
read_handle.buffer = cstring;
|
||||
read_handle.buffer_len = strlen(cstring);
|
||||
read_handle.offset = 0;
|
||||
|
||||
s_keyhole = TlsAlloc();
|
||||
TlsSetValue(s_keyhole, &read_handle);
|
||||
|
||||
nbytes = property_read_query_memsize(
|
||||
boot_property_cstring_read, s_keyhole, 0, 0);
|
||||
|
||||
if (nbytes < 0) {
|
||||
log_fatal("Error querying configuration string");
|
||||
}
|
||||
|
||||
buffer = xmalloc(nbytes);
|
||||
prop = property_create(
|
||||
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
|
||||
PROPERTY_FLAG_APPEND,
|
||||
buffer,
|
||||
nbytes);
|
||||
|
||||
read_handle.offset = 0;
|
||||
if (!property_insert_read(prop, 0, boot_property_cstring_read, s_keyhole)) {
|
||||
log_fatal("Error inserting configuration string");
|
||||
}
|
||||
|
||||
TlsFree(s_keyhole);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
void boot_property_free(struct property *prop)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
buffer = property_desc_to_buffer(prop);
|
||||
property_destroy(prop);
|
||||
free(buffer);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef LAUNCHER_PROPERTY_H
|
||||
#define LAUNCHER_PROPERTY_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
struct property *boot_property_load(const char *filename);
|
||||
struct property *boot_property_load_cstring(const char *cstring);
|
||||
void boot_property_free(struct property *prop);
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,5 @@
|
||||
#define LOG_MODULE "stubs"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -116,6 +118,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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user