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