refactor(launcher): Major re-work of launcher

Kudos to Shiz for providing the groundwork for this.

Fundamentally re-think how launcher operates and
bootstrapping the games is managed and configured.

This brings it significantly closer to how the original
bootstrap is doing the job: launcher now utilizes the
data (structures) provided by the bootstrap.xml configuration
file. This creates compatibility with vanilla data dumps
and original stock images. Note that bemanitools does not
include any code or means to run DRM'd data, only decrypted.

But, this allows users to keep decrypted dumps as stock as
possible which means:

* No copying around of property files anymore
* Keep the modules/ folder with the binaries
* Have bemanitools binaries separate in the data
* No need to edit/customize the original configuration files

A list of key features of the "new" launcher:

* Boostrap games by following the configuration provided by
  stock game's bootstrap.xml files
* Custom launcher.xml configuration file that adds further
  launcher configurable features, composability of
  bootstrap.xml configuration(s) as well as configuration
  overriding/stacking of selected types of configurations,
  e.g. eamuse config, avs-config. The latter eliminates
  the need for modifying stock config files in the prop/
  folder
* Unified logging system: launcher and AVS logging uses
  the same logger, all output can now be in a single file
* Original features such as various hook types still
  available

Due to the significant architectural changes, this also
breaks with any backwards compatibility to existing
launcher setups. Thus, users need to migrate by re-applying
the new configuration format and migrating their config
parameters accordingly.

Further migration instructions and updated documentation
will be provided upon release.

Co-authored-by: Shiz <hi@shiz.me>
This commit is contained in:
icex2
2024-02-03 15:19:20 +01:00
co-authored by Shiz
parent 53308027b4
commit 24e7dd508a
39 changed files with 4491 additions and 941 deletions
+16 -3
View File
@@ -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 \
+361
View File
@@ -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");
}
}
+39
View File
@@ -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
-72
View File
@@ -1,72 +0,0 @@
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "imports/avs.h"
#include "launcher/avs-context.h"
#include "util/log.h"
static void *avs_heap;
#ifdef AVS_HAS_STD_HEAP
static void *std_heap;
#endif
void avs_context_init(
struct property_node *config,
uint32_t avs_heap_size,
uint32_t std_heap_size,
avs_log_writer_t log_writer,
void *log_writer_ctx)
{
avs_heap = VirtualAlloc(
NULL, avs_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (avs_heap == NULL) {
log_fatal(
"Failed to VirtualAlloc %d byte AVS heap: %08x",
avs_heap_size,
(unsigned int) GetLastError());
}
#ifdef AVS_HAS_STD_HEAP
std_heap = VirtualAlloc(
NULL, std_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (std_heap == NULL) {
log_fatal(
"Failed to VirtualAlloc %d byte \"std\" heap: %08x",
std_heap_size,
(unsigned int) GetLastError());
}
#endif
#ifdef AVS_HAS_STD_HEAP
avs_boot(
config,
std_heap,
std_heap_size,
avs_heap,
avs_heap_size,
log_writer,
log_writer_ctx);
#else
/* AVS v2.16.xx and I suppose onward uses a unified heap */
avs_boot(config, avs_heap, avs_heap_size, NULL, log_writer, log_writer_ctx);
#endif
}
void avs_context_fini(void)
{
avs_shutdown();
#ifdef AVS_HAS_STD_HEAP
VirtualFree(std_heap, 0, MEM_RELEASE);
#endif
VirtualFree(avs_heap, 0, MEM_RELEASE);
}
-20
View File
@@ -1,20 +0,0 @@
#ifndef LAUNCHER_AVS_CONTEXT_H
#define LAUNCHER_AVS_CONTEXT_H
#include <stdint.h>
#include "imports/avs.h"
#if AVS_VERSION < 1600
#define AVS_HAS_STD_HEAP
#endif
void avs_context_init(
struct property_node *config,
uint32_t avs_heap_size,
uint32_t std_heap_size,
avs_log_writer_t log_writer,
void *log_writer_ctx);
void avs_context_fini(void);
#endif
+307
View File
@@ -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");
}
+17
View File
@@ -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
+538
View File
@@ -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");
}
+139
View File
@@ -0,0 +1,139 @@
#ifndef LAUNCHER_BOOTSTRAP_CONFIG_H
#define LAUNCHER_BOOTSTRAP_CONFIG_H
#include <stdbool.h>
#include <windows.h>
#include "imports/avs.h"
// should be enough for a while
#define DEFAULT_FILE_MAX 16
struct bootstrap_startup_config {
struct bootstrap_default_file_config {
struct bootstrap_default_file {
char src[64];
char dst[64];
} file[DEFAULT_FILE_MAX];
} default_file;
struct bootstrap_boot_config {
char config_file[64];
uint32_t avs_heap_size;
uint32_t std_heap_size;
char launch_config_file[64];
char mount_table_selector[16];
bool watcher_enable;
bool timemachine_enable;
} boot;
struct bootstrap_log_config {
char level[8];
char name[64];
char file[64];
uint32_t bufsz;
uint16_t output_delay_ms;
bool enable_console;
bool enable_sci;
bool enable_net;
bool enable_file;
bool rotate;
bool append;
uint16_t count;
} log;
struct bootstrap_minidump_config {
uint8_t count;
bool continue_;
bool log;
uint8_t type;
char path[64];
uint32_t symbufsz;
char search_path[64];
} minidump;
struct bootstrap_module_config {
char file[64];
char load_type[64];
struct property *app_config;
} module;
struct bootstrap_dlm_config {
char digest[16];
uint32_t size;
uint32_t ift_table;
uint32_t ift_insert;
uint32_t ift_remove;
};
struct bootstrap_dlm_config dlm_ntdll;
struct bootstrap_shield_config {
bool enable;
bool verbose;
bool use_loadlibrary;
char logger[64];
uint32_t sleep_min;
uint32_t sleep_blur;
uint32_t tick_sleep;
uint32_t tick_error;
uint8_t overwork_threshold;
uint32_t overwork_delay;
uint32_t pause_delay;
char whitelist_file[64];
char unlimited_key[10];
uint16_t killer_port;
} shield;
struct bootstrap_dongle_config {
char license_cn[32];
char account_cn[32];
char driver_dll[16];
bool disable_gc;
} dongle;
struct bootstrap_drm_config {
char dll[64];
char device[64];
char mount[64];
char fstype[64];
char options[64];
} drm;
struct bootstrap_lte_config {
bool enable;
char config_file[64];
char unlimited_key[10];
} lte;
struct bootstrap_ssl_config {
char options[64];
} ssl;
struct bootstrap_esign_config {
bool enable;
} esign;
struct bootstrap_eamuse_config {
bool enable;
bool sync;
bool enable_model;
char config_file[64];
bool updatecert_enable;
uint32_t updatecert_interval;
} eamuse;
};
struct bootstrap_config {
char release_code[16];
struct bootstrap_startup_config startup;
};
void bootstrap_config_init(struct bootstrap_config *config);
void bootstrap_config_load(
struct property *property,
const char *profile,
struct bootstrap_config *config);
#endif /* LAUNCHER_BOOTSTRAP_CONFIG_H */
+294
View File
@@ -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();
}
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef LAUNCHER_BOOTSTRAP_H
#define LAUNCHER_BOOTSTRAP_H
#include "launcher/bootstrap-config.h"
#include "launcher/ea3-ident-config.h"
#include "util/array.h"
void bootstrap_init(bool log_property_configs);
void bootstrap_log_init(const struct bootstrap_log_config *config);
void bootstrap_default_files_create(
const struct bootstrap_default_file_config *config);
void bootstrap_avs_init(
const struct bootstrap_boot_config *config,
const struct bootstrap_log_config *log_config,
struct property_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
+33
View File
@@ -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);
}
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef LAUNCHER_DEBUG_H
#define LAUNCHER_DEBUG_H
void debug_remote_debugger_trap();
#endif
-190
View File
@@ -1,190 +0,0 @@
#include <string.h>
#include "imports/avs.h"
#include "launcher/ea3-config.h"
#include "launcher/module.h"
#include "util/defs.h"
#include "util/hex.h"
#include "util/log.h"
#include "util/str.h"
PSMAP_BEGIN(ea3_ident_psmap)
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, softid, "/ea3/id/softid", "")
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, hardid, "/ea3/id/hardid", "")
PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, pcbid, "/ea3/id/pcbid", "")
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, model, "/ea3/soft/model")
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, dest, "/ea3/soft/dest")
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, spec, "/ea3/soft/spec")
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, rev, "/ea3/soft/rev")
PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, ext, "/ea3/soft/ext")
PSMAP_END
void ea3_ident_init(struct ea3_ident *ident)
{
memset(ident, 0, sizeof(*ident));
}
bool ea3_ident_from_property(
struct ea3_ident *ident, struct property *ea3_config)
{
return property_psmap_import(ea3_config, NULL, ident, ea3_ident_psmap);
}
void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident)
{
struct avs_net_interface netif;
int result;
result = avs_net_ctrl(1, &netif, sizeof(netif));
if (result < 0) {
log_fatal(
"avs_net_ctrl call to get MAC address returned error: %d", result);
}
ident->hardid[0] = '0';
ident->hardid[1] = '1';
ident->hardid[2] = '0';
ident->hardid[3] = '0';
hex_encode_uc(
netif.mac_addr,
sizeof(netif.mac_addr),
ident->hardid + 4,
sizeof(ident->hardid) - 4);
}
bool ea3_ident_invoke_module_init(
struct ea3_ident *ident,
const struct module_context *module,
struct property_node *app_config)
{
char sidcode_short[17];
char sidcode_long[21];
char security_code[9];
bool ok;
/* Set up security env vars */
str_format(
security_code,
lengthof(security_code),
"G*%s%s%s%s",
ident->model,
ident->dest,
ident->spec,
ident->rev);
std_setenv("/env/boot/version", "0.0.0");
std_setenv("/env/profile/security_code", security_code);
std_setenv("/env/profile/system_id", ident->pcbid);
std_setenv("/env/profile/account_id", ident->pcbid);
std_setenv("/env/profile/license_id", ident->softid);
std_setenv("/env/profile/software_id", ident->softid);
std_setenv("/env/profile/hardware_id", ident->hardid);
/* Set up the short sidcode string, let dll_entry_init mangle it */
str_format(
sidcode_short,
lengthof(sidcode_short),
"%s%s%s%s%s",
ident->model,
ident->dest,
ident->spec,
ident->rev,
ident->ext);
/* Set up long-form sidcode env var */
str_format(
sidcode_long,
lengthof(sidcode_long),
"%s:%s:%s:%s:%s",
ident->model,
ident->dest,
ident->spec,
ident->rev,
ident->ext);
/* Set this up beforehand, as certain games require it in dll_entry_init */
std_setenv("/env/profile/soft_id_code", sidcode_long);
ok = module_context_invoke_init(module, sidcode_short, app_config);
if (!ok) {
return false;
}
/* Back-propagate sidcode, as some games modify it during init */
memcpy(ident->model, sidcode_short + 0, sizeof(ident->model) - 1);
ident->dest[0] = sidcode_short[3];
ident->spec[0] = sidcode_short[4];
ident->rev[0] = sidcode_short[5];
memcpy(ident->ext, sidcode_short + 6, sizeof(ident->ext));
/* Set up long-form sidcode env var again */
str_format(
sidcode_long,
lengthof(sidcode_long),
"%s:%s:%s:%s:%s",
ident->model,
ident->dest,
ident->spec,
ident->rev,
ident->ext);
std_setenv("/env/profile/soft_id_code", sidcode_long);
return true;
}
void ea3_ident_to_property(
const struct ea3_ident *ident, struct property *ea3_config)
{
struct property_node *node;
int i;
for (i = 0; ea3_ident_psmap[i].type != 0xFF; i++) {
node = property_search(ea3_config, 0, ea3_ident_psmap[i].path);
if (node != NULL) {
property_node_remove(node);
}
}
property_psmap_export(ea3_config, NULL, ident, ea3_ident_psmap);
}
void ea3_ident_replace_property_bool(
struct property_node *node, const char *name, uint8_t val)
{
struct property_node *tmp;
tmp = property_search(NULL, node, name);
if (tmp) {
property_node_remove(tmp);
}
property_node_create(NULL, node, PROPERTY_TYPE_BOOL, name, val);
}
void ea3_ident_replace_property_str(
struct property_node *node, const char *name, const char *val)
{
struct property_node *tmp;
tmp = property_search(NULL, node, name);
if (tmp) {
property_node_remove(tmp);
}
tmp = property_node_create(NULL, node, PROPERTY_TYPE_STR, name, val);
}
-46
View File
@@ -1,46 +0,0 @@
#ifndef LAUNCHER_EA3_CONFIG_H
#define LAUNCHER_EA3_CONFIG_H
#include "imports/avs.h"
#include "launcher/module.h"
/* N.B. even though this might look like a Konami ABI, this is purely an
internal data structure. */
struct ea3_ident {
/* psmapped structure offset can't be zero for some stupid reason */
uint32_t dummy;
/* Initialized from ea3-config.xml, then fed back from sidcode_short */
char model[4];
char dest[4];
char spec[4];
char rev[4];
char ext[11];
/* Initialized from ea3-config.xml (hardware_id defaults to MAC addr) */
char softid[24];
char hardid[24];
char pcbid[24];
};
void ea3_ident_init(struct ea3_ident *ident);
bool ea3_ident_from_property(
struct ea3_ident *ident, struct property *ea3_config);
void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident);
bool ea3_ident_invoke_module_init(
struct ea3_ident *ident,
const struct module_context *module,
struct property_node *app_config);
void ea3_ident_to_property(
const struct ea3_ident *ident, struct property *ea3_config);
void ea3_ident_replace_property_bool(
struct property_node *node, const char *name, uint8_t val);
void ea3_ident_replace_property_str(
struct property_node *node, const char *name, const char *val);
#endif
+103
View File
@@ -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);
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef LAUNCHER_EA3_IDENT_CONFIG_H
#define LAUNCHER_EA3_IDENT_CONFIG_H
#include "imports/avs.h"
/* N.B. even though this might look like a Konami ABI, this is purely an
internal data structure. */
struct ea3_ident_config {
/* psmapped structure offset can't be zero for some stupid reason */
uint32_t dummy;
/* Initialized from ea3-config.xml, then fed back from sidcode_short */
char model[4];
char dest[4];
char spec[4];
char rev[4];
char ext[11];
/* Initialized from ea3-config.xml (hardware_id defaults to MAC addr) */
char softid[24];
char hardid[24];
char pcbid[24];
};
void ea3_ident_config_init(struct ea3_ident_config *config);
void ea3_ident_config_from_file_load(
const char *path, struct ea3_ident_config *config);
void ea3_ident_config_load(
struct property *property, struct ea3_ident_config *config);
bool ea3_ident_config_hardid_is_defined(struct ea3_ident_config *config);
void ea3_ident_config_hardid_from_ethernet_set(struct ea3_ident_config *config);
#endif
+125
View File
@@ -0,0 +1,125 @@
#define LOG_MODULE "eamuse-config"
#include <stdlib.h>
#include "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);
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef LAUNCHER_EAMUSE_CONFIG_H
#define LAUNCHER_EAMUSE_CONFIG_H
#include "imports/avs.h"
struct property *eamuse_config_avs_load(const char *path);
struct property_node *eamuse_config_root_get(struct property *property);
void eamuse_config_id_softid_set(struct property_node *node, const char *value);
void eamuse_config_id_hardid_set(struct property_node *node, const char *value);
void eamuse_config_id_pcbid_set(struct property_node *node, const char *value);
void eamuse_config_soft_model_set(
struct property_node *node, const char *value);
void eamuse_config_soft_dest_set(struct property_node *node, const char *value);
void eamuse_config_soft_spec_set(struct property_node *node, const char *value);
void eamuse_config_soft_rev_set(struct property_node *node, const char *value);
void eamuse_config_soft_ext_set(struct property_node *node, const char *value);
void eamuse_config_network_url_slash_set(
struct property_node *node, bool value);
void eamuse_config_network_service_url_set(
struct property_node *node, const char *value);
#endif
+25
View File
@@ -0,0 +1,25 @@
#define LOG_MODULE "eamuse"
#include "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");
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef LAUNCHER_EAMUSE_H
#define LAUNCHER_EAMUSE_H
#include "imports/avs.h"
void eamuse_init(struct property_node *node);
void eamuse_fini();
#endif
+34
View File
@@ -0,0 +1,34 @@
#define LOG_MODULE "hook"
#include <windows.h>
#include "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");
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef LAUNCHER_HOOK_H
#define LAUNCHER_HOOK_H
void hook_load_dll(const char *path);
#endif
+371
View File
@@ -0,0 +1,371 @@
#define LOG_MODULE "launcher-config"
#include <string.h>
#include "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);
}
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef LAUNCHER_CONFIG_H
#define LAUNCHER_CONFIG_H
#include <windows.h>
#include "util/array.h"
#define LAUNCHER_CONFIG_MAX_HOOK_DLL 16
struct launcher_config {
uint32_t version;
struct launcher_bootstrap_config {
char selector[128];
struct property *property;
} bootstrap;
struct launcher_avs_config {
struct property *property;
} avs;
struct launcher_ea3_ident_config {
struct property *property;
} ea3_ident;
struct launcher_eamuse_config {
struct property *property;
} eamuse;
struct launcher_hook_config {
char hook_dlls[LAUNCHER_CONFIG_MAX_HOOK_DLL][MAX_PATH];
char before_hook_dlls[LAUNCHER_CONFIG_MAX_HOOK_DLL][MAX_PATH];
char iat_hook_dlls[LAUNCHER_CONFIG_MAX_HOOK_DLL][MAX_PATH];
} hook;
struct launcher_debug_config {
bool remote_debugger;
bool log_property_configs;
} debug;
};
void launcher_config_init(struct launcher_config *config);
void launcher_config_load(
struct property *property, struct launcher_config *config);
bool launcher_config_add_hook_dll(
struct launcher_config *config, const char *path);
bool launcher_config_add_before_hook_dll(
struct launcher_config *config, const char *path);
bool launcher_config_add_iat_hook_dll(
struct launcher_config *config, const char *path);
void launcher_config_fini(struct launcher_config *config);
#endif
+543
View File
@@ -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);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef LAUNCHER_LAUNCHER_H
#define LAUNCHER_LAUNCHER_H
#include "launcher/options.h"
void launcher_main(const struct options *options);
#endif
+365
View File
@@ -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);
}
+42
View File
@@ -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
View File
@@ -1,338 +1,23 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "imports/avs-ea3.h"
#include "imports/avs.h"
#include "launcher/avs-context.h"
#include "launcher/ea3-config.h"
#include "launcher/module.h"
#include "launcher/launcher.h"
#include "launcher/options.h"
#include "launcher/property.h"
#include "launcher/stubs.h"
#include "launcher/version.h"
#include "util/codepage.h"
#include "util/defs.h"
#include "util/fs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/os.h"
#include "util/str.h"
/* Gratuitous API changes orz */
static AVS_LOG_WRITER(log_callback, chars, nchars, ctx)
{
wchar_t *utf16;
char *utf8;
int utf16_len;
int utf8_len;
int result;
DWORD nwritten;
HANDLE console;
HANDLE file;
/* Ignore existing NUL terminator */
nchars--;
/* Transcode shit_jis to UTF-8 */
utf16_len = MultiByteToWideChar(CP_SHIFT_JIS, 0, chars, nchars, NULL, 0);
if (utf16_len == 0) {
abort();
}
utf16 = xmalloc(sizeof(*utf16) * utf16_len);
result =
MultiByteToWideChar(CP_SHIFT_JIS, 0, chars, nchars, utf16, utf16_len);
if (result == 0) {
abort();
}
utf8_len =
WideCharToMultiByte(CP_UTF8, 0, utf16, utf16_len, NULL, 0, NULL, NULL);
if (utf8_len == 0) {
abort();
}
utf8 = xmalloc(utf8_len + 2);
result = WideCharToMultiByte(
CP_UTF8, 0, utf16, utf16_len, utf8, utf8_len, NULL, NULL);
if (result == 0) {
abort();
}
#if AVS_VERSION >= 1500
utf8[utf8_len + 0] = '\r';
utf8[utf8_len + 1] = '\n';
utf8_len += 2;
#endif
/* Write to console and log file */
file = (HANDLE) ctx;
console = GetStdHandle(STD_OUTPUT_HANDLE);
if (ctx != INVALID_HANDLE_VALUE) {
WriteFile(file, utf8, utf8_len, &nwritten, NULL);
}
WriteFile(console, utf8, utf8_len, &nwritten, NULL);
/* Clean up */
free(utf8);
free(utf16);
}
static void load_hook_dlls(struct array *hook_dlls)
{
const char *hook_dll;
for (size_t i = 0; i < hook_dlls->nitems; i++) {
hook_dll = *array_item(char *, hook_dlls, i);
if (LoadLibraryA(hook_dll) == NULL) {
LPSTR buffer;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &buffer,
0,
NULL);
log_fatal("%s: Failed to load hook DLL: %s", hook_dll, buffer);
LocalFree(buffer);
}
}
}
int main(int argc, const char **argv)
{
bool ok;
HANDLE logfile;
struct ea3_ident ea3;
struct module_context module;
struct options options;
struct property *app_config;
struct property *avs_config;
struct property *ea3_config;
struct property_node *app_config_root;
struct property_node *avs_config_root;
struct property_node *ea3_config_root;
log_to_writer(log_writer_file, stdout);
log_info(
"launcher build date %s, gitrev %s",
launcher_build_date,
launcher_gitrev);
/* Read command line */
options_init(&options);
if (!options_read_cmdline(&options, argc, argv)) {
options_print_usage();
return EXIT_FAILURE;
exit(EXIT_FAILURE);
}
/* If enabled, wait for a remote debugger to attach. Spawning launcher
with a debugger crashes it for some reason (e.g. on jubeat08). However,
starting the launcher separately and attaching a remote debugger works */
launcher_main(&options);
if (options.remote_debugger) {
log_info("Waiting until debugger attaches to remote process...");
while (true) {
BOOL res = FALSE;
if (!CheckRemoteDebuggerPresent(GetCurrentProcess(), &res)) {
log_fatal(
"CheckRemoteDebuggerPresent failed: %08x",
(unsigned int) GetLastError());
}
if (res) {
log_info("Debugger attached, resuming");
break;
}
Sleep(1000);
}
}
/* Start up AVS */
if (options.logfile != NULL) {
logfile = CreateFileA(
options.logfile,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
0,
NULL);
} else {
logfile = INVALID_HANDLE_VALUE;
}
avs_config = boot_property_load(options.avs_config_path);
avs_config_root = property_search(avs_config, 0, "/config");
if (avs_config_root == NULL) {
log_fatal("%s: /config missing", options.avs_config_path);
}
load_hook_dlls(&options.before_hook_dlls);
avs_context_init(
avs_config_root,
options.avs_heap_size,
options.std_heap_size,
log_callback,
logfile);
boot_property_free(avs_config);
log_to_external(
log_body_misc, log_body_info, log_body_warning, log_body_fatal);
os_version_log();
/* Load game DLL */
if (options.iat_hook_dlls.nitems > 0) {
module_context_init_with_iat_hooks(
&module, options.module, &options.iat_hook_dlls);
} else {
module_context_init(&module, options.module);
}
/* Load hook DLLs */
load_hook_dlls(&options.hook_dlls);
/* Inject GetModuleHandle hooks */
stubs_init();
/* Prepare ea3 config */
ea3_config = boot_property_load(options.ea3_config_path);
ea3_config_root = property_search(ea3_config, 0, "/ea3");
if (ea3_config_root == NULL) {
log_fatal("%s: /ea3 missing", options.ea3_config_path);
}
ea3_ident_init(&ea3);
if (!ea3_ident_from_property(&ea3, ea3_config)) {
log_fatal(
"%s: Error reading IDs from config file", options.ea3_config_path);
}
if (options.softid != NULL) {
str_cpy(ea3.softid, lengthof(ea3.softid), options.softid);
}
if (options.pcbid != NULL) {
str_cpy(ea3.pcbid, lengthof(ea3.pcbid), options.pcbid);
}
if (!ea3.hardid[0]) {
ea3_ident_hardid_from_ethernet(&ea3);
}
/* Invoke dll_entry_init */
if (path_exists(options.app_config_path)) {
app_config = boot_property_load(options.app_config_path);
} else {
log_warning(
"%s: app config file missing, using empty",
options.app_config_path);
app_config = boot_property_load_cstring("<param>dummy</param>");
}
app_config_root = property_search(app_config, 0, "/param");
if (app_config_root == NULL) {
log_fatal("%s: /param missing", options.app_config_path);
}
if (IsDebuggerPresent()) {
/* Opportunity for breakpoint setup etc */
DebugBreak();
}
ok = ea3_ident_invoke_module_init(&ea3, &module, app_config_root);
if (!ok) {
log_fatal("%s: dll_module_init() returned failure", options.module);
}
boot_property_free(app_config);
ea3_ident_to_property(&ea3, ea3_config);
if (options.override_urlslash_enabled) {
log_info(
"Overriding url_slash to: %d", options.override_urlslash_value);
ea3_ident_replace_property_bool(
ea3_config_root,
"/network/url_slash",
options.override_urlslash_value);
}
if (options.override_service != NULL) {
log_info("Overriding service url to: %s", options.override_service);
ea3_ident_replace_property_str(
ea3_config_root, "/network/services", options.override_service);
}
/* Start up e-Amusement client */
ea3_boot(ea3_config_root);
boot_property_free(ea3_config);
/* Run application */
module_context_invoke_main(&module);
/* Shut down */
ea3_shutdown();
log_to_writer(log_writer_file, stdout);
avs_context_fini();
if (logfile != INVALID_HANDLE_VALUE) {
CloseHandle(logfile);
}
module_context_fini(&module);
options_fini(&options);
return EXIT_SUCCESS;
}
}
+142 -14
View File
@@ -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");
}
+10 -8
View File
@@ -5,6 +5,8 @@
#include "imports/avs.h"
#include "launcher/ea3-ident-config.h"
#include "util/array.h"
struct module_context {
@@ -12,16 +14,16 @@ struct module_context {
char *path;
};
void module_context_init(struct module_context *module, const char *path);
void module_context_init_with_iat_hooks(
void module_init(struct module_context *module, const char *path);
void module_with_iat_hooks_init(
struct module_context *module,
const char *path,
struct array *iat_hook_dlls);
bool module_context_invoke_init(
const struct array *iat_hook_dlls);
void module_init_invoke(
const struct module_context *module,
char *sidcode,
struct property_node *app_config);
bool module_context_invoke_main(const struct module_context *module);
void module_context_fini(struct module_context *module);
struct ea3_ident_config *ea3_ident_config,
struct property_node *app_params_node);
bool module_main_invoke(const struct module_context *module);
void module_fini(struct module_context *module);
#endif
+96 -108
View File
@@ -1,3 +1,5 @@
#define LOG_MODULE "options"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -6,62 +8,42 @@
#include "launcher/options.h"
#include "util/array.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
#define DEFAULT_HEAP_SIZE 16777216
void options_init(struct options *options)
{
options->std_heap_size = DEFAULT_HEAP_SIZE;
options->avs_heap_size = DEFAULT_HEAP_SIZE;
options->app_config_path = "prop/app-config.xml";
options->avs_config_path = "prop/avs-config.xml";
options->ea3_config_path = "prop/ea3-config.xml";
options->softid = NULL;
options->pcbid = NULL;
options->module = NULL;
options->logfile = NULL;
options->remote_debugger = false;
array_init(&options->hook_dlls);
array_init(&options->before_hook_dlls);
array_init(&options->iat_hook_dlls);
memset(options, 0, sizeof(struct options));
options->override_service = NULL;
options->override_urlslash_enabled = false;
options->override_urlslash_value = false;
array_init(&options->hook.hook_dlls);
array_init(&options->hook.before_hook_dlls);
array_init(&options->hook.iat_hook_dlls);
}
bool options_read_cmdline(struct options *options, int argc, const char **argv)
{
bool got_launcher_config;
got_launcher_config = false;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'A':
case 'T':
if (i + 1 >= argc) {
return false;
}
options->app_config_path = argv[++i];
options->bootstrap.config_path = argv[++i];
break;
case 'E':
case 'Z':
if (i + 1 >= argc) {
return false;
}
options->ea3_config_path = argv[++i];
break;
case 'V':
if (i + 1 >= argc) {
return false;
}
options->avs_config_path = argv[++i];
options->bootstrap.selector = argv[++i];
break;
@@ -70,7 +52,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
return false;
}
options->pcbid = argv[++i];
options->eamuse.pcbid = argv[++i];
break;
@@ -79,7 +61,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
return false;
}
options->softid = argv[++i];
options->eamuse.softid = argv[++i];
break;
@@ -88,37 +70,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
return false;
}
options->avs_heap_size =
(size_t) strtol(argv[++i], NULL, 0);
if (options->avs_heap_size == 0) {
return false;
}
break;
#ifdef AVS_HAS_STD_HEAP
case 'T':
if (i + 1 >= argc) {
return false;
}
options->std_heap_size =
(size_t) strtol(argv[++i], NULL, 0);
if (options->std_heap_size == 0) {
return false;
}
break;
#endif
case 'K':
if (i + 1 >= argc) {
return false;
}
*array_append(const char *, &options->hook_dlls) =
*array_append(const char *, &options->hook.hook_dlls) =
argv[++i];
break;
@@ -128,7 +80,8 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv)
return false;
}
*array_append(const char *, &options->before_hook_dlls) =
*array_append(
const char *, &options->hook.before_hook_dlls) =
argv[++i];
break;
@@ -141,17 +94,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
View File
@@ -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);
+618
View File
@@ -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);
}
+46
View File
@@ -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
-135
View File
@@ -1,135 +0,0 @@
#include <windows.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "imports/avs.h"
#include "launcher/property.h"
#include "util/log.h"
#include "util/mem.h"
static int boot_property_fread(uint32_t context, void *bytes, size_t nbytes)
{
FILE *f;
f = TlsGetValue(context);
return fread(bytes, 1, nbytes, f);
}
struct cstring_read_handle {
const char *buffer;
size_t buffer_len;
size_t offset;
};
static int
boot_property_cstring_read(uint32_t context, void *bytes, size_t nbytes)
{
int result = 0;
struct cstring_read_handle *h = TlsGetValue(context);
if (h->offset < h->buffer_len) {
result = min(nbytes, h->buffer_len - h->offset);
memcpy(bytes, (const void *) (h->buffer + h->offset), result);
h->offset += result;
}
return result;
}
struct property *boot_property_load(const char *filename)
{
struct property *prop;
void *buffer;
int nbytes;
FILE *f;
uint32_t f_keyhole;
/* AVS callbacks are only given a 32-bit context parameter, even in 64-bit
builds of AVS. We allocate a 32-bit TLS key and pass the context in this
manner instead. Inefficient, but it works. */
f = fopen(filename, "r");
f_keyhole = TlsAlloc();
TlsSetValue(f_keyhole, f);
if (f == NULL) {
log_fatal("%s: Error opening configuration file", filename);
}
nbytes = property_read_query_memsize(boot_property_fread, f_keyhole, 0, 0);
if (nbytes < 0) {
log_fatal("%s: Error querying configuration file", filename);
}
buffer = xmalloc(nbytes);
prop = property_create(
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
PROPERTY_FLAG_APPEND,
buffer,
nbytes);
rewind(f);
if (!property_insert_read(prop, 0, boot_property_fread, f_keyhole)) {
log_fatal("%s: Error reading configuration file", filename);
}
TlsFree(f_keyhole);
fclose(f);
return prop;
}
struct property *boot_property_load_cstring(const char *cstring)
{
struct property *prop;
void *buffer;
int nbytes;
uint32_t s_keyhole;
// see above
struct cstring_read_handle read_handle;
read_handle.buffer = cstring;
read_handle.buffer_len = strlen(cstring);
read_handle.offset = 0;
s_keyhole = TlsAlloc();
TlsSetValue(s_keyhole, &read_handle);
nbytes = property_read_query_memsize(
boot_property_cstring_read, s_keyhole, 0, 0);
if (nbytes < 0) {
log_fatal("Error querying configuration string");
}
buffer = xmalloc(nbytes);
prop = property_create(
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE |
PROPERTY_FLAG_APPEND,
buffer,
nbytes);
read_handle.offset = 0;
if (!property_insert_read(prop, 0, boot_property_cstring_read, s_keyhole)) {
log_fatal("Error inserting configuration string");
}
TlsFree(s_keyhole);
return prop;
}
void boot_property_free(struct property *prop)
{
void *buffer;
buffer = property_desc_to_buffer(prop);
property_destroy(prop);
free(buffer);
}
-10
View File
@@ -1,10 +0,0 @@
#ifndef LAUNCHER_PROPERTY_H
#define LAUNCHER_PROPERTY_H
#include "imports/avs.h"
struct property *boot_property_load(const char *filename);
struct property *boot_property_load_cstring(const char *cstring);
void boot_property_free(struct property *prop);
#endif
+4
View File
@@ -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));
}