Bemanitools v5.26 release
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
avsexes += launcher
|
||||
rc_launcher := launcher.rc
|
||||
|
||||
ldflags_launcher := \
|
||||
-mconsole \
|
||||
|
||||
deplibs_launcher := \
|
||||
avs \
|
||||
avs-ea3 \
|
||||
|
||||
libs_launcher := \
|
||||
hook \
|
||||
util \
|
||||
|
||||
src_launcher := \
|
||||
avs-context.c \
|
||||
ea3-config.c \
|
||||
main.c \
|
||||
module.c \
|
||||
options.c \
|
||||
property.c \
|
||||
stubs.c \
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef LAUNCHER_AVS_CONTEXT_H
|
||||
#define LAUNCHER_AVS_CONTEXT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#if AVS_VERSION < 1600
|
||||
#define AVS_HAS_STD_HEAP
|
||||
#endif
|
||||
|
||||
void avs_context_init(struct property_node *config,
|
||||
uint32_t avs_heap_size, uint32_t std_heap_size,
|
||||
avs_log_writer_t log_writer, void *log_writer_ctx);
|
||||
void avs_context_fini(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
#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);
|
||||
|
||||
ok = module_context_invoke_init(module, sidcode_short, app_config);
|
||||
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Back-propagate sidcode */
|
||||
|
||||
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 */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="requireAdministrator" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include <winuser.h>
|
||||
1 RT_MANIFEST launcher.exe.manifest
|
||||
@@ -0,0 +1,276 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
#include "imports/avs-ea3.h"
|
||||
|
||||
#include "launcher/avs-context.h"
|
||||
#include "launcher/ea3-config.h"
|
||||
#include "launcher/module.h"
|
||||
#include "launcher/options.h"
|
||||
#include "launcher/property.h"
|
||||
#include "launcher/stubs.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
#include "util/codepage.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);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int i;
|
||||
const char *hook_dll;
|
||||
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);
|
||||
|
||||
/* Read command line */
|
||||
|
||||
options_init(&options);
|
||||
|
||||
if (!options_read_cmdline(&options, argc, argv)) {
|
||||
options_print_usage();
|
||||
|
||||
return 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 */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Load game DLL */
|
||||
|
||||
module_context_init(&module, options.module);
|
||||
|
||||
/* Load hook DLLs */
|
||||
|
||||
for (i = 0 ; i < options.hook_dlls.nitems ; i++) {
|
||||
hook_dll = *array_item(char *, &options.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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
||||
app_config = boot_property_load(options.app_config_path);
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
#include "imports/eapki.h"
|
||||
|
||||
#include "launcher/module.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
void module_context_init(struct module_context *module, const char *path)
|
||||
{
|
||||
log_assert(module != NULL);
|
||||
log_assert(path != NULL);
|
||||
|
||||
module->dll = LoadLibrary(path);
|
||||
|
||||
if (module->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 game DLL: %s", path, buffer);
|
||||
|
||||
LocalFree(buffer);
|
||||
}
|
||||
|
||||
module->path = str_dup(path);
|
||||
}
|
||||
|
||||
bool module_context_invoke_init(const struct module_context *module,
|
||||
char *sidcode, struct property_node *app_config)
|
||||
{
|
||||
dll_entry_init_t init;
|
||||
|
||||
log_assert(module != NULL);
|
||||
log_assert(sidcode != NULL);
|
||||
log_assert(app_config != NULL);
|
||||
|
||||
init = (void *) GetProcAddress(module->dll, "dll_entry_init");
|
||||
|
||||
if (init == NULL) {
|
||||
log_fatal(
|
||||
"%s: dll_entry_init not found. Is this a game DLL?",
|
||||
module->path);
|
||||
}
|
||||
|
||||
return init(sidcode, app_config);
|
||||
}
|
||||
|
||||
bool module_context_invoke_main(const struct module_context *module)
|
||||
{
|
||||
/* GCC warns if you call a variable "main" */
|
||||
dll_entry_main_t main_;
|
||||
|
||||
log_assert(module != NULL);
|
||||
|
||||
main_ = (void *) GetProcAddress(module->dll, "dll_entry_main");
|
||||
|
||||
if (main_ == NULL) {
|
||||
log_fatal(
|
||||
"%s: dll_entry_main not found. Is this a game DLL?",
|
||||
module->path);
|
||||
}
|
||||
|
||||
return main_();
|
||||
}
|
||||
|
||||
void module_context_fini(struct module_context *module)
|
||||
{
|
||||
if (module == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(module->path);
|
||||
|
||||
if (module->dll != NULL) {
|
||||
FreeLibrary(module->dll);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef LAUNCHER_MODULE_H
|
||||
#define LAUNCHER_MODULE_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
struct module_context {
|
||||
HMODULE dll;
|
||||
char *path;
|
||||
};
|
||||
|
||||
void module_context_init(struct module_context *module, const char *path);
|
||||
bool module_context_invoke_init(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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "launcher/options.h"
|
||||
|
||||
#include "util/array.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);
|
||||
}
|
||||
|
||||
bool options_read_cmdline(struct options *options, int argc, const char **argv)
|
||||
{
|
||||
for (int i = 1 ; i < argc ; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
switch (argv[i][1]) {
|
||||
case 'A':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->app_config_path = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
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];
|
||||
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->pcbid = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->softid = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if (i + 1 >= argc) {
|
||||
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)=argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
if (i + 1 >= argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
options->logfile = argv[++i];
|
||||
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
options->remote_debugger = true;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!options->module) {
|
||||
options->module = argv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options->module) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void options_print_usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: launcher.exe [launcher options...] <app.dll> [hooks options...] \n"
|
||||
"\n"
|
||||
" The following options can be specified before the app DLL path:\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"
|
||||
" -K [filename] Load hook DLL (can be specified multiple times)\n"
|
||||
" -Y [filename] Log to a file in addition to the console\n"
|
||||
" -D Halt the launcher before bootstrapping AVS until a"
|
||||
" remote debugger is attached\n"
|
||||
);
|
||||
}
|
||||
|
||||
void options_fini(struct options *options)
|
||||
{
|
||||
array_fini(&options->hook_dlls);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef LAUNCHER_OPTIONS_H
|
||||
#define LAUNCHER_OPTIONS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/array.h"
|
||||
|
||||
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;
|
||||
bool remote_debugger;
|
||||
};
|
||||
|
||||
void options_init(struct options *options);
|
||||
bool options_read_cmdline(struct options *options, int argc, const char **argv);
|
||||
void options_print_usage(void);
|
||||
void options_fini(struct options *options);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
#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 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 parsing 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 parsing configuration file", filename);
|
||||
}
|
||||
|
||||
TlsFree(f_keyhole);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
void boot_property_free(struct property *prop)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
buffer = property_desc_to_buffer(prop);
|
||||
property_destroy(prop);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef LAUNCHER_PROPERTY_H
|
||||
#define LAUNCHER_PROPERTY_H
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
struct property *boot_property_load(const char *filename);
|
||||
void boot_property_free(struct property *prop);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,167 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "launcher/stubs.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
struct ikey_status {
|
||||
uint32_t field_0;
|
||||
uint8_t field_4;
|
||||
uint8_t field_5;
|
||||
uint8_t field_6;
|
||||
uint8_t field_7;
|
||||
uint32_t cert_valid_start;
|
||||
uint32_t cert_valid_end;
|
||||
};
|
||||
|
||||
struct stub {
|
||||
const char *name;
|
||||
void *proc;
|
||||
};
|
||||
|
||||
typedef void (*bt_get_fucked_t)(uint32_t /* even in 64-bit */ context);
|
||||
|
||||
static HMODULE STDCALL my_GetModuleHandleA(const char *name);
|
||||
static HMODULE STDCALL my_GetModuleHandleW(const wchar_t *name);
|
||||
static void * STDCALL my_GetProcAddress(HMODULE dll, const char *name);
|
||||
|
||||
static void bt_get_ikey_status(struct ikey_status *ik);
|
||||
|
||||
#if AVS_VERSION >= 1600
|
||||
static void bt_get_fucked(bt_get_fucked_t callback, uint32_t ctx);
|
||||
#endif
|
||||
|
||||
static void bt_fcheck_init(void);
|
||||
static void *bt_fcheck_main(void *unknown);
|
||||
static void bt_fcheck_finish(void);
|
||||
|
||||
static HMODULE (STDCALL *real_GetModuleHandleA)(const char *name);
|
||||
static HMODULE (STDCALL *real_GetModuleHandleW)(const wchar_t *name);
|
||||
static void * (STDCALL *real_GetProcAddress)(HMODULE dll, const char *name);
|
||||
|
||||
static struct stub stub_list[] = {
|
||||
#if AVS_VERSION >= 1600
|
||||
{ "k_bt0001", bt_get_ikey_status },
|
||||
{ "k_bt0002", bt_get_fucked },
|
||||
#else
|
||||
{ "bt_get_ikey_status", bt_get_ikey_status },
|
||||
#endif
|
||||
{ "bt_fcheck_init", bt_fcheck_init },
|
||||
{ "bt_fcheck_main", bt_fcheck_main },
|
||||
{ "bt_fcheck_finish", bt_fcheck_finish },
|
||||
};
|
||||
|
||||
static struct hook_symbol stub_hook_syms[] = {
|
||||
{
|
||||
.name = "GetModuleHandleA",
|
||||
.patch = my_GetModuleHandleA,
|
||||
.link = (void *)&real_GetModuleHandleA
|
||||
},
|
||||
{
|
||||
.name = "GetModuleHandleW",
|
||||
.patch = my_GetModuleHandleW,
|
||||
.link = (void *)&real_GetModuleHandleW
|
||||
},
|
||||
{
|
||||
.name = "GetProcAddress",
|
||||
.patch = my_GetProcAddress,
|
||||
.link = (void *)&real_GetProcAddress
|
||||
},
|
||||
};
|
||||
|
||||
static HMODULE STDCALL my_GetModuleHandleA(const char *name)
|
||||
{
|
||||
/* Check name for null because winio32 calls this with null parameters
|
||||
when the module is loaded on win xp (doesn't happen on win 7) */
|
||||
if ( name != NULL &&
|
||||
(_stricmp(name, "kbt.dll") == 0 ||
|
||||
_stricmp(name, "kld.dll") == 0)) {
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
return real_GetModuleHandleA(name);
|
||||
}
|
||||
|
||||
static HMODULE STDCALL my_GetModuleHandleW(const wchar_t *name)
|
||||
{
|
||||
/* Check name for null because winio32 calls this with null parameters
|
||||
when the module is loaded on win xp (doesn't happen on win 7) */
|
||||
if ( name != NULL &&
|
||||
(wcsicmp(name, L"kbt.dll") == 0 ||
|
||||
wcsicmp(name, L"kld.dll") == 0)) {
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
return real_GetModuleHandleW(name);
|
||||
}
|
||||
|
||||
static void * STDCALL my_GetProcAddress(HMODULE dll, const char *name)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (dll != real_GetModuleHandleA(NULL)) {
|
||||
return real_GetProcAddress(dll, name);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < lengthof(stub_list) ; i++) {
|
||||
if (strcmp(stub_list[i].name, name) == 0) {
|
||||
return stub_list[i].proc;
|
||||
}
|
||||
}
|
||||
|
||||
log_warning("Request for unknown stub %s", name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void stubs_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"kernel32.dll",
|
||||
stub_hook_syms,
|
||||
lengthof(stub_hook_syms));
|
||||
}
|
||||
|
||||
static void bt_get_ikey_status(struct ikey_status *ik)
|
||||
{
|
||||
memset(ik, 0, sizeof(*ik) * 2);
|
||||
|
||||
ik[0].field_4 = true;
|
||||
ik[0].field_6 = true;
|
||||
ik[1].field_4 = true;
|
||||
ik[1].field_6 = true;
|
||||
|
||||
/* Not strictly necessary */
|
||||
ik[0].cert_valid_end = -1;
|
||||
ik[1].cert_valid_end = -1;
|
||||
}
|
||||
|
||||
#if AVS_VERSION >= 1600
|
||||
static void bt_get_fucked(bt_get_fucked_t callback, uint32_t ctx)
|
||||
{
|
||||
log_info(">>> k_bt0002");
|
||||
callback(ctx);
|
||||
log_info("<<< k_bt0002");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bt_fcheck_init(void)
|
||||
{}
|
||||
|
||||
static void *bt_fcheck_main(void *unknown)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bt_fcheck_finish(void)
|
||||
{}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef LAUNCHER_STUBS_H
|
||||
#define LAUNCHER_STUBS_H
|
||||
|
||||
void stubs_init(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user