Files
djhackersdev_bemanitools/src/main/launcher/avs.c
T
icex2 e61dfb3977 refactor: Move all hook and IO libraries to use btools log and thread api
Remove usages of AVS threads and logging functions from the bemanitools backend. Use the bemanitools 6 API instead
which propagates interfaces to call thread and log APIs.

Prior design decisions for using these were:
- have a single log stream through the AVS logging system for “neatness”, log output redirection options in launcher, and theoretically logging to an xrpc endpoint
- IIDX 19 to IIDX 24 crash the AVS logging engine in libavs due to IO hook code running in a non AVS thread (threads in ezusb library) causing a stackoverflow when trying to acquire a mutex (see dev journal 2018-02-10-logging-breakdown-avs.md for details)

With a flexible log sink architecture in the core of bemanitools, and a separate writer function that is hooked up as a log writer to AVS, log streams are still unified on a sink level. This takes care of having all log messaged sinked to the same targets no matter how many logging engines are using them.

This avoids going through the AVS logging engine with the IO related hooking code in iidx, which just uses the bemanitools logging engine instead.

Furthermore, this removes any needs to having to switch thread and logging implementations once AVS is booted which significantly simplifies the runtime orchestration during bootstrapping.

The log-server, which was specifically implemented for iidx to solve the threading issue, can also be removed now. Unfortunately, this also caused performance issues such as stuttering due to it’s rather simplistic implementation.
2024-08-15 11:34:31 +02:00

283 lines
6.0 KiB
C

#define LOG_MODULE "avs"
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "avs-ext/error.h"
#include "avs-ext/property-node.h"
#include "core/log-bt.h"
#include "core/property-node.h"
#include "iface-core/log.h"
#include "imports/avs.h"
#include "launcher/avs-config.h"
#include "launcher/avs.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);
}
void avs_fs_assert_root_device_exists(const core_property_node_t *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(const core_property_node_t *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(
const core_property_node_t *node,
uint32_t avs_heap_size,
uint32_t std_heap_size)
{
struct property_node *avs_node;
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");
avs_node = avs_ext_property_node_avs_property_node_get(node);
#ifdef AVS_HAS_STD_HEAP
avs_boot(
avs_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(
avs_node, avs_heap, avs_heap_size, NULL, _avs_context_log_writer, NULL);
#endif
log_misc("init done");
}
void avs_fs_file_copy(const char *src, const char *dst)
{
struct avs_stat st;
avs_error error;
log_assert(src);
log_assert(dst);
log_misc("Copying %s to %s...", src, dst);
error = avs_fs_lstat(src, &st);
if (AVS_IS_ERROR(error)) {
log_fatal(
"File source %s does not exist or is not accessible: %s",
src,
avs_ext_error_str(error));
}
error = avs_fs_copy(src, dst);
if (AVS_IS_ERROR(error)) {
log_fatal(
"Failed copying file %s to %s: %s",
src,
dst,
avs_ext_error_str(error));
}
}
void avs_fs_dir_log(const char *path)
{
const char *name;
avs_desc dir;
log_assert(path);
dir = avs_fs_opendir(path);
if (AVS_IS_ERROR(dir)) {
log_warning(
"Opening avs dir %s failed, skipping logging contents: %s",
path,
avs_ext_error_str(dir));
}
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");
}