From dd7d31f81fc58a9f3e9c973c6f0cf67daacd45ed Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 10:46:20 +0200 Subject: [PATCH 01/72] imports: add property_node_clone() and property_node_traversal() --- src/imports/import_32_1101_avs.def | 2 ++ src/imports/import_32_1304_avs.def | 2 ++ src/imports/import_32_1403_avs.def | 2 ++ src/imports/import_32_1508_avs.def | 2 ++ src/imports/import_32_1601_avs.def | 2 ++ src/imports/import_32_1603_avs.def | 2 ++ src/imports/import_32_1700_avs.def | 2 ++ src/imports/import_64_1508_avs.def | 2 ++ src/imports/import_64_1509_avs.def | 2 ++ src/imports/import_64_1601_avs.def | 2 ++ src/imports/import_64_1603_avs.def | 2 ++ src/imports/import_64_1700_avs.def | 2 ++ 12 files changed, 24 insertions(+) diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index cfc6f9b5..b892c78e 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -21,6 +21,8 @@ EXPORTS property_node_datasize @267 NONAME property_node_refer @278 NONAME property_node_remove @279 NONAME + property_node_clone @280 NONAME + property_node_traversal @282 NONAME property_psmap_import @288 NONAME property_psmap_export @287 NONAME property_read_query_memsize @291 NONAME diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index 489944a7..8f149ae4 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -21,6 +21,8 @@ EXPORTS property_node_datasize @249 NONAME property_node_refer @268 NONAME property_node_remove @129 NONAME + property_node_clone @130 NONAME + property_node_traversal @132 NONAME property_psmap_import @102 NONAME property_psmap_export @110 NONAME property_read_query_memsize @100 NONAME diff --git a/src/imports/import_32_1403_avs.def b/src/imports/import_32_1403_avs.def index 32d68b11..9a3b7c92 100644 --- a/src/imports/import_32_1403_avs.def +++ b/src/imports/import_32_1403_avs.def @@ -16,6 +16,8 @@ EXPORTS property_destroy @130 NONAME property_insert_read @133 NONAME property_node_remove @148 NONAME + property_node_clone @149 NONAME + property_node_traversal @151 NONAME property_psmap_import @163 NONAME property_psmap_export @164 NONAME property_read_query_memsize @161 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index 05419ed7..d937da48 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -24,6 +24,8 @@ EXPORTS property_node_create @145 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME + property_node_clone @147 NONAME + property_node_traversal @149 NONAME property_psmap_export @162 NONAME property_psmap_import @161 NONAME property_read_query_memsize @159 NONAME diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index e260a8c6..f815797a 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -13,6 +13,8 @@ EXPORTS property_search @141 NONAME property_node_create @142 NONAME property_node_remove @143 NONAME + property_node_clone @144 NONAME + property_node_traversal @146 NONAME property_node_refer @155 NONAME property_read_query_memsize @156 NONAME property_psmap_export @159 NONAME diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index 98f5fe7a..dfbc4263 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -13,6 +13,8 @@ EXPORTS property_search @162 NONAME property_node_create @163 NONAME property_node_remove @164 NONAME + property_node_clone @165 NONAME + property_node_traversal @167 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME diff --git a/src/imports/import_32_1700_avs.def b/src/imports/import_32_1700_avs.def index d8944d9b..0922175a 100644 --- a/src/imports/import_32_1700_avs.def +++ b/src/imports/import_32_1700_avs.def @@ -15,6 +15,8 @@ EXPORTS property_search @162 NONAME property_node_create @163 NONAME property_node_remove @164 NONAME + property_node_clone @165 NONAME + property_node_traversal @167 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME diff --git a/src/imports/import_64_1508_avs.def b/src/imports/import_64_1508_avs.def index bc9fbf06..5e5e2e66 100644 --- a/src/imports/import_64_1508_avs.def +++ b/src/imports/import_64_1508_avs.def @@ -24,6 +24,8 @@ EXPORTS property_node_create @145 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME + property_node_clone @147 NONAME + property_node_traversal @149 NONAME property_psmap_export @162 NONAME property_psmap_import @161 NONAME property_read_query_memsize @159 NONAME diff --git a/src/imports/import_64_1509_avs.def b/src/imports/import_64_1509_avs.def index ae6110a2..a24c1669 100644 --- a/src/imports/import_64_1509_avs.def +++ b/src/imports/import_64_1509_avs.def @@ -24,6 +24,8 @@ EXPORTS property_node_create @145 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME + property_node_clone @147 NONAME + property_node_traversal @149 NONAME property_psmap_export @162 NONAME property_psmap_import @161 NONAME property_read_query_memsize @159 NONAME diff --git a/src/imports/import_64_1601_avs.def b/src/imports/import_64_1601_avs.def index 5bbe4528..e337bcc9 100644 --- a/src/imports/import_64_1601_avs.def +++ b/src/imports/import_64_1601_avs.def @@ -13,6 +13,8 @@ EXPORTS property_search @141 NONAME property_node_create @142 NONAME property_node_remove @143 NONAME + property_node_clone @144 NONAME + property_node_traversal @146 NONAME property_node_refer @155 NONAME property_read_query_memsize @156 NONAME property_psmap_export @159 NONAME diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index 0355922f..59275628 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -13,6 +13,8 @@ EXPORTS property_search @162 NONAME property_node_create @163 NONAME property_node_remove @164 NONAME + property_node_clone @165 NONAME + property_node_traversal @167 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME diff --git a/src/imports/import_64_1700_avs.def b/src/imports/import_64_1700_avs.def index fe8cc6e6..68c74e0f 100644 --- a/src/imports/import_64_1700_avs.def +++ b/src/imports/import_64_1700_avs.def @@ -15,6 +15,8 @@ EXPORTS property_search @162 NONAME property_node_create @163 NONAME property_node_remove @164 NONAME + property_node_clone @165 NONAME + property_node_traversal @167 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME From 037a6ef6021699098720ae7e8a4bb6a106be7ac0 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 10:47:23 +0200 Subject: [PATCH 02/72] imports: clarify property_node_clone() prototype --- src/imports/avs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/imports/avs.h b/src/imports/avs.h index ff48a33e..84ecb4cd 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -203,8 +203,8 @@ int property_psmap_export( const struct property_psmap *psmap); struct property_node *property_node_clone( - struct property *new_parent, - int unk0, + struct property *parent_prop, + struct property_node *parent_node, struct property_node *src, bool deep); struct property_node *property_node_create( From 45e785507dbdedc7e93d9208e285b8b96839c0c0 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 10:47:57 +0200 Subject: [PATCH 03/72] launcher: override ea3 config using ea3-ident.xml --- src/main/launcher/main.c | 17 +++++++++++++++++ src/main/launcher/options.c | 1 + src/main/launcher/options.h | 1 + 3 files changed, 19 insertions(+) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 70c5184d..2cb7eba8 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -243,6 +243,23 @@ int main(int argc, const char **argv) log_fatal("%s: /ea3 missing", options.ea3_config_path); } + if (path_exists(options.ea3_ident_path)) { + log_info("%s: loading override", options.ea3_ident_path); + struct property *ea3_ident = boot_property_load(options.ea3_ident_path); + struct property_node *node = + property_search(ea3_ident, NULL, "/ea3_conf"); + if (node == NULL) { + log_fatal("%s: /ea3_conf missing", options.ea3_ident_path); + } + + for (node = property_node_traversal(node, TRAVERSE_FIRST_CHILD); node; + node = property_node_traversal(node, TRAVERSE_NEXT_SIBLING)) { + property_node_clone(NULL, ea3_config_root, node, TRUE); + } + + boot_property_free(ea3_ident); + } + ea3_ident_init(&ea3); if (!ea3_ident_from_property(&ea3, ea3_config)) { diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index 757555ec..d2467a39 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -19,6 +19,7 @@ void options_init(struct options *options) 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->ea3_ident_path = "prop/ea3-ident.xml"; options->softid = NULL; options->pcbid = NULL; options->module = NULL; diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 13f1c0fb..99dd0229 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -12,6 +12,7 @@ struct options { const char *app_config_path; const char *avs_config_path; const char *ea3_config_path; + const char *ea3_ident_path; const char *softid; const char *pcbid; const char *module; From aff97ca9760548c76f76cbc1ab9ee92673cba737 Mon Sep 17 00:00:00 2001 From: Shiz Date: Sun, 30 Apr 2023 12:12:35 +0200 Subject: [PATCH 04/72] launcher: parse options in two phases --- src/main/launcher/main.c | 1 + src/main/launcher/options.c | 13 +++++++++++++ src/main/launcher/options.h | 2 ++ 3 files changed, 16 insertions(+) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 2cb7eba8..280f0025 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -148,6 +148,7 @@ int main(int argc, const char **argv) /* Read command line */ options_init(&options); + options_read_early_cmdline(&options, argc, argv); if (!options_read_cmdline(&options, argc, argv)) { options_print_usage(); diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index d2467a39..e97fdcc1 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -34,6 +34,19 @@ void options_init(struct options *options) options->override_urlslash_value = false; } +void options_read_early_cmdline( + struct options *options, int argc, const char **argv) +{ + for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + default: + break; + } + } + } +} + bool options_read_cmdline(struct options *options, int argc, const char **argv) { for (int i = 1; i < argc; i++) { diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 99dd0229..86548968 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -27,6 +27,8 @@ struct options { }; void options_init(struct options *options); +void options_read_early_cmdline( + struct options *options, int argc, const char **argv); bool options_read_cmdline(struct options *options, int argc, const char **argv); void options_print_usage(void); void options_fini(struct options *options); From 62775d60785539837a2f3e12fdf404559f832e53 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 10:47:09 +0200 Subject: [PATCH 05/72] imports: add PROPERTY_TYPE_ATTR --- src/imports/avs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/imports/avs.h b/src/imports/avs.h index 84ecb4cd..a2376b3b 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -37,6 +37,7 @@ enum property_type { PROPERTY_TYPE_U64 = 9, PROPERTY_TYPE_BIN = 10, PROPERTY_TYPE_STR = 11, + PROPERTY_TYPE_ATTR = 46, PROPERTY_TYPE_BOOL = 52 }; From 09d7c1bbe3e5371a2560ad2b5f385882e788a45b Mon Sep 17 00:00:00 2001 From: Shiz Date: Sun, 30 Apr 2023 19:24:09 +0200 Subject: [PATCH 06/72] imports: add avs_fs_* definitions --- src/imports/avs.h | 38 +++++++++++++++++++++++++++++- src/imports/import_32_0_avs.def | 6 +++++ src/imports/import_32_1002_avs.def | 6 +++++ src/imports/import_32_1101_avs.def | 6 +++++ src/imports/import_32_1304_avs.def | 5 ++++ src/imports/import_32_1403_avs.def | 6 +++++ src/imports/import_32_1508_avs.def | 2 ++ src/imports/import_32_1601_avs.def | 6 +++++ src/imports/import_32_1603_avs.def | 6 +++++ src/imports/import_32_1700_avs.def | 6 +++++ src/imports/import_32_803_avs.def | 6 +++++ src/imports/import_64_1508_avs.def | 2 ++ src/imports/import_64_1509_avs.def | 2 ++ src/imports/import_64_1601_avs.def | 6 +++++ src/imports/import_64_1603_avs.def | 6 +++++ src/imports/import_64_1700_avs.def | 6 +++++ 16 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/imports/avs.h b/src/imports/avs.h index a2376b3b..a6b23f92 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -4,6 +4,7 @@ #include #include #include +#include enum property_create_flag { PROPERTY_FLAG_READ = 0x1, @@ -161,6 +162,8 @@ void avs_boot( void avs_shutdown(void); +typedef uint32_t avs_desc; + void log_body_fatal(const char *module, const char *fmt, ...); void log_body_info(const char *module, const char *fmt, ...); void log_body_misc(const char *module, const char *fmt, ...); @@ -232,7 +235,40 @@ void property_node_datasize(struct property_node *node); bool std_getenv(const char *key, char *val, uint32_t nbytes); void std_setenv(const char *key, const char *val); -void* avs_fs_open(const char* path, int mode, int flags); +struct avs_stat { + uint64_t st_atime; + uint64_t st_mtime; + uint64_t st_ctime; + uint32_t unk1; + uint32_t filesize; + struct stat padding; +}; + +enum avs_file_mode { + AVS_FILE_READ = 1, + AVS_FILE_WRITE = 2, + AVS_FILE_CREATE = 0x10, + AVS_FILE_TRUNCATE = 0x20, + AVS_FILE_EXCLUSIVE = 0x80, +}; + +enum avs_file_flag { + AVS_FILE_FLAG_SHARE_READ = 0x124, + AVS_FILE_FLAG_SHARE_WRITE = 0x92, +}; + +enum avs_seek_origin { + AVS_SEEK_SET = 0, + AVS_SEEK_CUR = 1, + AVS_SEEK_END = 2, +}; + +avs_desc avs_fs_open(const char *path, uint16_t mode, int flags); +int avs_fs_close(avs_desc desc); +size_t avs_fs_read(avs_desc desc, char *buf, uint32_t sz); +int avs_fs_lseek(avs_desc desc, long pos, int whence); +int avs_fs_lstat(const char *path, struct avs_stat *st); +int avs_fs_copy(const char *src, const char *dest); int avs_fs_addfs(void *filesys_struct); int avs_fs_mount( const char *mountpoint, const char *fsroot, const char *fstype, void *data); diff --git a/src/imports/import_32_0_avs.def b/src/imports/import_32_0_avs.def index cb2ea9d1..c808c1eb 100644 --- a/src/imports/import_32_0_avs.def +++ b/src/imports/import_32_0_avs.def @@ -2,6 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot + avs_fs_close + avs_fs_copy + avs_fs_lseek + avs_fs_lstat + avs_fs_open + avs_fs_read avs_net_ctrl avs_shutdown avs_thread_create diff --git a/src/imports/import_32_1002_avs.def b/src/imports/import_32_1002_avs.def index 6d1fd136..d421b1a3 100644 --- a/src/imports/import_32_1002_avs.def +++ b/src/imports/import_32_1002_avs.def @@ -2,6 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot + avs_fs_close + avs_fs_copy + avs_fs_lseek + avs_fs_lstat + avs_fs_open + avs_fs_read avs_net_ctrl avs_shutdown avs_thread_create diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index b892c78e..a8bc4048 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -2,6 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot @22 NONAME + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_net_ctrl @107 NONAME avs_shutdown @140 NONAME avs_thread_create @156 NONAME diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index 8f149ae4..dd287422 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -2,7 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot @237 NONAME + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME avs_fs_open @178 NONAME + avs_fs_read @573 NONAME avs_net_ctrl @15 NONAME avs_shutdown @333 NONAME avs_thread_create @183 NONAME diff --git a/src/imports/import_32_1403_avs.def b/src/imports/import_32_1403_avs.def index 9a3b7c92..61d2cd49 100644 --- a/src/imports/import_32_1403_avs.def +++ b/src/imports/import_32_1403_avs.def @@ -2,6 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot @298 NONAME + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_net_ctrl @100 NONAME avs_shutdown @299 NONAME avs_thread_create @6 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index d937da48..88d6ce52 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -5,8 +5,10 @@ EXPORTS avs_fs_close @65 NONAME avs_fs_lseek @59 NONAME avs_fs_lseek64 @60 NONAME + avs_fs_lstat @79 NONAME avs_fs_open @58 NONAME avs_fs_read @61 NONAME + avs_fs_copy @81 NONAME avs_net_ctrl @98 NONAME avs_shutdown @286 NONAME avs_thread_create @6 NONAME diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index f815797a..b6410ca6 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -1,6 +1,12 @@ LIBRARY libavs-win32 EXPORTS + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index dfbc4263..f815822a 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -1,6 +1,12 @@ LIBRARY libavs-win32 EXPORTS + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_32_1700_avs.def b/src/imports/import_32_1700_avs.def index 0922175a..a4813abe 100644 --- a/src/imports/import_32_1700_avs.def +++ b/src/imports/import_32_1700_avs.def @@ -6,7 +6,13 @@ EXPORTS avs_thread_exit @11 NONAME avs_thread_join @12 NONAME avs_fs_addfs @73 NONAME + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME avs_fs_mount @76 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_net_ctrl @119 NONAME property_create @145 NONAME property_destroy @146 NONAME diff --git a/src/imports/import_32_803_avs.def b/src/imports/import_32_803_avs.def index a7f59b51..8c6b93e2 100644 --- a/src/imports/import_32_803_avs.def +++ b/src/imports/import_32_803_avs.def @@ -2,6 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot + avs_fs_close + avs_fs_copy + avs_fs_lseek + avs_fs_lstat + avs_fs_open + avs_fs_read avs_net_ctrl avs_shutdown avs_thread_create diff --git a/src/imports/import_64_1508_avs.def b/src/imports/import_64_1508_avs.def index 5e5e2e66..210b7ad8 100644 --- a/src/imports/import_64_1508_avs.def +++ b/src/imports/import_64_1508_avs.def @@ -3,8 +3,10 @@ LIBRARY libavs-win64 EXPORTS avs_boot @285 NONAME avs_fs_close @65 NONAME + avs_fs_copy @81 NONAME avs_fs_lseek @59 NONAME avs_fs_lseek64 @60 NONAME + avs_fs_lstat @79 NONAME avs_fs_open @58 NONAME avs_fs_read @61 NONAME avs_net_ctrl @98 NONAME diff --git a/src/imports/import_64_1509_avs.def b/src/imports/import_64_1509_avs.def index a24c1669..9b6b945e 100644 --- a/src/imports/import_64_1509_avs.def +++ b/src/imports/import_64_1509_avs.def @@ -3,8 +3,10 @@ LIBRARY libavs-win64 EXPORTS avs_boot @285 NONAME avs_fs_close @65 NONAME + avs_fs_copy @81 NONAME avs_fs_lseek @59 NONAME avs_fs_lseek64 @60 NONAME + avs_fs_lstat @78 NONAME avs_fs_open @58 NONAME avs_fs_read @61 NONAME avs_net_ctrl @98 NONAME diff --git a/src/imports/import_64_1601_avs.def b/src/imports/import_64_1601_avs.def index e337bcc9..6dbb0f2f 100644 --- a/src/imports/import_64_1601_avs.def +++ b/src/imports/import_64_1601_avs.def @@ -1,6 +1,12 @@ LIBRARY libavs-win64 EXPORTS + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index 59275628..007bd5a8 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -1,6 +1,12 @@ LIBRARY libavs-win64 EXPORTS + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_64_1700_avs.def b/src/imports/import_64_1700_avs.def index 68c74e0f..1d1e0f00 100644 --- a/src/imports/import_64_1700_avs.def +++ b/src/imports/import_64_1700_avs.def @@ -6,7 +6,13 @@ EXPORTS avs_thread_exit @11 NONAME avs_thread_join @12 NONAME avs_fs_addfs @73 NONAME + avs_fs_close @573 NONAME + avs_fs_copy @573 NONAME + avs_fs_lseek @573 NONAME + avs_fs_lstat @573 NONAME avs_fs_mount @76 NONAME + avs_fs_open @573 NONAME + avs_fs_read @573 NONAME avs_net_ctrl @119 NONAME property_create @145 NONAME property_destroy @146 NONAME From bc4d83d303696c67ba56e83ffd6c9c2e8db44249 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 11:13:29 +0200 Subject: [PATCH 07/72] launcher: implement bootstrap.xml parsing --- src/main/launcher/Module.mk | 1 + src/main/launcher/bs-config.c | 271 ++++++++++++++++++++++++++++++++++ src/main/launcher/bs-config.h | 101 +++++++++++++ src/main/launcher/main.c | 20 +++ src/main/launcher/options.c | 34 +++++ src/main/launcher/options.h | 2 + 6 files changed, 429 insertions(+) create mode 100644 src/main/launcher/bs-config.c create mode 100644 src/main/launcher/bs-config.h diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index c9dfd078..8deb07a9 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -14,6 +14,7 @@ libs_launcher := \ src_launcher := \ avs-context.c \ + bs-config.c \ ea3-config.c \ main.c \ module.c \ diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bs-config.c new file mode 100644 index 00000000..763c890b --- /dev/null +++ b/src/main/launcher/bs-config.c @@ -0,0 +1,271 @@ +#define LOG_MODULE "bootstrap" +#include + +#include "imports/avs.h" + +#include "launcher/bs-config.h" + +#include "util/defs.h" +#include "util/hex.h" +#include "util/log.h" +#include "util/str.h" + +// clang-format off +PSMAP_BEGIN(bootstrap_startup_psmap) +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, avs_config_file, + "boot/file") +PSMAP_REQUIRED(PSMAP_TYPE_U32, struct bootstrap_startup_config, avs_heap_size, + "boot/heap_avs") +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, std_heap_size, + "boot/heap_std", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, mount_table_selector, + "boot/mounttable_selector", "boot") +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, watcher_enable, + "boot/watcher", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, timemachine_enable, + "boot/timemachine", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, launch_config_file, + "boot/launch_path", "/dev/raw/launch.xml") + +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, log_level, + "log/level", "all") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, log_name, + "log/name", "") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, log_file, + "log/file", "") +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, log_bufsz, + "log/sz_buf", 4096) +PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_startup_config, log_output_delay_ms, + "log/output_delay", 10) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_console, + "log/enable_console", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_sci, + "log/enable_netsci", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_net, + "log/enable_netlog", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_file, + "log/enable_file", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_rotate, + "log/rotate", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_append, + "log/append", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_startup_config, log_count, + "log/gen", 10) + +PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_startup_config, minidump_count, + "minidump/gen", 10) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, minidump_continue, + "minidump/cont_debug", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, minidump_log, + "minidump/echo_log", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_startup_config, minidump_type, + "minidump/dump_type", 2) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, minidump_path, + "minidump/path", "/dev/raw/minidump") +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, minidump_symbufsz, + "minidump/sz_symbuf", 32768) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, minidump_search_path, + "minidump/search", ".") + +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, module_file, + "component/file") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, module_load_type, + "component/load_type", "MEMORY") + +/* 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_startup_config, ntdll_size, + "dlml/ntdll/size", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, ntdll_ift_table, + "dlml/ntdll/ift_table", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, ntdll_ift_insert, + "dlml/ntdll/insert_ift", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, ntdll_ift_remove, + "dlml/ntdll/remove_ift", 0) + +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, shield_enable, + "shield/enable", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, shield_verbose, + "shield/verbose", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, shield_use_loadlibrary, + "shield/use_loadlibrary", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, shield_logger, + "shield/logger", "") +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_sleep_min, + "shield/sleepmin", 10) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_sleep_blur, + "shield/sleepblur", 90) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, shield_whitelist_file, + "shield/whitelist", "prop/whitelist.csv") +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_tick_sleep, + "shield/ticksleep", 100) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_tick_error, + "shield/tickerror", 1000) +PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_startup_config, shield_overwork_threshold, + "shield/overwork_threshold", 50) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_overwork_delay, + "shield/overwork_delay", 100) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_pause_delay, + "shield/pause_delay", 1000) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, shield_unlimited_key, + "shield/unlimited_key", "") +PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_startup_config, shield_killer_port, + "shield_killer/port", 5001) + +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, dongle_license_cn, + "dongle/license", "") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, dongle_account_cn, + "dongle/account", "") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, dongle_driver_dll, + "dongle/pkcs11_driver", "eTPKCS11.dll") +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, dongle_disable_gc, + "dongle/disable_gc", 0) + +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_dll, + "drm/dll") +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_fstype, + "drm/fstype") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_device, + "drm/device", "") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_mount, + "drm/dst", "/") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_options, + "drm/option", "") + +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, lte_enable, + "lte/enable", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, lte_config_file, + "lte/file", "/dev/nvram/lte-config.xml") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, lte_unlimited_key, + "lte/unlimited_key", "") + +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, ssl_options, + "ssl/option", "") + +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, esign_enable, + "esign/enable", 0) + +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, eamuse_enable, + "eamuse/enable", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, eamuse_sync, + "eamuse/sync", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, eamuse_enable_model, + "eamuse/enable_model", 0) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, eamuse_config_file, + "eamuse/file", "/dev/nvram/ea3-config.xml") +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, eamuse_updatecert_enable, + "eamuse/updatecert_enable", 1) +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, eamuse_updatecert_interval, + "eamuse/updatecert_interval", 0) +PSMAP_END + +PSMAP_BEGIN(bootstrap_psmap) +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_config, release_code, "/release_code") +PSMAP_END +// clang-format on + +#define ROOT_NODE "/config" +#define MODULE_PATH_PREFIX "modules/" + +const char *const inherited_nodes[] = { + "develop", + "default", + "log", + "minidump", + "boot", + "drm", + "ssl", + "eamuse", + "shield", + "esign", + "dongle", + "lte", +}; + +void bootstrap_config_init(struct bootstrap_config *config) +{ + memset(config, 0, sizeof(*config)); +} + +bool bootstrap_config_from_property( + struct bootstrap_config *config, + struct property *prop, + const char *selector) +{ + struct property_node *bootstrap_config = + property_search(prop, NULL, ROOT_NODE); + if (!bootstrap_config) { + log_warning(ROOT_NODE ": missing"); + return false; + } + log_misc(ROOT_NODE ": loading..."); + if (!property_psmap_import( + NULL, bootstrap_config, config, bootstrap_psmap)) { + log_warning(ROOT_NODE ": load failed"); + return false; + } + + /* Setup root startup node */ + struct property_node *startup_root = + property_search(NULL, bootstrap_config, "startup"); + if (!startup_root) { + log_warning(ROOT_NODE "/startup: missing"); + return false; + } + struct property_node *startup_config = + property_search(NULL, startup_root, selector); + if (!startup_config) { + log_warning(ROOT_NODE "/startup/%s: missing", selector); + return false; + } + + /* Resolve inheritance */ + struct property_node *startup_parent = startup_config; + for (;;) { + char inherit_name[64]; + int r = property_node_refer( + NULL, + startup_parent, + "inherit@", + PROPERTY_TYPE_ATTR, + inherit_name, + sizeof(inherit_name)); + if (r < 0) { + break; + } + + startup_parent = property_search(NULL, startup_root, inherit_name); + if (!startup_parent) { + log_warning(ROOT_NODE "/startup/%s: missing", inherit_name); + return false; + } + + for (int i = 0; i < _countof(inherited_nodes); i++) { + if (property_search(NULL, startup_config, inherited_nodes[i])) { + continue; + } + + struct property_node *node = + property_search(NULL, startup_parent, inherited_nodes[i]); + if (node) { + log_misc( + ROOT_NODE "/startup/%s: merging %s...", + inherit_name, + inherited_nodes[i]); + property_node_clone(NULL, startup_config, node, TRUE); + } + } + } + + /* Now parse the startup node */ + log_misc(ROOT_NODE "/startup/%s: loading merge result...", selector); + if (!property_psmap_import( + NULL, startup_config, &config->startup, bootstrap_startup_psmap)) { + log_warning(ROOT_NODE "/startup/%s: load failed", selector); + return false; + } + + return true; +} diff --git a/src/main/launcher/bs-config.h b/src/main/launcher/bs-config.h new file mode 100644 index 00000000..295a5def --- /dev/null +++ b/src/main/launcher/bs-config.h @@ -0,0 +1,101 @@ +#ifndef LAUNCHER_BS_CONFIG_H +#define LAUNCHER_BS_CONFIG_H + +#include +#include + +#include "imports/avs.h" + +struct bootstrap_startup_config { + char avs_config_file[64]; + char launch_config_file[64]; + uint32_t avs_heap_size; + uint32_t std_heap_size; + char mount_table_selector[16]; + bool watcher_enable; + bool timemachine_enable; + + char log_level[8]; + char log_name[64]; + char log_file[64]; + uint32_t log_bufsz; + uint16_t log_output_delay_ms; + bool log_enable_console; + bool log_enable_sci; + bool log_enable_net; + bool log_enable_file; + bool log_rotate; + bool log_append; + uint16_t log_count; + + uint8_t minidump_count; + bool minidump_continue; + bool minidump_log; + uint8_t minidump_type; + char minidump_path[64]; + uint32_t minidump_symbufsz; + char minidump_search_path[64]; + + char module_file[64]; + char module_load_type[64]; + + char ntdll_digest[16]; + uint32_t ntdll_size; + uint32_t ntdll_ift_table; + uint32_t ntdll_ift_insert; + uint32_t ntdll_ift_remove; + + bool shield_enable; + bool shield_verbose; + bool shield_use_loadlibrary; + char shield_logger[64]; + uint32_t shield_sleep_min; + uint32_t shield_sleep_blur; + uint32_t shield_tick_sleep; + uint32_t shield_tick_error; + uint8_t shield_overwork_threshold; + uint32_t shield_overwork_delay; + uint32_t shield_pause_delay; + char shield_whitelist_file[64]; + char shield_unlimited_key[10]; + uint16_t shield_killer_port; + + char dongle_license_cn[32]; + char dongle_account_cn[32]; + char dongle_driver_dll[16]; + bool dongle_disable_gc; + + char drm_dll[64]; + char drm_device[64]; + char drm_mount[64]; + char drm_fstype[64]; + char drm_options[64]; + + bool lte_enable; + char lte_config_file[64]; + char lte_unlimited_key[10]; + + char ssl_options[64]; + + bool esign_enable; + + bool eamuse_enable; + bool eamuse_sync; + bool eamuse_enable_model; + char eamuse_config_file[64]; + bool eamuse_updatecert_enable; + uint32_t eamuse_updatecert_interval; +}; + +struct bootstrap_config { + char release_code[16]; + struct bootstrap_startup_config startup; +}; + +void bootstrap_config_init(struct bootstrap_config *config); +bool bootstrap_config_from_property( + struct bootstrap_config *config, + struct property *prop, + const char *profile); + +#endif /* LAUNCHER_BS_CONFIG_H */ diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 280f0025..81a6d49e 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -9,6 +9,7 @@ #include "imports/avs.h" #include "launcher/avs-context.h" +#include "launcher/bs-config.h" #include "launcher/ea3-config.h" #include "launcher/module.h" #include "launcher/options.h" @@ -130,7 +131,9 @@ int main(int argc, const char **argv) struct ea3_ident ea3; struct module_context module; struct options options; + struct bootstrap_config bs; + struct property *bootstrap_config = NULL; struct property *app_config; struct property *avs_config; struct property *ea3_config; @@ -150,6 +153,20 @@ int main(int argc, const char **argv) options_init(&options); options_read_early_cmdline(&options, argc, argv); + bootstrap_config_init(&bs); + if (options.bootstrap_selector) { + bootstrap_config = boot_property_load(options.bootstrap_config_path); + log_info( + "Loading bootstrap selector '%s'...", options.bootstrap_selector); + if (!bootstrap_config_from_property( + &bs, bootstrap_config, options.bootstrap_selector)) { + log_fatal( + "%s: could not load configuration for '%s'", + options.bootstrap_config_path, + options.bootstrap_selector); + } + } + if (!options_read_cmdline(&options, argc, argv)) { options_print_usage(); @@ -309,6 +326,9 @@ int main(int argc, const char **argv) } boot_property_free(app_config); + if (bootstrap_config) { + boot_property_free(bootstrap_config); + } ea3_ident_to_property(&ea3, ea3_config); diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index e97fdcc1..b1abde36 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -16,6 +16,8 @@ void options_init(struct options *options) { options->std_heap_size = DEFAULT_HEAP_SIZE; options->avs_heap_size = DEFAULT_HEAP_SIZE; + options->bootstrap_config_path = "prop/bootstrap.xml"; + options->bootstrap_selector = NULL; options->app_config_path = "prop/app-config.xml"; options->avs_config_path = "prop/avs-config.xml"; options->ea3_config_path = "prop/ea3-config.xml"; @@ -40,6 +42,24 @@ void options_read_early_cmdline( for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { + case 'C': + if (i + 1 >= argc) { + return; + } + + options->bootstrap_config_path = argv[++i]; + + break; + + case 'Z': + if (i + 1 >= argc) { + return; + } + + options->bootstrap_selector = argv[++i]; + + break; + default: break; } @@ -52,6 +72,17 @@ 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 'C': + case 'Z': + /* handled by options_read_early_cmdline() */ + if (i + 1 >= argc) { + return false; + } + + ++i; + + break; + case 'A': if (i + 1 >= argc) { return false; @@ -229,6 +260,9 @@ void options_print_usage(void) " The following options can be specified before the app DLL " "path:\n" "\n" + " -C [filename] Bootstrap configuration file (default: " + "prop/bootstrap.xml)\n" + " -Z [selector] Bootstrap selector used in configuration\n" " -A [filename] App configuration file (default: " "prop/app-config.xml)\n" " -V [filename] AVS configuration file (default: " diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 86548968..85b0bbfb 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -9,6 +9,8 @@ struct options { size_t std_heap_size; size_t avs_heap_size; + const char *bootstrap_config_path; + const char *bootstrap_selector; const char *app_config_path; const char *avs_config_path; const char *ea3_config_path; From 110e80456c194f05e645fb47fdc699f48c45ba66 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 11:17:13 +0200 Subject: [PATCH 08/72] launcher: use app config from bootstrap config if present --- src/main/launcher/bs-config.c | 2 ++ src/main/launcher/bs-config.h | 1 + src/main/launcher/main.c | 14 +++++++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bs-config.c index 763c890b..54d6a466 100644 --- a/src/main/launcher/bs-config.c +++ b/src/main/launcher/bs-config.c @@ -267,5 +267,7 @@ bool bootstrap_config_from_property( return false; } + config->module_params = + property_search(NULL, startup_config, "component/param"); return true; } diff --git a/src/main/launcher/bs-config.h b/src/main/launcher/bs-config.h index 295a5def..8dfd70a7 100644 --- a/src/main/launcher/bs-config.h +++ b/src/main/launcher/bs-config.h @@ -90,6 +90,7 @@ struct bootstrap_startup_config { struct bootstrap_config { char release_code[16]; struct bootstrap_startup_config startup; + struct property_node *module_params; }; void bootstrap_config_init(struct bootstrap_config *config); diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 81a6d49e..ac5c31d5 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -134,7 +134,7 @@ int main(int argc, const char **argv) struct bootstrap_config bs; struct property *bootstrap_config = NULL; - struct property *app_config; + struct property *app_config = NULL; struct property *avs_config; struct property *ea3_config; @@ -299,17 +299,19 @@ int main(int argc, const char **argv) /* Invoke dll_entry_init */ - if (path_exists(options.app_config_path)) { + if (bs.module_params) { + app_config_root = bs.module_params; + } else if (path_exists(options.app_config_path)) { app_config = boot_property_load(options.app_config_path); + app_config_root = property_search(app_config, 0, "/param"); } else { log_warning( "%s: app config file missing, using empty", options.app_config_path); app_config = boot_property_load_cstring("dummy"); + app_config_root = property_search(app_config, 0, "/param"); } - app_config_root = property_search(app_config, 0, "/param"); - if (app_config_root == NULL) { log_fatal("%s: /param missing", options.app_config_path); } @@ -325,7 +327,9 @@ int main(int argc, const char **argv) log_fatal("%s: dll_module_init() returned failure", options.module); } - boot_property_free(app_config); + if (app_config) { + boot_property_free(app_config); + } if (bootstrap_config) { boot_property_free(bootstrap_config); } From bc4db2b3c757ecae2f24fed77a409a01efe6b212 Mon Sep 17 00:00:00 2001 From: Shiz Date: Sun, 30 Apr 2023 19:29:15 +0200 Subject: [PATCH 09/72] launcher: implement boot property parsing through AVS --- src/main/launcher/property.c | 151 ++++++++++++++++++++++------------- src/main/launcher/property.h | 1 + 2 files changed, 97 insertions(+), 55 deletions(-) diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index 2f5ded94..a6b1c836 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -11,6 +11,36 @@ #include "util/log.h" #include "util/mem.h" +typedef void (*rewinder)(uint32_t context); + +static struct property *do_property_load( + avs_reader_t reader, rewinder rewinder, uint32_t context, const char *name) +{ + struct property *prop; + void *buffer; + int nbytes; + + nbytes = property_read_query_memsize(reader, context, 0, 0); + + if (nbytes < 0) { + log_fatal("%s: Error querying configuration file", name); + } + + buffer = xmalloc(nbytes); + prop = property_create( + PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE | + PROPERTY_FLAG_APPEND, + buffer, + nbytes); + rewinder(context); + + if (!property_insert_read(prop, 0, reader, context)) { + log_fatal("%s: Error reading configuration file", name); + } + + return prop; +} + static int boot_property_fread(uint32_t context, void *bytes, size_t nbytes) { FILE *f; @@ -20,33 +50,17 @@ static int boot_property_fread(uint32_t context, void *bytes, size_t nbytes) 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) +static void boot_property_frewind(uint32_t context) { - 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; + FILE *f = TlsGetValue(context); + rewind(f); } struct property *boot_property_load(const char *filename) { - struct property *prop; - void *buffer; - int nbytes; FILE *f; uint32_t f_keyhole; + struct property *prop; /* 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 @@ -61,23 +75,8 @@ struct property *boot_property_load(const char *filename) 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); - } + prop = do_property_load( + boot_property_fread, boot_property_frewind, f_keyhole, filename); TlsFree(f_keyhole); @@ -85,12 +84,37 @@ struct property *boot_property_load(const char *filename) return prop; } + +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; +} + +static void boot_property_cstring_rewind(uint32_t context) +{ + struct cstring_read_handle *h = TlsGetValue(context); + h->offset = 0; +} + struct property *boot_property_load_cstring(const char *cstring) { - struct property *prop; - void *buffer; - int nbytes; uint32_t s_keyhole; + struct property *prop; // see above struct cstring_read_handle read_handle; @@ -101,26 +125,43 @@ struct property *boot_property_load_cstring(const char *cstring) s_keyhole = TlsAlloc(); TlsSetValue(s_keyhole, &read_handle); - nbytes = property_read_query_memsize( - boot_property_cstring_read, s_keyhole, 0, 0); + prop = do_property_load( + boot_property_cstring_read, + boot_property_cstring_rewind, + s_keyhole, + ""); - if (nbytes < 0) { - log_fatal("Error querying configuration string"); - } + TlsFree(s_keyhole); - buffer = xmalloc(nbytes); - prop = property_create( - PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE | - PROPERTY_FLAG_APPEND, - buffer, - nbytes); + return prop; +} - read_handle.offset = 0; - if (!property_insert_read(prop, 0, boot_property_cstring_read, s_keyhole)) { - log_fatal("Error inserting configuration string"); +static int boot_property_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 boot_property_avs_rewind(uint32_t context) +{ + avs_desc desc = (avs_desc) context; + avs_fs_lseek(desc, 0, AVS_SEEK_SET); +} + +struct property *boot_property_load_avs(const char *filename) +{ + avs_desc desc; + struct property *prop; + + desc = avs_fs_open(filename, AVS_FILE_READ, AVS_FILE_FLAG_SHARE_READ); + if (!desc) { + log_fatal("%s: Error opening configuration file", filename); } - TlsFree(s_keyhole); + prop = do_property_load( + boot_property_avs_read, boot_property_avs_rewind, desc, filename); + + avs_fs_close(desc); return prop; } diff --git a/src/main/launcher/property.h b/src/main/launcher/property.h index c3f14d0e..e230b9cb 100644 --- a/src/main/launcher/property.h +++ b/src/main/launcher/property.h @@ -4,6 +4,7 @@ #include "imports/avs.h" struct property *boot_property_load(const char *filename); +struct property *boot_property_load_avs(const char *filename); struct property *boot_property_load_cstring(const char *cstring); void boot_property_free(struct property *prop); From 628173f234500f3df6b382ca4c5fc6e59ab77b64 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 11:18:34 +0200 Subject: [PATCH 10/72] launcher: override options using bootstrap config --- src/main/launcher/main.c | 5 +++-- src/main/launcher/options.c | 24 +++++++++++++++++++++++- src/main/launcher/options.h | 4 ++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index ac5c31d5..e9640b5a 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -165,6 +165,7 @@ int main(int argc, const char **argv) options.bootstrap_config_path, options.bootstrap_selector); } + options_read_bootstrap(&options, &bs.startup); } if (!options_read_cmdline(&options, argc, argv)) { @@ -254,7 +255,7 @@ int main(int argc, const char **argv) /* Prepare ea3 config */ - ea3_config = boot_property_load(options.ea3_config_path); + ea3_config = boot_property_load_avs(options.ea3_config_path); ea3_config_root = property_search(ea3_config, 0, "/ea3"); if (ea3_config_root == NULL) { @@ -302,7 +303,7 @@ int main(int argc, const char **argv) if (bs.module_params) { app_config_root = bs.module_params; } else if (path_exists(options.app_config_path)) { - app_config = boot_property_load(options.app_config_path); + app_config = boot_property_load_avs(options.app_config_path); app_config_root = property_search(app_config, 0, "/param"); } else { log_warning( diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index b1abde36..d3d5165e 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -69,6 +69,7 @@ void options_read_early_cmdline( bool options_read_cmdline(struct options *options, int argc, const char **argv) { + bool got_module = false; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { @@ -237,8 +238,10 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv) break; } } else { - if (!options->module) { + /* override module from bootstrap config */ + if (!got_module) { options->module = argv[i]; + got_module = true; } } } @@ -250,6 +253,25 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv) } } +void options_read_bootstrap( + struct options *options, const struct bootstrap_startup_config *bs_config) +{ + options->avs_config_path = bs_config->avs_config_file; + options->avs_heap_size = bs_config->avs_heap_size; + options->std_heap_size = bs_config->std_heap_size; + options->ea3_config_path = bs_config->eamuse_config_file; + + if (bs_config->log_enable_file) { + if (bs_config->log_file[0]) { + options->logfile = bs_config->log_file; + } else if (bs_config->log_name[0]) { + options->logfile = bs_config->log_name; + } + } + + options->module = bs_config->module_file; +} + void options_print_usage(void) { fprintf( diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 85b0bbfb..5ebba261 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -6,6 +6,8 @@ #include "util/array.h" +#include "launcher/bs-config.h" + struct options { size_t std_heap_size; size_t avs_heap_size; @@ -32,6 +34,8 @@ void options_init(struct options *options); void options_read_early_cmdline( struct options *options, int argc, const char **argv); bool options_read_cmdline(struct options *options, int argc, const char **argv); +void options_read_bootstrap( + struct options *options, const struct bootstrap_startup_config *bs_config); void options_print_usage(void); void options_fini(struct options *options); From b9b58439a440a55ed33a6604c6163ea2c57ddf2f Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 11:05:16 +0200 Subject: [PATCH 11/72] launcher: copy default files according to bootstrap config --- src/main/launcher/bs-config.c | 40 +++++++++++++++++++++++++++++++++++ src/main/launcher/bs-config.h | 10 +++++++++ src/main/launcher/main.c | 17 +++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bs-config.c index 54d6a466..73b3c9bc 100644 --- a/src/main/launcher/bs-config.c +++ b/src/main/launcher/bs-config.c @@ -269,5 +269,45 @@ bool bootstrap_config_from_property( config->module_params = property_search(NULL, startup_config, "component/param"); + config->default_node = property_search(NULL, startup_config, "default"); + return true; +} + +bool bootstrap_config_iter_default_file( + struct bootstrap_config *config, + struct bootstrap_default_file *default_file) +{ + if (!config->default_file) { + config->default_file = + property_search(NULL, config->default_node, "file"); + } else { + config->default_file = property_node_traversal( + config->default_file, TRAVERSE_NEXT_SEARCH_RESULT); + } + if (!config->default_file) { + return false; + } + + int r; + r = property_node_refer( + NULL, + config->default_file, + "src@", + PROPERTY_TYPE_ATTR, + &default_file->src, + sizeof(default_file->src)); + if (r < 0) { + return false; + } + r = property_node_refer( + NULL, + config->default_file, + "dst@", + PROPERTY_TYPE_ATTR, + &default_file->dest, + sizeof(default_file->dest)); + if (r < 0) { + return false; + } return true; } diff --git a/src/main/launcher/bs-config.h b/src/main/launcher/bs-config.h index 8dfd70a7..9a103504 100644 --- a/src/main/launcher/bs-config.h +++ b/src/main/launcher/bs-config.h @@ -6,6 +6,11 @@ #include "imports/avs.h" +struct bootstrap_default_file { + char src[64]; + char dest[64]; +}; + struct bootstrap_startup_config { char avs_config_file[64]; char launch_config_file[64]; @@ -91,6 +96,8 @@ struct bootstrap_config { char release_code[16]; struct bootstrap_startup_config startup; struct property_node *module_params; + struct property_node *default_node; + struct property_node *default_file; }; void bootstrap_config_init(struct bootstrap_config *config); @@ -98,5 +105,8 @@ bool bootstrap_config_from_property( struct bootstrap_config *config, struct property *prop, const char *profile); +bool bootstrap_config_iter_default_file( + struct bootstrap_config *config, + struct bootstrap_default_file *default_file); #endif /* LAUNCHER_BS_CONFIG_H */ diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index e9640b5a..e52b73b1 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -236,6 +236,23 @@ int main(int argc, const char **argv) os_version_log(); + /* Do late bootstrap initialisation */ + + struct bootstrap_default_file default_file; + while (bootstrap_config_iter_default_file(&bs, &default_file)) { + struct avs_stat st; + if (avs_fs_lstat(default_file.dest, &st)) { + continue; + } + log_misc("%s: copying from %s...", default_file.dest, default_file.src); + if (avs_fs_copy(default_file.src, default_file.dest) < 0) { + log_fatal( + "%s: could not copy from %s", + default_file.dest, + default_file.src); + } + } + /* Load game DLL */ if (options.iat_hook_dlls.nitems > 0) { From 6bdab5b6e2de154983fa6ce5f8d7d1de31635715 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 10:59:55 +0200 Subject: [PATCH 12/72] imports: add property_remove() implementation --- src/imports/avs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/imports/avs.h b/src/imports/avs.h index a6b23f92..976e6c76 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -232,6 +232,16 @@ struct property_node *property_node_traversal( struct property_node *node, enum property_node_traversal direction); void property_node_datasize(struct property_node *node); +static inline void property_remove(struct property *prop, struct property_node *node, const char *path) +{ + struct property_node *cur = property_search(prop, node, path); + while (cur) { + struct property_node *next = property_node_traversal(node, TRAVERSE_NEXT_SEARCH_RESULT); + property_node_remove(cur); + cur = next; + } +} + bool std_getenv(const char *key, char *val, uint32_t nbytes); void std_setenv(const char *key, const char *val); From 82ddc75a0e8f9d0499154b76eedf14644458e122 Mon Sep 17 00:00:00 2001 From: Shiz Date: Mon, 1 May 2023 11:06:51 +0200 Subject: [PATCH 13/72] launcher: update AVS config using bootstrap config --- src/main/launcher/bs-config.c | 33 +++++++++++++++++++++++++++++++++ src/main/launcher/bs-config.h | 3 +++ src/main/launcher/main.c | 1 + 3 files changed, 37 insertions(+) diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bs-config.c index 73b3c9bc..2d380acf 100644 --- a/src/main/launcher/bs-config.c +++ b/src/main/launcher/bs-config.c @@ -269,10 +269,43 @@ bool bootstrap_config_from_property( config->module_params = property_search(NULL, startup_config, "component/param"); + config->log_node = property_search(NULL, startup_config, "log"); config->default_node = property_search(NULL, startup_config, "default"); return true; } +void bootstrap_config_update_avs( + const struct bootstrap_config *config, struct property_node *avs_root) +{ + if (config->module_params) { + property_remove(NULL, avs_root, "mode/product"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_BOOL, "mode/product", 1); + property_remove(NULL, avs_root, "net/enable_raw"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_BOOL, "net/enable_raw", 1); + property_remove(NULL, avs_root, "net/eaudp/enable"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_BOOL, "net/eaudp/enable", 1); + property_remove(NULL, avs_root, "sntp/ea_on"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_BOOL, "sntp/ea_on", 1); + } + if (config->startup.drm_device[0]) { + property_remove(NULL, avs_root, "fs/root/device"); + property_node_create( + NULL, + avs_root, + PROPERTY_TYPE_STR, + "fs/root/device", + config->startup.drm_device); + } + if (config->log_node) { + property_remove(NULL, avs_root, "log"); + property_node_clone(NULL, avs_root, config->log_node, TRUE); + } +} + bool bootstrap_config_iter_default_file( struct bootstrap_config *config, struct bootstrap_default_file *default_file) diff --git a/src/main/launcher/bs-config.h b/src/main/launcher/bs-config.h index 9a103504..8725fed0 100644 --- a/src/main/launcher/bs-config.h +++ b/src/main/launcher/bs-config.h @@ -96,6 +96,7 @@ struct bootstrap_config { char release_code[16]; struct bootstrap_startup_config startup; struct property_node *module_params; + struct property_node *log_node; struct property_node *default_node; struct property_node *default_file; }; @@ -105,6 +106,8 @@ bool bootstrap_config_from_property( struct bootstrap_config *config, struct property *prop, const char *profile); +void bootstrap_config_update_avs( + const struct bootstrap_config *config, struct property_node *avs_root); bool bootstrap_config_iter_default_file( struct bootstrap_config *config, struct bootstrap_default_file *default_file); diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index e52b73b1..eb02befc 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -219,6 +219,7 @@ int main(int argc, const char **argv) if (avs_config_root == NULL) { log_fatal("%s: /config missing", options.avs_config_path); } + bootstrap_config_update_avs(&bs, avs_config_root); load_hook_dlls(&options.before_hook_dlls); From e366a0042e9b0ddcd038d78daabbc5725d4d58b3 Mon Sep 17 00:00:00 2001 From: icex2 Date: Tue, 8 Aug 2023 22:18:49 +0200 Subject: [PATCH 14/72] fix/chore(ddr): Distribution package versioning and missing bat files Fill various (minor) gaps in version numbering of ddr distribution packages: * Make A20 and A20+ their own versions to stick to the existing versioning scheme in btools * Add missing distribution bat files for 17 and 18 * Update docs * Name distribution packages accordingly --- Module.mk | 12 ++++++++---- README.md | 9 ++++++--- dist/ddr/gamestart-17.bat | 15 +++++++++++++++ dist/ddr/gamestart-18.bat | 15 +++++++++++++++ doc/ddrhook/ddrhook2.md | 2 ++ 5 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 dist/ddr/gamestart-17.bat create mode 100644 dist/ddr/gamestart-18.bat diff --git a/Module.mk b/Module.mk index 4178a424..f525b5d1 100644 --- a/Module.mk +++ b/Module.mk @@ -700,7 +700,7 @@ $(zipdir)/ddr-13.zip: \ $(V)echo ... $@ $(V)zip -j $@ $^ -$(zipdir)/ddr-14-to-16.zip: \ +$(zipdir)/ddr-14-to-18.zip: \ build/bin/avs2_1508-32/launcher.exe \ build/bin/avs2_1508-32/ddrhook2.dll \ build/bin/avs2_1508-32/unicorntail.dll \ @@ -712,11 +712,13 @@ $(zipdir)/ddr-14-to-16.zip: \ dist/ddr/gamestart-14.bat \ dist/ddr/gamestart-15.bat \ dist/ddr/gamestart-16.bat \ + dist/ddr/gamestart-17.bat \ + dist/ddr/gamestart-18.bat \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ -$(zipdir)/ddr-16-x64.zip: \ +$(zipdir)/ddr-16-to-18-x64.zip: \ build/bin/avs2_1603-64/launcher.exe \ build/bin/avs2_1603-64/ddrhook2.dll \ build/bin/avs2_1603-64/unicorntail.dll \ @@ -726,6 +728,8 @@ $(zipdir)/ddr-16-x64.zip: \ build/bin/indep-64/geninput.dll \ dist/ddr/config.bat \ dist/ddr/gamestart-16.bat \ + dist/ddr/gamestart-17.bat \ + dist/ddr/gamestart-18.bat \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -827,8 +831,8 @@ $(BUILDDIR)/bemanitools.zip: \ $(zipdir)/ddr-12.zip \ $(zipdir)/ddr-12-us.zip \ $(zipdir)/ddr-13.zip \ - $(zipdir)/ddr-14-to-16.zip \ - $(zipdir)/ddr-16-x64.zip \ + $(zipdir)/ddr-14-to-18.zip \ + $(zipdir)/ddr-16-to-18-x64.zip \ $(zipdir)/ddr-hwio-x86.zip \ $(zipdir)/ddr-hwio-x64.zip \ $(zipdir)/doc.zip \ diff --git a/README.md b/README.md index d320361e..cfe7b216 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,12 @@ The following games are supported with their corresponding hook-libraries. * Dance Dance Revolution X (`ddr-11.zip`): [ddrhook1](doc/ddrhook/ddrhook1.md) * Dance Dance Revolution X2 (US/EU regions) (`ddr-12-us.zip`): [ddrhook1](doc/ddrhook/ddrhook1.md) * Dance Dance Revolution X2 (JP region) (`ddr-12.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) - * Dance Dance Revolution 2013 (`ddr-14-to-16.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) - * Dance Dance Revolution 2014 (`ddr-14-to-16.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) - * Dance Dance Revolution A (`ddr-14-to-16.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) + * Dance Dance Revolution X3 vs. 2ndMIX (`ddr-13.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) + * Dance Dance Revolution 2013 (`ddr-14-to-18.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) + * Dance Dance Revolution 2014 (`ddr-14-to-18.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) + * Dance Dance Revolution A (`ddr-14-to-18.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) + * Dance Dance Revolution A20 (`ddr-14-to-18.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) + * Dance Dance Revolution A20+ (`ddr-14-to-18.zip`): [ddrhook2](doc/ddrhook/ddrhook2.md) * [Beatmania IIDX](doc/iidxhook/README.md) * Beatmania IIDX 9th Style (`iidx-09-to-12.zip`): [iidxhook1](doc/iidxhook/iidxhook1.md) * Beatmania IIDX 10th Style (`iidx-09-to-12.zip`): [iidxhook1](doc/iidxhook/iidxhook1.md) diff --git a/dist/ddr/gamestart-17.bat b/dist/ddr/gamestart-17.bat new file mode 100644 index 00000000..d108118b --- /dev/null +++ b/dist/ddr/gamestart-17.bat @@ -0,0 +1,15 @@ +@echo off + +cd /d %~dp0 + +if not exist conf\nvram mkdir conf\nvram +if not exist conf\nvram\ea3-config.xml copy prop\eamuse-config.xml conf\nvram\ea3-config.xml +if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml +if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml +if not exist conf\nvram\testmode-v.xml copy prop\testmode-v.xml conf\nvram\testmode-v.xml +if not exist conf\raw mkdir conf\raw + +regsvr32 /s com\k-clvsd.dll +regsvr32 /s com\xactengine2_10.dll + +.\launcher.exe -H 33554432 -K .\ddrhook2.dll .\arkmdxp3.dll %* diff --git a/dist/ddr/gamestart-18.bat b/dist/ddr/gamestart-18.bat new file mode 100644 index 00000000..d108118b --- /dev/null +++ b/dist/ddr/gamestart-18.bat @@ -0,0 +1,15 @@ +@echo off + +cd /d %~dp0 + +if not exist conf\nvram mkdir conf\nvram +if not exist conf\nvram\ea3-config.xml copy prop\eamuse-config.xml conf\nvram\ea3-config.xml +if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml +if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml +if not exist conf\nvram\testmode-v.xml copy prop\testmode-v.xml conf\nvram\testmode-v.xml +if not exist conf\raw mkdir conf\raw + +regsvr32 /s com\k-clvsd.dll +regsvr32 /s com\xactengine2_10.dll + +.\launcher.exe -H 33554432 -K .\ddrhook2.dll .\arkmdxp3.dll %* diff --git a/doc/ddrhook/ddrhook2.md b/doc/ddrhook/ddrhook2.md index 38c0f44d..0cfddcbe 100644 --- a/doc/ddrhook/ddrhook2.md +++ b/doc/ddrhook/ddrhook2.md @@ -9,6 +9,8 @@ The following games are supported by this hook library: * Dance Dance Revolution 2013 * Dance Dance Revolution 2014 * Dance Dance Revolution A +* Dance Dance Revolution A20 +* Dance Dance Revolution A20+ Note that different builds of the same hook library are required to run the different versions. See different distribution packages, e.g. `ddr-12.zip`, From 096b5f1912b41130b09ce8cd97d56e128b2ddd5c Mon Sep 17 00:00:00 2001 From: icex2 Date: Wed, 9 Aug 2023 23:24:52 +0200 Subject: [PATCH 15/72] fix(avs): 573 ordinals of avs version 1304 replaced Used libavs of ddr x3 --- src/imports/import_32_1304_avs.def | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index dd287422..b14d093f 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -2,12 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot @237 NONAME - avs_fs_close @573 NONAME - avs_fs_copy @573 NONAME - avs_fs_lseek @573 NONAME - avs_fs_lstat @573 NONAME + avs_fs_close @276 NONAME + avs_fs_copy @283 NONAME + avs_fs_lseek @16 NONAME + avs_fs_lstat @97 NONAME avs_fs_open @178 NONAME - avs_fs_read @573 NONAME + avs_fs_read @306 NONAME avs_net_ctrl @15 NONAME avs_shutdown @333 NONAME avs_thread_create @183 NONAME From 538a31253d554f51b1fb0c6d023a4f198c8453ed Mon Sep 17 00:00:00 2001 From: icex2 Date: Wed, 9 Aug 2023 23:53:51 +0200 Subject: [PATCH 16/72] fix(avs): 573 place holder ordinals replaced, avs 1101 Used libavs of iidx 18 --- src/imports/import_32_1101_avs.def | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index a8bc4048..dca3e1fd 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -2,12 +2,12 @@ LIBRARY libavs-win32 EXPORTS avs_boot @22 NONAME - avs_fs_close @573 NONAME - avs_fs_copy @573 NONAME - avs_fs_lseek @573 NONAME - avs_fs_lstat @573 NONAME - avs_fs_open @573 NONAME - avs_fs_read @573 NONAME + avs_fs_close @60 NONAME + avs_fs_copy @63 NONAME + avs_fs_lseek @70 NONAME + avs_fs_lstat @71 NONAME + avs_fs_open @75 NONAME + avs_fs_read @77 NONAME avs_net_ctrl @107 NONAME avs_shutdown @140 NONAME avs_thread_create @156 NONAME From dee8a11e9679716f769ebb928640ffe09ceded25 Mon Sep 17 00:00:00 2001 From: icex2 Date: Wed, 9 Aug 2023 23:54:21 +0200 Subject: [PATCH 17/72] fix(avs): 573 place holder ordinals replaced, avs 1601 Used iidx 21 libavs as source --- src/imports/import_32_1601_avs.def | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index b6410ca6..6f35669b 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -1,12 +1,12 @@ LIBRARY libavs-win32 EXPORTS - avs_fs_close @573 NONAME - avs_fs_copy @573 NONAME - avs_fs_lseek @573 NONAME - avs_fs_lstat @573 NONAME - avs_fs_open @573 NONAME - avs_fs_read @573 NONAME + avs_fs_close @65 NONAME + avs_fs_copy @81 NONAME + avs_fs_lseek @59 NONAME + avs_fs_lstat @79 NONAME + avs_fs_open @58 NONAME + avs_fs_read @61 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME From 1c7439a809fea02cf783277338d96f90f5492032 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 12:08:56 +0200 Subject: [PATCH 18/72] fix(imports): Add missing property_node_name entry to all AVS versions Use the dummy "573" ordinal for now. The method is already listed in avs.h but wasn't used thus far. Require to write some helper/ debug functions to print property node trees from in-memory data, e.g. after modifying them during the boot process. --- src/imports/import_32_1101_avs.def | 1 + src/imports/import_32_1304_avs.def | 1 + src/imports/import_32_1403_avs.def | 1 + src/imports/import_32_1508_avs.def | 1 + src/imports/import_32_1601_avs.def | 1 + src/imports/import_32_1603_avs.def | 1 + src/imports/import_32_1700_avs.def | 1 + src/imports/import_64_1508_avs.def | 1 + src/imports/import_64_1509_avs.def | 1 + src/imports/import_64_1601_avs.def | 1 + src/imports/import_64_1603_avs.def | 1 + src/imports/import_64_1700_avs.def | 1 + 12 files changed, 12 insertions(+) diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index dca3e1fd..7b35c43c 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -25,6 +25,7 @@ EXPORTS property_insert_read @255 NONAME property_node_create @266 NONAME property_node_datasize @267 NONAME + property_node_name @573 NONAME property_node_refer @278 NONAME property_node_remove @279 NONAME property_node_clone @280 NONAME diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index b14d093f..63d3f0a0 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -24,6 +24,7 @@ EXPORTS property_insert_read @23 NONAME property_node_create @316 NONAME property_node_datasize @249 NONAME + property_node_name @573 NONAME property_node_refer @268 NONAME property_node_remove @129 NONAME property_node_clone @130 NONAME diff --git a/src/imports/import_32_1403_avs.def b/src/imports/import_32_1403_avs.def index 61d2cd49..d5d84704 100644 --- a/src/imports/import_32_1403_avs.def +++ b/src/imports/import_32_1403_avs.def @@ -21,6 +21,7 @@ EXPORTS property_desc_to_buffer @131 NONAME property_destroy @130 NONAME property_insert_read @133 NONAME + property_node_name @573 NONAME property_node_remove @148 NONAME property_node_clone @149 NONAME property_node_traversal @151 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index 88d6ce52..03a6285f 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -24,6 +24,7 @@ EXPORTS property_destroy @128 NONAME property_insert_read @131 NONAME property_node_create @145 NONAME + property_node_name @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME property_node_clone @147 NONAME diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index 6f35669b..495247d2 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -18,6 +18,7 @@ EXPORTS property_insert_read @128 NONAME property_search @141 NONAME property_node_create @142 NONAME + property_node_name @573 NONAME property_node_remove @143 NONAME property_node_clone @144 NONAME property_node_traversal @146 NONAME diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index f815822a..23b35d9c 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -18,6 +18,7 @@ EXPORTS property_insert_read @149 NONAME property_search @162 NONAME property_node_create @163 NONAME + property_node_name @573 NONAME property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME diff --git a/src/imports/import_32_1700_avs.def b/src/imports/import_32_1700_avs.def index a4813abe..bc81b757 100644 --- a/src/imports/import_32_1700_avs.def +++ b/src/imports/import_32_1700_avs.def @@ -20,6 +20,7 @@ EXPORTS property_insert_read @149 NONAME property_search @162 NONAME property_node_create @163 NONAME + property_node_name @573 NONAME property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME diff --git a/src/imports/import_64_1508_avs.def b/src/imports/import_64_1508_avs.def index 210b7ad8..2c96f613 100644 --- a/src/imports/import_64_1508_avs.def +++ b/src/imports/import_64_1508_avs.def @@ -24,6 +24,7 @@ EXPORTS property_destroy @128 NONAME property_insert_read @131 NONAME property_node_create @145 NONAME + property_node_name @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME property_node_clone @147 NONAME diff --git a/src/imports/import_64_1509_avs.def b/src/imports/import_64_1509_avs.def index 9b6b945e..73fa9b86 100644 --- a/src/imports/import_64_1509_avs.def +++ b/src/imports/import_64_1509_avs.def @@ -24,6 +24,7 @@ EXPORTS property_destroy @128 NONAME property_insert_read @131 NONAME property_node_create @145 NONAME + property_node_name @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME property_node_clone @147 NONAME diff --git a/src/imports/import_64_1601_avs.def b/src/imports/import_64_1601_avs.def index 6dbb0f2f..13bc785b 100644 --- a/src/imports/import_64_1601_avs.def +++ b/src/imports/import_64_1601_avs.def @@ -18,6 +18,7 @@ EXPORTS property_insert_read @128 NONAME property_search @141 NONAME property_node_create @142 NONAME + property_node_name @573 NONAME property_node_remove @143 NONAME property_node_clone @144 NONAME property_node_traversal @146 NONAME diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index 007bd5a8..5c9a1bc6 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -18,6 +18,7 @@ EXPORTS property_insert_read @149 NONAME property_search @162 NONAME property_node_create @163 NONAME + property_node_name @573 NONAME property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME diff --git a/src/imports/import_64_1700_avs.def b/src/imports/import_64_1700_avs.def index 1d1e0f00..b5ffa600 100644 --- a/src/imports/import_64_1700_avs.def +++ b/src/imports/import_64_1700_avs.def @@ -20,6 +20,7 @@ EXPORTS property_insert_read @149 NONAME property_search @162 NONAME property_node_create @163 NONAME + property_node_name @573 NONAME property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME From e4d000cd848234fd573d42d102f6bb9aed48b5f9 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 13:50:51 +0200 Subject: [PATCH 19/72] fix(import): Incorrect ordinal for property_node_traverse AVS 1304 Caused a lot of headaches until I started to doubt that the correct function is being called... This was tested and works with launcher on ddr x3 --- src/imports/import_32_1304_avs.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index 63d3f0a0..3442fa5b 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -28,7 +28,7 @@ EXPORTS property_node_refer @268 NONAME property_node_remove @129 NONAME property_node_clone @130 NONAME - property_node_traversal @132 NONAME + property_node_traversal @93 NONAME property_psmap_import @102 NONAME property_psmap_export @110 NONAME property_read_query_memsize @100 NONAME From 99335838474acbc22dac81fa97210af21a732fb8 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 13:52:31 +0200 Subject: [PATCH 20/72] fix(import): ordinals for property_node_name avs 1304 and 1508 AVS version used on ddr-13 and ddr-14-to-18 --- src/imports/import_32_1304_avs.def | 2 +- src/imports/import_32_1508_avs.def | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index 3442fa5b..a856b93b 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -24,7 +24,7 @@ EXPORTS property_insert_read @23 NONAME property_node_create @316 NONAME property_node_datasize @249 NONAME - property_node_name @573 NONAME + property_node_name @255 NONAME property_node_refer @268 NONAME property_node_remove @129 NONAME property_node_clone @130 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index 03a6285f..f9ef0835 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -24,7 +24,7 @@ EXPORTS property_destroy @128 NONAME property_insert_read @131 NONAME property_node_create @145 NONAME - property_node_name @573 NONAME + property_node_name @150 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME property_node_clone @147 NONAME From 007f07010d60bee0ef80d61202d4000d668e19d1 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 13:53:29 +0200 Subject: [PATCH 21/72] feat(import): avs, add placeholder support for property_node_read Mark with 573 ordinal that is not being found to highlight that the ordinal needs to be fixed. The function is being used to implement util/debug plumbing for handling property data in launcher. --- src/imports/avs.h | 1 + src/imports/import_32_0_avs.def | 1 + src/imports/import_32_1002_avs.def | 1 + src/imports/import_32_1101_avs.def | 1 + src/imports/import_32_1304_avs.def | 1 + src/imports/import_32_1403_avs.def | 1 + src/imports/import_32_1508_avs.def | 1 + src/imports/import_32_1601_avs.def | 1 + src/imports/import_32_1603_avs.def | 1 + src/imports/import_32_1700_avs.def | 1 + src/imports/import_32_803_avs.def | 1 + src/imports/import_64_1508_avs.def | 1 + src/imports/import_64_1509_avs.def | 1 + src/imports/import_64_1601_avs.def | 1 + src/imports/import_64_1603_avs.def | 1 + src/imports/import_64_1700_avs.def | 1 + 16 files changed, 16 insertions(+) diff --git a/src/imports/avs.h b/src/imports/avs.h index 976e6c76..9045066c 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -231,6 +231,7 @@ enum property_type property_node_type(struct property_node *node); struct property_node *property_node_traversal( struct property_node *node, enum property_node_traversal direction); void property_node_datasize(struct property_node *node); +void property_node_read(struct property_node *node, enum property_type type, void* data, uint32_t data_size); static inline void property_remove(struct property *prop, struct property_node *node, const char *path) { diff --git a/src/imports/import_32_0_avs.def b/src/imports/import_32_0_avs.def index c808c1eb..273e5be8 100644 --- a/src/imports/import_32_0_avs.def +++ b/src/imports/import_32_0_avs.def @@ -32,6 +32,7 @@ EXPORTS property_node_clone property_node_create property_node_name + property_node_read property_node_refer property_node_remove property_node_type diff --git a/src/imports/import_32_1002_avs.def b/src/imports/import_32_1002_avs.def index d421b1a3..771a2ae7 100644 --- a/src/imports/import_32_1002_avs.def +++ b/src/imports/import_32_1002_avs.def @@ -33,6 +33,7 @@ EXPORTS property_node_create property_node_datasize property_node_name + property_node_read property_node_refer property_node_remove property_node_type diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index 7b35c43c..a7d9d2bc 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -26,6 +26,7 @@ EXPORTS property_node_create @266 NONAME property_node_datasize @267 NONAME property_node_name @573 NONAME + property_node_read @573 NONAME property_node_refer @278 NONAME property_node_remove @279 NONAME property_node_clone @280 NONAME diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index a856b93b..59a353b7 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -25,6 +25,7 @@ EXPORTS property_node_create @316 NONAME property_node_datasize @249 NONAME property_node_name @255 NONAME + property_node_read @573 NONAME property_node_refer @268 NONAME property_node_remove @129 NONAME property_node_clone @130 NONAME diff --git a/src/imports/import_32_1403_avs.def b/src/imports/import_32_1403_avs.def index d5d84704..cd74863a 100644 --- a/src/imports/import_32_1403_avs.def +++ b/src/imports/import_32_1403_avs.def @@ -22,6 +22,7 @@ EXPORTS property_destroy @130 NONAME property_insert_read @133 NONAME property_node_name @573 NONAME + property_node_read @573 NONAME property_node_remove @148 NONAME property_node_clone @149 NONAME property_node_traversal @151 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index f9ef0835..8d6aa367 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -25,6 +25,7 @@ EXPORTS property_insert_read @131 NONAME property_node_create @145 NONAME property_node_name @150 NONAME + property_node_read @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME property_node_clone @147 NONAME diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index 495247d2..3139ed83 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -22,6 +22,7 @@ EXPORTS property_node_remove @143 NONAME property_node_clone @144 NONAME property_node_traversal @146 NONAME + property_node_read @573 NONAME property_node_refer @155 NONAME property_read_query_memsize @156 NONAME property_psmap_export @159 NONAME diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index 23b35d9c..7dea3152 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -22,6 +22,7 @@ EXPORTS property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME + property_node_read @573 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME diff --git a/src/imports/import_32_1700_avs.def b/src/imports/import_32_1700_avs.def index bc81b757..161853e3 100644 --- a/src/imports/import_32_1700_avs.def +++ b/src/imports/import_32_1700_avs.def @@ -24,6 +24,7 @@ EXPORTS property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME + property_node_read @573 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME diff --git a/src/imports/import_32_803_avs.def b/src/imports/import_32_803_avs.def index 8c6b93e2..37f2b3e6 100644 --- a/src/imports/import_32_803_avs.def +++ b/src/imports/import_32_803_avs.def @@ -33,6 +33,7 @@ EXPORTS property_node_create property_node_datasize property_node_name + property_node_read property_node_refer property_node_remove property_node_type diff --git a/src/imports/import_64_1508_avs.def b/src/imports/import_64_1508_avs.def index 2c96f613..e2a36116 100644 --- a/src/imports/import_64_1508_avs.def +++ b/src/imports/import_64_1508_avs.def @@ -25,6 +25,7 @@ EXPORTS property_insert_read @131 NONAME property_node_create @145 NONAME property_node_name @573 NONAME + property_node_read @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME property_node_clone @147 NONAME diff --git a/src/imports/import_64_1509_avs.def b/src/imports/import_64_1509_avs.def index 73fa9b86..db96f82c 100644 --- a/src/imports/import_64_1509_avs.def +++ b/src/imports/import_64_1509_avs.def @@ -25,6 +25,7 @@ EXPORTS property_insert_read @131 NONAME property_node_create @145 NONAME property_node_name @573 NONAME + property_node_read @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME property_node_clone @147 NONAME diff --git a/src/imports/import_64_1601_avs.def b/src/imports/import_64_1601_avs.def index 13bc785b..870c5aa3 100644 --- a/src/imports/import_64_1601_avs.def +++ b/src/imports/import_64_1601_avs.def @@ -22,6 +22,7 @@ EXPORTS property_node_remove @143 NONAME property_node_clone @144 NONAME property_node_traversal @146 NONAME + property_node_read @573 NONAME property_node_refer @155 NONAME property_read_query_memsize @156 NONAME property_psmap_export @159 NONAME diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index 5c9a1bc6..3461e7b5 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -22,6 +22,7 @@ EXPORTS property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME + property_node_read @573 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME diff --git a/src/imports/import_64_1700_avs.def b/src/imports/import_64_1700_avs.def index b5ffa600..006bed38 100644 --- a/src/imports/import_64_1700_avs.def +++ b/src/imports/import_64_1700_avs.def @@ -24,6 +24,7 @@ EXPORTS property_node_remove @164 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME + property_node_read @573 NONAME property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME From 67f2a33e6a3fa729eca36d404a3a45b16f33413f Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 18:58:18 +0200 Subject: [PATCH 22/72] fix(import): Fix property_read_node avs 1304 Used on ddr x3 --- src/imports/import_32_1304_avs.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index 59a353b7..4d33be4f 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -25,7 +25,7 @@ EXPORTS property_node_create @316 NONAME property_node_datasize @249 NONAME property_node_name @255 NONAME - property_node_read @573 NONAME + property_node_read @2 NONAME property_node_refer @268 NONAME property_node_remove @129 NONAME property_node_clone @130 NONAME From c641fc4a15e973ed2b986cc56035f2c3ffcc9328 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 18:59:46 +0200 Subject: [PATCH 23/72] feat(launcher): Add functions for debug logging of property trees Useful to check what's been read into memory and what launcher modified along the way --- src/main/launcher/main.c | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index eb02befc..041ad179 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -123,6 +123,47 @@ static void load_hook_dlls(struct array *hook_dlls) } } +static void log_property_node_tree_rec(struct property_node *parent_node, const char* parent_path) +{ + char cur_path[4096]; + // 256 found in AVS code as size used on property_node_name + char cur_node_name[256]; + char leaf_node_data[2048]; + struct property_node* child_node; + + // 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_node_read(parent_node, PROPERTY_TYPE_STR, leaf_node_data, sizeof(leaf_node_data)); + + log_misc("%s: %s", cur_path, leaf_node_data); + } else { + while (child_node) { + log_property_node_tree_rec(child_node, cur_path); + + child_node = property_node_traversal(child_node, TRAVERSE_NEXT_SIBLING); + } + } +} + +static void log_property_tree(struct property *property) +{ + log_property_node_tree_rec(property_search(property, NULL, "/"), ""); +} + +static void log_property_node_tree(struct property_node *parent_node) +{ + log_property_node_tree_rec(parent_node, ""); +} + int main(int argc, const char **argv) { bool ok; From 5f8c3523fbbef24681b6afbfa13cb6491881e1ac Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 19:00:42 +0200 Subject: [PATCH 24/72] feat(launcher): Static logging with full output on start Ensure we get full visibility about what's going on once the configured logging takes over. --- src/main/launcher/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 041ad179..ccfda0d6 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -183,7 +183,10 @@ int main(int argc, const char **argv) struct property_node *avs_config_root; struct property_node *ea3_config_root; + // Static logging setup until we got AVS up and running log_to_writer(log_writer_file, stdout); + log_set_level(LOG_LEVEL_MISC); + log_info( "launcher build date %s, gitrev %s", launcher_build_date, From 2251bbb0a6da611a8752a9874280d19e2d6aad0c Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 19:02:18 +0200 Subject: [PATCH 25/72] chore(launcher): Add more debug log output, improve debugability --- src/main/launcher/main.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index ccfda0d6..0920537a 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -191,6 +191,7 @@ int main(int argc, const char **argv) "launcher build date %s, gitrev %s", launcher_build_date, launcher_gitrev); + os_version_log(); /* Read command line */ @@ -199,9 +200,13 @@ int main(int argc, const char **argv) bootstrap_config_init(&bs); if (options.bootstrap_selector) { + log_misc("Bootstrap selector specified: %s", options.bootstrap_selector); + bootstrap_config = boot_property_load(options.bootstrap_config_path); + log_info( "Loading bootstrap selector '%s'...", options.bootstrap_selector); + if (!bootstrap_config_from_property( &bs, bootstrap_config, options.bootstrap_selector)) { log_fatal( @@ -209,9 +214,14 @@ int main(int argc, const char **argv) options.bootstrap_config_path, options.bootstrap_selector); } + options_read_bootstrap(&options, &bs.startup); + + log_misc("Finished reading bootstrap"); } + log_misc("Reading command line options..."); + if (!options_read_cmdline(&options, argc, argv)) { options_print_usage(); @@ -242,6 +252,8 @@ int main(int argc, const char **argv) } } + log_misc("Preparing AVS..."); + /* Start up AVS */ if (options.logfile != NULL) { @@ -260,13 +272,21 @@ int main(int argc, const char **argv) avs_config = boot_property_load(options.avs_config_path); avs_config_root = property_search(avs_config, 0, "/config"); + log_property_tree(avs_config); + + // Sleep(100000000); + if (avs_config_root == NULL) { log_fatal("%s: /config missing", options.avs_config_path); } + bootstrap_config_update_avs(&bs, avs_config_root); + log_misc("Loading before hook dlls..."); load_hook_dlls(&options.before_hook_dlls); + log_misc("Initializing AVS..."); + avs_context_init( avs_config_root, options.avs_heap_size, @@ -276,11 +296,11 @@ int main(int argc, const char **argv) boot_property_free(avs_config); + log_info("Bootstrap complete, switching loggers"); + log_to_external( log_body_misc, log_body_info, log_body_warning, log_body_fatal); - os_version_log(); - /* Do late bootstrap initialisation */ struct bootstrap_default_file default_file; @@ -317,6 +337,8 @@ int main(int argc, const char **argv) /* Prepare ea3 config */ + log_misc("Preparing ea3 configuration..."); + ea3_config = boot_property_load_avs(options.ea3_config_path); ea3_config_root = property_search(ea3_config, 0, "/ea3"); From 9dbb2581e97740c666565da38fddf92715392b87 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 11 Aug 2023 19:03:01 +0200 Subject: [PATCH 26/72] chore(launcher): Improve error handling and log output default files --- src/main/launcher/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 0920537a..f6e186f6 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -306,10 +306,19 @@ int main(int argc, const char **argv) struct bootstrap_default_file default_file; while (bootstrap_config_iter_default_file(&bs, &default_file)) { struct avs_stat st; + + log_misc("%s: copying from %s...", default_file.dest, default_file.src); + + if (avs_fs_lstat(default_file.src, &st)) { + log_fatal("Default file source %s does not exist or is not accessible", default_file.src); + continue; + } + if (avs_fs_lstat(default_file.dest, &st)) { + log_fatal("Default file destination %s does not exist or is not accessible", default_file.dest); continue; } - log_misc("%s: copying from %s...", default_file.dest, default_file.src); + if (avs_fs_copy(default_file.src, default_file.dest) < 0) { log_fatal( "%s: could not copy from %s", From f21c9082ea1ecc966c7580646773669c3f82a751 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 14:38:40 +0100 Subject: [PATCH 27/72] sqash to previous --- src/main/launcher/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index f6e186f6..31738f00 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -311,7 +311,6 @@ int main(int argc, const char **argv) if (avs_fs_lstat(default_file.src, &st)) { log_fatal("Default file source %s does not exist or is not accessible", default_file.src); - continue; } if (avs_fs_lstat(default_file.dest, &st)) { From 0c47b15e5c010bdb9b4bfb259d97a554c984d2c8 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 14:39:17 +0100 Subject: [PATCH 28/72] chore: Improve debug visibility, print current working dir With several items in the bootstrapping process being FS related, it's quite helpful to see/check which current working directory is set --- src/main/launcher/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 31738f00..fc1121e6 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -192,6 +192,9 @@ int main(int argc, const char **argv) launcher_build_date, launcher_gitrev); os_version_log(); + char buffer_tmp[MAX_PATH]; + getcwd(buffer_tmp, sizeof(buffer_tmp)); + log_info("Current working directory: %s", buffer_tmp); /* Read command line */ From 6e114321b21c2182e8ebd4e529dd3e328acd3b7e Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 14:43:18 +0100 Subject: [PATCH 29/72] fix(launcher): Bootstrap copying files Error checking was incorrect for source file and the destination file might not exist, so no need to check --- src/main/launcher/main.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index fc1121e6..7fe79736 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -303,24 +303,20 @@ int main(int argc, const char **argv) log_to_external( log_body_misc, log_body_info, log_body_warning, log_body_fatal); - + /* Do late bootstrap initialisation */ struct bootstrap_default_file default_file; + while (bootstrap_config_iter_default_file(&bs, &default_file)) { struct avs_stat st; - + log_misc("%s: copying from %s...", default_file.dest, default_file.src); - if (avs_fs_lstat(default_file.src, &st)) { + if (!avs_fs_lstat(default_file.src, &st)) { log_fatal("Default file source %s does not exist or is not accessible", default_file.src); } - if (avs_fs_lstat(default_file.dest, &st)) { - log_fatal("Default file destination %s does not exist or is not accessible", default_file.dest); - continue; - } - if (avs_fs_copy(default_file.src, default_file.dest) < 0) { log_fatal( "%s: could not copy from %s", From 5e0ec49090daeff0d82f54d50c8cc9ab51be58f9 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 14:44:32 +0100 Subject: [PATCH 30/72] feat(avs): Add dir related avs_fs functions Not being used in the main flow of launcher, but it was very useful to be able to write some debug code to list folders in the avs to debug issues with files not existing etc. Only 1304 has the proper ordinals set for now, everything else is stubbed with the 573 ordinal and needs to be followed up. --- src/imports/avs.h | 3 +++ src/imports/import_32_1002_avs.def | 3 +++ src/imports/import_32_1101_avs.def | 3 +++ src/imports/import_32_1304_avs.def | 3 +++ src/imports/import_32_1403_avs.def | 3 +++ src/imports/import_32_1508_avs.def | 3 +++ src/imports/import_32_1601_avs.def | 3 +++ src/imports/import_32_1603_avs.def | 3 +++ src/imports/import_32_1700_avs.def | 3 +++ src/imports/import_64_1508_avs.def | 3 +++ src/imports/import_64_1509_avs.def | 3 +++ src/imports/import_64_1601_avs.def | 3 +++ src/imports/import_64_1603_avs.def | 3 +++ src/imports/import_64_1700_avs.def | 3 +++ 14 files changed, 42 insertions(+) diff --git a/src/imports/avs.h b/src/imports/avs.h index 9045066c..ab1bdd6a 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -283,6 +283,9 @@ int avs_fs_copy(const char *src, const char *dest); int avs_fs_addfs(void *filesys_struct); int avs_fs_mount( const char *mountpoint, const char *fsroot, const char *fstype, void *data); +avs_desc avs_fs_opendir(const char *path); +const char* avs_fs_readdir(avs_desc dir); +void avs_fs_closedir(avs_desc dir); bool avs_is_active(); diff --git a/src/imports/import_32_1002_avs.def b/src/imports/import_32_1002_avs.def index 771a2ae7..44b469ab 100644 --- a/src/imports/import_32_1002_avs.def +++ b/src/imports/import_32_1002_avs.def @@ -8,6 +8,9 @@ EXPORTS avs_fs_lstat avs_fs_open avs_fs_read + avs_fs_opendir + avs_fs_readdir + avs_fs_closedir avs_net_ctrl avs_shutdown avs_thread_create diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index a7d9d2bc..99475974 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -8,6 +8,9 @@ EXPORTS avs_fs_lstat @71 NONAME avs_fs_open @75 NONAME avs_fs_read @77 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_net_ctrl @107 NONAME avs_shutdown @140 NONAME avs_thread_create @156 NONAME diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index 4d33be4f..c8351c4c 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -8,6 +8,9 @@ EXPORTS avs_fs_lstat @97 NONAME avs_fs_open @178 NONAME avs_fs_read @306 NONAME + avs_fs_opendir @216 NONAME + avs_fs_readdir @130 NONAME + avs_fs_closedir @131 NONAME avs_net_ctrl @15 NONAME avs_shutdown @333 NONAME avs_thread_create @183 NONAME diff --git a/src/imports/import_32_1403_avs.def b/src/imports/import_32_1403_avs.def index cd74863a..8e2f3214 100644 --- a/src/imports/import_32_1403_avs.def +++ b/src/imports/import_32_1403_avs.def @@ -8,6 +8,9 @@ EXPORTS avs_fs_lstat @573 NONAME avs_fs_open @573 NONAME avs_fs_read @573 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_net_ctrl @100 NONAME avs_shutdown @299 NONAME avs_thread_create @6 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index 8d6aa367..7695752f 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -9,6 +9,9 @@ EXPORTS avs_fs_open @58 NONAME avs_fs_read @61 NONAME avs_fs_copy @81 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_net_ctrl @98 NONAME avs_shutdown @286 NONAME avs_thread_create @6 NONAME diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index 3139ed83..efbf7ab0 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -7,6 +7,9 @@ EXPORTS avs_fs_lstat @79 NONAME avs_fs_open @58 NONAME avs_fs_read @61 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index 7dea3152..ddf08990 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -7,6 +7,9 @@ EXPORTS avs_fs_lstat @573 NONAME avs_fs_open @573 NONAME avs_fs_read @573 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_32_1700_avs.def b/src/imports/import_32_1700_avs.def index 161853e3..abf50d58 100644 --- a/src/imports/import_32_1700_avs.def +++ b/src/imports/import_32_1700_avs.def @@ -13,6 +13,9 @@ EXPORTS avs_fs_mount @76 NONAME avs_fs_open @573 NONAME avs_fs_read @573 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_net_ctrl @119 NONAME property_create @145 NONAME property_destroy @146 NONAME diff --git a/src/imports/import_64_1508_avs.def b/src/imports/import_64_1508_avs.def index e2a36116..9c1bbbde 100644 --- a/src/imports/import_64_1508_avs.def +++ b/src/imports/import_64_1508_avs.def @@ -9,6 +9,9 @@ EXPORTS avs_fs_lstat @79 NONAME avs_fs_open @58 NONAME avs_fs_read @61 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_net_ctrl @98 NONAME avs_shutdown @286 NONAME avs_thread_create @6 NONAME diff --git a/src/imports/import_64_1509_avs.def b/src/imports/import_64_1509_avs.def index db96f82c..8fd72ebf 100644 --- a/src/imports/import_64_1509_avs.def +++ b/src/imports/import_64_1509_avs.def @@ -9,6 +9,9 @@ EXPORTS avs_fs_lstat @78 NONAME avs_fs_open @58 NONAME avs_fs_read @61 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_net_ctrl @98 NONAME avs_shutdown @286 NONAME avs_thread_create @6 NONAME diff --git a/src/imports/import_64_1601_avs.def b/src/imports/import_64_1601_avs.def index 870c5aa3..d3219d53 100644 --- a/src/imports/import_64_1601_avs.def +++ b/src/imports/import_64_1601_avs.def @@ -7,6 +7,9 @@ EXPORTS avs_fs_lstat @573 NONAME avs_fs_open @573 NONAME avs_fs_read @573 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index 3461e7b5..0de69f2b 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -7,6 +7,9 @@ EXPORTS avs_fs_lstat @573 NONAME avs_fs_open @573 NONAME avs_fs_read @573 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME diff --git a/src/imports/import_64_1700_avs.def b/src/imports/import_64_1700_avs.def index 006bed38..ecf110de 100644 --- a/src/imports/import_64_1700_avs.def +++ b/src/imports/import_64_1700_avs.def @@ -13,6 +13,9 @@ EXPORTS avs_fs_mount @76 NONAME avs_fs_open @573 NONAME avs_fs_read @573 NONAME + avs_fs_opendir @573 NONAME + avs_fs_readdir @573 NONAME + avs_fs_closedir @573 NONAME avs_net_ctrl @119 NONAME property_create @145 NONAME property_destroy @146 NONAME From bb2854cd4e5574fb1901b965a428bc54c4f53c94 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 14:46:35 +0100 Subject: [PATCH 31/72] feat(launcher): Create raw and nvram dirs if they don't exist Required for some versions of AVS, first occurance on 1304, that doesn't seem to auto create these, so launcher should not rely on that implicitly. --- src/main/launcher/main.c | 100 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 7fe79736..650fc72c 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -154,6 +154,100 @@ static void log_property_node_tree_rec(struct property_node *parent_node, const } } +static void log_avs_fs_dir(const char *path) +{ + const char* name; + + 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); +} + +static void create_avs_config_fs_dir( + struct property *prop, + struct property_node *node, + const char *folder_name) +{ + char fs_path[1024]; + char fs_type[255]; + char device_path[1024]; + struct property_node *fs_node; + int res; + + memset(fs_path, 0, sizeof(fs_path)); + memset(fs_type, 0, sizeof(fs_type)); + + str_cpy(fs_path, sizeof(fs_path), "/fs/"); + str_cat(fs_path, sizeof(fs_path), folder_name); + + fs_node = property_search(prop, node, fs_path); + + if (!fs_node) { + log_warning("Could not find file system node %s in avs configuration", fs_path); + return; + } + + res = property_node_refer(prop, fs_node, "device", PROPERTY_TYPE_STR, + device_path, sizeof(device_path)); + + if (res < 0) { + log_fatal("Getting 'device' attribute from avs config entry %s failed", fs_path); + } + + // 'fstype' attribute is optional and defaults to value 'fs' + if (!property_search(prop, fs_node, "fstype")) { + if (path_exists(device_path)) { + // skip if exists already + return; + } + + log_misc("Creating avs directory %s", device_path); + + if (!path_mkdir(device_path)) { + log_fatal("Creating directory %s failed", device_path); + } + } else { + res = property_node_refer(prop, fs_node, "fstype", PROPERTY_TYPE_STR, + fs_type, sizeof(fs_type)); + + if (res < 0) { + log_fatal("Getting 'fstype' attribute from avs config entry %s failed", fs_path); + } + + if (!strcmp(fs_type, "fs") || !strcmp(fs_type, "nvram")) { + if (path_exists(device_path)) { + // skip if exists already + return; + } + + log_misc("Creating avs directory %s", device_path); + + if (!path_mkdir(device_path)) { + log_fatal("Creating directory %s failed", device_path); + } + } else { + log_fatal("Cannot create folders for unsupported file system type %s of path %s in avs config", + fs_type, fs_path); + } + } +} + static void log_property_tree(struct property *property) { log_property_node_tree_rec(property_search(property, NULL, "/"), ""); @@ -285,6 +379,12 @@ int main(int argc, const char **argv) bootstrap_config_update_avs(&bs, avs_config_root); + log_misc("Creating file system directories for nvram and raw if not exist..."); + + // create nvram and raw directories if possible for non-mounttable configurations + create_avs_config_fs_dir(avs_config, avs_config_root, "nvram"); + create_avs_config_fs_dir(avs_config, avs_config_root, "raw"); + log_misc("Loading before hook dlls..."); load_hook_dlls(&options.before_hook_dlls); From 985d63473e083b03f60c3cb585d74f03117640cd Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 14:47:27 +0100 Subject: [PATCH 32/72] chore: Remove debug code --- src/main/launcher/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 650fc72c..a486ec7e 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -371,8 +371,6 @@ int main(int argc, const char **argv) log_property_tree(avs_config); - // Sleep(100000000); - if (avs_config_root == NULL) { log_fatal("%s: /config missing", options.avs_config_path); } From 7aa37ad7b39cc34912d9ca8cc319466f6ff6dd32 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 15:07:54 +0100 Subject: [PATCH 33/72] refactor(launcher): Move avs FS dir creation stuff to module Part of the entire "setup AVS stuff", so it fits well into the avs_context_init method which declutters the launcher main method significantly --- src/main/launcher/avs-context.c | 84 +++++++++++++++++++++++++++++++-- src/main/launcher/avs-context.h | 3 +- src/main/launcher/main.c | 76 +---------------------------- 3 files changed, 84 insertions(+), 79 deletions(-) diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index 1e391db1..86df64b0 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -8,7 +8,9 @@ #include "launcher/avs-context.h" +#include "util/fs.h" #include "util/log.h" +#include "util/str.h" static void *avs_heap; @@ -16,13 +18,89 @@ static void *avs_heap; static void *std_heap; #endif +static void _avs_context_create_config_fs_dir( + struct property *prop, + struct property_node *node, + const char *folder_name) +{ + char fs_path[1024]; + char fs_type[255]; + char device_path[1024]; + struct property_node *fs_node; + int res; + + memset(fs_path, 0, sizeof(fs_path)); + memset(fs_type, 0, sizeof(fs_type)); + + str_cpy(fs_path, sizeof(fs_path), "/fs/"); + str_cat(fs_path, sizeof(fs_path), folder_name); + + fs_node = property_search(prop, node, fs_path); + + if (!fs_node) { + log_warning("Could not find file system node %s in avs configuration", fs_path); + return; + } + + res = property_node_refer(prop, fs_node, "device", PROPERTY_TYPE_STR, + device_path, sizeof(device_path)); + + if (res < 0) { + log_fatal("Getting 'device' attribute from avs config entry %s failed", fs_path); + } + + // 'fstype' attribute is optional and defaults to value 'fs' + if (!property_search(prop, fs_node, "fstype")) { + if (path_exists(device_path)) { + // skip if exists already + return; + } + + log_misc("Creating avs directory %s", device_path); + + if (!path_mkdir(device_path)) { + log_fatal("Creating directory %s failed", device_path); + } + } else { + res = property_node_refer(prop, fs_node, "fstype", PROPERTY_TYPE_STR, + fs_type, sizeof(fs_type)); + + if (res < 0) { + log_fatal("Getting 'fstype' attribute from avs config entry %s failed", fs_path); + } + + if (!strcmp(fs_type, "fs") || !strcmp(fs_type, "nvram")) { + if (path_exists(device_path)) { + // skip if exists already + return; + } + + log_misc("Creating avs directory %s", device_path); + + if (!path_mkdir(device_path)) { + log_fatal("Creating directory %s failed", device_path); + } + } else { + log_fatal("Cannot create folders for unsupported file system type %s of path %s in avs config", + fs_type, fs_path); + } + } +} + void avs_context_init( - struct property_node *config, + struct property *config_prop, + struct property_node *config_node, uint32_t avs_heap_size, uint32_t std_heap_size, avs_log_writer_t log_writer, void *log_writer_ctx) { + log_misc("Creating AVS file system directories for nvram and raw if not exist..."); + + // create nvram and raw directories if possible for non-mounttable configurations + _avs_context_create_config_fs_dir(config_prop, config_node, "nvram"); + _avs_context_create_config_fs_dir(config_prop, config_node, "raw"); + avs_heap = VirtualAlloc( NULL, avs_heap_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); @@ -47,7 +125,7 @@ void avs_context_init( #ifdef AVS_HAS_STD_HEAP avs_boot( - config, + config_node, std_heap, std_heap_size, avs_heap, @@ -56,7 +134,7 @@ void avs_context_init( 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); + avs_boot(config_node, avs_heap, avs_heap_size, NULL, log_writer, log_writer_ctx); #endif } diff --git a/src/main/launcher/avs-context.h b/src/main/launcher/avs-context.h index d4532eb4..9fee2f34 100644 --- a/src/main/launcher/avs-context.h +++ b/src/main/launcher/avs-context.h @@ -10,7 +10,8 @@ #endif void avs_context_init( - struct property_node *config, + struct property *config_prop, + struct property_node *config_node, uint32_t avs_heap_size, uint32_t std_heap_size, avs_log_writer_t log_writer, diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index a486ec7e..70f8cdb9 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -179,75 +179,6 @@ static void log_avs_fs_dir(const char *path) avs_fs_closedir(dir); } -static void create_avs_config_fs_dir( - struct property *prop, - struct property_node *node, - const char *folder_name) -{ - char fs_path[1024]; - char fs_type[255]; - char device_path[1024]; - struct property_node *fs_node; - int res; - - memset(fs_path, 0, sizeof(fs_path)); - memset(fs_type, 0, sizeof(fs_type)); - - str_cpy(fs_path, sizeof(fs_path), "/fs/"); - str_cat(fs_path, sizeof(fs_path), folder_name); - - fs_node = property_search(prop, node, fs_path); - - if (!fs_node) { - log_warning("Could not find file system node %s in avs configuration", fs_path); - return; - } - - res = property_node_refer(prop, fs_node, "device", PROPERTY_TYPE_STR, - device_path, sizeof(device_path)); - - if (res < 0) { - log_fatal("Getting 'device' attribute from avs config entry %s failed", fs_path); - } - - // 'fstype' attribute is optional and defaults to value 'fs' - if (!property_search(prop, fs_node, "fstype")) { - if (path_exists(device_path)) { - // skip if exists already - return; - } - - log_misc("Creating avs directory %s", device_path); - - if (!path_mkdir(device_path)) { - log_fatal("Creating directory %s failed", device_path); - } - } else { - res = property_node_refer(prop, fs_node, "fstype", PROPERTY_TYPE_STR, - fs_type, sizeof(fs_type)); - - if (res < 0) { - log_fatal("Getting 'fstype' attribute from avs config entry %s failed", fs_path); - } - - if (!strcmp(fs_type, "fs") || !strcmp(fs_type, "nvram")) { - if (path_exists(device_path)) { - // skip if exists already - return; - } - - log_misc("Creating avs directory %s", device_path); - - if (!path_mkdir(device_path)) { - log_fatal("Creating directory %s failed", device_path); - } - } else { - log_fatal("Cannot create folders for unsupported file system type %s of path %s in avs config", - fs_type, fs_path); - } - } -} - static void log_property_tree(struct property *property) { log_property_node_tree_rec(property_search(property, NULL, "/"), ""); @@ -377,18 +308,13 @@ int main(int argc, const char **argv) bootstrap_config_update_avs(&bs, avs_config_root); - log_misc("Creating file system directories for nvram and raw if not exist..."); - - // create nvram and raw directories if possible for non-mounttable configurations - create_avs_config_fs_dir(avs_config, avs_config_root, "nvram"); - create_avs_config_fs_dir(avs_config, avs_config_root, "raw"); - log_misc("Loading before hook dlls..."); load_hook_dlls(&options.before_hook_dlls); log_misc("Initializing AVS..."); avs_context_init( + avs_config, avs_config_root, options.avs_heap_size, options.std_heap_size, From 1bdb6d989b6c754ced5491b3dee0edc2ae908af3 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 15:20:38 +0100 Subject: [PATCH 34/72] refactor(launcher): Move more AVS related setup code to module The avs-context module is already very opinionated on how to setup AVS. The logging code in main isn't re-used anywhere else, so further abstracting these AVS related bits and providing a cleaner abstracting for the main part of launcher improves overall comprehensibility and creates cleaner scopes for all the plumbing that needs to be done, imo. --- src/main/launcher/avs-context.c | 119 ++++++++++++++++++++++++++++++-- src/main/launcher/avs-context.h | 3 +- src/main/launcher/main.c | 94 +------------------------ 3 files changed, 116 insertions(+), 100 deletions(-) diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index 86df64b0..d41c75b1 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -1,3 +1,5 @@ +#define LOG_MODULE "avs-context" + #include #include @@ -8,8 +10,10 @@ #include "launcher/avs-context.h" +#include "util/codepage.h" #include "util/fs.h" #include "util/log.h" +#include "util/mem.h" #include "util/str.h" static void *avs_heap; @@ -18,6 +22,104 @@ static void *avs_heap; static void *std_heap; #endif +static HANDLE _avs_context_logfile; + +/* 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; + 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 HANDLE _avs_context_create_logfile(const char *path) +{ + HANDLE handle; + + if (path == NULL) { + log_misc("No logfile path specified, logging to file disabled"); + handle = INVALID_HANDLE_VALUE; + } else { + handle = CreateFileA( + path, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + 0, + NULL); + + if (handle == INVALID_HANDLE_VALUE) { + log_warning("Creating log file %s failed: %lX", path, GetLastError()); + } + } + + return handle; +} + static void _avs_context_create_config_fs_dir( struct property *prop, struct property_node *node, @@ -92,9 +194,10 @@ void avs_context_init( struct property_node *config_node, uint32_t avs_heap_size, uint32_t std_heap_size, - avs_log_writer_t log_writer, - void *log_writer_ctx) + const char* path_logfile) { + _avs_context_logfile = _avs_context_create_logfile(path_logfile); + log_misc("Creating AVS file system directories for nvram and raw if not exist..."); // create nvram and raw directories if possible for non-mounttable configurations @@ -123,6 +226,8 @@ void avs_context_init( } #endif + log_misc("Calling avs_boot"); + #ifdef AVS_HAS_STD_HEAP avs_boot( config_node, @@ -130,11 +235,11 @@ void avs_context_init( std_heap_size, avs_heap, avs_heap_size, - log_writer, - log_writer_ctx); + _avs_context_log_writer, + _avs_context_logfile); #else /* AVS v2.16.xx and I suppose onward uses a unified heap */ - avs_boot(config_node, avs_heap, avs_heap_size, NULL, log_writer, log_writer_ctx); + avs_boot(config_node, avs_heap, avs_heap_size, NULL, _avs_context_log_writer, _avs_context_logfile); #endif } @@ -142,6 +247,10 @@ void avs_context_fini(void) { avs_shutdown(); + if (_avs_context_logfile != INVALID_HANDLE_VALUE) { + CloseHandle(_avs_context_logfile); + } + #ifdef AVS_HAS_STD_HEAP VirtualFree(std_heap, 0, MEM_RELEASE); #endif diff --git a/src/main/launcher/avs-context.h b/src/main/launcher/avs-context.h index 9fee2f34..a296fd82 100644 --- a/src/main/launcher/avs-context.h +++ b/src/main/launcher/avs-context.h @@ -14,8 +14,7 @@ void avs_context_init( struct property_node *config_node, uint32_t avs_heap_size, uint32_t std_heap_size, - avs_log_writer_t log_writer, - void *log_writer_ctx); + const char* path_logfile); void avs_context_fini(void); #endif diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 70f8cdb9..e757112d 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -17,85 +17,12 @@ #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; @@ -192,7 +119,6 @@ static void log_property_node_tree(struct property_node *parent_node) int main(int argc, const char **argv) { bool ok; - HANDLE logfile; struct ea3_ident ea3; struct module_context module; @@ -284,19 +210,6 @@ int main(int argc, const char **argv) /* 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"); @@ -318,8 +231,7 @@ int main(int argc, const char **argv) avs_config_root, options.avs_heap_size, options.std_heap_size, - log_callback, - logfile); + options.logfile); boot_property_free(avs_config); @@ -485,10 +397,6 @@ int main(int argc, const char **argv) log_to_writer(log_writer_file, stdout); avs_context_fini(); - if (logfile != INVALID_HANDLE_VALUE) { - CloseHandle(logfile); - } - module_context_fini(&module); options_fini(&options); From 36bd082cb091f79d47824a95c44d816231dd8bae Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 15:24:21 +0100 Subject: [PATCH 35/72] refactor(launcher): Move initial launcher info logging to func Cleanup and scoping improvements in main function --- src/main/launcher/main.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index e757112d..70e2bca5 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -23,6 +23,20 @@ #include "util/os.h" #include "util/str.h" +static void log_launcher_and_env_info() +{ + char buffer_tmp[MAX_PATH]; + + log_info( + "launcher build date %s, gitrev %s", + launcher_build_date, + launcher_gitrev); + os_version_log(); + + getcwd(buffer_tmp, sizeof(buffer_tmp)); + log_info("Current working directory: %s", buffer_tmp); +} + static void load_hook_dlls(struct array *hook_dlls) { const char *hook_dll; @@ -138,14 +152,7 @@ int main(int argc, const char **argv) log_to_writer(log_writer_file, stdout); log_set_level(LOG_LEVEL_MISC); - log_info( - "launcher build date %s, gitrev %s", - launcher_build_date, - launcher_gitrev); - os_version_log(); - char buffer_tmp[MAX_PATH]; - getcwd(buffer_tmp, sizeof(buffer_tmp)); - log_info("Current working directory: %s", buffer_tmp); + log_launcher_and_env_info(); /* Read command line */ From 6fd006410a2c232ee4d2a7cf2a57492f4160c2c5 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 15:27:03 +0100 Subject: [PATCH 36/72] refactor(launcher): Move remote debugger trap to func Improves readability in main function --- src/main/launcher/main.c | 42 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 70e2bca5..fdfc404b 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -37,6 +37,30 @@ static void log_launcher_and_env_info() log_info("Current working directory: %s", buffer_tmp); } +static void trap_remote_debugger() +{ + 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); + } +} + static void load_hook_dlls(struct array *hook_dlls) { const char *hook_dll; @@ -194,23 +218,7 @@ int main(int argc, const char **argv) 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); - } + trap_remote_debugger(); } log_misc("Preparing AVS..."); From 454b2138e3cff1ff7e879454d396154722a89625 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 16:46:17 +0100 Subject: [PATCH 37/72] refactor(launcher): Separate bootstrap config and launcher options There was quite some duct-tape in-place to deal with two ways of "bootstrapping" with launcher: - Using plain command line args: The "old" method - Using bootstrap.xml Naturally, key items overlapped as they must be done in order to make games boot: - AVS setup - Logger setup - Module and parameters Consolidate all these into the bootstrap configuration and add two "bootstrap" paths right after reading all options: - From command line options - From file (bootstrap.xml) This cleans up a lot of branching statements that started literring the main method and made it hard to reason about. --- src/main/launcher/Module.mk | 1 + src/main/launcher/bootstrap-context.c | 94 +++++++++ src/main/launcher/bootstrap-context.h | 27 +++ src/main/launcher/main.c | 280 ++++++++++++-------------- src/main/launcher/options.c | 58 +----- src/main/launcher/options.h | 4 - 6 files changed, 255 insertions(+), 209 deletions(-) create mode 100644 src/main/launcher/bootstrap-context.c create mode 100644 src/main/launcher/bootstrap-context.h diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index 8deb07a9..f8fecb7c 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -14,6 +14,7 @@ libs_launcher := \ src_launcher := \ avs-context.c \ + bootstrap-context.c \ bs-config.c \ ea3-config.c \ main.c \ diff --git a/src/main/launcher/bootstrap-context.c b/src/main/launcher/bootstrap-context.c new file mode 100644 index 00000000..ab288b72 --- /dev/null +++ b/src/main/launcher/bootstrap-context.c @@ -0,0 +1,94 @@ +#define LOG_MODULE "bootstrap-context" + +#include "launcher/bs-config.h" +#include "launcher/property.h" + +#include "util/log.h" +#include "util/str.h" + +void bootstrap_context_init( + const char *avs_config_path, + const char *ea3_config_path, + size_t std_heap_size, + size_t avs_heap_size, + const char *logfile, + const char *module, + struct bootstrap_config *config) +{ + log_assert(avs_config_path); + log_assert(ea3_config_path); + log_assert(module); + + log_info("Bootstrap from options"); + + bootstrap_config_init(config); + + str_cpy(config->startup.avs_config_file, sizeof(config->startup.avs_config_file), avs_config_path); + str_cpy(config->startup.eamuse_config_file, sizeof(config->startup.eamuse_config_file), ea3_config_path); + + config->startup.avs_heap_size = avs_heap_size; + config->startup.std_heap_size = std_heap_size; + + if (logfile) { + config->startup.log_enable_file = true; + + str_cpy(config->startup.log_file, sizeof(config->startup.log_file), logfile); + str_cpy(config->startup.log_name, sizeof(config->startup.log_name), logfile); + } + + str_cpy(config->startup.module_file, sizeof(config->startup.module_file), module); +} + +void bootstrap_context_init_from_file( + const char *config_path, + const char *selector, + struct bootstrap_config *config) +{ + struct property *property; + + log_assert(config_path); + log_assert(selector); + log_assert(config); + + log_info("Bootstrap from configuration %s with selector %s", config_path, selector); + + bootstrap_config_init(config); + property = boot_property_load(config_path); + + log_info( + "Loading bootstrap selector '%s'...", selector); + + if (!bootstrap_config_from_property(config, property, selector)) { + log_fatal( + "%s: could not load configuration for '%s'", + config_path, + selector); + } + + boot_property_free(property); +} + +void bootstrap_context_post_avs_setup(struct bootstrap_config *config) +{ + struct bootstrap_default_file default_file; + struct avs_stat st; + + log_assert(config); + + log_misc("Bootstrap post AVS setup"); + + while (bootstrap_config_iter_default_file(config, &default_file)) { + log_misc("%s: copying from %s...", default_file.dest, default_file.src); + + if (!avs_fs_lstat(default_file.src, &st)) { + log_fatal("Default file source %s does not exist or is not accessible", default_file.src); + } + + if (avs_fs_copy(default_file.src, default_file.dest) < 0) { + log_fatal( + "%s: could not copy from %s", + default_file.dest, + default_file.src); + } + } +} \ No newline at end of file diff --git a/src/main/launcher/bootstrap-context.h b/src/main/launcher/bootstrap-context.h new file mode 100644 index 00000000..b0573456 --- /dev/null +++ b/src/main/launcher/bootstrap-context.h @@ -0,0 +1,27 @@ +#ifndef LAUNCHER_BOOTSTRAP_CONTEXT_H +#define LAUNCHER_BOOTSTRAP_CONTEXT_H + +#include + +#include "launcher/bs-config.h" + +void bootstrap_context_init(); +void bootstrap_context_finit(); + +void bootstrap_context_init( + const char *avs_config_path, + const char *ea3_config_path, + size_t std_heap_size, + size_t avs_heap_size, + const char *logfile, + const char *module, + struct bootstrap_config *config); + +void bootstrap_context_init_from_file( + const char *config_path, + const char *selector, + struct bootstrap_config *config); + +void bootstrap_context_post_avs_setup(const struct bootstrap_config *config); + +#endif \ No newline at end of file diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index fdfc404b..3a8d60f1 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -9,6 +9,7 @@ #include "imports/avs.h" #include "launcher/avs-context.h" +#include "launcher/bootstrap-context.h" #include "launcher/bs-config.h" #include "launcher/ea3-config.h" #include "launcher/module.h" @@ -23,71 +24,6 @@ #include "util/os.h" #include "util/str.h" -static void log_launcher_and_env_info() -{ - char buffer_tmp[MAX_PATH]; - - log_info( - "launcher build date %s, gitrev %s", - launcher_build_date, - launcher_gitrev); - os_version_log(); - - getcwd(buffer_tmp, sizeof(buffer_tmp)); - log_info("Current working directory: %s", buffer_tmp); -} - -static void trap_remote_debugger() -{ - 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); - } -} - -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); - } - } -} - static void log_property_node_tree_rec(struct property_node *parent_node, const char* parent_path) { char cur_path[4096]; @@ -154,6 +90,104 @@ static void log_property_node_tree(struct property_node *parent_node) log_property_node_tree_rec(parent_node, ""); } +static void log_launcher_and_env_info() +{ + char buffer_tmp[MAX_PATH]; + + log_info( + "launcher build date %s, gitrev %s", + launcher_build_date, + launcher_gitrev); + os_version_log(); + + getcwd(buffer_tmp, sizeof(buffer_tmp)); + log_info("Current working directory: %s", buffer_tmp); +} + +static void trap_remote_debugger() +{ + 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); + } +} + +static void avs_initialize(struct bootstrap_config *bootstrap_config) +{ + struct property *avs_config; + struct property_node *avs_config_root; + + log_misc("AVS initialize..."); + + avs_config = boot_property_load(bootstrap_config->startup.avs_config_file); + avs_config_root = property_search(avs_config, 0, "/config"); + + log_property_tree(avs_config); + + if (avs_config_root == NULL) { + log_fatal("%s: /config missing", bootstrap_config->startup.avs_config_file); + } + + bootstrap_config_update_avs(bootstrap_config, avs_config_root); + + avs_context_init( + avs_config, + avs_config_root, + bootstrap_config->startup.avs_heap_size, + bootstrap_config->startup.std_heap_size, + bootstrap_config->startup.log_file); + + boot_property_free(avs_config); + + log_misc("AVS logger available, switching to AVS loggers"); + + log_to_external( + log_body_misc, log_body_info, log_body_warning, log_body_fatal); +} + +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; @@ -161,15 +195,12 @@ int main(int argc, const char **argv) struct ea3_ident ea3; struct module_context module; struct options options; - struct bootstrap_config bs; + struct bootstrap_config bootstrap_config; - struct property *bootstrap_config = NULL; struct property *app_config = NULL; - 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; // Static logging setup until we got AVS up and running @@ -178,36 +209,12 @@ int main(int argc, const char **argv) log_launcher_and_env_info(); - /* Read command line */ + log_misc("Reading command line options..."); options_init(&options); - options_read_early_cmdline(&options, argc, argv); - - bootstrap_config_init(&bs); - if (options.bootstrap_selector) { - log_misc("Bootstrap selector specified: %s", options.bootstrap_selector); - - bootstrap_config = boot_property_load(options.bootstrap_config_path); - - log_info( - "Loading bootstrap selector '%s'...", options.bootstrap_selector); - - if (!bootstrap_config_from_property( - &bs, bootstrap_config, options.bootstrap_selector)) { - log_fatal( - "%s: could not load configuration for '%s'", - options.bootstrap_config_path, - options.bootstrap_selector); - } - - options_read_bootstrap(&options, &bs.startup); - log_misc("Finished reading bootstrap"); - } - - log_misc("Reading command line options..."); - - if (!options_read_cmdline(&options, argc, argv)) { + if (!options_read_cmdline(&options, argc, argv) || + (!options.bootstrap_selector && !options.module)) { options_print_usage(); return EXIT_FAILURE; @@ -221,61 +228,31 @@ int main(int argc, const char **argv) trap_remote_debugger(); } - log_misc("Preparing AVS..."); - - /* Start up AVS */ - - avs_config = boot_property_load(options.avs_config_path); - avs_config_root = property_search(avs_config, 0, "/config"); - - log_property_tree(avs_config); - - if (avs_config_root == NULL) { - log_fatal("%s: /config missing", options.avs_config_path); - } - - bootstrap_config_update_avs(&bs, avs_config_root); - log_misc("Loading before hook dlls..."); - load_hook_dlls(&options.before_hook_dlls); - - log_misc("Initializing AVS..."); - avs_context_init( - avs_config, - avs_config_root, - options.avs_heap_size, - options.std_heap_size, - options.logfile); - - boot_property_free(avs_config); - - log_info("Bootstrap complete, switching loggers"); - - log_to_external( - log_body_misc, log_body_info, log_body_warning, log_body_fatal); - - /* Do late bootstrap initialisation */ - - struct bootstrap_default_file default_file; - - while (bootstrap_config_iter_default_file(&bs, &default_file)) { - struct avs_stat st; - - log_misc("%s: copying from %s...", default_file.dest, default_file.src); - - if (!avs_fs_lstat(default_file.src, &st)) { - log_fatal("Default file source %s does not exist or is not accessible", default_file.src); - } + load_hook_dlls(&options.before_hook_dlls); - if (avs_fs_copy(default_file.src, default_file.dest) < 0) { - log_fatal( - "%s: could not copy from %s", - default_file.dest, - default_file.src); - } + // Launcher supports two major boot modes: "manual" vs. bootstrap.xml + if (!options.bootstrap_selector) { + bootstrap_context_init( + options.avs_config_path, + options.ea3_config_path, + options.std_heap_size, + options.avs_heap_size, + options.logfile, + options.module, + &bootstrap_config); + } else { + bootstrap_context_init_from_file( + options.bootstrap_config_path, + options.bootstrap_selector, + &bootstrap_config); } + avs_initialize(&bootstrap_config); + + bootstrap_context_post_avs_setup(&bootstrap_config); + /* Load game DLL */ if (options.iat_hook_dlls.nitems > 0) { @@ -297,11 +274,11 @@ int main(int argc, const char **argv) log_misc("Preparing ea3 configuration..."); - ea3_config = boot_property_load_avs(options.ea3_config_path); + ea3_config = boot_property_load_avs(bootstrap_config.startup.eamuse_config_file); ea3_config_root = property_search(ea3_config, 0, "/ea3"); if (ea3_config_root == NULL) { - log_fatal("%s: /ea3 missing", options.ea3_config_path); + log_fatal("%s: /ea3 missing", bootstrap_config.startup.eamuse_config_file); } if (path_exists(options.ea3_ident_path)) { @@ -342,8 +319,8 @@ int main(int argc, const char **argv) /* Invoke dll_entry_init */ - if (bs.module_params) { - app_config_root = bs.module_params; + if (bootstrap_config.module_params) { + app_config_root = bootstrap_config.module_params; } else if (path_exists(options.app_config_path)) { app_config = boot_property_load_avs(options.app_config_path); app_config_root = property_search(app_config, 0, "/param"); @@ -373,9 +350,6 @@ int main(int argc, const char **argv) if (app_config) { boot_property_free(app_config); } - if (bootstrap_config) { - boot_property_free(bootstrap_config); - } ea3_ident_to_property(&ea3, ea3_config); diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index d3d5165e..5e50528c 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -36,15 +36,16 @@ void options_init(struct options *options) options->override_urlslash_value = false; } -void options_read_early_cmdline( - struct options *options, int argc, const char **argv) +bool options_read_cmdline(struct options *options, int argc, const char **argv) { + bool got_module = false; + for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'C': if (i + 1 >= argc) { - return; + return false; } options->bootstrap_config_path = argv[++i]; @@ -52,35 +53,11 @@ void options_read_early_cmdline( break; case 'Z': - if (i + 1 >= argc) { - return; - } - - options->bootstrap_selector = argv[++i]; - - break; - - default: - break; - } - } - } -} - -bool options_read_cmdline(struct options *options, int argc, const char **argv) -{ - bool got_module = false; - for (int i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - switch (argv[i][1]) { - case 'C': - case 'Z': - /* handled by options_read_early_cmdline() */ if (i + 1 >= argc) { return false; } - ++i; + options->bootstrap_selector = argv[++i]; break; @@ -246,30 +223,7 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv) } } - if (options->module) { - return true; - } else { - return false; - } -} - -void options_read_bootstrap( - struct options *options, const struct bootstrap_startup_config *bs_config) -{ - options->avs_config_path = bs_config->avs_config_file; - options->avs_heap_size = bs_config->avs_heap_size; - options->std_heap_size = bs_config->std_heap_size; - options->ea3_config_path = bs_config->eamuse_config_file; - - if (bs_config->log_enable_file) { - if (bs_config->log_file[0]) { - options->logfile = bs_config->log_file; - } else if (bs_config->log_name[0]) { - options->logfile = bs_config->log_name; - } - } - - options->module = bs_config->module_file; + return true; } void options_print_usage(void) diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 5ebba261..b962046a 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -31,11 +31,7 @@ struct options { }; void options_init(struct options *options); -void options_read_early_cmdline( - struct options *options, int argc, const char **argv); bool options_read_cmdline(struct options *options, int argc, const char **argv); -void options_read_bootstrap( - struct options *options, const struct bootstrap_startup_config *bs_config); void options_print_usage(void); void options_fini(struct options *options); From 9caf4ade7f90c6c5c63eac52b7da776b6c2c8740 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 19:02:49 +0100 Subject: [PATCH 38/72] refactor(launcher): Improve overall structure of launcher Further split things into separate functions to improve the structure of the main function. --- src/main/launcher/Module.mk | 2 +- src/main/launcher/bootstrap-context.c | 10 +- src/main/launcher/bootstrap-context.h | 1 + .../launcher/{ea3-config.c => ea3-ident.c} | 116 ++---- .../launcher/{ea3-config.h => ea3-ident.h} | 9 +- src/main/launcher/main.c | 388 ++++++++++++------ 6 files changed, 302 insertions(+), 224 deletions(-) rename src/main/launcher/{ea3-config.c => ea3-ident.c} (50%) rename src/main/launcher/{ea3-config.h => ea3-ident.h} (84%) diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index f8fecb7c..8f89518e 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -16,7 +16,7 @@ src_launcher := \ avs-context.c \ bootstrap-context.c \ bs-config.c \ - ea3-config.c \ + ea3-ident.c \ main.c \ module.c \ options.c \ diff --git a/src/main/launcher/bootstrap-context.c b/src/main/launcher/bootstrap-context.c index ab288b72..aeb6cf82 100644 --- a/src/main/launcher/bootstrap-context.c +++ b/src/main/launcher/bootstrap-context.c @@ -42,30 +42,28 @@ void bootstrap_context_init( void bootstrap_context_init_from_file( const char *config_path, const char *selector, + struct property **bootstrap_config_property, struct bootstrap_config *config) { - struct property *property; - log_assert(config_path); log_assert(selector); + log_assert(bootstrap_config_property); log_assert(config); log_info("Bootstrap from configuration %s with selector %s", config_path, selector); bootstrap_config_init(config); - property = boot_property_load(config_path); + *bootstrap_config_property = boot_property_load(config_path); log_info( "Loading bootstrap selector '%s'...", selector); - if (!bootstrap_config_from_property(config, property, selector)) { + if (!bootstrap_config_from_property(config, *bootstrap_config_property, selector)) { log_fatal( "%s: could not load configuration for '%s'", config_path, selector); } - - boot_property_free(property); } void bootstrap_context_post_avs_setup(struct bootstrap_config *config) diff --git a/src/main/launcher/bootstrap-context.h b/src/main/launcher/bootstrap-context.h index b0573456..a4a26d87 100644 --- a/src/main/launcher/bootstrap-context.h +++ b/src/main/launcher/bootstrap-context.h @@ -20,6 +20,7 @@ void bootstrap_context_init( void bootstrap_context_init_from_file( const char *config_path, const char *selector, + struct property **bootstrap_config_property, struct bootstrap_config *config); void bootstrap_context_post_avs_setup(const struct bootstrap_config *config); diff --git a/src/main/launcher/ea3-config.c b/src/main/launcher/ea3-ident.c similarity index 50% rename from src/main/launcher/ea3-config.c rename to src/main/launcher/ea3-ident.c index 684ba466..0c421ab9 100644 --- a/src/main/launcher/ea3-config.c +++ b/src/main/launcher/ea3-ident.c @@ -2,8 +2,9 @@ #include "imports/avs.h" -#include "launcher/ea3-config.h" +#include "launcher/ea3-ident.h" #include "launcher/module.h" +#include "launcher/property.h" #include "util/defs.h" #include "util/hex.h" @@ -26,10 +27,29 @@ 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) +void ea3_ident_initialize_from_file( + const char *path, + struct ea3_ident *ea3_ident) { - return property_psmap_import(ea3_config, NULL, ident, ea3_ident_psmap); + struct property *property; + struct property_node *node; + + log_assert(path); + log_assert(ea3_ident); + + property = boot_property_load(path); + node = property_search(property, NULL, "/ea3_conf"); + + if (node == NULL) { + log_fatal("%s: /ea3_conf missing", path); + } + + if (!property_psmap_import(property, NULL, ea3_ident, ea3_ident_psmap)) { + log_fatal( + "%s: Error reading IDs from config file", path); + } + + boot_property_free(property); } void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident) @@ -56,94 +76,6 @@ void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident) 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) { diff --git a/src/main/launcher/ea3-config.h b/src/main/launcher/ea3-ident.h similarity index 84% rename from src/main/launcher/ea3-config.h rename to src/main/launcher/ea3-ident.h index ada44008..fef05b7f 100644 --- a/src/main/launcher/ea3-config.h +++ b/src/main/launcher/ea3-ident.h @@ -1,5 +1,5 @@ -#ifndef LAUNCHER_EA3_CONFIG_H -#define LAUNCHER_EA3_CONFIG_H +#ifndef LAUNCHER_EA3_IDENT_H +#define LAUNCHER_EA3_IDENT_H #include "imports/avs.h" @@ -29,13 +29,10 @@ struct ea3_ident { }; void ea3_ident_init(struct ea3_ident *ident); +void ea3_ident_initialize_from_file(const char *path, struct ea3_ident *ea3_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( diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 3a8d60f1..986cc628 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -11,7 +11,7 @@ #include "launcher/avs-context.h" #include "launcher/bootstrap-context.h" #include "launcher/bs-config.h" -#include "launcher/ea3-config.h" +#include "launcher/ea3-ident.h" #include "launcher/module.h" #include "launcher/options.h" #include "launcher/property.h" @@ -43,6 +43,7 @@ static void log_property_node_tree_rec(struct property_node *parent_node, const // parent node is a leaf node, print all data of it if (child_node == NULL) { + // TODO reading ints as string seems to result in empty values when printing them? property_node_read(parent_node, PROPERTY_TYPE_STR, leaf_node_data, sizeof(leaf_node_data)); log_misc("%s: %s", cur_path, leaf_node_data); @@ -128,33 +129,35 @@ static void trap_remote_debugger() } } -static void avs_initialize(struct bootstrap_config *bootstrap_config) +static void avs_initialize( + struct bootstrap_config *bootstrap_config, + struct property **avs_config_property) { - struct property *avs_config; - struct property_node *avs_config_root; + struct property_node *avs_config_node; - log_misc("AVS initialize..."); + log_assert(bootstrap_config); + log_assert(avs_config_property); - avs_config = boot_property_load(bootstrap_config->startup.avs_config_file); - avs_config_root = property_search(avs_config, 0, "/config"); + log_misc("AVS initialize..."); - log_property_tree(avs_config); + *avs_config_property = boot_property_load(bootstrap_config->startup.avs_config_file); + avs_config_node = property_search(*avs_config_property, 0, "/config"); - if (avs_config_root == NULL) { + if (avs_config_node == NULL) { log_fatal("%s: /config missing", bootstrap_config->startup.avs_config_file); } - bootstrap_config_update_avs(bootstrap_config, avs_config_root); + bootstrap_config_update_avs(bootstrap_config, avs_config_node); + + log_property_tree(*avs_config_property); avs_context_init( - avs_config, - avs_config_root, + *avs_config_property, + avs_config_node, bootstrap_config->startup.avs_heap_size, bootstrap_config->startup.std_heap_size, bootstrap_config->startup.log_file); - boot_property_free(avs_config); - log_misc("AVS logger available, switching to AVS loggers"); log_to_external( @@ -188,27 +191,220 @@ static void load_hook_dlls(struct array *hook_dlls) } } -int main(int argc, const char **argv) +static void ea3_ident_config_setup( + const char *ea3_ident_path, + const char *softid, + const char *pcbid, + struct ea3_ident *ea3_ident) { + log_assert(ea3_ident); + + ea3_ident_init(ea3_ident); + + if (ea3_ident_path) { + ea3_ident_initialize_from_file(ea3_ident_path, ea3_ident); + } + + if (!softid) { + str_cpy(ea3_ident->softid, lengthof(ea3_ident->softid), softid); + } + + if (!pcbid) { + str_cpy(ea3_ident->pcbid, lengthof(ea3_ident->pcbid), pcbid); + } + + if (!ea3_ident->hardid[0]) { + ea3_ident_hardid_from_ethernet(ea3_ident); + } +} + +static void app_config_setup( + const struct bootstrap_config *bootstrap_config, + const char *app_config_path, + struct property **app_config_property, + struct property_node **app_config_node) +{ + log_assert(bootstrap_config); + log_assert(app_config_property); + log_assert(app_config_node); + + if (bootstrap_config->module_params) { + *app_config_property = NULL; + *app_config_node = bootstrap_config->module_params; + } else if (app_config_path && path_exists(app_config_path)) { + *app_config_property = boot_property_load_avs(app_config_path); + + *app_config_node = property_search(*app_config_property, 0, "/param"); + + if (app_config_node == NULL) { + log_fatal("%s: /param missing", app_config_path); + } + } else { + log_warning("Explicit app config (file) missing, defaulting to empty"); + + *app_config_property = boot_property_load_cstring("dummy"); + *app_config_node = property_search(*app_config_property, 0, "/param"); + } +} + +void invoke_dll_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; - struct ea3_ident ea3; - struct module_context module; + /* 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); + + log_property_node_tree(app_config); + + ok = module_context_invoke_init(module, sidcode_short, app_config); + + if (!ok) { + log_fatal("%s: dll_module_init() returned failure", module->path); + } + + /* 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); +} + +static void ea3_config_setup( + const struct ea3_ident *ea3_ident, + const char *eamuse_config_file, + bool override_urlslash_enabled, + bool override_urlslash_value, + const char *service_url, + struct property **ea3_config_property) +{ + struct property_node *ea3_config_node; + + log_assert(ea3_ident); + log_assert(eamuse_config_file); + log_assert(ea3_config_property); + + log_misc("Preparing ea3 configuration..."); + + *ea3_config_property = boot_property_load_avs(eamuse_config_file); + ea3_config_node = property_search(*ea3_config_property, 0, "/ea3"); + + if (ea3_config_node == NULL) { + log_fatal("%s: /ea3 missing", eamuse_config_file); + } + + ea3_ident_to_property(ea3_ident, *ea3_config_property); + + if (override_urlslash_enabled) { + log_misc( + "Overriding url_slash to: %d", override_urlslash_value); + + ea3_ident_replace_property_bool( + ea3_config_node, + "/network/url_slash", + override_urlslash_value); + } + + if (service_url) { + log_misc("Overriding service url to: %s", service_url); + + ea3_ident_replace_property_str( + ea3_config_node, "/network/services", service_url); + } +} + +int main(int argc, const char **argv) +{ struct options options; + + struct property *bootstrap_config_property; struct bootstrap_config bootstrap_config; - struct property *app_config = NULL; - struct property *ea3_config; + struct module_context module; + + struct property *avs_config_property; - struct property_node *app_config_root; - struct property_node *ea3_config_root; + struct ea3_ident ea3_ident; + + struct property *app_config_property; + struct property_node *app_config_node; + + struct property *ea3_config_property; + + /* Static logging setup prior AVS available */ - // Static logging setup until we got AVS up and running log_to_writer(log_writer_file, stdout); log_set_level(LOG_LEVEL_MISC); log_launcher_and_env_info(); + /* Command line (override) options */ + log_misc("Reading command line options..."); options_init(&options); @@ -228,11 +424,13 @@ int main(int argc, const char **argv) trap_remote_debugger(); } - log_misc("Loading before hook dlls..."); + /* Before hook dlls */ + log_misc("Loading before hook dlls..."); load_hook_dlls(&options.before_hook_dlls); - // Launcher supports two major boot modes: "manual" vs. bootstrap.xml + /* Bootstrap either via a bootstrap.xml file or command line arguments */ + if (!options.bootstrap_selector) { bootstrap_context_init( options.avs_config_path, @@ -242,14 +440,21 @@ int main(int argc, const char **argv) options.logfile, options.module, &bootstrap_config); + + bootstrap_config_property = NULL; } else { bootstrap_context_init_from_file( options.bootstrap_config_path, options.bootstrap_selector, + &bootstrap_config_property, &bootstrap_config); + + log_property_tree(bootstrap_config_property); } - avs_initialize(&bootstrap_config); + /* AVS */ + + avs_initialize(&bootstrap_config, &avs_config_property); bootstrap_context_post_avs_setup(&bootstrap_config); @@ -257,9 +462,9 @@ int main(int argc, const char **argv) if (options.iat_hook_dlls.nitems > 0) { module_context_init_with_iat_hooks( - &module, options.module, &options.iat_hook_dlls); + &module, bootstrap_config.startup.module_file, &options.iat_hook_dlls); } else { - module_context_init(&module, options.module); + module_context_init(&module, bootstrap_config.startup.module_file); } /* Load hook DLLs */ @@ -270,110 +475,43 @@ int main(int argc, const char **argv) stubs_init(); - /* Prepare ea3 config */ - - log_misc("Preparing ea3 configuration..."); - - ea3_config = boot_property_load_avs(bootstrap_config.startup.eamuse_config_file); - ea3_config_root = property_search(ea3_config, 0, "/ea3"); - - if (ea3_config_root == NULL) { - log_fatal("%s: /ea3 missing", bootstrap_config.startup.eamuse_config_file); - } - - if (path_exists(options.ea3_ident_path)) { - log_info("%s: loading override", options.ea3_ident_path); - struct property *ea3_ident = boot_property_load(options.ea3_ident_path); - struct property_node *node = - property_search(ea3_ident, NULL, "/ea3_conf"); - if (node == NULL) { - log_fatal("%s: /ea3_conf missing", options.ea3_ident_path); - } - - for (node = property_node_traversal(node, TRAVERSE_FIRST_CHILD); node; - node = property_node_traversal(node, TRAVERSE_NEXT_SIBLING)) { - property_node_clone(NULL, ea3_config_root, node, TRUE); - } - - boot_property_free(ea3_ident); - } - - 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 */ + /* More configuration setup */ - if (bootstrap_config.module_params) { - app_config_root = bootstrap_config.module_params; - } else if (path_exists(options.app_config_path)) { - app_config = boot_property_load_avs(options.app_config_path); - app_config_root = property_search(app_config, 0, "/param"); - } else { - log_warning( - "%s: app config file missing, using empty", - options.app_config_path); - app_config = boot_property_load_cstring("dummy"); - app_config_root = property_search(app_config, 0, "/param"); - } + ea3_ident_config_setup( + options.ea3_ident_path, + options.softid, + options.pcbid, + &ea3_ident); + + app_config_setup( + &bootstrap_config, + options.app_config_path, + &app_config_property, + &app_config_node); - if (app_config_root == NULL) { - log_fatal("%s: /param missing", options.app_config_path); - } + /* Opportunity for breakpoint setup etc */ 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); - } - - if (app_config) { - boot_property_free(app_config); - } + /* Initialize of game module */ - ea3_ident_to_property(&ea3, ea3_config); + invoke_dll_module_init(&ea3_ident, &module, app_config_node); - 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); + /* Start up e-Amusement client */ - ea3_ident_replace_property_str( - ea3_config_root, "/network/services", options.override_service); - } + ea3_config_setup( + &ea3_ident, + bootstrap_config.startup.eamuse_config_file, + options.override_urlslash_enabled, + options.override_urlslash_value, + options.override_service, + &ea3_config_property); - /* Start up e-Amusement client */ + log_property_tree(ea3_config_property); - ea3_boot(ea3_config_root); - boot_property_free(ea3_config); + ea3_boot(property_search(ea3_config_property, 0, "/ea3")); /* Run application */ @@ -382,11 +520,23 @@ int main(int argc, const char **argv) /* Shut down */ ea3_shutdown(); + boot_property_free(ea3_config_property); + + app_config_node = NULL; + if (app_config_property) { + boot_property_free(app_config_property); + } log_to_writer(log_writer_file, stdout); avs_context_fini(); + boot_property_free(avs_config_property); module_context_fini(&module); + + if (bootstrap_config_property) { + boot_property_free(bootstrap_config_property); + } + options_fini(&options); return EXIT_SUCCESS; From 4c15b81a4ea58122b180667617eaf48cd39f928f Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 19:12:53 +0100 Subject: [PATCH 39/72] refactor(launcher): Move property logging functions Fit better in already existing property module --- src/main/launcher/main.c | 50 +++--------------------------------- src/main/launcher/property.c | 45 ++++++++++++++++++++++++++++++++ src/main/launcher/property.h | 2 ++ 3 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 986cc628..d24b44a3 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -24,38 +24,6 @@ #include "util/os.h" #include "util/str.h" -static void log_property_node_tree_rec(struct property_node *parent_node, const char* parent_path) -{ - char cur_path[4096]; - // 256 found in AVS code as size used on property_node_name - char cur_node_name[256]; - char leaf_node_data[2048]; - struct property_node* child_node; - - // 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) { - // TODO reading ints as string seems to result in empty values when printing them? - property_node_read(parent_node, PROPERTY_TYPE_STR, leaf_node_data, sizeof(leaf_node_data)); - - log_misc("%s: %s", cur_path, leaf_node_data); - } else { - while (child_node) { - log_property_node_tree_rec(child_node, cur_path); - - child_node = property_node_traversal(child_node, TRAVERSE_NEXT_SIBLING); - } - } -} - static void log_avs_fs_dir(const char *path) { const char* name; @@ -81,16 +49,6 @@ static void log_avs_fs_dir(const char *path) avs_fs_closedir(dir); } -static void log_property_tree(struct property *property) -{ - log_property_node_tree_rec(property_search(property, NULL, "/"), ""); -} - -static void log_property_node_tree(struct property_node *parent_node) -{ - log_property_node_tree_rec(parent_node, ""); -} - static void log_launcher_and_env_info() { char buffer_tmp[MAX_PATH]; @@ -149,7 +107,7 @@ static void avs_initialize( bootstrap_config_update_avs(bootstrap_config, avs_config_node); - log_property_tree(*avs_config_property); + boot_property_log(*avs_config_property); avs_context_init( *avs_config_property, @@ -304,7 +262,7 @@ void invoke_dll_module_init( std_setenv("/env/profile/soft_id_code", sidcode_long); - log_property_node_tree(app_config); + boot_property_node_log(app_config); ok = module_context_invoke_init(module, sidcode_short, app_config); @@ -449,7 +407,7 @@ int main(int argc, const char **argv) &bootstrap_config_property, &bootstrap_config); - log_property_tree(bootstrap_config_property); + boot_property_log(bootstrap_config_property); } /* AVS */ @@ -509,7 +467,7 @@ int main(int argc, const char **argv) options.override_service, &ea3_config_property); - log_property_tree(ea3_config_property); + boot_property_log(ea3_config_property); ea3_boot(property_search(ea3_config_property, 0, "/ea3")); diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index a6b1c836..8823c582 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -10,6 +10,7 @@ #include "util/log.h" #include "util/mem.h" +#include "util/str.h" typedef void (*rewinder)(uint32_t context); @@ -56,6 +57,50 @@ static void boot_property_frewind(uint32_t context) rewind(f); } +static void boot_property_log_node_tree_rec( + struct property_node *parent_node, + const char* parent_path) +{ + char cur_path[4096]; + // 256 found in AVS code as size used on property_node_name + char cur_node_name[256]; + char leaf_node_data[2048]; + struct property_node* child_node; + + // 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) { + // TODO reading ints as string seems to result in empty values when printing them? + property_node_read(parent_node, PROPERTY_TYPE_STR, leaf_node_data, sizeof(leaf_node_data)); + + log_misc("%s: %s", cur_path, leaf_node_data); + } else { + while (child_node) { + boot_property_log_node_tree_rec(child_node, cur_path); + + child_node = property_node_traversal(child_node, TRAVERSE_NEXT_SIBLING); + } + } +} + +void boot_property_log(struct property *property) +{ + boot_property_log_node_tree_rec(property_search(property, NULL, "/"), ""); +} + +void boot_property_node_log(struct property_node *node) +{ + boot_property_log_node_tree_rec(node, ""); +} + struct property *boot_property_load(const char *filename) { FILE *f; diff --git a/src/main/launcher/property.h b/src/main/launcher/property.h index e230b9cb..61ca6e92 100644 --- a/src/main/launcher/property.h +++ b/src/main/launcher/property.h @@ -3,6 +3,8 @@ #include "imports/avs.h" +void boot_property_log(struct property *property); +void boot_property_node_log(struct property_node *node); struct property *boot_property_load(const char *filename); struct property *boot_property_load_avs(const char *filename); struct property *boot_property_load_cstring(const char *cstring); From c03f1d544f876f51268eb5141e464bc005a489e9 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 19:29:22 +0100 Subject: [PATCH 40/72] fix(launcher): Debug printing of property (node) structures Consider the different node types and print them accordingly. Skip a couple that are (currently) less relevant and require more effort implementing in a reasonable maner, e.g. binary data. --- src/imports/import_32_1101_avs.def | 1 + src/imports/import_32_1304_avs.def | 1 + src/imports/import_32_1403_avs.def | 1 + src/imports/import_32_1508_avs.def | 1 + src/imports/import_32_1601_avs.def | 1 + src/imports/import_32_1603_avs.def | 1 + src/imports/import_32_1700_avs.def | 1 + src/imports/import_64_1508_avs.def | 1 + src/imports/import_64_1509_avs.def | 1 + src/imports/import_64_1601_avs.def | 1 + src/imports/import_64_1603_avs.def | 1 + src/imports/import_64_1700_avs.def | 1 + src/main/launcher/property.c | 57 +++++++++++++++++++++++++++--- 13 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index 99475974..97728ab0 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -32,6 +32,7 @@ EXPORTS property_node_read @573 NONAME property_node_refer @278 NONAME property_node_remove @279 NONAME + property_node_type @573 NONAME property_node_clone @280 NONAME property_node_traversal @282 NONAME property_psmap_import @288 NONAME diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index c8351c4c..8742a244 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -31,6 +31,7 @@ EXPORTS property_node_read @2 NONAME property_node_refer @268 NONAME property_node_remove @129 NONAME + property_node_type @329 NONAME property_node_clone @130 NONAME property_node_traversal @93 NONAME property_psmap_import @102 NONAME diff --git a/src/imports/import_32_1403_avs.def b/src/imports/import_32_1403_avs.def index 8e2f3214..ec2ac3ec 100644 --- a/src/imports/import_32_1403_avs.def +++ b/src/imports/import_32_1403_avs.def @@ -27,6 +27,7 @@ EXPORTS property_node_name @573 NONAME property_node_read @573 NONAME property_node_remove @148 NONAME + property_node_type @573 NONAME property_node_clone @149 NONAME property_node_traversal @151 NONAME property_psmap_import @163 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index 7695752f..dad547ba 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -31,6 +31,7 @@ EXPORTS property_node_read @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME + property_node_type @573 NONAME property_node_clone @147 NONAME property_node_traversal @149 NONAME property_psmap_export @162 NONAME diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index efbf7ab0..7856a59f 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -23,6 +23,7 @@ EXPORTS property_node_create @142 NONAME property_node_name @573 NONAME property_node_remove @143 NONAME + property_node_type @573 NONAME property_node_clone @144 NONAME property_node_traversal @146 NONAME property_node_read @573 NONAME diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index ddf08990..3dadd42f 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -23,6 +23,7 @@ EXPORTS property_node_create @163 NONAME property_node_name @573 NONAME property_node_remove @164 NONAME + property_node_type @573 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME property_node_read @573 NONAME diff --git a/src/imports/import_32_1700_avs.def b/src/imports/import_32_1700_avs.def index abf50d58..d21ce753 100644 --- a/src/imports/import_32_1700_avs.def +++ b/src/imports/import_32_1700_avs.def @@ -25,6 +25,7 @@ EXPORTS property_node_create @163 NONAME property_node_name @573 NONAME property_node_remove @164 NONAME + property_node_type @573 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME property_node_read @573 NONAME diff --git a/src/imports/import_64_1508_avs.def b/src/imports/import_64_1508_avs.def index 9c1bbbde..c906ebf6 100644 --- a/src/imports/import_64_1508_avs.def +++ b/src/imports/import_64_1508_avs.def @@ -31,6 +31,7 @@ EXPORTS property_node_read @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME + property_node_type @573 NONAME property_node_clone @147 NONAME property_node_traversal @149 NONAME property_psmap_export @162 NONAME diff --git a/src/imports/import_64_1509_avs.def b/src/imports/import_64_1509_avs.def index 8fd72ebf..4513fabc 100644 --- a/src/imports/import_64_1509_avs.def +++ b/src/imports/import_64_1509_avs.def @@ -31,6 +31,7 @@ EXPORTS property_node_read @573 NONAME property_node_refer @158 NONAME property_node_remove @146 NONAME + property_node_type @573 NONAME property_node_clone @147 NONAME property_node_traversal @149 NONAME property_psmap_export @162 NONAME diff --git a/src/imports/import_64_1601_avs.def b/src/imports/import_64_1601_avs.def index d3219d53..cd749a13 100644 --- a/src/imports/import_64_1601_avs.def +++ b/src/imports/import_64_1601_avs.def @@ -23,6 +23,7 @@ EXPORTS property_node_create @142 NONAME property_node_name @573 NONAME property_node_remove @143 NONAME + property_node_type @573 NONAME property_node_clone @144 NONAME property_node_traversal @146 NONAME property_node_read @573 NONAME diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index 0de69f2b..7fc98711 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -23,6 +23,7 @@ EXPORTS property_node_create @163 NONAME property_node_name @573 NONAME property_node_remove @164 NONAME + property_node_type @573 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME property_node_read @573 NONAME diff --git a/src/imports/import_64_1700_avs.def b/src/imports/import_64_1700_avs.def index ecf110de..bea09857 100644 --- a/src/imports/import_64_1700_avs.def +++ b/src/imports/import_64_1700_avs.def @@ -25,6 +25,7 @@ EXPORTS property_node_create @163 NONAME property_node_name @573 NONAME property_node_remove @164 NONAME + property_node_type @573 NONAME property_node_clone @165 NONAME property_node_traversal @167 NONAME property_node_read @573 NONAME diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index 8823c582..b075fff3 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -64,8 +64,12 @@ static void boot_property_log_node_tree_rec( char cur_path[4096]; // 256 found in AVS code as size used on property_node_name char cur_node_name[256]; - char leaf_node_data[2048]; + char leaf_node_data_str[2048]; + int64_t leaf_node_data_dec_s; + uint64_t leaf_node_data_dec_u; + struct property_node* child_node; + enum property_type property_type; // Carry on the full root path down the node tree property_node_name(parent_node, cur_node_name, sizeof(cur_node_name)); @@ -78,10 +82,53 @@ static void boot_property_log_node_tree_rec( // parent node is a leaf node, print all data of it if (child_node == NULL) { - // TODO reading ints as string seems to result in empty values when printing them? - property_node_read(parent_node, PROPERTY_TYPE_STR, leaf_node_data, sizeof(leaf_node_data)); - - log_misc("%s: %s", cur_path, leaf_node_data); + property_type = property_node_type(parent_node); + + switch (property_type) { + case PROPERTY_TYPE_VOID: + log_misc("%s: ", cur_path); + break; + + case PROPERTY_TYPE_S8: + case PROPERTY_TYPE_S16: + case PROPERTY_TYPE_S32: + case PROPERTY_TYPE_S64: + property_node_read(parent_node, property_type, &leaf_node_data_dec_s, sizeof(leaf_node_data_dec_s)); + log_misc("%s: %lld", cur_path, leaf_node_data_dec_s); + break; + + case PROPERTY_TYPE_U8: + case PROPERTY_TYPE_U16: + case PROPERTY_TYPE_U32: + case PROPERTY_TYPE_U64: + property_node_read(parent_node, property_type, &leaf_node_data_dec_u, sizeof(leaf_node_data_dec_u)); + log_misc("%s: %llu", cur_path, leaf_node_data_dec_u); + break; + + case PROPERTY_TYPE_STR: + property_node_read(parent_node, property_type, leaf_node_data_str, sizeof(leaf_node_data_str)); + log_misc("%s: %s", cur_path, leaf_node_data_str); + + break; + + case PROPERTY_TYPE_BOOL: + property_node_read(parent_node, property_type, &leaf_node_data_dec_s, sizeof(leaf_node_data_dec_s)); + log_misc("%s: %d", cur_path, leaf_node_data_dec_s != 0); + + break; + + case PROPERTY_TYPE_BIN: + log_misc("%s: ", cur_path); + break; + + case PROPERTY_TYPE_ATTR: + log_misc("%s: ", cur_path); + break; + + default: + log_misc("%s: ", cur_path); + break; + } } else { while (child_node) { boot_property_log_node_tree_rec(child_node, cur_path); From 16fe7f1b642b8f7afa89e933499247799986cd31 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 19:35:30 +0100 Subject: [PATCH 41/72] refactor(launcher): Inline avs_init func Have the configuration setup separate to streamline with how other parts, e.g. ea3, app params, are handling it. --- src/main/launcher/avs-context.c | 2 +- src/main/launcher/main.c | 34 ++++++++++++++++----------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index d41c75b1..12ac86ec 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -226,7 +226,7 @@ void avs_context_init( } #endif - log_misc("Calling avs_boot"); + log_info("Calling avs_boot"); #ifdef AVS_HAS_STD_HEAP avs_boot( diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index d24b44a3..e7b4a203 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -87,7 +87,7 @@ static void trap_remote_debugger() } } -static void avs_initialize( +static void avs_config_setup( struct bootstrap_config *bootstrap_config, struct property **avs_config_property) { @@ -96,8 +96,6 @@ static void avs_initialize( log_assert(bootstrap_config); log_assert(avs_config_property); - log_misc("AVS initialize..."); - *avs_config_property = boot_property_load(bootstrap_config->startup.avs_config_file); avs_config_node = property_search(*avs_config_property, 0, "/config"); @@ -106,20 +104,6 @@ static void avs_initialize( } bootstrap_config_update_avs(bootstrap_config, avs_config_node); - - boot_property_log(*avs_config_property); - - avs_context_init( - *avs_config_property, - avs_config_node, - bootstrap_config->startup.avs_heap_size, - bootstrap_config->startup.std_heap_size, - bootstrap_config->startup.log_file); - - log_misc("AVS logger available, switching to AVS loggers"); - - log_to_external( - log_body_misc, log_body_info, log_body_warning, log_body_fatal); } static void load_hook_dlls(struct array *hook_dlls) @@ -412,7 +396,21 @@ int main(int argc, const char **argv) /* AVS */ - avs_initialize(&bootstrap_config, &avs_config_property); + avs_config_setup(&bootstrap_config, &avs_config_property); + + boot_property_log(avs_config_property); + + avs_context_init( + avs_config_property, + property_search(avs_config_property, 0, "/config"), + bootstrap_config.startup.avs_heap_size, + bootstrap_config.startup.std_heap_size, + bootstrap_config.startup.log_file); + + log_misc("AVS logger available, switching to AVS loggers"); + + log_to_external( + log_body_misc, log_body_info, log_body_warning, log_body_fatal); bootstrap_context_post_avs_setup(&bootstrap_config); From 62cf7fd73988a6a8282e49e8e14cd153088e4a03 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 19:38:17 +0100 Subject: [PATCH 42/72] feat(launcher): Guard property logging with options flag Avoids too much noise in the logs and it's also not good to have this enabled by default with log level misc as the property configurations contain sensitive data/credentials that we should not log "by accident". Most users won't be aware of that. --- src/main/launcher/main.c | 22 +++++++++++++++++----- src/main/launcher/options.c | 11 +++++++++++ src/main/launcher/options.h | 1 + 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index e7b4a203..86eccfac 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -246,8 +246,6 @@ void invoke_dll_module_init( std_setenv("/env/profile/soft_id_code", sidcode_long); - boot_property_node_log(app_config); - ok = module_context_invoke_init(module, sidcode_short, app_config); if (!ok) { @@ -391,14 +389,20 @@ int main(int argc, const char **argv) &bootstrap_config_property, &bootstrap_config); - boot_property_log(bootstrap_config_property); + if (options.log_property_configs) { + log_misc("Property bootstrap-config"); + boot_property_log(bootstrap_config_property); + } } /* AVS */ avs_config_setup(&bootstrap_config, &avs_config_property); - boot_property_log(avs_config_property); + if (options.log_property_configs) { + log_misc("Property avs-config"); + boot_property_log(avs_config_property); + } avs_context_init( avs_config_property, @@ -453,6 +457,11 @@ int main(int argc, const char **argv) /* Initialize of game module */ + if (options.log_property_configs) { + log_misc("Property app-config"); + boot_property_node_log(app_config_node); + } + invoke_dll_module_init(&ea3_ident, &module, app_config_node); /* Start up e-Amusement client */ @@ -465,7 +474,10 @@ int main(int argc, const char **argv) options.override_service, &ea3_config_property); - boot_property_log(ea3_config_property); + if (options.log_property_configs) { + log_misc("Property ea3-config"); + boot_property_log(ea3_config_property); + } ea3_boot(property_search(ea3_config_property, 0, "/ea3")); diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index 5e50528c..54205fcf 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -26,6 +26,7 @@ void options_init(struct options *options) options->pcbid = NULL; options->module = NULL; options->logfile = NULL; + options->log_property_configs = false; options->remote_debugger = false; array_init(&options->hook_dlls); array_init(&options->before_hook_dlls); @@ -178,6 +179,11 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv) break; + case 'L': + options->log_property_configs = true; + + break; + case 'S': if (i + 1 >= argc) { return false; @@ -261,6 +267,11 @@ void options_print_usage(void) "before execution" "(can be specified multiple times)\n" " -Y [filename] Log to a file in addition to the console\n" + " -L 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." " -D Halt the launcher before bootstrapping AVS " "until a" " remote debugger is attached\n"); diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index b962046a..8c153f32 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -21,6 +21,7 @@ struct options { const char *pcbid; const char *module; const char *logfile; + bool log_property_configs; struct array hook_dlls; struct array before_hook_dlls; struct array iat_hook_dlls; From 719ef0067f2a15b4994c85e3fdf42582d4ce241e Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 19:55:29 +0100 Subject: [PATCH 43/72] refactor(launcher): Move replacing property nodes to property module --- src/main/launcher/ea3-ident.c | 30 +----------------------------- src/main/launcher/ea3-ident.h | 6 +----- src/main/launcher/main.c | 10 +++++++--- src/main/launcher/property.c | 34 ++++++++++++++++++++++++++++++++++ src/main/launcher/property.h | 4 ++++ 5 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/main/launcher/ea3-ident.c b/src/main/launcher/ea3-ident.c index 0c421ab9..03052e58 100644 --- a/src/main/launcher/ea3-ident.c +++ b/src/main/launcher/ea3-ident.c @@ -91,32 +91,4 @@ void ea3_ident_to_property( } 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); -} +} \ No newline at end of file diff --git a/src/main/launcher/ea3-ident.h b/src/main/launcher/ea3-ident.h index fef05b7f..029408da 100644 --- a/src/main/launcher/ea3-ident.h +++ b/src/main/launcher/ea3-ident.h @@ -35,9 +35,5 @@ bool ea3_ident_from_property( void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident); 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 diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 86eccfac..2d899b3c 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -304,7 +304,8 @@ static void ea3_config_setup( log_misc( "Overriding url_slash to: %d", override_urlslash_value); - ea3_ident_replace_property_bool( + boot_property_node_replace_bool( + *ea3_config_property, ea3_config_node, "/network/url_slash", override_urlslash_value); @@ -313,8 +314,11 @@ static void ea3_config_setup( if (service_url) { log_misc("Overriding service url to: %s", service_url); - ea3_ident_replace_property_str( - ea3_config_node, "/network/services", service_url); + boot_property_node_replace_str( + *ea3_config_property, + ea3_config_node, + "/network/services", + service_url); } } diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index b075fff3..aa25bfaa 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -258,6 +258,40 @@ struct property *boot_property_load_avs(const char *filename) return prop; } +void boot_property_node_replace_bool( + struct property *property, + struct property_node *node, + const char *name, + uint8_t val) +{ + struct property_node *tmp; + + tmp = property_search(property, node, name); + + if (tmp) { + property_node_remove(tmp); + } + + property_node_create(property, node, PROPERTY_TYPE_BOOL, name, val); +} + +void boot_property_node_replace_str( + struct property *property, + struct property_node *node, + const char *name, + const char *val) +{ + struct property_node *tmp; + + tmp = property_search(property, node, name); + + if (tmp) { + property_node_remove(tmp); + } + + property_node_create(property, node, PROPERTY_TYPE_STR, name, val); +} + void boot_property_free(struct property *prop) { void *buffer; diff --git a/src/main/launcher/property.h b/src/main/launcher/property.h index 61ca6e92..6c016358 100644 --- a/src/main/launcher/property.h +++ b/src/main/launcher/property.h @@ -8,6 +8,10 @@ void boot_property_node_log(struct property_node *node); struct property *boot_property_load(const char *filename); struct property *boot_property_load_avs(const char *filename); struct property *boot_property_load_cstring(const char *cstring); +void boot_property_node_replace_bool(struct property *property, + struct property_node *node, const char *name, uint8_t val); +void boot_property_node_replace_str(struct property *property, + struct property_node *node, const char *name, const char *val); void boot_property_free(struct property *prop); #endif From 6a531be6680bf8b94e050b08bd85c057e2ba6252 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 20:49:44 +0100 Subject: [PATCH 44/72] feat(launcher): AVS dev/raw and dev/nvram override command line opt --- src/main/launcher/avs-context.c | 56 +++++++++++++++++++++++++++++++++ src/main/launcher/avs-context.h | 3 ++ src/main/launcher/main.c | 21 ++++++++++++- src/main/launcher/options.c | 20 +++++++++--- src/main/launcher/options.h | 1 + 5 files changed, 95 insertions(+), 6 deletions(-) diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index 12ac86ec..f4990b3b 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -9,6 +9,7 @@ #include "imports/avs.h" #include "launcher/avs-context.h" +#include "launcher/property.h" #include "util/codepage.h" #include "util/fs.h" @@ -189,6 +190,61 @@ static void _avs_context_create_config_fs_dir( } } +void avs_context_property_set_local_fs_nvram_raw( + struct property *config_prop, + 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; + + 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(config_prop, NULL, "config/fs"); + + if (!fs_node) { + log_fatal("Cannot find config/fs in avs config"); + } + + // Check if "new" mounttable config is used for dev/nvram and dev/raw or legacy config + if (property_search(config_prop, fs_node, "mounttable")) { + property_remove(config_prop, fs_node, "mounttable"); + + mounttable_node = property_node_create(config_prop, fs_node, PROPERTY_TYPE_VOID, "mounttable"); + + vfs_node = property_node_create(config_prop, mounttable_node, PROPERTY_TYPE_VOID, "vfs"); + + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot"); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs"); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_raw); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/raw"); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1"); + + vfs_node = property_node_create(config_prop, mounttable_node, PROPERTY_TYPE_VOID, "vfs"); + + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot"); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs"); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_nvram); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/nvram"); + property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1"); + } else { + boot_property_node_replace_str(config_prop, fs_node, "nvram/device", path_dev_raw); + boot_property_node_replace_str(config_prop, fs_node, "nvram/fstype", "fs"); + boot_property_node_replace_str(config_prop, fs_node, "nvram/option", "vf=1,posix=1"); + + boot_property_node_replace_str(config_prop, fs_node, "raw/device", path_dev_nvram); + boot_property_node_replace_str(config_prop, fs_node, "raw/fstype", "fs"); + boot_property_node_replace_str(config_prop, fs_node, "raw/option", "vf=1,posix=1"); + } +} + void avs_context_init( struct property *config_prop, struct property_node *config_node, diff --git a/src/main/launcher/avs-context.h b/src/main/launcher/avs-context.h index a296fd82..3d0a0900 100644 --- a/src/main/launcher/avs-context.h +++ b/src/main/launcher/avs-context.h @@ -15,6 +15,9 @@ void avs_context_init( uint32_t avs_heap_size, uint32_t std_heap_size, const char* path_logfile); +void avs_context_property_set_local_fs_nvram_raw( + struct property *config_prop, + const char* dev_nvram_raw_path); void avs_context_fini(void); #endif diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 2d899b3c..ac8d4333 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -89,6 +89,7 @@ static void trap_remote_debugger() static void avs_config_setup( struct bootstrap_config *bootstrap_config, + const char* dev_nvram_raw_path, struct property **avs_config_property) { struct property_node *avs_config_node; @@ -103,6 +104,21 @@ static void avs_config_setup( log_fatal("%s: /config missing", bootstrap_config->startup.avs_config_file); } + if (dev_nvram_raw_path) { + if (!path_exists(dev_nvram_raw_path)) { + log_warning("Override local file system dev/nvram and dev/raw path %s does not exist, creating", + dev_nvram_raw_path); + + if (!path_mkdir(dev_nvram_raw_path)) { + log_fatal("Creating directory %s failed", dev_nvram_raw_path); + } + } + + avs_context_property_set_local_fs_nvram_raw( + *avs_config_property, + dev_nvram_raw_path); + } + bootstrap_config_update_avs(bootstrap_config, avs_config_node); } @@ -401,7 +417,10 @@ int main(int argc, const char **argv) /* AVS */ - avs_config_setup(&bootstrap_config, &avs_config_property); + avs_config_setup( + &bootstrap_config, + options.avs_fs_dev_nvram_raw_path, + &avs_config_property); if (options.log_property_configs) { log_misc("Property avs-config"); diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index 54205fcf..44f11fae 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -22,6 +22,7 @@ void options_init(struct options *options) options->avs_config_path = "prop/avs-config.xml"; options->ea3_config_path = "prop/ea3-config.xml"; options->ea3_ident_path = "prop/ea3-ident.xml"; + options->avs_fs_dev_nvram_raw_path = NULL; options->softid = NULL; options->pcbid = NULL; options->module = NULL; @@ -89,6 +90,15 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv) break; + case 'F': + if (i + 1 >= argc) { + return false; + } + + options->avs_fs_dev_nvram_raw_path = argv[++i]; + + break; + case 'P': if (i + 1 >= argc) { return false; @@ -255,6 +265,8 @@ void options_print_usage(void) #ifdef AVS_HAS_STD_HEAP " -T [bytes] 'std' heap size (default 16777216)\n" #endif + " -F [path] Specify a local file system path to point " + "dev/nvram and dev/raw to (default: use avs config)\n" " -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" @@ -264,17 +276,15 @@ void options_print_usage(void) " -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" " -Y [filename] Log to a file in addition to the console\n" " -L 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." + "this information before redaction.\n" " -D Halt the launcher before bootstrapping AVS " - "until a" - " remote debugger is attached\n"); + "until a remote debugger is attached\n"); } void options_fini(struct options *options) diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 8c153f32..77d890d6 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -17,6 +17,7 @@ struct options { const char *avs_config_path; const char *ea3_config_path; const char *ea3_ident_path; + const char *avs_fs_dev_nvram_raw_path; const char *softid; const char *pcbid; const char *module; From 4617ba1bcd879197d23d5d10e3c7eb46324ab926 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 21:14:59 +0100 Subject: [PATCH 45/72] feat(launcher): Command line opt to set log level for all loggers --- src/main/launcher/avs-context.c | 58 ++++++++++++++++++++++++++++++++- src/main/launcher/avs-context.h | 3 ++ src/main/launcher/main.c | 13 ++++++++ src/main/launcher/options.c | 22 +++++++++++-- src/main/launcher/options.h | 3 ++ 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index f4990b3b..b056060c 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -13,7 +13,6 @@ #include "util/codepage.h" #include "util/fs.h" -#include "util/log.h" #include "util/mem.h" #include "util/str.h" @@ -245,6 +244,63 @@ void avs_context_property_set_local_fs_nvram_raw( } } +void avs_context_property_set_log_level(struct property *config_prop, enum log_level loglevel) +{ + struct property_node *log_level_node; + enum property_type type; + + log_level_node = property_search(config_prop, NULL, "config/log/level"); + + if (!log_level_node) { + log_fatal("config/log/level missing in AVS configuration"); + } + + type = property_node_type(log_level_node); + + // Different AVS config formats depending on AVS version, detect based on the existing values + switch (type) { + case PROPERTY_TYPE_STR: + const char *loglevel_str; + + switch (loglevel) { + case LOG_LEVEL_FATAL: + loglevel_str = "fatal"; + break; + + case LOG_LEVEL_WARNING: + loglevel_str = "warn"; + break; + + case LOG_LEVEL_INFO: + loglevel_str = "info"; + break; + + case LOG_LEVEL_MISC: + loglevel_str = "misc"; + break; + + default: + log_fatal("Unsupported log level: %d", loglevel); + break; + } + + property_node_remove(log_level_node); + property_node_create(config_prop, log_level_node, PROPERTY_TYPE_STR, NULL, loglevel_str); + + break; + + case PROPERTY_TYPE_U32: + property_node_remove(log_level_node); + property_node_create(config_prop, log_level_node, PROPERTY_TYPE_U32, NULL, loglevel); + + break; + + default: + log_fatal("Unsupported property type %d for config/log/level node in AVS config", type); + break; + } +} + void avs_context_init( struct property *config_prop, struct property_node *config_node, diff --git a/src/main/launcher/avs-context.h b/src/main/launcher/avs-context.h index 3d0a0900..6688839f 100644 --- a/src/main/launcher/avs-context.h +++ b/src/main/launcher/avs-context.h @@ -5,6 +5,8 @@ #include "imports/avs.h" +#include "util/log.h" + #if AVS_VERSION < 1600 #define AVS_HAS_STD_HEAP #endif @@ -18,6 +20,7 @@ void avs_context_init( void avs_context_property_set_local_fs_nvram_raw( struct property *config_prop, const char* dev_nvram_raw_path); +void avs_context_property_set_log_level(struct property *config_prop, enum log_level loglevel); void avs_context_fini(void); #endif diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index ac8d4333..d063ae2e 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -90,6 +90,8 @@ static void trap_remote_debugger() static void avs_config_setup( struct bootstrap_config *bootstrap_config, const char* dev_nvram_raw_path, + bool override_loglevel_enabled, + enum log_level loglevel, struct property **avs_config_property) { struct property_node *avs_config_node; @@ -119,6 +121,10 @@ static void avs_config_setup( dev_nvram_raw_path); } + if (override_loglevel_enabled) { + avs_context_property_set_log_level(*avs_config_property, loglevel); + } + bootstrap_config_update_avs(bootstrap_config, avs_config_node); } @@ -376,6 +382,11 @@ int main(int argc, const char **argv) return EXIT_FAILURE; } + // Enforce user configured log level + if (options.override_loglevel_enabled) { + log_set_level(options.loglevel); + } + /* 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 */ @@ -420,6 +431,8 @@ int main(int argc, const char **argv) avs_config_setup( &bootstrap_config, options.avs_fs_dev_nvram_raw_path, + options.override_loglevel_enabled, + options.loglevel, &avs_config_property); if (options.log_property_configs) { diff --git a/src/main/launcher/options.c b/src/main/launcher/options.c index 44f11fae..051244aa 100644 --- a/src/main/launcher/options.c +++ b/src/main/launcher/options.c @@ -6,8 +6,6 @@ #include "launcher/options.h" -#include "util/array.h" -#include "util/log.h" #include "util/str.h" #define DEFAULT_HEAP_SIZE 16777216 @@ -26,6 +24,8 @@ void options_init(struct options *options) options->softid = NULL; options->pcbid = NULL; options->module = NULL; + options->override_loglevel_enabled = false; + options->loglevel = LOG_LEVEL_INFO; options->logfile = NULL; options->log_property_configs = false; options->remote_debugger = false; @@ -180,6 +180,22 @@ bool options_read_cmdline(struct options *options, int argc, const char **argv) break; } + case 'N': + if (i + 1 >= argc) { + return false; + } + + long tmp = strtol(argv[++i], NULL, 0); + + if (tmp < LOG_LEVEL_FATAL || tmp > LOG_LEVEL_MISC) { + return false; + } + + options->loglevel = (enum log_level) tmp; + options->override_loglevel_enabled = true; + + break; + case 'Y': if (i + 1 >= argc) { return false; @@ -277,6 +293,8 @@ void options_print_usage(void) "(can be specified multiple times)\n" " -I [filename] Load pre-hook DLL that overrides IAT reference " "before execution (can be specified multiple times)\n" + " -N [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" " -L Log all loaded and final (property) " "configuration that launcher uses for bootstrapping. IMPORTANT: DO NOT " diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 77d890d6..28605f7b 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -5,6 +5,7 @@ #include #include "util/array.h" +#include "util/log.h" #include "launcher/bs-config.h" @@ -21,6 +22,8 @@ struct options { const char *softid; const char *pcbid; const char *module; + bool override_loglevel_enabled; + enum log_level loglevel; const char *logfile; bool log_property_configs; struct array hook_dlls; From 725959340309ed9e52214157811a3d926e023345 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 22:48:20 +0100 Subject: [PATCH 46/72] feat(launcher): Add dedicated logging backend This handles all logging, similar to inject (copy-pasted from there). It merges the log streams of launcher and AVS to enable: - All log messages go to a single log file - Considers setting up logging as early as possible, prior AVS - Bonus log coloring on all log messages to console to support debugging efforts --- src/main/launcher/Module.mk | 1 + src/main/launcher/avs-context.c | 59 +------ src/main/launcher/avs-context.h | 3 +- src/main/launcher/logger.c | 295 ++++++++++++++++++++++++++++++++ src/main/launcher/logger.h | 22 +++ src/main/launcher/main.c | 33 +--- 6 files changed, 337 insertions(+), 76 deletions(-) create mode 100644 src/main/launcher/logger.c create mode 100644 src/main/launcher/logger.h diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index 8f89518e..914633f4 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -17,6 +17,7 @@ src_launcher := \ bootstrap-context.c \ bs-config.c \ ea3-ident.c \ + logger.c \ main.c \ module.c \ options.c \ diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index b056060c..c66c6571 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -9,6 +9,7 @@ #include "imports/avs.h" #include "launcher/avs-context.h" +#include "launcher/logger.h" #include "launcher/property.h" #include "util/codepage.h" @@ -22,8 +23,6 @@ static void *avs_heap; static void *std_heap; #endif -static HANDLE _avs_context_logfile; - /* Gratuitous API changes orz */ static AVS_LOG_WRITER(_avs_context_log_writer, chars, nchars, ctx) { @@ -32,9 +31,6 @@ static AVS_LOG_WRITER(_avs_context_log_writer, chars, nchars, ctx) int utf16_len; int utf8_len; int result; - DWORD nwritten; - HANDLE console; - HANDLE file; /* Ignore existing NUL terminator */ @@ -63,7 +59,7 @@ static AVS_LOG_WRITER(_avs_context_log_writer, chars, nchars, ctx) abort(); } - utf8 = xmalloc(utf8_len + 2); + utf8 = xmalloc(utf8_len + 3); result = WideCharToMultiByte( CP_UTF8, 0, utf16, utf16_len, utf8, utf8_len, NULL, NULL); @@ -78,16 +74,11 @@ static AVS_LOG_WRITER(_avs_context_log_writer, chars, nchars, ctx) 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); - } + // Clean string terminate + utf8[utf8_len] = '\0'; - WriteFile(console, utf8, utf8_len, &nwritten, NULL); + // Write to launcher's dedicated logging backend + logger_log_avs_log_message(utf8, utf8_len); /* Clean up */ @@ -95,31 +86,6 @@ static AVS_LOG_WRITER(_avs_context_log_writer, chars, nchars, ctx) free(utf16); } -static HANDLE _avs_context_create_logfile(const char *path) -{ - HANDLE handle; - - if (path == NULL) { - log_misc("No logfile path specified, logging to file disabled"); - handle = INVALID_HANDLE_VALUE; - } else { - handle = CreateFileA( - path, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - CREATE_ALWAYS, - 0, - NULL); - - if (handle == INVALID_HANDLE_VALUE) { - log_warning("Creating log file %s failed: %lX", path, GetLastError()); - } - } - - return handle; -} - static void _avs_context_create_config_fs_dir( struct property *prop, struct property_node *node, @@ -305,11 +271,8 @@ void avs_context_init( struct property *config_prop, struct property_node *config_node, uint32_t avs_heap_size, - uint32_t std_heap_size, - const char* path_logfile) + uint32_t std_heap_size) { - _avs_context_logfile = _avs_context_create_logfile(path_logfile); - log_misc("Creating AVS file system directories for nvram and raw if not exist..."); // create nvram and raw directories if possible for non-mounttable configurations @@ -348,10 +311,10 @@ void avs_context_init( avs_heap, avs_heap_size, _avs_context_log_writer, - _avs_context_logfile); + NULL); #else /* AVS v2.16.xx and I suppose onward uses a unified heap */ - avs_boot(config_node, avs_heap, avs_heap_size, NULL, _avs_context_log_writer, _avs_context_logfile); + avs_boot(config_node, avs_heap, avs_heap_size, NULL, _avs_context_log_writer, NULL); #endif } @@ -359,10 +322,6 @@ void avs_context_fini(void) { avs_shutdown(); - if (_avs_context_logfile != INVALID_HANDLE_VALUE) { - CloseHandle(_avs_context_logfile); - } - #ifdef AVS_HAS_STD_HEAP VirtualFree(std_heap, 0, MEM_RELEASE); #endif diff --git a/src/main/launcher/avs-context.h b/src/main/launcher/avs-context.h index 6688839f..82184987 100644 --- a/src/main/launcher/avs-context.h +++ b/src/main/launcher/avs-context.h @@ -15,8 +15,7 @@ void avs_context_init( struct property *config_prop, struct property_node *config_node, uint32_t avs_heap_size, - uint32_t std_heap_size, - const char* path_logfile); + uint32_t std_heap_size); void avs_context_property_set_local_fs_nvram_raw( struct property *config_prop, const char* dev_nvram_raw_path); diff --git a/src/main/launcher/logger.c b/src/main/launcher/logger.c new file mode 100644 index 00000000..83a64005 --- /dev/null +++ b/src/main/launcher/logger.c @@ -0,0 +1,295 @@ +#define LOG_MODULE "launcher-logger" + +#include +#include +#include +#include +#include +#include + +#include "launcher/logger.h" +#include "launcher/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_console_avs(const char *chars) +{ + char color; + size_t color_len; + // See "util/log.c", has to align + char buffer[65536]; + char tmp; + const char* timestamp; + const char* msg; + + static const size_t timestamp_len = strlen("[----/--/-- --:--:--]"); + + timestamp = chars; + msg = timestamp + timestamp_len + 1; // +1 is the space + + color_len = logger_msg_coloring_len(msg); + + // Check if we could detect which part to color, otherwise just write the + // whole log message without any coloring logic + if (color_len > 0) { + color = logger_console_determine_color(msg); + + strcpy(buffer, msg); + + // Mask start of log message for coloring + tmp = buffer[color_len]; + buffer[color_len] = '\0'; + + printf("%.21s ", timestamp); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); + printf("%s", buffer); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); + + // Write actual message non colored + buffer[color_len] = tmp; + printf("%s\n", buffer + color_len); + } else { + printf("%s\n", chars); + } +} + +static void logger_file( + void *ctx, const char *chars, size_t nchars, const char *timestamp_str) +{ + if (ctx) { + fwrite(timestamp_str, 1, strlen(timestamp_str), (FILE *) ctx); + fwrite(chars, 1, nchars, (FILE *) ctx); + fflush((FILE *) ctx); + } +} + +static void logger_file_avs(const char *chars, size_t len) +{ + if (log_file) { + fwrite(chars, 1, len, log_file); + fflush(log_file); + } +} + +static void logger_writer(void *ctx, const char *chars, size_t nchars) +{ + const char *timestamp_str; + + // Different threads logging the same destination, e.g. debugger thread, + // main thread + + WaitForSingleObject(log_mutex, INFINITE); + + timestamp_str = logger_get_formatted_timestamp(); + + logger_console(ctx, chars, nchars, timestamp_str); + logger_file(ctx, chars, nchars, timestamp_str); + + ReleaseMutex(log_mutex); +} + +static void logger_log_header() +{ + log_info( + "\n" + " .__ .__ \n" + " | | _____ __ __ ____ ____ | |__ ___________ \n" + " | | \\__ \\ | | \\/ \\_/ ___\\| | \\_/ __ \\_ __ \\ \n" + " | |__/ __ \\| | / | \\ \\___| Y \\ ___/| | \\/ \n" + " |____(____ /____/|___| /\\___ >___| /\\___ >__| \n" + " \\/ \\/ \\/ \\/ \\/ "); + + log_info( + "launcher build date %s, gitrev %s", + launcher_build_date, + launcher_gitrev); +} + +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_avs_log_message(char *str, size_t len) +{ + bool use_crlf; + +#if AVS_VERSION >= 1500 + use_crlf = true; +#else + use_crlf = false; +#endif + + // Different threads logging the same destination, e.g. debugger thread, + // main thread + + WaitForSingleObject(log_mutex, INFINITE); + + // Write to file first, tokenizing trashes the string + logger_file_avs(str, len); + + // The character stream provided here can contain multiple lines as AVS + // manages buffered writes to the logger backend. We need to split them + // up for the logging backend to allow for further enhancements + + char *line = strtok(str, use_crlf ? "\r\n" : "\n"); + + while (line) { + logger_console_avs(line); + line = strtok(NULL, use_crlf ? "\r\n" : "\n"); + } + + ReleaseMutex(log_mutex); +} + +void logger_finit() +{ + log_misc("Logger finit"); + + if (log_file) { + fclose(log_file); + } + + CloseHandle(log_mutex); +} \ No newline at end of file diff --git a/src/main/launcher/logger.h b/src/main/launcher/logger.h new file mode 100644 index 00000000..6efcc5bf --- /dev/null +++ b/src/main/launcher/logger.h @@ -0,0 +1,22 @@ +#include + +/** + * Initialize launcher's early logger backend (before AVS's logger is available). + * + * @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 log message from AVS to the logging backend. + * + * @param str String to log + * @param len Total length of the log string + */ +void logger_log_avs_log_message(char *str, size_t len); + +/** + * Shutdown and cleanup the logging backend. + */ +void logger_finit(); \ No newline at end of file diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index d063ae2e..f67d55e7 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -12,11 +12,11 @@ #include "launcher/bootstrap-context.h" #include "launcher/bs-config.h" #include "launcher/ea3-ident.h" +#include "launcher/logger.h" #include "launcher/module.h" #include "launcher/options.h" #include "launcher/property.h" #include "launcher/stubs.h" -#include "launcher/version.h" #include "util/defs.h" #include "util/fs.h" @@ -49,14 +49,10 @@ static void log_avs_fs_dir(const char *path) avs_fs_closedir(dir); } -static void log_launcher_and_env_info() +static void log_env_info() { char buffer_tmp[MAX_PATH]; - log_info( - "launcher build date %s, gitrev %s", - launcher_build_date, - launcher_gitrev); os_version_log(); getcwd(buffer_tmp, sizeof(buffer_tmp)); @@ -362,17 +358,6 @@ int main(int argc, const char **argv) struct property *ea3_config_property; - /* Static logging setup prior AVS available */ - - log_to_writer(log_writer_file, stdout); - log_set_level(LOG_LEVEL_MISC); - - log_launcher_and_env_info(); - - /* Command line (override) options */ - - log_misc("Reading command line options..."); - options_init(&options); if (!options_read_cmdline(&options, argc, argv) || @@ -382,11 +367,17 @@ int main(int argc, const char **argv) return EXIT_FAILURE; } + /* Static logging setup prior AVS available */ + + logger_init(options.logfile); + // Enforce user configured log level if (options.override_loglevel_enabled) { log_set_level(options.loglevel); } + log_env_info(); + /* 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 */ @@ -444,13 +435,7 @@ int main(int argc, const char **argv) avs_config_property, property_search(avs_config_property, 0, "/config"), bootstrap_config.startup.avs_heap_size, - bootstrap_config.startup.std_heap_size, - bootstrap_config.startup.log_file); - - log_misc("AVS logger available, switching to AVS loggers"); - - log_to_external( - log_body_misc, log_body_info, log_body_warning, log_body_fatal); + bootstrap_config.startup.std_heap_size); bootstrap_context_post_avs_setup(&bootstrap_config); From 87037ee98301425dc807f3e5a5673947d2dccb54 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 19 Nov 2023 23:03:30 +0100 Subject: [PATCH 47/72] fix(launcher): ea3-ident copying to /ea3 config node --- src/main/launcher/ea3-ident.c | 29 ++++++++++++++++++----------- src/main/launcher/property.c | 4 ++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/launcher/ea3-ident.c b/src/main/launcher/ea3-ident.c index 03052e58..feed5d28 100644 --- a/src/main/launcher/ea3-ident.c +++ b/src/main/launcher/ea3-ident.c @@ -12,14 +12,14 @@ #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_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, softid, "/id/softid", "") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, hardid, "/id/hardid", "") +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct ea3_ident, pcbid, "/id/pcbid", "") +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, model, "/soft/model") +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, dest, "/soft/dest") +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, spec, "/soft/spec") +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, rev, "/soft/rev") +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct ea3_ident, ext, "/soft/ext") PSMAP_END void ea3_ident_init(struct ea3_ident *ident) @@ -44,7 +44,7 @@ void ea3_ident_initialize_from_file( log_fatal("%s: /ea3_conf missing", path); } - if (!property_psmap_import(property, NULL, ea3_ident, ea3_ident_psmap)) { + if (!property_psmap_import(property, node, ea3_ident, ea3_ident_psmap)) { log_fatal( "%s: Error reading IDs from config file", path); } @@ -79,16 +79,23 @@ void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident) void ea3_ident_to_property( const struct ea3_ident *ident, struct property *ea3_config) { + struct property_node *ea3_node; struct property_node *node; int i; + ea3_node = property_search(ea3_config, NULL, "/ea3"); + + if (ea3_node == NULL) { + log_fatal("ea3 config is missing /ea3 node"); + } + for (i = 0; ea3_ident_psmap[i].type != 0xFF; i++) { - node = property_search(ea3_config, 0, ea3_ident_psmap[i].path); + node = property_search(ea3_config, ea3_node, ea3_ident_psmap[i].path); if (node != NULL) { property_node_remove(node); } } - property_psmap_export(ea3_config, NULL, ident, ea3_ident_psmap); + property_psmap_export(ea3_config, ea3_node, ident, ea3_ident_psmap); } \ No newline at end of file diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index aa25bfaa..72e9a1bb 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -1,3 +1,5 @@ +#define LOG_MODULE "launcher-property" + #include #include @@ -275,6 +277,8 @@ void boot_property_node_replace_bool( property_node_create(property, node, PROPERTY_TYPE_BOOL, name, val); } + + void boot_property_node_replace_str( struct property *property, struct property_node *node, From 115a620fce512d0eada453aca6e0503dc76da04b Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 14:35:06 +0100 Subject: [PATCH 48/72] feat(launcher): Distinguish u8 vs. bool for different AVS version Older AVS versions did not support the bool type, yet, and used u8 instead. This needs to be considered in order to avoid memory corruptions with reading/writing property (maps) that lead to odd application crashes --- src/main/launcher/property.c | 19 +++++++++++++++++-- src/main/launcher/property.h | 4 +++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index 72e9a1bb..7f352e33 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -260,7 +260,7 @@ struct property *boot_property_load_avs(const char *filename) return prop; } -void boot_property_node_replace_bool( +void boot_property_node_replace_u8( struct property *property, struct property_node *node, const char *name, @@ -274,10 +274,25 @@ void boot_property_node_replace_bool( property_node_remove(tmp); } - property_node_create(property, node, PROPERTY_TYPE_BOOL, name, val); + property_node_create(property, node, PROPERTY_TYPE_U8, name, val); } +void boot_property_node_replace_bool( + struct property *property, + struct property_node *node, + const char *name, + bool val) +{ + struct property_node *tmp; + + tmp = property_search(property, node, name); + + if (tmp) { + property_node_remove(tmp); + } + property_node_create(property, node, PROPERTY_TYPE_BOOL, name, val); +} void boot_property_node_replace_str( struct property *property, diff --git a/src/main/launcher/property.h b/src/main/launcher/property.h index 6c016358..99ef8ea5 100644 --- a/src/main/launcher/property.h +++ b/src/main/launcher/property.h @@ -8,8 +8,10 @@ void boot_property_node_log(struct property_node *node); struct property *boot_property_load(const char *filename); struct property *boot_property_load_avs(const char *filename); struct property *boot_property_load_cstring(const char *cstring); -void boot_property_node_replace_bool(struct property *property, +void boot_property_node_replace_u8(struct property *property, struct property_node *node, const char *name, uint8_t val); +void boot_property_node_replace_bool(struct property *property, + struct property_node *node, const char *name, bool val); void boot_property_node_replace_str(struct property *property, struct property_node *node, const char *name, const char *val); void boot_property_free(struct property *prop); From f6ab9f3e9b943f340041cbc344bf799996cc7b88 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 14:36:15 +0100 Subject: [PATCH 49/72] fix(launcher): Property debug printing, respect data types/sizes Caused various data to be logged/printed incorrectly due to incorrect data sizes resulting in incorrect memory reads --- src/main/launcher/property.c | 78 +++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index 7f352e33..e8993da1 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -66,9 +66,6 @@ static void boot_property_log_node_tree_rec( char cur_path[4096]; // 256 found in AVS code as size used on property_node_name char cur_node_name[256]; - char leaf_node_data_str[2048]; - int64_t leaf_node_data_dec_s; - uint64_t leaf_node_data_dec_u; struct property_node* child_node; enum property_type property_type; @@ -91,31 +88,75 @@ static void boot_property_log_node_tree_rec( log_misc("%s: ", cur_path); break; - case PROPERTY_TYPE_S8: - case PROPERTY_TYPE_S16: + case PROPERTY_TYPE_S8: + int8_t value_s8; + + property_node_read(parent_node, property_type, &value_s8, sizeof(value_s8)); + log_misc("%s: %d", cur_path, value_s8); + break; + + case PROPERTY_TYPE_S16: + int16_t value_s16; + + property_node_read(parent_node, property_type, &value_s16, sizeof(value_s16)); + log_misc("%s: %d", cur_path, value_s16); + break; + case PROPERTY_TYPE_S32: + int32_t value_s32; + + property_node_read(parent_node, property_type, &value_s32, sizeof(value_s32)); + log_misc("%s: %d", cur_path, value_s32); + break; + case PROPERTY_TYPE_S64: - property_node_read(parent_node, property_type, &leaf_node_data_dec_s, sizeof(leaf_node_data_dec_s)); - log_misc("%s: %lld", cur_path, leaf_node_data_dec_s); + int64_t value_s64; + + property_node_read(parent_node, property_type, &value_s64, sizeof(value_s64)); + log_misc("%s: %lld", cur_path, value_s64); break; - case PROPERTY_TYPE_U8: - case PROPERTY_TYPE_U16: - case PROPERTY_TYPE_U32: + case PROPERTY_TYPE_U8: + uint8_t value_u8; + + property_node_read(parent_node, property_type, &value_u8, sizeof(value_u8)); + log_misc("%s: %u", cur_path, value_u8); + break; + + case PROPERTY_TYPE_U16: + uint16_t value_u16; + + property_node_read(parent_node, property_type, &value_u16, sizeof(value_u16)); + log_misc("%s: %u", cur_path, value_u16); + break; + + case PROPERTY_TYPE_U32: + uint32_t value_u32; + + property_node_read(parent_node, property_type, &value_u32, sizeof(value_u32)); + log_misc("%s: %u", cur_path, value_u32); + break; + case PROPERTY_TYPE_U64: - property_node_read(parent_node, property_type, &leaf_node_data_dec_u, sizeof(leaf_node_data_dec_u)); - log_misc("%s: %llu", cur_path, leaf_node_data_dec_u); + uint64_t value_u64; + + property_node_read(parent_node, property_type, &value_u64, sizeof(value_u64)); + log_misc("%s: %llu", cur_path, value_u64); break; case PROPERTY_TYPE_STR: - property_node_read(parent_node, property_type, leaf_node_data_str, sizeof(leaf_node_data_str)); - log_misc("%s: %s", cur_path, leaf_node_data_str); + char value_str[4096]; + + 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, &leaf_node_data_dec_s, sizeof(leaf_node_data_dec_s)); - log_misc("%s: %d", cur_path, leaf_node_data_dec_s != 0); + bool value_bool; + + property_node_read(parent_node, property_type, &value_bool, sizeof(value_bool)); + log_misc("%s: %d", cur_path, value_bool); break; @@ -128,7 +169,7 @@ static void boot_property_log_node_tree_rec( break; default: - log_misc("%s: ", cur_path); + log_misc("%s: (%d)", cur_path, property_type); break; } } else { @@ -247,7 +288,8 @@ struct property *boot_property_load_avs(const char *filename) avs_desc desc; struct property *prop; - desc = avs_fs_open(filename, AVS_FILE_READ, AVS_FILE_FLAG_SHARE_READ); + // TODO changing mode from read mode to 0 makes DDRX3 reading the ea3-config work, otherwise it fails querying it + desc = avs_fs_open(filename, 0, AVS_FILE_FLAG_SHARE_READ); if (!desc) { log_fatal("%s: Error opening configuration file", filename); } From 763f4bf9d8d7cef0f35c4623c1bb0ca9cae4d62a Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 14:42:22 +0100 Subject: [PATCH 50/72] fix(launcher/avs): Add untyped type to avs defs, dispatch in property log As stated in the comment, having not __type attribute attached, AVS (at least 2.13.6) returns a different value and doesn't treat this as a string by default. Call this "untyped" for now to be able to handle it as a string read on the logging function. --- src/imports/avs.h | 4 +++- src/main/launcher/property.c | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/imports/avs.h b/src/imports/avs.h index ab1bdd6a..a3beff24 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -39,7 +39,9 @@ enum property_type { PROPERTY_TYPE_BIN = 10, PROPERTY_TYPE_STR = 11, PROPERTY_TYPE_ATTR = 46, - PROPERTY_TYPE_BOOL = 52 + PROPERTY_TYPE_BOOL = 52, + // Missing __type attribute + PROPERTY_TYPE_UNTYPED = 65, }; struct property; diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index e8993da1..df14b63d 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -168,6 +168,15 @@ static void boot_property_log_node_tree_rec( log_misc("%s: ", cur_path); break; + // Treat as string + case PROPERTY_TYPE_UNTYPED: + char value_untyped[4096]; + + property_node_read(parent_node, property_type, value_untyped, sizeof(value_untyped)); + log_misc("%s: %s", cur_path, value_untyped); + + break; + default: log_misc("%s: (%d)", cur_path, property_type); break; From 0a2a71bf0d14032187266d454aacd751513eed27 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 14:44:34 +0100 Subject: [PATCH 51/72] fix(launcher): Incorrect "does exist" check causing crash --- src/main/launcher/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index f67d55e7..74bc42b4 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -165,11 +165,11 @@ static void ea3_ident_config_setup( ea3_ident_initialize_from_file(ea3_ident_path, ea3_ident); } - if (!softid) { + if (softid) { str_cpy(ea3_ident->softid, lengthof(ea3_ident->softid), softid); } - if (!pcbid) { + if (pcbid) { str_cpy(ea3_ident->pcbid, lengthof(ea3_ident->pcbid), pcbid); } From 97794b51dff6c1afebcf87768517d80b0b6b8b3c Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 14:45:28 +0100 Subject: [PATCH 52/72] chore(launcher): Add more logging statements, improve debugability --- src/main/launcher/main.c | 13 +++++++++++++ src/main/launcher/stubs.c | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 74bc42b4..6c7b9009 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -131,6 +131,8 @@ static void load_hook_dlls(struct array *hook_dlls) for (size_t i = 0; i < hook_dlls->nitems; i++) { hook_dll = *array_item(char *, hook_dlls, i); + log_info("Load hook dll: %s", hook_dll); + if (LoadLibraryA(hook_dll) == NULL) { LPSTR buffer; @@ -162,6 +164,7 @@ static void ea3_ident_config_setup( ea3_ident_init(ea3_ident); if (ea3_ident_path) { + log_misc("Loading ea3-ident from file: %s", ea3_ident_path); ea3_ident_initialize_from_file(ea3_ident_path, ea3_ident); } @@ -192,6 +195,8 @@ static void app_config_setup( *app_config_property = NULL; *app_config_node = bootstrap_config->module_params; } else if (app_config_path && path_exists(app_config_path)) { + log_misc("Loading avs-config from file: %s", app_config_path); + *app_config_property = boot_property_load_avs(app_config_path); *app_config_node = property_search(*app_config_property, 0, "/param"); @@ -309,6 +314,8 @@ static void ea3_config_setup( log_misc("Preparing ea3 configuration..."); + log_misc("Loading ea3-config from file: %s", eamuse_config_file); + *ea3_config_property = boot_property_load_avs(eamuse_config_file); ea3_config_node = property_search(*ea3_config_property, 0, "/ea3"); @@ -441,6 +448,8 @@ int main(int argc, const char **argv) /* Load game DLL */ + log_info("Load game DLL: %s", bootstrap_config.startup.module_file); + if (options.iat_hook_dlls.nitems > 0) { module_context_init_with_iat_hooks( &module, bootstrap_config.startup.module_file, &options.iat_hook_dlls); @@ -508,6 +517,8 @@ int main(int argc, const char **argv) /* Shut down */ + log_info("Shutting down launcher..."); + ea3_shutdown(); boot_property_free(ea3_config_property); @@ -528,5 +539,7 @@ int main(int argc, const char **argv) options_fini(&options); + log_info("Shutdown complete"); + return EXIT_SUCCESS; } diff --git a/src/main/launcher/stubs.c b/src/main/launcher/stubs.c index a524273b..0a3f9a95 100644 --- a/src/main/launcher/stubs.c +++ b/src/main/launcher/stubs.c @@ -116,6 +116,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)); } From 8dab550c38cdfd22655a0de7eb0e91dd004fe969 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 14:45:53 +0100 Subject: [PATCH 53/72] fix(launcher): Use different types on avs-config on different AVS versions See the comment in the changeset explaining the problem --- src/main/launcher/bs-config.c | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bs-config.c index 2d380acf..63038b97 100644 --- a/src/main/launcher/bs-config.c +++ b/src/main/launcher/bs-config.c @@ -277,6 +277,43 @@ bool bootstrap_config_from_property( void bootstrap_config_update_avs( const struct bootstrap_config *config, struct property_node *avs_root) { +// Different AVS version generations changed the property types in the avs-config.xml slightly +// which needs to be considered to avoid property map reading/write to cause memory corruption +// which naturally lead to hard to debug application failures +// Furthermore, some attributes didn't exist on older versions + +// TODO fix AVS version here, must be 1306 for DDR X3 but is wrong everywhere right now >_< +#if AVS_VERSION <= 1304 + if (config->module_params) { + property_remove(NULL, avs_root, "mode/product"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_U8, "mode/product", 1); + property_remove(NULL, avs_root, "net/enable_raw"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_U8, "net/enable_raw", 1); + property_remove(NULL, avs_root, "net/eaudp/enable"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_U8, "net/eaudp/enable", 1); + property_remove(NULL, avs_root, "sntp/ea_on"); + property_node_create( + NULL, avs_root, PROPERTY_TYPE_U8, "sntp/ea_on", 1); + } + + if (config->startup.drm_device[0]) { + property_remove(NULL, avs_root, "fs/root/device"); + property_node_create( + NULL, + avs_root, + PROPERTY_TYPE_STR, + "fs/root/device", + config->startup.drm_device); + } + + if (config->log_node) { + property_remove(NULL, avs_root, "log"); + property_node_clone(NULL, avs_root, config->log_node, TRUE); + } +#else if (config->module_params) { property_remove(NULL, avs_root, "mode/product"); property_node_create( @@ -291,6 +328,7 @@ void bootstrap_config_update_avs( property_node_create( NULL, avs_root, PROPERTY_TYPE_BOOL, "sntp/ea_on", 1); } + if (config->startup.drm_device[0]) { property_remove(NULL, avs_root, "fs/root/device"); property_node_create( @@ -300,10 +338,12 @@ void bootstrap_config_update_avs( "fs/root/device", config->startup.drm_device); } + if (config->log_node) { property_remove(NULL, avs_root, "log"); property_node_clone(NULL, avs_root, config->log_node, TRUE); } +#endif } bool bootstrap_config_iter_default_file( From 40facd4b71e26ec97c6855d87ca14a4c9d8eabbd Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 15:10:55 +0100 Subject: [PATCH 54/72] chore(launcher): Improve resilience Assert data that is expected not to be NULL --- src/main/launcher/avs-context.c | 3 +++ src/main/launcher/main.c | 14 +++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index c66c6571..0560c191 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -273,6 +273,9 @@ void avs_context_init( uint32_t avs_heap_size, uint32_t std_heap_size) { + log_assert(config_prop); + log_assert(config_node); + log_misc("Creating AVS file system directories for nvram and raw if not exist..."); // create nvram and raw directories if possible for non-mounttable configurations diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 6c7b9009..cd5cf832 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -332,7 +332,7 @@ static void ea3_config_setup( boot_property_node_replace_bool( *ea3_config_property, ea3_config_node, - "/network/url_slash", + "network/url_slash", override_urlslash_value); } @@ -342,7 +342,7 @@ static void ea3_config_setup( boot_property_node_replace_str( *ea3_config_property, ea3_config_node, - "/network/services", + "network/services", service_url); } } @@ -364,6 +364,7 @@ int main(int argc, const char **argv) struct property_node *app_config_node; struct property *ea3_config_property; + struct property_node *ea3_config_node; options_init(&options); @@ -509,8 +510,14 @@ int main(int argc, const char **argv) boot_property_log(ea3_config_property); } - ea3_boot(property_search(ea3_config_property, 0, "/ea3")); + log_info("Booting ea3..."); + + ea3_config_node = property_search(ea3_config_property, 0, "/ea3"); + log_assert(ea3_config_node); + + ea3_boot(ea3_config_node); + /* Run application */ module_context_invoke_main(&module); @@ -520,6 +527,7 @@ int main(int argc, const char **argv) log_info("Shutting down launcher..."); ea3_shutdown(); + ea3_config_node = NULL; boot_property_free(ea3_config_property); app_config_node = NULL; From 6b7cfc4d7883ce6b83ac17ef97744ca489f038c3 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 16:12:45 +0100 Subject: [PATCH 55/72] chore(launcher/avs): Add terminator psmap type 0xff value terminates a psmap definition, improves readability --- src/imports/avs.h | 3 ++- src/main/launcher/ea3-ident.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/imports/avs.h b/src/imports/avs.h index a3beff24..b0a18def 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -70,6 +70,7 @@ enum psmap_type { PSMAP_TYPE_STR = 10, PSMAP_TYPE_ATTR = 45, PSMAP_TYPE_BOOL = 50, + PSMAP_TYPE_TERMINATOR = 0xFF, }; #define PSMAP_FLAG_HAVE_DEFAULT 0x01 @@ -106,7 +107,7 @@ struct property_psmap { #define PSMAP_END \ { \ - 0xFF, 0, 0, 0, NULL, 0 \ + PSMAP_TYPE_TERMINATOR, 0, 0, 0, NULL, 0 \ } \ } \ ; diff --git a/src/main/launcher/ea3-ident.c b/src/main/launcher/ea3-ident.c index feed5d28..0dc7304f 100644 --- a/src/main/launcher/ea3-ident.c +++ b/src/main/launcher/ea3-ident.c @@ -89,7 +89,7 @@ void ea3_ident_to_property( log_fatal("ea3 config is missing /ea3 node"); } - for (i = 0; ea3_ident_psmap[i].type != 0xFF; i++) { + for (i = 0; ea3_ident_psmap[i].type != PSMAP_TYPE_TERMINATOR; i++) { node = property_search(ea3_config, ea3_node, ea3_ident_psmap[i].path); if (node != NULL) { From f3aad704662599dc2b6856d8624ad54b18a775a3 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 26 Nov 2023 17:13:49 +0100 Subject: [PATCH 56/72] fix(avs): Incorrect AVS version for ddr-13 The correct one to use is 2.13.06 and not .04. Furthermore, the ordinal of property_node_clone was incorrect which has resulted in odd crashes when using launcher with ddr-13 All other avs API calls were matching and fine though regarding their ordinal mappings --- Module.mk | 8 ++--- src/imports/import_32_1306_avs-ea3.def | 5 +++ src/imports/import_32_1306_avs.def | 42 ++++++++++++++++++++++++++ src/main/launcher/bs-config.c | 4 +-- 4 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 src/imports/import_32_1306_avs-ea3.def create mode 100644 src/imports/import_32_1306_avs.def diff --git a/Module.mk b/Module.mk index f525b5d1..4ad823fb 100644 --- a/Module.mk +++ b/Module.mk @@ -80,7 +80,7 @@ cflags += \ # Each AVS-dependent project should consume the earliest AVS import definition # that is still ABI-compatible with the real build its target links against. -avsvers_32 := 1700 1603 1601 1508 1403 1304 1101 1002 803 0 +avsvers_32 := 1700 1603 1601 1508 1403 1306 1304 1101 1002 803 0 avsvers_64 := 1700 1603 1601 1509 1508 imps += avs avs-ea3 @@ -685,9 +685,9 @@ $(zipdir)/ddr-12.zip: \ $(V)zip -j $@ $^ $(zipdir)/ddr-13.zip: \ - build/bin/avs2_1304-32/launcher.exe \ - build/bin/avs2_1304-32/ddrhook2.dll \ - build/bin/avs2_1304-32/unicorntail.dll \ + build/bin/avs2_1306-32/launcher.exe \ + build/bin/avs2_1306-32/ddrhook2.dll \ + build/bin/avs2_1306-32/unicorntail.dll \ build/bin/indep-32/config.exe \ build/bin/indep-32/ddrio.dll \ build/bin/indep-32/ddrio-mm.dll \ diff --git a/src/imports/import_32_1306_avs-ea3.def b/src/imports/import_32_1306_avs-ea3.def new file mode 100644 index 00000000..2606209e --- /dev/null +++ b/src/imports/import_32_1306_avs-ea3.def @@ -0,0 +1,5 @@ +LIBRARY libavs-win32-ea3 + +EXPORTS + ea3_boot @94 NONAME + ea3_shutdown @97 NONAME diff --git a/src/imports/import_32_1306_avs.def b/src/imports/import_32_1306_avs.def new file mode 100644 index 00000000..8e39a98e --- /dev/null +++ b/src/imports/import_32_1306_avs.def @@ -0,0 +1,42 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_boot @237 NONAME == XC058ba50000f4 + avs_fs_close @276 NONAME == XC058ba500011b + avs_fs_copy @283 NONAME == XC058ba5000122 + avs_fs_lseek @16 NONAME == XC058ba500000f + avs_fs_lstat @97 NONAME == XC058ba5000063 + avs_fs_open @178 NONAME == XC058ba50000b6 + avs_fs_read @306 NONAME == XC058ba5000139 + avs_fs_opendir @216 NONAME == XC058ba50000dd + avs_fs_readdir @130 NONAME == XC058ba5000086 + avs_fs_closedir @131 NONAME == XC058ba5000087 + avs_net_ctrl @15 NONAME == XC058ba500000e + avs_shutdown @333 NONAME == XC058ba5000154 + avs_thread_create @183 NONAME == XC058ba50000bb + avs_thread_destroy @76 NONAME == XC058ba500004e + avs_thread_exit @147 NONAME == XC058ba5000097 + avs_thread_join @92 NONAME == XC058ba500005e + log_body_misc @44 NONAME == XC058ba500002d + log_body_info @339 NONAME == XC058ba500015a + log_body_warning @219 NONAME == XC058ba50000e1 + log_body_fatal @128 NONAME == XC058ba5000084 + property_create @256 NONAME == XC058ba5000107 + property_desc_to_buffer @201 NONAME == XC058ba50000cd + property_destroy @264 NONAME == XC058ba500010f + property_insert_read @23 NONAME == XC058ba5000016 + property_node_create @316 NONAME == XC058ba5000143 + property_node_datasize @249 NONAME == XC058ba5000100 + property_node_name @255 NONAME == XC058ba5000106 + property_node_read @2 NONAME == XC058ba5000001 + property_node_refer @268 NONAME == XC058ba5000113 + property_node_remove @129 NONAME == XC058ba5000085 + property_node_type @329 NONAME == XC058ba5000150 + property_node_clone @252 NONAME == XC058ba5000103 + property_node_traversal @93 NONAME == XC058ba500005f + property_psmap_import @102 NONAME == XC058ba5000068 + property_psmap_export @110 NONAME == XC058ba5000071 + property_read_query_memsize @100 NONAME == XC058ba5000066 + property_search @244 NONAME == XC058ba50000fb + std_getenv @226 NONAME == XC058ba50000e8 + std_setenv @114 NONAME == XC058ba5000075 diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bs-config.c index 63038b97..9b927169 100644 --- a/src/main/launcher/bs-config.c +++ b/src/main/launcher/bs-config.c @@ -281,9 +281,7 @@ void bootstrap_config_update_avs( // which needs to be considered to avoid property map reading/write to cause memory corruption // which naturally lead to hard to debug application failures // Furthermore, some attributes didn't exist on older versions - -// TODO fix AVS version here, must be 1306 for DDR X3 but is wrong everywhere right now >_< -#if AVS_VERSION <= 1304 +#if AVS_VERSION <= 1306 if (config->module_params) { property_remove(NULL, avs_root, "mode/product"); property_node_create( From dc2dd7b9a5c620b03dbdfd8a5392a715f012b2b5 Mon Sep 17 00:00:00 2001 From: icex2 Date: Thu, 30 Nov 2023 22:17:03 +0100 Subject: [PATCH 57/72] fix: Imports for AVS 2.15.8 and 2.16.3 Replace placeholder 573 ordinals with ones sourced from actual binaries to make ddr 14-18 work with launcher --- src/imports/import_32_1508_avs.def | 10 +++++----- src/imports/import_32_1603_avs.def | 24 ++++++++++++------------ src/imports/import_64_1603_avs.def | 24 ++++++++++++------------ 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index dad547ba..a36816b3 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -9,9 +9,9 @@ EXPORTS avs_fs_open @58 NONAME avs_fs_read @61 NONAME avs_fs_copy @81 NONAME - avs_fs_opendir @573 NONAME - avs_fs_readdir @573 NONAME - avs_fs_closedir @573 NONAME + avs_fs_opendir @72 NONAME == XCd229cc0000f0 + avs_fs_readdir @73 NONAME == XCd229cc0000bb + avs_fs_closedir @74 NONAME == XCd229cc0000b8 avs_net_ctrl @98 NONAME avs_shutdown @286 NONAME avs_thread_create @6 NONAME @@ -28,10 +28,10 @@ EXPORTS property_insert_read @131 NONAME property_node_create @145 NONAME property_node_name @150 NONAME - property_node_read @573 NONAME + property_node_read @154 NONAME == XCd229cc0000f3 property_node_refer @158 NONAME property_node_remove @146 NONAME - property_node_type @573 NONAME + property_node_type @151 NONAME == XCd229cc000071 property_node_clone @147 NONAME property_node_traversal @149 NONAME property_psmap_export @162 NONAME diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index 3dadd42f..fb95df3c 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -1,15 +1,15 @@ LIBRARY libavs-win32 EXPORTS - avs_fs_close @573 NONAME - avs_fs_copy @573 NONAME - avs_fs_lseek @573 NONAME - avs_fs_lstat @573 NONAME - avs_fs_open @573 NONAME - avs_fs_read @573 NONAME - avs_fs_opendir @573 NONAME - avs_fs_readdir @573 NONAME - avs_fs_closedir @573 NONAME + avs_fs_close @86 NONAME == XCnbrep7000055 + avs_fs_copy @102 NONAME == XCnbrep7000065 + avs_fs_lseek @80 NONAME == XCnbrep700004f + avs_fs_lstat @100 NONAME == XCnbrep7000063 + avs_fs_open @79 NONAME == XCnbrep700004e + avs_fs_read @82 NONAME == XCnbrep7000051 + avs_fs_opendir @93 NONAME == XCnbrep700005c + avs_fs_readdir @94 NONAME == XCnbrep700005d + avs_fs_closedir @95 NONAME == XCnbrep700005e avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME @@ -21,12 +21,12 @@ EXPORTS property_insert_read @149 NONAME property_search @162 NONAME property_node_create @163 NONAME - property_node_name @573 NONAME + property_node_name @168 NONAME == XCnbrep70000a7 property_node_remove @164 NONAME - property_node_type @573 NONAME + property_node_type @169 NONAME == XCnbrep70000a8 property_node_clone @165 NONAME property_node_traversal @167 NONAME - property_node_read @573 NONAME + property_node_read @172 NONAME == XCnbrep70000ab property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index 7fc98711..c6c06695 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -1,15 +1,15 @@ LIBRARY libavs-win64 EXPORTS - avs_fs_close @573 NONAME - avs_fs_copy @573 NONAME - avs_fs_lseek @573 NONAME - avs_fs_lstat @573 NONAME - avs_fs_open @573 NONAME - avs_fs_read @573 NONAME - avs_fs_opendir @573 NONAME - avs_fs_readdir @573 NONAME - avs_fs_closedir @573 NONAME + avs_fs_close @86 NONAME == XCnbrep7000055 + avs_fs_copy @102 NONAME == XCnbrep7000065 + avs_fs_lseek @80 NONAME == XCnbrep700004f + avs_fs_lstat @100 NONAME == XCnbrep7000063 + avs_fs_open @79 NONAME == XCnbrep700004e + avs_fs_read @82 NONAME == XCnbrep7000051 + avs_fs_opendir @93 NONAME == XCnbrep700005c + avs_fs_readdir @94 NONAME == XCnbrep700005d + avs_fs_closedir @95 NONAME == XCnbrep700005e avs_thread_create @5 NONAME avs_thread_destroy @7 NONAME avs_thread_exit @11 NONAME @@ -21,12 +21,12 @@ EXPORTS property_insert_read @149 NONAME property_search @162 NONAME property_node_create @163 NONAME - property_node_name @573 NONAME + property_node_name @168 NONAME == XCnbrep70000a7 property_node_remove @164 NONAME - property_node_type @573 NONAME + property_node_type @169 NONAME == XCnbrep70000a8 property_node_clone @165 NONAME property_node_traversal @167 NONAME - property_node_read @573 NONAME + property_node_read @172 NONAME == XCnbrep70000ab property_node_refer @176 NONAME property_read_query_memsize @177 NONAME property_psmap_import @179 NONAME From 4a85084395dab2ec3132ab3fc5161d30f121e406 Mon Sep 17 00:00:00 2001 From: icex2 Date: Thu, 30 Nov 2023 22:18:14 +0100 Subject: [PATCH 58/72] chore(launcher): Add more logging to improve debugability --- src/main/launcher/module.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/launcher/module.c b/src/main/launcher/module.c index b3beff26..8e5a7944 100644 --- a/src/main/launcher/module.c +++ b/src/main/launcher/module.c @@ -236,6 +236,7 @@ bool module_context_invoke_main(const struct module_context *module) { /* GCC warns if you call a variable "main" */ dll_entry_main_t main_; + bool result; log_assert(module != NULL); @@ -246,7 +247,13 @@ 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) From f884cc770b1b33753a05e7b718e661db613f724f Mon Sep 17 00:00:00 2001 From: icex2 Date: Thu, 30 Nov 2023 22:21:15 +0100 Subject: [PATCH 59/72] fix(launcher): AVS open mode incompatible flags See the comment in the changeset. Consider this a sort-of stop gap solution for now until there is more information/ knowledge about the mode flags. --- src/main/launcher/property.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index df14b63d..cb632caf 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -294,11 +294,24 @@ static void boot_property_avs_rewind(uint32_t context) struct property *boot_property_load_avs(const char *filename) { + uint16_t mode; avs_desc desc; struct property *prop; - // TODO changing mode from read mode to 0 makes DDRX3 reading the ea3-config work, otherwise it fails querying it - desc = avs_fs_open(filename, 0, AVS_FILE_FLAG_SHARE_READ); + // Apparently, the mode enum flags have changed, but this is currently unverified to apply + // a generic solution to avs.h, so keep this contained for now as a "hack" + // Using the wrong flag here, querying the eamuse-config.xml file fails on DDR on + // older AVS versions +#if AVS_VERSION <= 1306 + // Currently only verified with 1306 and older on DDR + mode = 0; +#else + // Currently only verified with 1508 on DDR + mode = AVS_FILE_READ; +#endif + + desc = avs_fs_open(filename, mode, AVS_FILE_FLAG_SHARE_READ); + if (!desc) { log_fatal("%s: Error opening configuration file", filename); } From ac137183149a8effc502d411681e255aff654f34 Mon Sep 17 00:00:00 2001 From: icex2 Date: Thu, 30 Nov 2023 22:22:50 +0100 Subject: [PATCH 60/72] feat(launcher): Implement ea3 enable/disable bootstrap flag --- src/main/launcher/main.c | 45 +++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index cd5cf832..74068263 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -497,27 +497,32 @@ int main(int argc, const char **argv) /* Start up e-Amusement client */ - ea3_config_setup( - &ea3_ident, - bootstrap_config.startup.eamuse_config_file, - options.override_urlslash_enabled, - options.override_urlslash_value, - options.override_service, - &ea3_config_property); + if (bootstrap_config.startup.eamuse_enable) { + ea3_config_setup( + &ea3_ident, + bootstrap_config.startup.eamuse_config_file, + options.override_urlslash_enabled, + options.override_urlslash_value, + options.override_service, + &ea3_config_property); - if (options.log_property_configs) { - log_misc("Property ea3-config"); - boot_property_log(ea3_config_property); - } + if (options.log_property_configs) { + log_misc("Property ea3-config"); + boot_property_log(ea3_config_property); + } - log_info("Booting ea3..."); + log_info("Booting ea3..."); - ea3_config_node = property_search(ea3_config_property, 0, "/ea3"); + ea3_config_node = property_search(ea3_config_property, 0, "/ea3"); - log_assert(ea3_config_node); + log_assert(ea3_config_node); + + ea3_boot(ea3_config_node); + } else { + ea3_config_property = NULL; + ea3_config_node = NULL; + } - ea3_boot(ea3_config_node); - /* Run application */ module_context_invoke_main(&module); @@ -526,9 +531,11 @@ int main(int argc, const char **argv) log_info("Shutting down launcher..."); - ea3_shutdown(); - ea3_config_node = NULL; - boot_property_free(ea3_config_property); + if (bootstrap_config.startup.eamuse_enable) { + ea3_shutdown(); + ea3_config_node = NULL; + boot_property_free(ea3_config_property); + } app_config_node = NULL; if (app_config_property) { From 580d4f064df6e69aaf1659d06bc2b04240e519cb Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 1 Dec 2023 23:00:45 +0100 Subject: [PATCH 61/72] fix(avs): avs_file_mode enum change with 2.13.06 Information provided by co-author Co-authored-by: LupinThidr --- src/imports/avs.h | 15 +++++++++++++-- src/main/launcher/property.c | 14 +------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/imports/avs.h b/src/imports/avs.h index b0a18def..4cc5c4e7 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -258,13 +258,24 @@ struct avs_stat { struct stat padding; }; +#if AVS_VERSION <= 1306 enum avs_file_mode { - AVS_FILE_READ = 1, - AVS_FILE_WRITE = 2, + AVS_FILE_READ = 0x00, + AVS_FILE_WRITE = 0x01, + AVS_FILE_READ_WRITE = 0x02, AVS_FILE_CREATE = 0x10, AVS_FILE_TRUNCATE = 0x20, AVS_FILE_EXCLUSIVE = 0x80, }; +#else +enum avs_file_mode { + AVS_FILE_READ = 0x01, + AVS_FILE_WRITE = 0x02, + AVS_FILE_CREATE = 0x10, + AVS_FILE_TRUNCATE = 0x20, + AVS_FILE_EXCLUSIVE = 0x80, +}; +#endif enum avs_file_flag { AVS_FILE_FLAG_SHARE_READ = 0x124, diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index cb632caf..938ba5c7 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -298,19 +298,7 @@ struct property *boot_property_load_avs(const char *filename) avs_desc desc; struct property *prop; - // Apparently, the mode enum flags have changed, but this is currently unverified to apply - // a generic solution to avs.h, so keep this contained for now as a "hack" - // Using the wrong flag here, querying the eamuse-config.xml file fails on DDR on - // older AVS versions -#if AVS_VERSION <= 1306 - // Currently only verified with 1306 and older on DDR - mode = 0; -#else - // Currently only verified with 1508 on DDR - mode = AVS_FILE_READ; -#endif - - desc = avs_fs_open(filename, mode, AVS_FILE_FLAG_SHARE_READ); + desc = avs_fs_open(filename, AVS_FILE_READ, AVS_FILE_FLAG_SHARE_READ); if (!desc) { log_fatal("%s: Error opening configuration file", filename); From 3f7258b3bcdd8c7d867828061ed8bcf88a05b650 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 31 Dec 2023 16:56:57 +0100 Subject: [PATCH 62/72] fix: To build with mingw version in docker build container My local mingw version is newer than that, so everything was considered fine so far. --- src/main/launcher/avs-context.c | 3 +-- src/main/launcher/property.c | 35 +++++++++++---------------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index 0560c191..5211bfaf 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -214,6 +214,7 @@ void avs_context_property_set_log_level(struct property *config_prop, enum log_l { struct property_node *log_level_node; enum property_type type; + const char *loglevel_str; log_level_node = property_search(config_prop, NULL, "config/log/level"); @@ -226,8 +227,6 @@ void avs_context_property_set_log_level(struct property *config_prop, enum log_l // Different AVS config formats depending on AVS version, detect based on the existing values switch (type) { case PROPERTY_TYPE_STR: - const char *loglevel_str; - switch (loglevel) { case LOG_LEVEL_FATAL: loglevel_str = "fatal"; diff --git a/src/main/launcher/property.c b/src/main/launcher/property.c index 938ba5c7..82cd6eff 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property.c @@ -70,6 +70,18 @@ static void boot_property_log_node_tree_rec( 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; + char value_untyped[4096]; + // Carry on the full root path down the node tree property_node_name(parent_node, cur_node_name, sizeof(cur_node_name)); @@ -89,72 +101,52 @@ static void boot_property_log_node_tree_rec( break; case PROPERTY_TYPE_S8: - int8_t value_s8; - property_node_read(parent_node, property_type, &value_s8, sizeof(value_s8)); log_misc("%s: %d", cur_path, value_s8); break; case PROPERTY_TYPE_S16: - int16_t value_s16; - property_node_read(parent_node, property_type, &value_s16, sizeof(value_s16)); log_misc("%s: %d", cur_path, value_s16); break; case PROPERTY_TYPE_S32: - int32_t value_s32; - property_node_read(parent_node, property_type, &value_s32, sizeof(value_s32)); log_misc("%s: %d", cur_path, value_s32); break; case PROPERTY_TYPE_S64: - int64_t value_s64; - property_node_read(parent_node, property_type, &value_s64, sizeof(value_s64)); log_misc("%s: %lld", cur_path, value_s64); break; case PROPERTY_TYPE_U8: - uint8_t value_u8; - property_node_read(parent_node, property_type, &value_u8, sizeof(value_u8)); log_misc("%s: %u", cur_path, value_u8); break; case PROPERTY_TYPE_U16: - uint16_t value_u16; - property_node_read(parent_node, property_type, &value_u16, sizeof(value_u16)); log_misc("%s: %u", cur_path, value_u16); break; case PROPERTY_TYPE_U32: - uint32_t value_u32; - property_node_read(parent_node, property_type, &value_u32, sizeof(value_u32)); log_misc("%s: %u", cur_path, value_u32); break; case PROPERTY_TYPE_U64: - uint64_t value_u64; - property_node_read(parent_node, property_type, &value_u64, sizeof(value_u64)); log_misc("%s: %llu", cur_path, value_u64); break; case PROPERTY_TYPE_STR: - char value_str[4096]; - 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: - bool value_bool; - property_node_read(parent_node, property_type, &value_bool, sizeof(value_bool)); log_misc("%s: %d", cur_path, value_bool); @@ -170,8 +162,6 @@ static void boot_property_log_node_tree_rec( // Treat as string case PROPERTY_TYPE_UNTYPED: - char value_untyped[4096]; - property_node_read(parent_node, property_type, value_untyped, sizeof(value_untyped)); log_misc("%s: %s", cur_path, value_untyped); @@ -294,7 +284,6 @@ static void boot_property_avs_rewind(uint32_t context) struct property *boot_property_load_avs(const char *filename) { - uint16_t mode; avs_desc desc; struct property *prop; From 4e8a56f6cdbf6300fe4afb50bab758e4c29fdd1e Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 13:25:54 +0100 Subject: [PATCH 63/72] refactor(launcher): Improve structure of bootstrap configuration Use nested structs instead of namespacing with variable names. Adds flexibility to improve modularization of launcher as bootstrap.xml's structure is inheritently providing a structure for having different modules in launcher. --- src/main/launcher/bootstrap-context.c | 18 +- src/main/launcher/bs-config.c | 247 ++++++++++++++++++-------- src/main/launcher/bs-config.h | 200 ++++++++++++--------- src/main/launcher/main.c | 20 +-- 4 files changed, 308 insertions(+), 177 deletions(-) diff --git a/src/main/launcher/bootstrap-context.c b/src/main/launcher/bootstrap-context.c index aeb6cf82..bd460478 100644 --- a/src/main/launcher/bootstrap-context.c +++ b/src/main/launcher/bootstrap-context.c @@ -23,20 +23,20 @@ void bootstrap_context_init( bootstrap_config_init(config); - str_cpy(config->startup.avs_config_file, sizeof(config->startup.avs_config_file), avs_config_path); - str_cpy(config->startup.eamuse_config_file, sizeof(config->startup.eamuse_config_file), ea3_config_path); + str_cpy(config->startup.avs.config_file, sizeof(config->startup.avs.config_file), avs_config_path); + str_cpy(config->startup.eamuse.config_file, sizeof(config->startup.eamuse.config_file), ea3_config_path); - config->startup.avs_heap_size = avs_heap_size; - config->startup.std_heap_size = std_heap_size; + config->startup.avs.avs_heap_size = avs_heap_size; + config->startup.avs.std_heap_size = std_heap_size; if (logfile) { - config->startup.log_enable_file = true; + config->startup.log.enable_file = true; - str_cpy(config->startup.log_file, sizeof(config->startup.log_file), logfile); - str_cpy(config->startup.log_name, sizeof(config->startup.log_name), logfile); + str_cpy(config->startup.log.file, sizeof(config->startup.log.file), logfile); + str_cpy(config->startup.log.name, sizeof(config->startup.log.name), logfile); } - str_cpy(config->startup.module_file, sizeof(config->startup.module_file), module); + str_cpy(config->startup.module.file, sizeof(config->startup.module.file), module); } void bootstrap_context_init_from_file( @@ -68,7 +68,7 @@ void bootstrap_context_init_from_file( void bootstrap_context_post_avs_setup(struct bootstrap_config *config) { - struct bootstrap_default_file default_file; + struct bootstrap_default_file_config default_file; struct avs_stat st; log_assert(config); diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bs-config.c index 9b927169..3c8079e2 100644 --- a/src/main/launcher/bs-config.c +++ b/src/main/launcher/bs-config.c @@ -11,153 +11,178 @@ #include "util/str.h" // clang-format off -PSMAP_BEGIN(bootstrap_startup_psmap) -PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, avs_config_file, +PSMAP_BEGIN(bootstrap_startup_avs_psmap) +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_avs_config, config_file, "boot/file") -PSMAP_REQUIRED(PSMAP_TYPE_U32, struct bootstrap_startup_config, avs_heap_size, +PSMAP_REQUIRED(PSMAP_TYPE_U32, struct bootstrap_avs_config, avs_heap_size, "boot/heap_avs") -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, std_heap_size, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_avs_config, std_heap_size, "boot/heap_std", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, mount_table_selector, +PSMAP_END + +PSMAP_BEGIN(bootstrap_startup_boot_psmap) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_boot_config, mount_table_selector, "boot/mounttable_selector", "boot") -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, watcher_enable, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_boot_config, watcher_enable, "boot/watcher", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, timemachine_enable, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_boot_config, timemachine_enable, "boot/timemachine", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, launch_config_file, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_boot_config, launch_config_file, "boot/launch_path", "/dev/raw/launch.xml") +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, log_level, +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_startup_config, log_name, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_log_config, name, "log/name", "") -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, log_file, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_log_config, file, "log/file", "") -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, log_bufsz, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_log_config, bufsz, "log/sz_buf", 4096) -PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_startup_config, log_output_delay_ms, +PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_log_config, output_delay_ms, "log/output_delay", 10) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_console, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_console, "log/enable_console", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_sci, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_sci, "log/enable_netsci", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_net, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_net, "log/enable_netlog", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_enable_file, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, enable_file, "log/enable_file", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_rotate, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, rotate, "log/rotate", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, log_append, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_log_config, append, "log/append", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_startup_config, log_count, +PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_log_config, count, "log/gen", 10) +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_startup_config, minidump_count, +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_startup_config, minidump_continue, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_minidump_config, continue_, "minidump/cont_debug", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, minidump_log, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_minidump_config, log, "minidump/echo_log", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_startup_config, minidump_type, +PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_minidump_config, type, "minidump/dump_type", 2) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, minidump_path, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_minidump_config, path, "minidump/path", "/dev/raw/minidump") -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, minidump_symbufsz, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_minidump_config, symbufsz, "minidump/sz_symbuf", 32768) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, minidump_search_path, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_minidump_config, search_path, "minidump/search", ".") +PSMAP_END -PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, module_file, +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_startup_config, module_load_type, +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_startup_config, ntdll_size, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, size, "dlml/ntdll/size", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, ntdll_ift_table, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, ift_table, "dlml/ntdll/ift_table", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, ntdll_ift_insert, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, ift_insert, "dlml/ntdll/insert_ift", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, ntdll_ift_remove, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_dlm_config, ift_remove, "dlml/ntdll/remove_ift", 0) +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, shield_enable, +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_startup_config, shield_verbose, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_shield_config, verbose, "shield/verbose", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, shield_use_loadlibrary, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_shield_config, use_loadlibrary, "shield/use_loadlibrary", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, shield_logger, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_shield_config, logger, "shield/logger", "") -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_sleep_min, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, sleep_min, "shield/sleepmin", 10) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_sleep_blur, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, sleep_blur, "shield/sleepblur", 90) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, shield_whitelist_file, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_shield_config, whitelist_file, "shield/whitelist", "prop/whitelist.csv") -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_tick_sleep, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, tick_sleep, "shield/ticksleep", 100) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_tick_error, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, tick_error, "shield/tickerror", 1000) -PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_startup_config, shield_overwork_threshold, +PSMAP_OPTIONAL(PSMAP_TYPE_U8, struct bootstrap_shield_config, overwork_threshold, "shield/overwork_threshold", 50) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_overwork_delay, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, overwork_delay, "shield/overwork_delay", 100) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, shield_pause_delay, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_shield_config, pause_delay, "shield/pause_delay", 1000) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, shield_unlimited_key, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_shield_config, unlimited_key, "shield/unlimited_key", "") -PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_startup_config, shield_killer_port, +PSMAP_OPTIONAL(PSMAP_TYPE_U16, struct bootstrap_shield_config, killer_port, "shield_killer/port", 5001) +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, dongle_license_cn, +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_startup_config, dongle_account_cn, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_dongle_config, account_cn, "dongle/account", "") -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, dongle_driver_dll, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_dongle_config, driver_dll, "dongle/pkcs11_driver", "eTPKCS11.dll") -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, dongle_disable_gc, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_dongle_config, disable_gc, "dongle/disable_gc", 0) +PSMAP_END -PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_dll, +PSMAP_BEGIN(bootstrap_startup_drm_psmap) +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_drm_config, dll, "drm/dll") -PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_fstype, +PSMAP_REQUIRED(PSMAP_TYPE_STR, struct bootstrap_drm_config, fstype, "drm/fstype") -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_device, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, device, "drm/device", "") -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_mount, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, mount, "drm/dst", "/") -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, drm_options, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_drm_config, options, "drm/option", "") +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, lte_enable, +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_startup_config, lte_config_file, +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_startup_config, lte_unlimited_key, +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_lte_config, unlimited_key, "lte/unlimited_key", "") +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, ssl_options, +PSMAP_BEGIN(bootstrap_startup_ssl_psmap) +PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_ssl_config, options, "ssl/option", "") +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, esign_enable, +PSMAP_BEGIN(bootstrap_startup_esign_psmap) +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_esign_config, enable, "esign/enable", 0) +PSMAP_END -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, eamuse_enable, +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_startup_config, eamuse_sync, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_eamuse_config, sync, "eamuse/sync", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_startup_config, eamuse_enable_model, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_eamuse_config, enable_model, "eamuse/enable_model", 0) -PSMAP_OPTIONAL(PSMAP_TYPE_STR, struct bootstrap_startup_config, eamuse_config_file, +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_startup_config, eamuse_updatecert_enable, +PSMAP_OPTIONAL(PSMAP_TYPE_BOOL, struct bootstrap_eamuse_config, updatecert_enable, "eamuse/updatecert_enable", 1) -PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_startup_config, eamuse_updatecert_interval, +PSMAP_OPTIONAL(PSMAP_TYPE_U32, struct bootstrap_eamuse_config, updatecert_interval, "eamuse/updatecert_interval", 0) PSMAP_END @@ -261,9 +286,82 @@ bool bootstrap_config_from_property( /* Now parse the startup node */ log_misc(ROOT_NODE "/startup/%s: loading merge result...", selector); + + if (!property_psmap_import( + NULL, startup_config, &config->startup.avs, bootstrap_startup_avs_psmap)) { + log_warning(ROOT_NODE "/startup/%s/boot (avs): load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.boot, bootstrap_startup_boot_psmap)) { + log_warning(ROOT_NODE "/startup/%s/boot: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.log, bootstrap_startup_log_psmap)) { + log_warning(ROOT_NODE "/startup/%s/log: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.minidump, bootstrap_startup_minidump_psmap)) { + log_warning(ROOT_NODE "/startup/%s/minidump: load failed", selector); + return false; + } + if (!property_psmap_import( - NULL, startup_config, &config->startup, bootstrap_startup_psmap)) { - log_warning(ROOT_NODE "/startup/%s: load failed", selector); + NULL, startup_config, &config->startup.module, bootstrap_startup_module_psmap)) { + log_warning(ROOT_NODE "/startup/%s/component: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.dlm_ntdll, bootstrap_startup_dlm_psmap)) { + log_warning(ROOT_NODE "/startup/%s/dlm/ntdll: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.shield, bootstrap_startup_shield_psmap)) { + log_warning(ROOT_NODE "/startup/%s/shield: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.dongle, bootstrap_startup_dongle_psmap)) { + log_warning(ROOT_NODE "/startup/%s/dongle: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.drm, bootstrap_startup_drm_psmap)) { + log_warning(ROOT_NODE "/startup/%s/drm: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.lte, bootstrap_startup_lte_psmap)) { + log_warning(ROOT_NODE "/startup/%s/lte: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.ssl, bootstrap_startup_ssl_psmap)) { + log_warning(ROOT_NODE "/startup/%s/ssl: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.esign, bootstrap_startup_esign_psmap)) { + log_warning(ROOT_NODE "/startup/%s/esign: load failed", selector); + return false; + } + + if (!property_psmap_import( + NULL, startup_config, &config->startup.eamuse, bootstrap_startup_eamuse_psmap)) { + log_warning(ROOT_NODE "/startup/%s/eamuse: load failed", selector); return false; } @@ -271,6 +369,7 @@ bool bootstrap_config_from_property( property_search(NULL, startup_config, "component/param"); config->log_node = property_search(NULL, startup_config, "log"); config->default_node = property_search(NULL, startup_config, "default"); + return true; } @@ -297,14 +396,14 @@ void bootstrap_config_update_avs( NULL, avs_root, PROPERTY_TYPE_U8, "sntp/ea_on", 1); } - if (config->startup.drm_device[0]) { + if (config->startup.drm.device[0]) { property_remove(NULL, avs_root, "fs/root/device"); property_node_create( NULL, avs_root, PROPERTY_TYPE_STR, "fs/root/device", - config->startup.drm_device); + config->startup.drm.device); } if (config->log_node) { @@ -327,14 +426,14 @@ void bootstrap_config_update_avs( NULL, avs_root, PROPERTY_TYPE_BOOL, "sntp/ea_on", 1); } - if (config->startup.drm_device[0]) { + if (config->startup.drm.device[0]) { property_remove(NULL, avs_root, "fs/root/device"); property_node_create( NULL, avs_root, PROPERTY_TYPE_STR, "fs/root/device", - config->startup.drm_device); + config->startup.drm.device); } if (config->log_node) { @@ -346,7 +445,7 @@ void bootstrap_config_update_avs( bool bootstrap_config_iter_default_file( struct bootstrap_config *config, - struct bootstrap_default_file *default_file) + struct bootstrap_default_file_config *default_file) { if (!config->default_file) { config->default_file = diff --git a/src/main/launcher/bs-config.h b/src/main/launcher/bs-config.h index 8725fed0..84a2bc8c 100644 --- a/src/main/launcher/bs-config.h +++ b/src/main/launcher/bs-config.h @@ -6,92 +6,122 @@ #include "imports/avs.h" -struct bootstrap_default_file { - char src[64]; - char dest[64]; -}; - struct bootstrap_startup_config { - char avs_config_file[64]; - char launch_config_file[64]; - uint32_t avs_heap_size; - uint32_t std_heap_size; - char mount_table_selector[16]; - bool watcher_enable; - bool timemachine_enable; - - char log_level[8]; - char log_name[64]; - char log_file[64]; - uint32_t log_bufsz; - uint16_t log_output_delay_ms; - bool log_enable_console; - bool log_enable_sci; - bool log_enable_net; - bool log_enable_file; - bool log_rotate; - bool log_append; - uint16_t log_count; - - uint8_t minidump_count; - bool minidump_continue; - bool minidump_log; - uint8_t minidump_type; - char minidump_path[64]; - uint32_t minidump_symbufsz; - char minidump_search_path[64]; - - char module_file[64]; - char module_load_type[64]; - - char ntdll_digest[16]; - uint32_t ntdll_size; - uint32_t ntdll_ift_table; - uint32_t ntdll_ift_insert; - uint32_t ntdll_ift_remove; - - bool shield_enable; - bool shield_verbose; - bool shield_use_loadlibrary; - char shield_logger[64]; - uint32_t shield_sleep_min; - uint32_t shield_sleep_blur; - uint32_t shield_tick_sleep; - uint32_t shield_tick_error; - uint8_t shield_overwork_threshold; - uint32_t shield_overwork_delay; - uint32_t shield_pause_delay; - char shield_whitelist_file[64]; - char shield_unlimited_key[10]; - uint16_t shield_killer_port; - - char dongle_license_cn[32]; - char dongle_account_cn[32]; - char dongle_driver_dll[16]; - bool dongle_disable_gc; - - char drm_dll[64]; - char drm_device[64]; - char drm_mount[64]; - char drm_fstype[64]; - char drm_options[64]; - - bool lte_enable; - char lte_config_file[64]; - char lte_unlimited_key[10]; - - char ssl_options[64]; - - bool esign_enable; - - bool eamuse_enable; - bool eamuse_sync; - bool eamuse_enable_model; - char eamuse_config_file[64]; - bool eamuse_updatecert_enable; - uint32_t eamuse_updatecert_interval; + struct bootstrap_default_file_config { + char src[64]; + char dest[64]; + } default_file[16]; // should be enough for a while + + struct bootstrap_avs_config { + char config_file[64]; + uint32_t avs_heap_size; + uint32_t std_heap_size; + } avs; + + struct bootstrap_boot_config { + 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]; + } 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; }; +// TODO make this fully parsed and avoid dealing with property nodes in here struct bootstrap_config { char release_code[16]; struct bootstrap_startup_config startup; @@ -106,10 +136,12 @@ bool bootstrap_config_from_property( struct bootstrap_config *config, struct property *prop, const char *profile); + +// TODO this should rather move somewhere else? void bootstrap_config_update_avs( const struct bootstrap_config *config, struct property_node *avs_root); bool bootstrap_config_iter_default_file( struct bootstrap_config *config, - struct bootstrap_default_file *default_file); + struct bootstrap_default_file_config *default_file); #endif /* LAUNCHER_BS_CONFIG_H */ diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 74068263..7c1e7fa5 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -95,11 +95,11 @@ static void avs_config_setup( log_assert(bootstrap_config); log_assert(avs_config_property); - *avs_config_property = boot_property_load(bootstrap_config->startup.avs_config_file); + *avs_config_property = boot_property_load(bootstrap_config->startup.avs.config_file); avs_config_node = property_search(*avs_config_property, 0, "/config"); if (avs_config_node == NULL) { - log_fatal("%s: /config missing", bootstrap_config->startup.avs_config_file); + log_fatal("%s: /config missing", bootstrap_config->startup.avs.config_file); } if (dev_nvram_raw_path) { @@ -442,20 +442,20 @@ int main(int argc, const char **argv) avs_context_init( avs_config_property, property_search(avs_config_property, 0, "/config"), - bootstrap_config.startup.avs_heap_size, - bootstrap_config.startup.std_heap_size); + bootstrap_config.startup.avs.avs_heap_size, + bootstrap_config.startup.avs.std_heap_size); bootstrap_context_post_avs_setup(&bootstrap_config); /* Load game DLL */ - log_info("Load game DLL: %s", bootstrap_config.startup.module_file); + log_info("Load game DLL: %s", bootstrap_config.startup.module.file); if (options.iat_hook_dlls.nitems > 0) { module_context_init_with_iat_hooks( - &module, bootstrap_config.startup.module_file, &options.iat_hook_dlls); + &module, bootstrap_config.startup.module.file, &options.iat_hook_dlls); } else { - module_context_init(&module, bootstrap_config.startup.module_file); + module_context_init(&module, bootstrap_config.startup.module.file); } /* Load hook DLLs */ @@ -497,10 +497,10 @@ int main(int argc, const char **argv) /* Start up e-Amusement client */ - if (bootstrap_config.startup.eamuse_enable) { + if (bootstrap_config.startup.eamuse.enable) { ea3_config_setup( &ea3_ident, - bootstrap_config.startup.eamuse_config_file, + bootstrap_config.startup.eamuse.config_file, options.override_urlslash_enabled, options.override_urlslash_value, options.override_service, @@ -531,7 +531,7 @@ int main(int argc, const char **argv) log_info("Shutting down launcher..."); - if (bootstrap_config.startup.eamuse_enable) { + if (bootstrap_config.startup.eamuse.enable) { ea3_shutdown(); ea3_config_node = NULL; boot_property_free(ea3_config_property); From 06ad0c194cd88814cd6f99e251cf87065a69a15f Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 13:29:09 +0100 Subject: [PATCH 64/72] refactor(launcher): Rename bs-config -> bootstrap-config Aligns naming and avoids (personal) confusion --- src/main/launcher/Module.mk | 2 +- src/main/launcher/{bs-config.c => bootstrap-config.c} | 2 +- src/main/launcher/{bs-config.h => bootstrap-config.h} | 0 src/main/launcher/bootstrap-context.c | 2 +- src/main/launcher/bootstrap-context.h | 2 +- src/main/launcher/main.c | 2 +- src/main/launcher/options.h | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename src/main/launcher/{bs-config.c => bootstrap-config.c} (99%) rename src/main/launcher/{bs-config.h => bootstrap-config.h} (100%) diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index 914633f4..dcb25476 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -14,8 +14,8 @@ libs_launcher := \ src_launcher := \ avs-context.c \ + bootstrap-config.c \ bootstrap-context.c \ - bs-config.c \ ea3-ident.c \ logger.c \ main.c \ diff --git a/src/main/launcher/bs-config.c b/src/main/launcher/bootstrap-config.c similarity index 99% rename from src/main/launcher/bs-config.c rename to src/main/launcher/bootstrap-config.c index 3c8079e2..0158d868 100644 --- a/src/main/launcher/bs-config.c +++ b/src/main/launcher/bootstrap-config.c @@ -3,7 +3,7 @@ #include "imports/avs.h" -#include "launcher/bs-config.h" +#include "launcher/bootstrap-config.h" #include "util/defs.h" #include "util/hex.h" diff --git a/src/main/launcher/bs-config.h b/src/main/launcher/bootstrap-config.h similarity index 100% rename from src/main/launcher/bs-config.h rename to src/main/launcher/bootstrap-config.h diff --git a/src/main/launcher/bootstrap-context.c b/src/main/launcher/bootstrap-context.c index bd460478..5aa6079c 100644 --- a/src/main/launcher/bootstrap-context.c +++ b/src/main/launcher/bootstrap-context.c @@ -1,6 +1,6 @@ #define LOG_MODULE "bootstrap-context" -#include "launcher/bs-config.h" +#include "launcher/bootstrap-config.h" #include "launcher/property.h" #include "util/log.h" diff --git a/src/main/launcher/bootstrap-context.h b/src/main/launcher/bootstrap-context.h index a4a26d87..5e76e280 100644 --- a/src/main/launcher/bootstrap-context.h +++ b/src/main/launcher/bootstrap-context.h @@ -3,7 +3,7 @@ #include -#include "launcher/bs-config.h" +#include "launcher/bootstrap-config.h" void bootstrap_context_init(); void bootstrap_context_finit(); diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 7c1e7fa5..d5d46ca3 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -9,8 +9,8 @@ #include "imports/avs.h" #include "launcher/avs-context.h" +#include "launcher/bootstrap-config.h" #include "launcher/bootstrap-context.h" -#include "launcher/bs-config.h" #include "launcher/ea3-ident.h" #include "launcher/logger.h" #include "launcher/module.h" diff --git a/src/main/launcher/options.h b/src/main/launcher/options.h index 28605f7b..61241add 100644 --- a/src/main/launcher/options.h +++ b/src/main/launcher/options.h @@ -7,7 +7,7 @@ #include "util/array.h" #include "util/log.h" -#include "launcher/bs-config.h" +#include "launcher/bootstrap-config.h" struct options { size_t std_heap_size; From ebfcbf08bf2b7b34e82916b7c0bbea10bea424f8 Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 13:30:09 +0100 Subject: [PATCH 65/72] chore(launcher): Remove unused function declarations Likely left-over that I forgot to remove --- src/main/launcher/bootstrap-context.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/launcher/bootstrap-context.h b/src/main/launcher/bootstrap-context.h index 5e76e280..ae269e8a 100644 --- a/src/main/launcher/bootstrap-context.h +++ b/src/main/launcher/bootstrap-context.h @@ -5,9 +5,6 @@ #include "launcher/bootstrap-config.h" -void bootstrap_context_init(); -void bootstrap_context_finit(); - void bootstrap_context_init( const char *avs_config_path, const char *ea3_config_path, From ba1c934737ad157ca6cb5851599d745407fff21f Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 13:45:01 +0100 Subject: [PATCH 66/72] refactor(launcher): Split eamuse stuff into module Just moves everything eamuse related from main to a separate module as a first step. Doesn't address ownership of options/config or other stuff such as parameter/config overriding, yet. --- src/main/launcher/Module.mk | 1 + src/main/launcher/eamuse.c | 108 ++++++++++++++++++++++++++++++++++++ src/main/launcher/eamuse.h | 14 +++++ src/main/launcher/main.c | 92 +++--------------------------- 4 files changed, 131 insertions(+), 84 deletions(-) create mode 100644 src/main/launcher/eamuse.c create mode 100644 src/main/launcher/eamuse.h diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index dcb25476..925704df 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -17,6 +17,7 @@ src_launcher := \ bootstrap-config.c \ bootstrap-context.c \ ea3-ident.c \ + eamuse.c \ logger.c \ main.c \ module.c \ diff --git a/src/main/launcher/eamuse.c b/src/main/launcher/eamuse.c new file mode 100644 index 00000000..60bcd01b --- /dev/null +++ b/src/main/launcher/eamuse.c @@ -0,0 +1,108 @@ +#include "eamuse.h" + +#include "ea3-ident.h" +#include "property.h" +#include "options.h" + +#include "imports/avs-ea3.h" +#include "util/log.h" + +static const struct bootstrap_eamuse_config* eamuse_config; +static struct property_node *ea3_config_node; +static struct property *ea3_config_property; + +static void ea3_config_setup( + const struct ea3_ident *ea3_ident, + const char *eamuse_config_file, + bool override_urlslash_enabled, + bool override_urlslash_value, + const char *service_url, + struct property **ea3_config_property) +{ + struct property_node *ea3_config_node; + + log_assert(ea3_ident); + log_assert(eamuse_config_file); + log_assert(ea3_config_property); + + log_misc("Preparing ea3 configuration..."); + + log_misc("Loading ea3-config from file: %s", eamuse_config_file); + + *ea3_config_property = boot_property_load_avs(eamuse_config_file); + ea3_config_node = property_search(*ea3_config_property, 0, "/ea3"); + + if (ea3_config_node == NULL) { + log_fatal("%s: /ea3 missing", eamuse_config_file); + } + + ea3_ident_to_property(ea3_ident, *ea3_config_property); + + if (override_urlslash_enabled) { + log_misc( + "Overriding url_slash to: %d", override_urlslash_value); + + boot_property_node_replace_bool( + *ea3_config_property, + ea3_config_node, + "network/url_slash", + override_urlslash_value); + } + + if (service_url) { + log_misc("Overriding service url to: %s", service_url); + + boot_property_node_replace_str( + *ea3_config_property, + ea3_config_node, + "network/services", + service_url); + } +} + +void eamuse_init( + const struct bootstrap_eamuse_config* config, + const struct ea3_ident* ea3_ident, + const struct options* options) +{ + log_assert(config); + log_assert(ea3_ident); + log_assert(options); + + eamuse_config = config; + + if (eamuse_config->enable) { + ea3_config_setup( + ea3_ident, + eamuse_config->config_file, + options->override_urlslash_enabled, + options->override_urlslash_value, + options->override_service, + &ea3_config_property); + + if (options->log_property_configs) { + log_misc("Property ea3-config"); + boot_property_log(ea3_config_property); + } + + log_info("Booting ea3..."); + + ea3_config_node = property_search(ea3_config_property, 0, "/ea3"); + + log_assert(ea3_config_node); + + ea3_boot(ea3_config_node); + } else { + ea3_config_property = NULL; + ea3_config_node = NULL; + } +} + +void eamuse_fini() +{ + if (eamuse_config->enable) { + ea3_shutdown(); + ea3_config_node = NULL; + boot_property_free(ea3_config_property); + } +} \ No newline at end of file diff --git a/src/main/launcher/eamuse.h b/src/main/launcher/eamuse.h new file mode 100644 index 00000000..f0a21a91 --- /dev/null +++ b/src/main/launcher/eamuse.h @@ -0,0 +1,14 @@ +#ifndef LAUNCHER_EAMUSE_H +#define LAUNCHER_EAMUSE_H + +#include "bootstrap-config.h" +#include "ea3-ident.h" +#include "options.h" + +void eamuse_init(const struct bootstrap_eamuse_config* config, + const struct ea3_ident* ea3_ident, + const struct options* options); + +void eamuse_fini(); + +#endif \ No newline at end of file diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index d5d46ca3..b1e1904e 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -12,6 +12,7 @@ #include "launcher/bootstrap-config.h" #include "launcher/bootstrap-context.h" #include "launcher/ea3-ident.h" +#include "launcher/eamuse.h" #include "launcher/logger.h" #include "launcher/module.h" #include "launcher/options.h" @@ -298,55 +299,6 @@ void invoke_dll_module_init( std_setenv("/env/profile/soft_id_code", sidcode_long); } -static void ea3_config_setup( - const struct ea3_ident *ea3_ident, - const char *eamuse_config_file, - bool override_urlslash_enabled, - bool override_urlslash_value, - const char *service_url, - struct property **ea3_config_property) -{ - struct property_node *ea3_config_node; - - log_assert(ea3_ident); - log_assert(eamuse_config_file); - log_assert(ea3_config_property); - - log_misc("Preparing ea3 configuration..."); - - log_misc("Loading ea3-config from file: %s", eamuse_config_file); - - *ea3_config_property = boot_property_load_avs(eamuse_config_file); - ea3_config_node = property_search(*ea3_config_property, 0, "/ea3"); - - if (ea3_config_node == NULL) { - log_fatal("%s: /ea3 missing", eamuse_config_file); - } - - ea3_ident_to_property(ea3_ident, *ea3_config_property); - - if (override_urlslash_enabled) { - log_misc( - "Overriding url_slash to: %d", override_urlslash_value); - - boot_property_node_replace_bool( - *ea3_config_property, - ea3_config_node, - "network/url_slash", - override_urlslash_value); - } - - if (service_url) { - log_misc("Overriding service url to: %s", service_url); - - boot_property_node_replace_str( - *ea3_config_property, - ea3_config_node, - "network/services", - service_url); - } -} - int main(int argc, const char **argv) { struct options options; @@ -361,11 +313,8 @@ int main(int argc, const char **argv) struct ea3_ident ea3_ident; struct property *app_config_property; - struct property_node *app_config_node; - - struct property *ea3_config_property; - struct property_node *ea3_config_node; - + struct property_node *app_config_node; + options_init(&options); if (!options_read_cmdline(&options, argc, argv) || @@ -497,31 +446,10 @@ int main(int argc, const char **argv) /* Start up e-Amusement client */ - if (bootstrap_config.startup.eamuse.enable) { - ea3_config_setup( - &ea3_ident, - bootstrap_config.startup.eamuse.config_file, - options.override_urlslash_enabled, - options.override_urlslash_value, - options.override_service, - &ea3_config_property); - - if (options.log_property_configs) { - log_misc("Property ea3-config"); - boot_property_log(ea3_config_property); - } - - log_info("Booting ea3..."); - - ea3_config_node = property_search(ea3_config_property, 0, "/ea3"); - - log_assert(ea3_config_node); - - ea3_boot(ea3_config_node); - } else { - ea3_config_property = NULL; - ea3_config_node = NULL; - } + eamuse_init( + &bootstrap_config.startup.eamuse, + &ea3_ident, + &options); /* Run application */ @@ -531,11 +459,7 @@ int main(int argc, const char **argv) log_info("Shutting down launcher..."); - if (bootstrap_config.startup.eamuse.enable) { - ea3_shutdown(); - ea3_config_node = NULL; - boot_property_free(ea3_config_property); - } + eamuse_fini(); app_config_node = NULL; if (app_config_property) { From b829d2fe703e3171591dfbeb36d54514307b86dd Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 18:38:34 +0100 Subject: [PATCH 67/72] refactor(launcher): Split eamuse config related stuff into module This includes dealing with loading the configuration from a file to any kind of mutation of the configuration that launcher needs to do, e.g. injecting ea3-ident or overriding parameters (based on command line params) --- src/main/launcher/Module.mk | 1 + src/main/launcher/ea3-ident.c | 24 ------- src/main/launcher/ea3-ident.h | 4 +- src/main/launcher/eamuse-config.c | 103 ++++++++++++++++++++++++++++++ src/main/launcher/eamuse-config.h | 20 ++++++ src/main/launcher/eamuse.c | 103 +++++++++--------------------- src/main/launcher/eamuse.h | 13 ++-- src/main/launcher/main.c | 6 +- 8 files changed, 168 insertions(+), 106 deletions(-) create mode 100644 src/main/launcher/eamuse-config.c create mode 100644 src/main/launcher/eamuse-config.h diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index 925704df..e9af7727 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -18,6 +18,7 @@ src_launcher := \ bootstrap-context.c \ ea3-ident.c \ eamuse.c \ + eamuse-config.c \ logger.c \ main.c \ module.c \ diff --git a/src/main/launcher/ea3-ident.c b/src/main/launcher/ea3-ident.c index 0dc7304f..6e8f5c9b 100644 --- a/src/main/launcher/ea3-ident.c +++ b/src/main/launcher/ea3-ident.c @@ -74,28 +74,4 @@ void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident) sizeof(netif.mac_addr), ident->hardid + 4, sizeof(ident->hardid) - 4); -} - -void ea3_ident_to_property( - const struct ea3_ident *ident, struct property *ea3_config) -{ - struct property_node *ea3_node; - struct property_node *node; - int i; - - ea3_node = property_search(ea3_config, NULL, "/ea3"); - - if (ea3_node == NULL) { - log_fatal("ea3 config is missing /ea3 node"); - } - - for (i = 0; ea3_ident_psmap[i].type != PSMAP_TYPE_TERMINATOR; i++) { - node = property_search(ea3_config, ea3_node, ea3_ident_psmap[i].path); - - if (node != NULL) { - property_node_remove(node); - } - } - - property_psmap_export(ea3_config, ea3_node, ident, ea3_ident_psmap); } \ No newline at end of file diff --git a/src/main/launcher/ea3-ident.h b/src/main/launcher/ea3-ident.h index 029408da..9e0dd715 100644 --- a/src/main/launcher/ea3-ident.h +++ b/src/main/launcher/ea3-ident.h @@ -28,12 +28,12 @@ struct ea3_ident { char pcbid[24]; }; +extern struct property_psmap ea3_ident_psmap[9]; + void ea3_ident_init(struct ea3_ident *ident); void ea3_ident_initialize_from_file(const char *path, struct ea3_ident *ea3_ident); bool ea3_ident_from_property( struct ea3_ident *ident, struct property *ea3_config); void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident); -void ea3_ident_to_property( - const struct ea3_ident *ident, struct property *ea3_config); #endif diff --git a/src/main/launcher/eamuse-config.c b/src/main/launcher/eamuse-config.c new file mode 100644 index 00000000..a2a2f0a4 --- /dev/null +++ b/src/main/launcher/eamuse-config.c @@ -0,0 +1,103 @@ +#define LOG_MODULE "eamuse-config" + +#include + +#include "imports/avs.h" + +#include "launcher/ea3-ident.h" +#include "launcher/eamuse-config.h" +#include "launcher/property.h" + +#include "util/log.h" + +#define EAMUSE_CONFIG_ROOT_NODE "/ea3" + +struct property* eamuse_config_load_from_avs_path( + const char *avs_path) +{ + struct property *property; + + log_assert(avs_path); + + log_misc("Loading ea3-config from avs path: %s", avs_path); + + property = boot_property_load_avs(avs_path); + + // Check if root node exists, call already errors if not + eamuse_config_resolve_root_node(property); + + return property; +} + +struct property_node* eamuse_config_resolve_root_node(struct property *property) +{ + struct property_node *node; + + log_assert(property); + + node = property_search(property, 0, EAMUSE_CONFIG_ROOT_NODE); + + if (node == NULL) { + log_fatal("%s: " EAMUSE_CONFIG_ROOT_NODE " missing"); + } + + return node; +} + +void eamuse_config_inject_ea3_ident( + struct property *eamuse_property, + const struct ea3_ident *ea3_ident) +{ + struct property_node *ea3_node; + struct property_node *node; + int i; + + log_misc("Injecting ea3_ident data..."); + + ea3_node = eamuse_config_resolve_root_node(eamuse_property); + + for (i = 0; ea3_ident_psmap[i].type != PSMAP_TYPE_TERMINATOR; i++) { + node = property_search(eamuse_property, ea3_node, ea3_ident_psmap[i].path); + + if (node != NULL) { + property_node_remove(node); + } + } + + property_psmap_export(eamuse_property, ea3_node, ea3_ident, ea3_ident_psmap); +} + +void eamuse_config_inject_parameters( + struct property *eamuse_property, + bool urlslash_enabled, + bool urlslash_value, + const char *service_url) +{ + struct property_node *node; + + log_assert(eamuse_property); + log_assert(service_url); + + node = eamuse_config_resolve_root_node(eamuse_property); + + if (urlslash_enabled) { + log_misc( + "Overriding url_slash to: %d", urlslash_value); + + boot_property_node_replace_bool( + eamuse_property, + node, + "network/url_slash", + urlslash_value); + } + + if (service_url) { + log_misc("Overriding service url to: %s", service_url); + + boot_property_node_replace_str( + eamuse_property, + node, + "network/services", + service_url); + } +} \ No newline at end of file diff --git a/src/main/launcher/eamuse-config.h b/src/main/launcher/eamuse-config.h new file mode 100644 index 00000000..9417c538 --- /dev/null +++ b/src/main/launcher/eamuse-config.h @@ -0,0 +1,20 @@ +#ifndef LAUNCHER_EAMUSE_CONFIG_H +#define LAUNCHER_EAMUSE_CONFIG_H + +#include "launcher/property.h" + +struct property* eamuse_config_load_from_avs_path(const char *avs_path); + +struct property_node* eamuse_config_resolve_root_node(struct property *property); + +void eamuse_config_inject_ea3_ident( + struct property *eamuse_property, + const struct ea3_ident *ea3_ident); + +void eamuse_config_inject_parameters( + struct property *eamuse_property, + bool urlslash_enabled, + bool urlslash_value, + const char *service_url); + +#endif \ No newline at end of file diff --git a/src/main/launcher/eamuse.c b/src/main/launcher/eamuse.c index 60bcd01b..c609f855 100644 --- a/src/main/launcher/eamuse.c +++ b/src/main/launcher/eamuse.c @@ -1,108 +1,63 @@ +#define LOG_MODULE "eamuse" + #include "eamuse.h" #include "ea3-ident.h" +#include "eamuse-config.h" #include "property.h" #include "options.h" #include "imports/avs-ea3.h" #include "util/log.h" -static const struct bootstrap_eamuse_config* eamuse_config; -static struct property_node *ea3_config_node; -static struct property *ea3_config_property; - -static void ea3_config_setup( - const struct ea3_ident *ea3_ident, - const char *eamuse_config_file, - bool override_urlslash_enabled, - bool override_urlslash_value, - const char *service_url, - struct property **ea3_config_property) -{ - struct property_node *ea3_config_node; - - log_assert(ea3_ident); - log_assert(eamuse_config_file); - log_assert(ea3_config_property); - - log_misc("Preparing ea3 configuration..."); - - log_misc("Loading ea3-config from file: %s", eamuse_config_file); - - *ea3_config_property = boot_property_load_avs(eamuse_config_file); - ea3_config_node = property_search(*ea3_config_property, 0, "/ea3"); - - if (ea3_config_node == NULL) { - log_fatal("%s: /ea3 missing", eamuse_config_file); - } - - ea3_ident_to_property(ea3_ident, *ea3_config_property); - - if (override_urlslash_enabled) { - log_misc( - "Overriding url_slash to: %d", override_urlslash_value); - - boot_property_node_replace_bool( - *ea3_config_property, - ea3_config_node, - "network/url_slash", - override_urlslash_value); - } - - if (service_url) { - log_misc("Overriding service url to: %s", service_url); - - boot_property_node_replace_str( - *ea3_config_property, - ea3_config_node, - "network/services", - service_url); - } -} +static struct property *_eamuse_property; void eamuse_init( const struct bootstrap_eamuse_config* config, const struct ea3_ident* ea3_ident, - const struct options* options) + bool override_urlslash_enabled, + bool override_urlslash_value, + const char *override_service_url, + bool log_property_config) { + struct property_node *node; + log_assert(config); log_assert(ea3_ident); - log_assert(options); + log_assert(override_service_url); - eamuse_config = config; + if (config->enable) { + _eamuse_property = eamuse_config_load_from_avs_path(config->config_file); - if (eamuse_config->enable) { - ea3_config_setup( - ea3_ident, - eamuse_config->config_file, - options->override_urlslash_enabled, - options->override_urlslash_value, - options->override_service, - &ea3_config_property); + eamuse_config_inject_ea3_ident(_eamuse_property, ea3_ident); + eamuse_config_inject_parameters( + _eamuse_property, + override_urlslash_enabled, + override_urlslash_value, + override_service_url); - if (options->log_property_configs) { + if (log_property_config) { log_misc("Property ea3-config"); - boot_property_log(ea3_config_property); + boot_property_log(_eamuse_property); } - log_info("Booting ea3..."); + node = eamuse_config_resolve_root_node(_eamuse_property); - ea3_config_node = property_search(ea3_config_property, 0, "/ea3"); + log_info("Booting ea3..."); - log_assert(ea3_config_node); + ea3_boot(node); - ea3_boot(ea3_config_node); + log_misc("Booting ea3 done"); } else { - ea3_config_property = NULL; - ea3_config_node = NULL; + _eamuse_property = NULL; } } void eamuse_fini() { - if (eamuse_config->enable) { + if (_eamuse_property) { ea3_shutdown(); - ea3_config_node = NULL; - boot_property_free(ea3_config_property); + boot_property_free(_eamuse_property); + _eamuse_property = NULL; } } \ No newline at end of file diff --git a/src/main/launcher/eamuse.h b/src/main/launcher/eamuse.h index f0a21a91..7287ebfe 100644 --- a/src/main/launcher/eamuse.h +++ b/src/main/launcher/eamuse.h @@ -1,13 +1,16 @@ #ifndef LAUNCHER_EAMUSE_H #define LAUNCHER_EAMUSE_H -#include "bootstrap-config.h" -#include "ea3-ident.h" -#include "options.h" +#include "launcher/bootstrap-config.h" +#include "launcher/ea3-ident.h" -void eamuse_init(const struct bootstrap_eamuse_config* config, +void eamuse_init( + const struct bootstrap_eamuse_config* config, const struct ea3_ident* ea3_ident, - const struct options* options); + bool override_urlslash_enabled, + bool override_urlslash_value, + const char *override_service_url, + bool log_property_config); void eamuse_fini(); diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index b1e1904e..0da4df11 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -13,6 +13,7 @@ #include "launcher/bootstrap-context.h" #include "launcher/ea3-ident.h" #include "launcher/eamuse.h" +#include "launcher/eamuse-config.h" #include "launcher/logger.h" #include "launcher/module.h" #include "launcher/options.h" @@ -449,7 +450,10 @@ int main(int argc, const char **argv) eamuse_init( &bootstrap_config.startup.eamuse, &ea3_ident, - &options); + options.override_urlslash_enabled, + options.override_urlslash_value, + options.override_service, + options.log_property_configs); /* Run application */ From 1700a5f3d489e4bd0c57f9133052df104178bb00 Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 18:57:05 +0100 Subject: [PATCH 68/72] refactor(launcher): Rename property module Align with naming scheme and avoid using "boot" since this is now being used more like helpers everywhere... so let's go with a generic "util" even that's not great either. --- src/main/launcher/Module.mk | 2 +- src/main/launcher/avs-context.c | 14 ++--- src/main/launcher/bootstrap-context.c | 4 +- src/main/launcher/ea3-ident.c | 6 +- src/main/launcher/eamuse-config.c | 8 +-- src/main/launcher/eamuse-config.h | 2 +- src/main/launcher/eamuse.c | 16 ++--- src/main/launcher/main.c | 20 +++---- .../launcher/{property.c => property-util.c} | 58 +++++++++---------- src/main/launcher/property-util.h | 19 ++++++ src/main/launcher/property.h | 19 ------ 11 files changed, 84 insertions(+), 84 deletions(-) rename src/main/launcher/{property.c => property-util.c} (84%) create mode 100644 src/main/launcher/property-util.h delete mode 100644 src/main/launcher/property.h diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index e9af7727..e47e6dba 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -23,7 +23,7 @@ src_launcher := \ main.c \ module.c \ options.c \ - property.c \ + property-util.c \ stubs.c \ version.c \ diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs-context.c index 5211bfaf..35c29464 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs-context.c @@ -10,7 +10,7 @@ #include "launcher/avs-context.h" #include "launcher/logger.h" -#include "launcher/property.h" +#include "launcher/property-util.h" #include "util/codepage.h" #include "util/fs.h" @@ -200,13 +200,13 @@ void avs_context_property_set_local_fs_nvram_raw( property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/nvram"); property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1"); } else { - boot_property_node_replace_str(config_prop, fs_node, "nvram/device", path_dev_raw); - boot_property_node_replace_str(config_prop, fs_node, "nvram/fstype", "fs"); - boot_property_node_replace_str(config_prop, fs_node, "nvram/option", "vf=1,posix=1"); + property_util_node_replace_str(config_prop, fs_node, "nvram/device", path_dev_raw); + property_util_node_replace_str(config_prop, fs_node, "nvram/fstype", "fs"); + property_util_node_replace_str(config_prop, fs_node, "nvram/option", "vf=1,posix=1"); - boot_property_node_replace_str(config_prop, fs_node, "raw/device", path_dev_nvram); - boot_property_node_replace_str(config_prop, fs_node, "raw/fstype", "fs"); - boot_property_node_replace_str(config_prop, fs_node, "raw/option", "vf=1,posix=1"); + property_util_node_replace_str(config_prop, fs_node, "raw/device", path_dev_nvram); + property_util_node_replace_str(config_prop, fs_node, "raw/fstype", "fs"); + property_util_node_replace_str(config_prop, fs_node, "raw/option", "vf=1,posix=1"); } } diff --git a/src/main/launcher/bootstrap-context.c b/src/main/launcher/bootstrap-context.c index 5aa6079c..6a67b2ab 100644 --- a/src/main/launcher/bootstrap-context.c +++ b/src/main/launcher/bootstrap-context.c @@ -1,7 +1,7 @@ #define LOG_MODULE "bootstrap-context" #include "launcher/bootstrap-config.h" -#include "launcher/property.h" +#include "launcher/property-util.h" #include "util/log.h" #include "util/str.h" @@ -53,7 +53,7 @@ void bootstrap_context_init_from_file( log_info("Bootstrap from configuration %s with selector %s", config_path, selector); bootstrap_config_init(config); - *bootstrap_config_property = boot_property_load(config_path); + *bootstrap_config_property = property_util_load_file(config_path); log_info( "Loading bootstrap selector '%s'...", selector); diff --git a/src/main/launcher/ea3-ident.c b/src/main/launcher/ea3-ident.c index 6e8f5c9b..7b116c6b 100644 --- a/src/main/launcher/ea3-ident.c +++ b/src/main/launcher/ea3-ident.c @@ -4,7 +4,7 @@ #include "launcher/ea3-ident.h" #include "launcher/module.h" -#include "launcher/property.h" +#include "launcher/property-util.h" #include "util/defs.h" #include "util/hex.h" @@ -37,7 +37,7 @@ void ea3_ident_initialize_from_file( log_assert(path); log_assert(ea3_ident); - property = boot_property_load(path); + property = property_util_load_file(path); node = property_search(property, NULL, "/ea3_conf"); if (node == NULL) { @@ -49,7 +49,7 @@ void ea3_ident_initialize_from_file( "%s: Error reading IDs from config file", path); } - boot_property_free(property); + property_util_free(property); } void ea3_ident_hardid_from_ethernet(struct ea3_ident *ident) diff --git a/src/main/launcher/eamuse-config.c b/src/main/launcher/eamuse-config.c index a2a2f0a4..edcc0f84 100644 --- a/src/main/launcher/eamuse-config.c +++ b/src/main/launcher/eamuse-config.c @@ -6,7 +6,7 @@ #include "launcher/ea3-ident.h" #include "launcher/eamuse-config.h" -#include "launcher/property.h" +#include "launcher/property-util.h" #include "util/log.h" @@ -21,7 +21,7 @@ struct property* eamuse_config_load_from_avs_path( log_misc("Loading ea3-config from avs path: %s", avs_path); - property = boot_property_load_avs(avs_path); + property = property_util_load_avs(avs_path); // Check if root node exists, call already errors if not eamuse_config_resolve_root_node(property); @@ -84,7 +84,7 @@ void eamuse_config_inject_parameters( log_misc( "Overriding url_slash to: %d", urlslash_value); - boot_property_node_replace_bool( + property_util_node_replace_bool( eamuse_property, node, "network/url_slash", @@ -94,7 +94,7 @@ void eamuse_config_inject_parameters( if (service_url) { log_misc("Overriding service url to: %s", service_url); - boot_property_node_replace_str( + property_util_node_replace_str( eamuse_property, node, "network/services", diff --git a/src/main/launcher/eamuse-config.h b/src/main/launcher/eamuse-config.h index 9417c538..e93701a3 100644 --- a/src/main/launcher/eamuse-config.h +++ b/src/main/launcher/eamuse-config.h @@ -1,7 +1,7 @@ #ifndef LAUNCHER_EAMUSE_CONFIG_H #define LAUNCHER_EAMUSE_CONFIG_H -#include "launcher/property.h" +#include "imports/avs.h" struct property* eamuse_config_load_from_avs_path(const char *avs_path); diff --git a/src/main/launcher/eamuse.c b/src/main/launcher/eamuse.c index c609f855..88709676 100644 --- a/src/main/launcher/eamuse.c +++ b/src/main/launcher/eamuse.c @@ -1,13 +1,13 @@ #define LOG_MODULE "eamuse" -#include "eamuse.h" +#include "imports/avs-ea3.h" -#include "ea3-ident.h" -#include "eamuse-config.h" -#include "property.h" -#include "options.h" +#include "launcher/ea3-ident.h" +#include "launcher/eamuse.h" +#include "launcher/eamuse-config.h" +#include "launcher/property-util.h" +#include "launcher/options.h" -#include "imports/avs-ea3.h" #include "util/log.h" static struct property *_eamuse_property; @@ -38,7 +38,7 @@ void eamuse_init( if (log_property_config) { log_misc("Property ea3-config"); - boot_property_log(_eamuse_property); + property_util_log(_eamuse_property); } node = eamuse_config_resolve_root_node(_eamuse_property); @@ -57,7 +57,7 @@ void eamuse_fini() { if (_eamuse_property) { ea3_shutdown(); - boot_property_free(_eamuse_property); + property_util_free(_eamuse_property); _eamuse_property = NULL; } } \ No newline at end of file diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 0da4df11..728569e6 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -17,7 +17,7 @@ #include "launcher/logger.h" #include "launcher/module.h" #include "launcher/options.h" -#include "launcher/property.h" +#include "launcher/property-util.h" #include "launcher/stubs.h" #include "util/defs.h" @@ -97,7 +97,7 @@ static void avs_config_setup( log_assert(bootstrap_config); log_assert(avs_config_property); - *avs_config_property = boot_property_load(bootstrap_config->startup.avs.config_file); + *avs_config_property = property_util_load_file(bootstrap_config->startup.avs.config_file); avs_config_node = property_search(*avs_config_property, 0, "/config"); if (avs_config_node == NULL) { @@ -199,7 +199,7 @@ static void app_config_setup( } else if (app_config_path && path_exists(app_config_path)) { log_misc("Loading avs-config from file: %s", app_config_path); - *app_config_property = boot_property_load_avs(app_config_path); + *app_config_property = property_util_load_avs(app_config_path); *app_config_node = property_search(*app_config_property, 0, "/param"); @@ -209,7 +209,7 @@ static void app_config_setup( } else { log_warning("Explicit app config (file) missing, defaulting to empty"); - *app_config_property = boot_property_load_cstring("dummy"); + *app_config_property = property_util_load_cstring("dummy"); *app_config_node = property_search(*app_config_property, 0, "/param"); } } @@ -371,7 +371,7 @@ int main(int argc, const char **argv) if (options.log_property_configs) { log_misc("Property bootstrap-config"); - boot_property_log(bootstrap_config_property); + property_util_log(bootstrap_config_property); } } @@ -386,7 +386,7 @@ int main(int argc, const char **argv) if (options.log_property_configs) { log_misc("Property avs-config"); - boot_property_log(avs_config_property); + property_util_log(avs_config_property); } avs_context_init( @@ -440,7 +440,7 @@ int main(int argc, const char **argv) if (options.log_property_configs) { log_misc("Property app-config"); - boot_property_node_log(app_config_node); + property_util_node_log(app_config_node); } invoke_dll_module_init(&ea3_ident, &module, app_config_node); @@ -467,17 +467,17 @@ int main(int argc, const char **argv) app_config_node = NULL; if (app_config_property) { - boot_property_free(app_config_property); + property_util_log(app_config_property); } log_to_writer(log_writer_file, stdout); avs_context_fini(); - boot_property_free(avs_config_property); + property_util_log(avs_config_property); module_context_fini(&module); if (bootstrap_config_property) { - boot_property_free(bootstrap_config_property); + property_util_log(bootstrap_config_property); } options_fini(&options); diff --git a/src/main/launcher/property.c b/src/main/launcher/property-util.c similarity index 84% rename from src/main/launcher/property.c rename to src/main/launcher/property-util.c index 82cd6eff..0c84a5d9 100644 --- a/src/main/launcher/property.c +++ b/src/main/launcher/property-util.c @@ -1,4 +1,4 @@ -#define LOG_MODULE "launcher-property" +#define LOG_MODULE "property-util" #include @@ -8,7 +8,7 @@ #include "imports/avs.h" -#include "launcher/property.h" +#include "launcher/property-util.h" #include "util/log.h" #include "util/mem.h" @@ -16,7 +16,7 @@ typedef void (*rewinder)(uint32_t context); -static struct property *do_property_load( +static struct property *property_util_do_load( avs_reader_t reader, rewinder rewinder, uint32_t context, const char *name) { struct property *prop; @@ -44,7 +44,7 @@ static struct property *do_property_load( return prop; } -static int boot_property_fread(uint32_t context, void *bytes, size_t nbytes) +static int property_util_fread(uint32_t context, void *bytes, size_t nbytes) { FILE *f; @@ -53,13 +53,13 @@ static int boot_property_fread(uint32_t context, void *bytes, size_t nbytes) return fread(bytes, 1, nbytes, f); } -static void boot_property_frewind(uint32_t context) +static void property_util_frewind(uint32_t context) { FILE *f = TlsGetValue(context); rewind(f); } -static void boot_property_log_node_tree_rec( +static void property_util_log_node_tree_rec( struct property_node *parent_node, const char* parent_path) { @@ -173,24 +173,24 @@ static void boot_property_log_node_tree_rec( } } else { while (child_node) { - boot_property_log_node_tree_rec(child_node, cur_path); + property_util_log_node_tree_rec(child_node, cur_path); child_node = property_node_traversal(child_node, TRAVERSE_NEXT_SIBLING); } } } -void boot_property_log(struct property *property) +void property_util_log(struct property *property) { - boot_property_log_node_tree_rec(property_search(property, NULL, "/"), ""); + property_util_log_node_tree_rec(property_search(property, NULL, "/"), ""); } -void boot_property_node_log(struct property_node *node) +void property_util_node_log(struct property_node *node) { - boot_property_log_node_tree_rec(node, ""); + property_util_log_node_tree_rec(node, ""); } -struct property *boot_property_load(const char *filename) +struct property *property_util_load_file(const char *filename) { FILE *f; uint32_t f_keyhole; @@ -209,8 +209,8 @@ struct property *boot_property_load(const char *filename) log_fatal("%s: Error opening configuration file", filename); } - prop = do_property_load( - boot_property_fread, boot_property_frewind, f_keyhole, filename); + prop = property_util_do_load( + property_util_fread, property_util_frewind, f_keyhole, filename); TlsFree(f_keyhole); @@ -226,7 +226,7 @@ struct cstring_read_handle { }; static int -boot_property_cstring_read(uint32_t context, void *bytes, size_t nbytes) +property_util_cstring_read(uint32_t context, void *bytes, size_t nbytes) { int result = 0; struct cstring_read_handle *h = TlsGetValue(context); @@ -239,13 +239,13 @@ boot_property_cstring_read(uint32_t context, void *bytes, size_t nbytes) return result; } -static void boot_property_cstring_rewind(uint32_t context) +static void property_util_cstring_rewind(uint32_t context) { struct cstring_read_handle *h = TlsGetValue(context); h->offset = 0; } -struct property *boot_property_load_cstring(const char *cstring) +struct property *property_util_load_cstring(const char *cstring) { uint32_t s_keyhole; struct property *prop; @@ -259,9 +259,9 @@ struct property *boot_property_load_cstring(const char *cstring) s_keyhole = TlsAlloc(); TlsSetValue(s_keyhole, &read_handle); - prop = do_property_load( - boot_property_cstring_read, - boot_property_cstring_rewind, + prop = property_util_do_load( + property_util_cstring_read, + property_util_cstring_rewind, s_keyhole, ""); @@ -270,19 +270,19 @@ struct property *boot_property_load_cstring(const char *cstring) return prop; } -static int boot_property_avs_read(uint32_t context, void *bytes, size_t nbytes) +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 boot_property_avs_rewind(uint32_t context) +static void property_util_avs_rewind(uint32_t context) { avs_desc desc = (avs_desc) context; avs_fs_lseek(desc, 0, AVS_SEEK_SET); } -struct property *boot_property_load_avs(const char *filename) +struct property *property_util_load_avs(const char *filename) { avs_desc desc; struct property *prop; @@ -293,15 +293,15 @@ struct property *boot_property_load_avs(const char *filename) log_fatal("%s: Error opening configuration file", filename); } - prop = do_property_load( - boot_property_avs_read, boot_property_avs_rewind, desc, filename); + prop = property_util_do_load( + property_util_avs_read, property_util_avs_rewind, desc, filename); avs_fs_close(desc); return prop; } -void boot_property_node_replace_u8( +void property_util_node_replace_u8( struct property *property, struct property_node *node, const char *name, @@ -318,7 +318,7 @@ void boot_property_node_replace_u8( property_node_create(property, node, PROPERTY_TYPE_U8, name, val); } -void boot_property_node_replace_bool( +void property_util_node_replace_bool( struct property *property, struct property_node *node, const char *name, @@ -335,7 +335,7 @@ void boot_property_node_replace_bool( property_node_create(property, node, PROPERTY_TYPE_BOOL, name, val); } -void boot_property_node_replace_str( +void property_util_node_replace_str( struct property *property, struct property_node *node, const char *name, @@ -352,7 +352,7 @@ void boot_property_node_replace_str( property_node_create(property, node, PROPERTY_TYPE_STR, name, val); } -void boot_property_free(struct property *prop) +void property_util_free(struct property *prop) { void *buffer; diff --git a/src/main/launcher/property-util.h b/src/main/launcher/property-util.h new file mode 100644 index 00000000..05b96e73 --- /dev/null +++ b/src/main/launcher/property-util.h @@ -0,0 +1,19 @@ +#ifndef PROPERTY_UTIL_H +#define PROPERTY_UTIL_H + +#include "imports/avs.h" + +void property_util_log(struct property *property); +void property_util_node_log(struct property_node *node); +struct property *property_util_load_file(const char *filename); +struct property *property_util_load_avs(const char *filename); +struct property *property_util_load_cstring(const char *cstring); +void property_util_node_replace_u8(struct property *property, + struct property_node *node, const char *name, uint8_t val); +void property_util_node_replace_bool(struct property *property, + struct property_node *node, const char *name, bool val); +void property_util_node_replace_str(struct property *property, + struct property_node *node, const char *name, const char *val); +void property_util_free(struct property *prop); + +#endif diff --git a/src/main/launcher/property.h b/src/main/launcher/property.h deleted file mode 100644 index 99ef8ea5..00000000 --- a/src/main/launcher/property.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LAUNCHER_PROPERTY_H -#define LAUNCHER_PROPERTY_H - -#include "imports/avs.h" - -void boot_property_log(struct property *property); -void boot_property_node_log(struct property_node *node); -struct property *boot_property_load(const char *filename); -struct property *boot_property_load_avs(const char *filename); -struct property *boot_property_load_cstring(const char *cstring); -void boot_property_node_replace_u8(struct property *property, - struct property_node *node, const char *name, uint8_t val); -void boot_property_node_replace_bool(struct property *property, - struct property_node *node, const char *name, bool val); -void boot_property_node_replace_str(struct property *property, - struct property_node *node, const char *name, const char *val); -void boot_property_free(struct property *prop); - -#endif From 12e254ec1fff9c8c17c2749c7bd12efb8a3e88d4 Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 20:04:10 +0100 Subject: [PATCH 69/72] refactor(launcher): Move avs config stuff to own module Continue modularization by separating configuration concerns from the actual module/component. --- src/main/launcher/Module.mk | 1 + src/main/launcher/avs-config.c | 274 +++++++++++++++++++++++++++ src/main/launcher/avs-config.h | 27 +++ src/main/launcher/bootstrap-config.c | 68 +------ src/main/launcher/bootstrap-config.h | 2 +- src/main/launcher/eamuse-config.c | 2 +- src/main/launcher/main.c | 16 +- 7 files changed, 317 insertions(+), 73 deletions(-) create mode 100644 src/main/launcher/avs-config.c create mode 100644 src/main/launcher/avs-config.h diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index e47e6dba..abdb769b 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -13,6 +13,7 @@ libs_launcher := \ util \ src_launcher := \ + avs-config.c \ avs-context.c \ bootstrap-config.c \ bootstrap-context.c \ diff --git a/src/main/launcher/avs-config.c b/src/main/launcher/avs-config.c new file mode 100644 index 00000000..d3d513d6 --- /dev/null +++ b/src/main/launcher/avs-config.c @@ -0,0 +1,274 @@ +#define LOG_MODULE "avs-config" + +#include + +#include "imports/avs.h" + +#include "imports/avs.h" + +#include "launcher/avs-config.h" +#include "launcher/property-util.h" + +#include "util/log.h" +#include "util/str.h" + +#define AVS_CONFIG_ROOT_NODE "/config" + +struct property* avs_config_load_from_file_path(const char *filepath) +{ + struct property *property; + + log_assert(filepath); + + log_misc("Loading avs-config from file path: %s", filepath); + + property = property_util_load_file(filepath); + + // Check if root node exists, call already errors if not + avs_config_resolve_root_node(property); + + return property; +} + +struct property_node* avs_config_resolve_root_node(struct property *property) +{ + struct property_node *node; + + log_assert(property); + + node = property_search(property, 0, AVS_CONFIG_ROOT_NODE); + + if (node == NULL) { + log_fatal("Root node " AVS_CONFIG_ROOT_NODE " in AVS config missing"); + } + + return node; +} + +void avs_config_set_mode_product(struct property *property, bool enable) +{ + struct property_node *node; + + log_assert(property); + + node = avs_config_resolve_root_node(property); + + property_remove(NULL, node, "mode/product"); + +#if AVS_VERSION <= 1306 + property_node_create( + NULL, node, PROPERTY_TYPE_U8, "mode/product", enable ? 1 : 0); +#else + property_node_create( + NULL, node, PROPERTY_TYPE_BOOL, "mode/product", enable ? 1 : 0); +#endif +} + +void avs_config_set_net_raw(struct property *property, bool enable) +{ + struct property_node *node; + + log_assert(property); + + node = avs_config_resolve_root_node(property); + + property_remove(NULL, node, "net/enable_raw"); + +#if AVS_VERSION <= 1306 + property_node_create( + NULL, node, PROPERTY_TYPE_U8, "net/enable_raw", enable ? 1 : 0); +#else + property_node_create( + NULL, node, PROPERTY_TYPE_BOOL, "net/enable_raw", enable ? 1 : 0); +#endif +} + +void avs_config_set_net_eaudp(struct property *property, bool enable) +{ + struct property_node *node; + + log_assert(property); + + node = avs_config_resolve_root_node(property); + + property_remove(NULL, node, "net/eaudp/enable"); + +#if AVS_VERSION <= 1306 + property_node_create( + NULL, node, PROPERTY_TYPE_U8, "net/eaudp/enable", enable ? 1 : 0); +#else + property_node_create( + NULL, node, PROPERTY_TYPE_BOOL, "net/eaudp/enable", enable ? 1 : 0); +#endif +} + +void avs_config_set_sntp_ea(struct property *property, bool on) +{ + struct property_node *node; + + log_assert(property); + + node = avs_config_resolve_root_node(property); + + property_remove(NULL, node, "sntp/ea_on"); + +#if AVS_VERSION <= 1306 + property_node_create( + NULL, node, PROPERTY_TYPE_U8, "sntp/ea_on", on ? 1 : 0); +#else + property_node_create( + NULL, node, PROPERTY_TYPE_BOOL, "sntp/ea_on", on ? 1 : 0); +#endif +} + +void avs_config_set_fs_root_device(struct property *property, const char *path) +{ + struct property_node *node; + + log_assert(property); + log_assert(path); + + node = avs_config_resolve_root_node(property); + + property_remove(NULL, node, "fs/root/device"); + + property_node_create( + NULL, + node, + PROPERTY_TYPE_STR, + "fs/root/device", + path); +} + +void avs_config_set_logging(struct property *property, const struct bootstrap_config *config) +{ + struct property_node *node; + + log_assert(property); + log_assert(config); + + if (config->log_node) { + node = avs_config_resolve_root_node(property); + + property_remove(NULL, node, "log"); + property_node_clone(NULL, node, config->log_node, TRUE); + } +} + +void avs_config_set_log_level( + struct property *property, + enum log_level loglevel) +{ + struct property_node *log_level_node; + enum property_type type; + const char *loglevel_str; + + log_assert(property); + + log_level_node = property_search(property, NULL, "config/log/level"); + + if (!log_level_node) { + log_fatal("config/log/level missing in AVS configuration"); + } + + type = property_node_type(log_level_node); + + // Different AVS config formats depending on AVS version, detect based on the existing values + switch (type) { + case PROPERTY_TYPE_STR: + switch (loglevel) { + case LOG_LEVEL_FATAL: + loglevel_str = "fatal"; + break; + + case LOG_LEVEL_WARNING: + loglevel_str = "warn"; + break; + + case LOG_LEVEL_INFO: + loglevel_str = "info"; + break; + + case LOG_LEVEL_MISC: + loglevel_str = "misc"; + break; + + default: + log_fatal("Unsupported log level: %d", loglevel); + break; + } + + property_node_remove(log_level_node); + property_node_create(property, log_level_node, PROPERTY_TYPE_STR, NULL, loglevel_str); + + break; + + case PROPERTY_TYPE_U32: + property_node_remove(log_level_node); + property_node_create(property, log_level_node, PROPERTY_TYPE_U32, NULL, loglevel); + + break; + + default: + log_fatal("Unsupported property type %d for config/log/level node in AVS config", type); + break; + } +} + +void avs_config_set_local_fs_path_dev_nvram_and_raw( + struct property* property, + 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(property); + 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(property, NULL, "config/fs"); + + if (!fs_node) { + log_fatal("Cannot find config/fs in avs config"); + } + + // Check if "new" mounttable config is used for dev/nvram and dev/raw or legacy config + if (property_search(property, fs_node, "mounttable")) { + property_remove(property, fs_node, "mounttable"); + + mounttable_node = property_node_create(property, fs_node, PROPERTY_TYPE_VOID, "mounttable"); + + vfs_node = property_node_create(property, mounttable_node, PROPERTY_TYPE_VOID, "vfs"); + + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot"); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs"); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_raw); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/raw"); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1"); + + vfs_node = property_node_create(property, mounttable_node, PROPERTY_TYPE_VOID, "vfs"); + + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot"); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs"); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_nvram); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/nvram"); + property_node_create(property, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1"); + } else { + property_util_node_replace_str(property, fs_node, "nvram/device", path_dev_raw); + property_util_node_replace_str(property, fs_node, "nvram/fstype", "fs"); + property_util_node_replace_str(property, fs_node, "nvram/option", "vf=1,posix=1"); + + property_util_node_replace_str(property, fs_node, "raw/device", path_dev_nvram); + property_util_node_replace_str(property, fs_node, "raw/fstype", "fs"); + property_util_node_replace_str(property, fs_node, "raw/option", "vf=1,posix=1"); + } +} \ No newline at end of file diff --git a/src/main/launcher/avs-config.h b/src/main/launcher/avs-config.h new file mode 100644 index 00000000..9e0c2d37 --- /dev/null +++ b/src/main/launcher/avs-config.h @@ -0,0 +1,27 @@ +#ifndef LAUNCHER_AVS_CONFIG_H +#define LAUNCHER_AVS_CONFIG_H + +#include "imports/avs.h" + +#include "launcher/bootstrap-config.h" + +#include "util/log.h" + +struct property* avs_config_load_from_file_path(const char *filepath); +struct property_node* avs_config_resolve_root_node(struct property *property); +void avs_config_set_mode_product(struct property *property, bool enable); +void avs_config_set_net_raw(struct property *property, bool enable); +void avs_config_set_net_eaudp(struct property *property, bool enable); +void avs_config_set_sntp_ea(struct property *property, bool on); +void avs_config_set_fs_root_device(struct property *property, const char *path); +void avs_config_set_logging( + struct property *property, + const struct bootstrap_config *config); +void avs_config_set_log_level( + struct property *property, + enum log_level loglevel); +void avs_config_set_local_fs_path_dev_nvram_and_raw( + struct property* property, + const char* dev_nvram_raw_path); + +#endif \ No newline at end of file diff --git a/src/main/launcher/bootstrap-config.c b/src/main/launcher/bootstrap-config.c index 0158d868..b5b4bb36 100644 --- a/src/main/launcher/bootstrap-config.c +++ b/src/main/launcher/bootstrap-config.c @@ -3,6 +3,7 @@ #include "imports/avs.h" +#include "launcher/avs-config.h" #include "launcher/bootstrap-config.h" #include "util/defs.h" @@ -374,73 +375,20 @@ bool bootstrap_config_from_property( } void bootstrap_config_update_avs( - const struct bootstrap_config *config, struct property_node *avs_root) + const struct bootstrap_config *config, struct property *avs_property) { -// Different AVS version generations changed the property types in the avs-config.xml slightly -// which needs to be considered to avoid property map reading/write to cause memory corruption -// which naturally lead to hard to debug application failures -// Furthermore, some attributes didn't exist on older versions -#if AVS_VERSION <= 1306 - if (config->module_params) { - property_remove(NULL, avs_root, "mode/product"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_U8, "mode/product", 1); - property_remove(NULL, avs_root, "net/enable_raw"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_U8, "net/enable_raw", 1); - property_remove(NULL, avs_root, "net/eaudp/enable"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_U8, "net/eaudp/enable", 1); - property_remove(NULL, avs_root, "sntp/ea_on"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_U8, "sntp/ea_on", 1); - } - - if (config->startup.drm.device[0]) { - property_remove(NULL, avs_root, "fs/root/device"); - property_node_create( - NULL, - avs_root, - PROPERTY_TYPE_STR, - "fs/root/device", - config->startup.drm.device); - } - - if (config->log_node) { - property_remove(NULL, avs_root, "log"); - property_node_clone(NULL, avs_root, config->log_node, TRUE); - } -#else - if (config->module_params) { - property_remove(NULL, avs_root, "mode/product"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_BOOL, "mode/product", 1); - property_remove(NULL, avs_root, "net/enable_raw"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_BOOL, "net/enable_raw", 1); - property_remove(NULL, avs_root, "net/eaudp/enable"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_BOOL, "net/eaudp/enable", 1); - property_remove(NULL, avs_root, "sntp/ea_on"); - property_node_create( - NULL, avs_root, PROPERTY_TYPE_BOOL, "sntp/ea_on", 1); - } + avs_config_set_mode_product(avs_property, true); + avs_config_set_net_raw(avs_property, true); + avs_config_set_net_eaudp(avs_property, true); + avs_config_set_sntp_ea(avs_property, true); if (config->startup.drm.device[0]) { - property_remove(NULL, avs_root, "fs/root/device"); - property_node_create( - NULL, - avs_root, - PROPERTY_TYPE_STR, - "fs/root/device", - config->startup.drm.device); + avs_config_set_fs_root_device(avs_property, config->startup.drm.device); } if (config->log_node) { - property_remove(NULL, avs_root, "log"); - property_node_clone(NULL, avs_root, config->log_node, TRUE); + avs_config_set_logging(avs_property, config); } -#endif } bool bootstrap_config_iter_default_file( diff --git a/src/main/launcher/bootstrap-config.h b/src/main/launcher/bootstrap-config.h index 84a2bc8c..aba13040 100644 --- a/src/main/launcher/bootstrap-config.h +++ b/src/main/launcher/bootstrap-config.h @@ -139,7 +139,7 @@ bool bootstrap_config_from_property( // TODO this should rather move somewhere else? void bootstrap_config_update_avs( - const struct bootstrap_config *config, struct property_node *avs_root); + const struct bootstrap_config *config, struct property *avs_property); bool bootstrap_config_iter_default_file( struct bootstrap_config *config, struct bootstrap_default_file_config *default_file); diff --git a/src/main/launcher/eamuse-config.c b/src/main/launcher/eamuse-config.c index edcc0f84..54214ec9 100644 --- a/src/main/launcher/eamuse-config.c +++ b/src/main/launcher/eamuse-config.c @@ -38,7 +38,7 @@ struct property_node* eamuse_config_resolve_root_node(struct property *property) node = property_search(property, 0, EAMUSE_CONFIG_ROOT_NODE); if (node == NULL) { - log_fatal("%s: " EAMUSE_CONFIG_ROOT_NODE " missing"); + log_fatal("Root node " EAMUSE_CONFIG_ROOT_NODE " in eamuse config missing"); } return node; diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 728569e6..944ea5f7 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -8,6 +8,7 @@ #include "imports/avs-ea3.h" #include "imports/avs.h" +#include "launcher/avs-config.h" #include "launcher/avs-context.h" #include "launcher/bootstrap-config.h" #include "launcher/bootstrap-context.h" @@ -92,17 +93,10 @@ static void avs_config_setup( enum log_level loglevel, struct property **avs_config_property) { - struct property_node *avs_config_node; - log_assert(bootstrap_config); log_assert(avs_config_property); - *avs_config_property = property_util_load_file(bootstrap_config->startup.avs.config_file); - avs_config_node = property_search(*avs_config_property, 0, "/config"); - - if (avs_config_node == NULL) { - log_fatal("%s: /config missing", bootstrap_config->startup.avs.config_file); - } + *avs_config_property = avs_config_load_from_file_path(bootstrap_config->startup.avs.config_file); if (dev_nvram_raw_path) { if (!path_exists(dev_nvram_raw_path)) { @@ -114,16 +108,16 @@ static void avs_config_setup( } } - avs_context_property_set_local_fs_nvram_raw( + avs_config_set_local_fs_path_dev_nvram_and_raw( *avs_config_property, dev_nvram_raw_path); } if (override_loglevel_enabled) { - avs_context_property_set_log_level(*avs_config_property, loglevel); + avs_config_set_log_level(*avs_config_property, loglevel); } - bootstrap_config_update_avs(bootstrap_config, avs_config_node); + bootstrap_config_update_avs(bootstrap_config, *avs_config_property); } static void load_hook_dlls(struct array *hook_dlls) From b3e7b82eb2ecda3a33fe627a89809c473e30170f Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 20:08:00 +0100 Subject: [PATCH 70/72] refactor(launcher): Rename avs-context -> avs No need for having extra "context" postfix. It's AVS and launcher sets it up accordingly to make things work --- src/main/launcher/Module.mk | 2 +- src/main/launcher/{avs-context.c => avs.c} | 117 +-------------------- src/main/launcher/{avs-context.h => avs.h} | 8 +- src/main/launcher/main.c | 6 +- 4 files changed, 9 insertions(+), 124 deletions(-) rename src/main/launcher/{avs-context.c => avs.c} (54%) rename src/main/launcher/{avs-context.h => avs.h} (54%) diff --git a/src/main/launcher/Module.mk b/src/main/launcher/Module.mk index abdb769b..6c8fc7b6 100644 --- a/src/main/launcher/Module.mk +++ b/src/main/launcher/Module.mk @@ -13,8 +13,8 @@ libs_launcher := \ util \ src_launcher := \ + avs.c \ avs-config.c \ - avs-context.c \ bootstrap-config.c \ bootstrap-context.c \ ea3-ident.c \ diff --git a/src/main/launcher/avs-context.c b/src/main/launcher/avs.c similarity index 54% rename from src/main/launcher/avs-context.c rename to src/main/launcher/avs.c index 35c29464..c5fc3ea2 100644 --- a/src/main/launcher/avs-context.c +++ b/src/main/launcher/avs.c @@ -8,7 +8,7 @@ #include "imports/avs.h" -#include "launcher/avs-context.h" +#include "launcher/avs.h" #include "launcher/logger.h" #include "launcher/property-util.h" @@ -155,118 +155,7 @@ static void _avs_context_create_config_fs_dir( } } -void avs_context_property_set_local_fs_nvram_raw( - struct property *config_prop, - 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; - - 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(config_prop, NULL, "config/fs"); - - if (!fs_node) { - log_fatal("Cannot find config/fs in avs config"); - } - - // Check if "new" mounttable config is used for dev/nvram and dev/raw or legacy config - if (property_search(config_prop, fs_node, "mounttable")) { - property_remove(config_prop, fs_node, "mounttable"); - - mounttable_node = property_node_create(config_prop, fs_node, PROPERTY_TYPE_VOID, "mounttable"); - - vfs_node = property_node_create(config_prop, mounttable_node, PROPERTY_TYPE_VOID, "vfs"); - - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot"); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs"); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_raw); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/raw"); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1"); - - vfs_node = property_node_create(config_prop, mounttable_node, PROPERTY_TYPE_VOID, "vfs"); - - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "name", "boot"); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "fstype", "fs"); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "src", path_dev_nvram); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "dest", "/dev/nvram"); - property_node_create(config_prop, vfs_node, PROPERTY_TYPE_ATTR, "opt", "vf=1,posix=1"); - } else { - property_util_node_replace_str(config_prop, fs_node, "nvram/device", path_dev_raw); - property_util_node_replace_str(config_prop, fs_node, "nvram/fstype", "fs"); - property_util_node_replace_str(config_prop, fs_node, "nvram/option", "vf=1,posix=1"); - - property_util_node_replace_str(config_prop, fs_node, "raw/device", path_dev_nvram); - property_util_node_replace_str(config_prop, fs_node, "raw/fstype", "fs"); - property_util_node_replace_str(config_prop, fs_node, "raw/option", "vf=1,posix=1"); - } -} - -void avs_context_property_set_log_level(struct property *config_prop, enum log_level loglevel) -{ - struct property_node *log_level_node; - enum property_type type; - const char *loglevel_str; - - log_level_node = property_search(config_prop, NULL, "config/log/level"); - - if (!log_level_node) { - log_fatal("config/log/level missing in AVS configuration"); - } - - type = property_node_type(log_level_node); - - // Different AVS config formats depending on AVS version, detect based on the existing values - switch (type) { - case PROPERTY_TYPE_STR: - switch (loglevel) { - case LOG_LEVEL_FATAL: - loglevel_str = "fatal"; - break; - - case LOG_LEVEL_WARNING: - loglevel_str = "warn"; - break; - - case LOG_LEVEL_INFO: - loglevel_str = "info"; - break; - - case LOG_LEVEL_MISC: - loglevel_str = "misc"; - break; - - default: - log_fatal("Unsupported log level: %d", loglevel); - break; - } - - property_node_remove(log_level_node); - property_node_create(config_prop, log_level_node, PROPERTY_TYPE_STR, NULL, loglevel_str); - - break; - - case PROPERTY_TYPE_U32: - property_node_remove(log_level_node); - property_node_create(config_prop, log_level_node, PROPERTY_TYPE_U32, NULL, loglevel); - - break; - - default: - log_fatal("Unsupported property type %d for config/log/level node in AVS config", type); - break; - } -} - -void avs_context_init( +void avs_init( struct property *config_prop, struct property_node *config_node, uint32_t avs_heap_size, @@ -320,7 +209,7 @@ void avs_context_init( #endif } -void avs_context_fini(void) +void avs_fini(void) { avs_shutdown(); diff --git a/src/main/launcher/avs-context.h b/src/main/launcher/avs.h similarity index 54% rename from src/main/launcher/avs-context.h rename to src/main/launcher/avs.h index 82184987..1d46cda8 100644 --- a/src/main/launcher/avs-context.h +++ b/src/main/launcher/avs.h @@ -11,15 +11,11 @@ #define AVS_HAS_STD_HEAP #endif -void avs_context_init( +void avs_init( struct property *config_prop, struct property_node *config_node, uint32_t avs_heap_size, uint32_t std_heap_size); -void avs_context_property_set_local_fs_nvram_raw( - struct property *config_prop, - const char* dev_nvram_raw_path); -void avs_context_property_set_log_level(struct property *config_prop, enum log_level loglevel); -void avs_context_fini(void); +void avs_fini(void); #endif diff --git a/src/main/launcher/main.c b/src/main/launcher/main.c index 944ea5f7..77dd02ca 100644 --- a/src/main/launcher/main.c +++ b/src/main/launcher/main.c @@ -9,7 +9,7 @@ #include "imports/avs.h" #include "launcher/avs-config.h" -#include "launcher/avs-context.h" +#include "launcher/avs.h" #include "launcher/bootstrap-config.h" #include "launcher/bootstrap-context.h" #include "launcher/ea3-ident.h" @@ -383,7 +383,7 @@ int main(int argc, const char **argv) property_util_log(avs_config_property); } - avs_context_init( + avs_init( avs_config_property, property_search(avs_config_property, 0, "/config"), bootstrap_config.startup.avs.avs_heap_size, @@ -465,7 +465,7 @@ int main(int argc, const char **argv) } log_to_writer(log_writer_file, stdout); - avs_context_fini(); + avs_fini(); property_util_log(avs_config_property); module_context_fini(&module); From 8ab634bab96334a7b6bcfbdfc4b1ec61b388df40 Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 1 Jan 2024 20:10:59 +0100 Subject: [PATCH 71/72] refactor(launcher): Move avs fs dir logging function to avs module Fits better into the module/context of AVS than the main module of launcher. --- src/main/launcher/avs.c | 27 +++++++++++++++++++++++++++ src/main/launcher/avs.h | 1 + 2 files changed, 28 insertions(+) diff --git a/src/main/launcher/avs.c b/src/main/launcher/avs.c index c5fc3ea2..f7f041a8 100644 --- a/src/main/launcher/avs.c +++ b/src/main/launcher/avs.c @@ -209,6 +209,33 @@ void avs_init( #endif } +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) { avs_shutdown(); diff --git a/src/main/launcher/avs.h b/src/main/launcher/avs.h index 1d46cda8..e7421e5b 100644 --- a/src/main/launcher/avs.h +++ b/src/main/launcher/avs.h @@ -16,6 +16,7 @@ void avs_init( struct property_node *config_node, uint32_t avs_heap_size, uint32_t std_heap_size); +void avs_fs_dir_log(const char *path); void avs_fini(void); #endif From 4798ed805affb158dbf1286e6d322a857ebddec5 Mon Sep 17 00:00:00 2001 From: icex2 Date: Wed, 3 Jan 2024 00:04:16 +0100 Subject: [PATCH 72/72] wip --- GNUmakefile | 4 +- Module.mk | 114 ++++ dist/bst/gamestart1.bat | 34 +- dist/bst/gamestart2.bat | 34 +- dist/bst/launcher-01.xml | 75 +++ dist/bst/launcher-02.xml | 75 +++ dist/ddr/gamestart-12.bat | 41 +- dist/ddr/gamestart-13.bat | 41 +- dist/ddr/gamestart-14.bat | 46 +- dist/ddr/gamestart-15.bat | 46 +- dist/ddr/gamestart-16.bat | 47 +- dist/ddr/gamestart-17.bat | 47 +- dist/ddr/gamestart-18.bat | 47 +- dist/ddr/launcher-12.xml | 75 +++ dist/ddr/launcher-13.xml | 75 +++ dist/ddr/launcher-14.xml | 75 +++ dist/ddr/launcher-15.xml | 75 +++ dist/ddr/launcher-16.xml | 76 +++ dist/ddr/launcher-17.xml | 76 +++ dist/ddr/launcher-18.xml | 76 +++ dist/dwarfstack/32/dwarfstack.dll | Bin 0 -> 555008 bytes dist/dwarfstack/64/dwarfstack.dll | Bin 0 -> 519168 bytes dist/dwarfstack/readme.md | 1 + dist/iidx/ea3-ident.xml | 10 + dist/iidx/eamuse-server.xml | 6 + dist/iidx/gamestart-18.bat | 36 +- dist/iidx/gamestart-19.bat | 36 +- dist/iidx/gamestart-20.bat | 34 +- dist/iidx/gamestart-21.bat | 34 +- dist/iidx/gamestart-22.bat | 34 +- dist/iidx/gamestart-23.bat | 34 +- dist/iidx/gamestart-24.bat | 34 +- dist/iidx/gamestart-25.bat | 40 +- dist/iidx/gamestart-26.bat | 40 +- dist/iidx/gamestart-27.bat | 40 +- dist/iidx/gamestart-28.bat | 40 +- dist/iidx/gamestart-29.bat | 40 +- dist/iidx/gamestart-30.bat | 40 +- dist/iidx/launcher-18.xml | 75 +++ dist/iidx/launcher-19.xml | 75 +++ dist/iidx/launcher-20.xml | 75 +++ dist/iidx/launcher-21.xml | 75 +++ dist/iidx/launcher-22.xml | 75 +++ dist/iidx/launcher-23.xml | 75 +++ dist/iidx/launcher-24.xml | 75 +++ dist/iidx/launcher-25.xml | 74 +++ dist/iidx/launcher-26.xml | 74 +++ dist/iidx/launcher-27.xml | 74 +++ dist/iidx/launcher-28.xml | 74 +++ dist/iidx/launcher-29.xml | 74 +++ dist/iidx/launcher-30.xml | 74 +++ dist/iidx/pcbid.xml | 7 + dist/jb/ea3-ident.xml | 10 + dist/jb/eamuse-server.xml | 6 + dist/jb/gamestart-03.bat | 35 +- dist/jb/gamestart-04.bat | 35 +- dist/jb/launcher-03.xml | 75 +++ dist/jb/launcher-04.xml | 75 +++ dist/jb/pcbid.xml | 7 + dist/sdvx/ea3-ident.xml | 10 + dist/sdvx/eamuse-server.xml | 6 + dist/sdvx/gamestart.bat | 35 ++ dist/sdvx/launcher.xml | 75 +++ dist/sdvx/pcbid.xml | 7 + dist/sdvx5/ea3-ident.xml | 10 + dist/sdvx5/eamuse-server.xml | 6 + dist/sdvx5/gamestart-cn.bat | 41 +- dist/sdvx5/gamestart.bat | 40 +- dist/sdvx5/launcher-cn.xml | 74 +++ dist/sdvx5/launcher.xml | 74 +++ dist/sdvx5/pcbid.xml | 7 + dist/shared/ea3-ident.xml | 11 + dist/shared/ea3-license.xml | 8 + dist/shared/ea3-service.xml | 7 + src/imports/avs.h | 54 +- src/imports/dwarfstack.h | 162 ++++++ src/imports/import_32_1002_avs.def | 1 + src/imports/import_32_1101_avs.def | 14 +- src/imports/import_32_1304_avs.def | 2 + src/imports/import_32_1306_avs.def | 2 + src/imports/import_32_1403_avs.def | 26 +- src/imports/import_32_1508_avs.def | 3 + src/imports/import_32_1601_avs.def | 15 +- src/imports/import_32_1603_avs.def | 3 + src/imports/import_32_1700_avs.def | 27 +- src/imports/import_32_indep_dwarfstack.def | 13 + src/imports/import_64_1508_avs.def | 15 +- src/imports/import_64_1509_avs.def | 15 +- src/imports/import_64_1601_avs.def | 27 +- src/imports/import_64_1603_avs.def | 3 + src/imports/import_64_1700_avs.def | 27 +- src/imports/import_64_indep_dwarfstack.def | 13 + src/main/avs-util/Module.mk | 6 + src/main/avs-util/error.c | 100 ++++ src/main/avs-util/error.h | 8 + src/main/inject/Module.mk | 2 + src/main/inject/debugger.c | 4 +- src/main/inject/main.c | 2 + src/main/launcher/Module.mk | 15 +- src/main/launcher/avs-config.c | 330 ++++++----- src/main/launcher/avs-config.h | 38 +- src/main/launcher/avs.c | 77 ++- src/main/launcher/avs.h | 18 +- src/main/launcher/bootstrap-config.c | 370 +++++++----- src/main/launcher/bootstrap-config.h | 46 +- src/main/launcher/bootstrap-context.c | 92 --- src/main/launcher/bootstrap-context.h | 25 - src/main/launcher/bootstrap.c | 294 ++++++++++ src/main/launcher/bootstrap.h | 31 + src/main/launcher/debug.c | 33 ++ src/main/launcher/debug.h | 6 + src/main/launcher/ea3-ident-config.c | 100 ++++ .../{ea3-ident.h => ea3-ident-config.h} | 20 +- src/main/launcher/ea3-ident.c | 77 --- src/main/launcher/eamuse-config.c | 118 ++-- src/main/launcher/eamuse-config.h | 24 +- src/main/launcher/eamuse.c | 58 +- src/main/launcher/eamuse.h | 12 +- src/main/launcher/hook.c | 34 ++ src/main/launcher/hook.h | 6 + src/main/launcher/launcher-config.c | 348 ++++++++++++ src/main/launcher/launcher-config.h | 54 ++ src/main/launcher/launcher.c | 528 ++++++++++++++++++ src/main/launcher/launcher.h | 8 + src/main/launcher/logger.c | 78 ++- src/main/launcher/logger.h | 36 +- src/main/launcher/main.c | 469 +--------------- src/main/launcher/module.c | 139 ++++- src/main/launcher/module.h | 18 +- src/main/launcher/options.c | 221 +++----- src/main/launcher/options.h | 60 +- src/main/launcher/property-util.c | 359 +++++++++--- src/main/launcher/property-util.h | 25 +- src/main/launcher/stubs.c | 2 + src/main/util/Module.mk | 1 + src/main/util/debug.c | 173 ++++++ src/main/util/debug.h | 10 + src/main/util/log.c | 9 + src/main/util/log.h | 1 + src/main/util/proc.c | 36 ++ src/main/util/proc.h | 3 + src/main/util/signal.c | 83 --- todo.md | 35 ++ 143 files changed, 6565 insertions(+), 1807 deletions(-) create mode 100644 dist/bst/launcher-01.xml create mode 100644 dist/bst/launcher-02.xml create mode 100644 dist/ddr/launcher-12.xml create mode 100644 dist/ddr/launcher-13.xml create mode 100644 dist/ddr/launcher-14.xml create mode 100644 dist/ddr/launcher-15.xml create mode 100644 dist/ddr/launcher-16.xml create mode 100644 dist/ddr/launcher-17.xml create mode 100644 dist/ddr/launcher-18.xml create mode 100644 dist/dwarfstack/32/dwarfstack.dll create mode 100644 dist/dwarfstack/64/dwarfstack.dll create mode 100644 dist/dwarfstack/readme.md create mode 100644 dist/iidx/ea3-ident.xml create mode 100644 dist/iidx/eamuse-server.xml create mode 100644 dist/iidx/launcher-18.xml create mode 100644 dist/iidx/launcher-19.xml create mode 100644 dist/iidx/launcher-20.xml create mode 100644 dist/iidx/launcher-21.xml create mode 100644 dist/iidx/launcher-22.xml create mode 100644 dist/iidx/launcher-23.xml create mode 100644 dist/iidx/launcher-24.xml create mode 100644 dist/iidx/launcher-25.xml create mode 100644 dist/iidx/launcher-26.xml create mode 100644 dist/iidx/launcher-27.xml create mode 100644 dist/iidx/launcher-28.xml create mode 100644 dist/iidx/launcher-29.xml create mode 100644 dist/iidx/launcher-30.xml create mode 100644 dist/iidx/pcbid.xml create mode 100644 dist/jb/ea3-ident.xml create mode 100644 dist/jb/eamuse-server.xml create mode 100644 dist/jb/launcher-03.xml create mode 100644 dist/jb/launcher-04.xml create mode 100644 dist/jb/pcbid.xml create mode 100644 dist/sdvx/ea3-ident.xml create mode 100644 dist/sdvx/eamuse-server.xml create mode 100644 dist/sdvx/launcher.xml create mode 100644 dist/sdvx/pcbid.xml create mode 100644 dist/sdvx5/ea3-ident.xml create mode 100644 dist/sdvx5/eamuse-server.xml create mode 100644 dist/sdvx5/launcher-cn.xml create mode 100644 dist/sdvx5/launcher.xml create mode 100644 dist/sdvx5/pcbid.xml create mode 100644 dist/shared/ea3-ident.xml create mode 100644 dist/shared/ea3-license.xml create mode 100644 dist/shared/ea3-service.xml create mode 100644 src/imports/dwarfstack.h create mode 100644 src/imports/import_32_indep_dwarfstack.def create mode 100644 src/imports/import_64_indep_dwarfstack.def create mode 100644 src/main/avs-util/Module.mk create mode 100644 src/main/avs-util/error.c create mode 100644 src/main/avs-util/error.h delete mode 100644 src/main/launcher/bootstrap-context.c delete mode 100644 src/main/launcher/bootstrap-context.h create mode 100644 src/main/launcher/bootstrap.c create mode 100644 src/main/launcher/bootstrap.h create mode 100644 src/main/launcher/debug.c create mode 100644 src/main/launcher/debug.h create mode 100644 src/main/launcher/ea3-ident-config.c rename src/main/launcher/{ea3-ident.h => ea3-ident-config.h} (52%) delete mode 100644 src/main/launcher/ea3-ident.c create mode 100644 src/main/launcher/hook.c create mode 100644 src/main/launcher/hook.h create mode 100644 src/main/launcher/launcher-config.c create mode 100644 src/main/launcher/launcher-config.h create mode 100644 src/main/launcher/launcher.c create mode 100644 src/main/launcher/launcher.h create mode 100644 src/main/util/debug.c create mode 100644 src/main/util/debug.h create mode 100644 todo.md diff --git a/GNUmakefile b/GNUmakefile index be0bd9ba..c7ffdde3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -25,7 +25,7 @@ toolchain_64 := x86_64-w64-mingw32- gitrev := $(shell git rev-parse HEAD) cppflags := -I src -I src/main -I src/test -DGITREV=$(gitrev) -cflags := -O2 -pipe -ffunction-sections -fdata-sections \ +cflags := -g -O2 -pipe -ffunction-sections -fdata-sections \ -Wall -std=c99 -DPSAPI_VERSION=1 cflags_release := -Werror ldflags := -Wl,--gc-sections -static-libgcc @@ -240,7 +240,6 @@ $$(dll_$1_$2_$3) $$(implib_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) \ $(V)$$(toolchain_$1)gcc -shared \ -o $$(dll_$1_$2_$3) -Wl,--out-implib,$$(implib_$1_$2_$3) \ -Wl,--start-group $$^ -Wl,--end-group $$(ldflags_$3) - $(V)$$(toolchain_$1)strip $$(dll_$1_$2_$3) $(V)$$(toolchain_$1)ranlib $$(implib_$1_$2_$3) endef @@ -257,7 +256,6 @@ $$(exe_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) $$(absdpl_$1_$2_$3) \ | $$(bindir_$1_$2) $(V)echo ... $$@ $(V)$$(toolchain_$1)gcc -o $$@ $$^ $$(ldflags_$3) - $(V)$$(toolchain_$1)strip $$@ endef diff --git a/Module.mk b/Module.mk index 4ad823fb..803bca7a 100644 --- a/Module.mk +++ b/Module.mk @@ -91,6 +91,7 @@ include src/main/acioemu/Module.mk include src/main/aciomgr/Module.mk include src/main/aciotest/Module.mk include src/main/asio/Module.mk +include src/main/avs-util/Module.mk include src/main/bio2drv/Module.mk include src/main/bio2emu-iidx/Module.mk include src/main/bio2emu/Module.mk @@ -257,6 +258,7 @@ $(zipdir)/iidx-09-to-12.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-09.bat \ dist/iidx/gamestart-10.bat \ @@ -282,6 +284,7 @@ $(zipdir)/iidx-13.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-13.bat \ dist/iidx/iidxhook-13.conf \ @@ -298,6 +301,7 @@ $(zipdir)/iidx-14-to-17.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-14.bat \ dist/iidx/gamestart-15.bat \ @@ -323,6 +327,10 @@ $(zipdir)/iidx-18.zip: \ dist/iidx/config.bat \ dist/iidx/gamestart-18.bat \ dist/iidx/iidxhook-18.conf \ + dist/iidx/launcher-18.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ dist/iidx/vefx.txt \ | $(zipdir)/ $(V)echo ... $@ @@ -335,6 +343,7 @@ $(zipdir)/iidx-18-cn.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-18-cn.bat \ dist/iidx/iidxhook-18-cn.conf \ @@ -351,9 +360,14 @@ $(zipdir)/iidx-19.zip: \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-19.bat \ dist/iidx/iidxhook-19.conf \ + dist/iidx/launcher-19.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ dist/iidx/vefx.txt \ | $(zipdir)/ $(V)echo ... $@ @@ -367,9 +381,14 @@ $(zipdir)/iidx-20.zip: \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-20.bat \ dist/iidx/iidxhook-20.conf \ + dist/iidx/launcher-20.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ dist/iidx/vefx.txt \ | $(zipdir)/ $(V)echo ... $@ @@ -382,6 +401,7 @@ $(zipdir)/iidx-20-cn.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-20-cn.bat \ dist/iidx/iidxhook-20-cn.conf \ @@ -398,6 +418,7 @@ $(zipdir)/iidx-21-to-24.zip: \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-21.bat \ dist/iidx/gamestart-22.bat \ @@ -407,6 +428,13 @@ $(zipdir)/iidx-21-to-24.zip: \ dist/iidx/iidxhook-22.conf \ dist/iidx/iidxhook-23.conf \ dist/iidx/iidxhook-24.conf \ + dist/iidx/launcher-21.xml \ + dist/iidx/launcher-22.xml \ + dist/iidx/launcher-23.xml \ + dist/iidx/launcher-24.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ dist/iidx/vefx.txt \ | $(zipdir)/ $(V)echo ... $@ @@ -420,11 +448,17 @@ $(zipdir)/iidx-25-to-26.zip: \ build/bin/indep-64/geninput.dll \ build/bin/indep-64/iidxio.dll \ build/bin/indep-64/vefxio.dll \ + dist/dwarfstack/64/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-25.bat \ dist/iidx/gamestart-26.bat \ dist/iidx/iidxhook-25.conf \ dist/iidx/iidxhook-26.conf \ + dist/iidx/launcher-25.xml \ + dist/iidx/launcher-26.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ dist/iidx/vefx.txt \ | $(zipdir)/ $(V)echo ... $@ @@ -438,6 +472,7 @@ $(zipdir)/iidx-27-to-30.zip: \ build/bin/indep-64/geninput.dll \ build/bin/indep-64/iidxio.dll \ build/bin/indep-64/vefxio.dll \ + dist/dwarfstack/64/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-27.bat \ dist/iidx/gamestart-28.bat \ @@ -447,6 +482,13 @@ $(zipdir)/iidx-27-to-30.zip: \ dist/iidx/iidxhook-28.conf \ dist/iidx/iidxhook-29.conf \ dist/iidx/iidxhook-30.conf \ + dist/iidx/launcher-27.xml \ + dist/iidx/launcher-28.xml \ + dist/iidx/launcher-29.xml \ + dist/iidx/launcher-30.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ dist/iidx/vefx.txt \ | $(zipdir)/ $(V)echo ... $@ @@ -485,6 +527,7 @@ $(zipdir)/jb-01.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-01.bat \ dist/jb/jbhook-01.conf \ @@ -499,6 +542,7 @@ $(zipdir)/jb-02.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-02.bat \ dist/jb/jbhook-02.conf \ @@ -513,8 +557,13 @@ $(zipdir)/jb-03.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-03.bat \ + dist/jb/launcher-03.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -526,8 +575,13 @@ $(zipdir)/jb-04.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-03.bat \ + dist/jb/launcher-03.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -539,8 +593,13 @@ $(zipdir)/jb-05-to-07.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-04.bat \ + dist/jb/launcher-04.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -552,8 +611,13 @@ $(zipdir)/jb-08.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-04.bat \ + dist/jb/launcher-04.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -576,8 +640,13 @@ $(zipdir)/sdvx-01-to-04.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/sdvxio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/sdvx/config.bat \ dist/sdvx/gamestart.bat \ + dist/sdvx/launcher.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -589,9 +658,14 @@ $(zipdir)/sdvx-05-to-06.zip: \ build/bin/indep-64/eamio.dll \ build/bin/indep-64/geninput.dll \ build/bin/indep-64/sdvxio.dll \ + dist/dwarfstack/64/dwarfstack.dll \ dist/sdvx5/config.bat \ dist/sdvx5/gamestart.bat \ dist/sdvx5/sdvxhook2.conf \ + dist/sdvx5/launcher.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -603,9 +677,14 @@ $(zipdir)/sdvx-05-cn.zip: \ build/bin/indep-64/eamio.dll \ build/bin/indep-64/geninput.dll \ build/bin/indep-64/sdvxio.dll \ + dist/dwarfstack/64/dwarfstack.dll \ dist/sdvx5/config.bat \ dist/sdvx5/gamestart-cn.bat \ dist/sdvx5/sdvxhook2-cn.conf \ + dist/sdvx5/launcher-cn.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -659,6 +738,7 @@ $(zipdir)/ddr-12-us.zip: \ build/bin/indep-32/ddrio-smx.dll \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/ddr/config.bat \ dist/ddr/gamestart-12-us.bat \ dist/ddr/gamestart-12-eu.bat \ @@ -678,8 +758,13 @@ $(zipdir)/ddr-12.zip: \ build/bin/indep-32/ddrio-smx.dll \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/ddr/config.bat \ dist/ddr/gamestart-12.bat \ + dist/ddr/launcher-12.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -694,8 +779,13 @@ $(zipdir)/ddr-13.zip: \ build/bin/indep-32/ddrio-smx.dll \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/ddr/config.bat \ dist/ddr/gamestart-13.bat \ + dist/ddr/launcher-13.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -708,12 +798,21 @@ $(zipdir)/ddr-14-to-18.zip: \ build/bin/indep-32/ddrio.dll \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/ddr/config.bat \ dist/ddr/gamestart-14.bat \ dist/ddr/gamestart-15.bat \ dist/ddr/gamestart-16.bat \ dist/ddr/gamestart-17.bat \ dist/ddr/gamestart-18.bat \ + dist/ddr/launcher-14.xml \ + dist/ddr/launcher-15.xml \ + dist/ddr/launcher-16.xml \ + dist/ddr/launcher-17.xml \ + dist/ddr/launcher-18.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -726,10 +825,17 @@ $(zipdir)/ddr-16-to-18-x64.zip: \ build/bin/indep-64/ddrio.dll \ build/bin/indep-64/eamio.dll \ build/bin/indep-64/geninput.dll \ + dist/dwarfstack/64/dwarfstack.dll \ dist/ddr/config.bat \ dist/ddr/gamestart-16.bat \ dist/ddr/gamestart-17.bat \ dist/ddr/gamestart-18.bat \ + dist/ddr/launcher-16.xml \ + dist/ddr/launcher-17.xml \ + dist/ddr/launcher-18.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -765,9 +871,15 @@ $(zipdir)/bst.zip: \ build/bin/indep-64/config.exe \ build/bin/indep-64/eamio.dll \ build/bin/indep-64/geninput.dll \ + dist/dwarfstack/64/dwarfstack.dll \ dist/bst/config.bat \ dist/bst/gamestart1.bat \ dist/bst/gamestart2.bat \ + dist/bst/launcher-01.xml \ + dist/bst/launcher-02.xml \ + dist/shared/ea3-ident.xml \ + dist/shared/ea3-license.xml \ + dist/shared/ea3-service.xml \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -j $@ $^ @@ -780,6 +892,7 @@ $(zipdir)/popn-15-to-18.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/popnio.dll \ build/bin/indep-32/ezusb2-popn-shim.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/popn/config.bat \ dist/popn/gamestart-15.bat \ dist/popn/gamestart-16.bat \ @@ -811,6 +924,7 @@ $(BUILDDIR)/tests.zip: \ build/bin/indep-32/iidxhook-util-config-gfx-test.exe \ build/bin/indep-32/iidxhook-util-config-misc-test.exe \ build/bin/indep-32/iidxhook-util-config-sec-test.exe \ + dist/dwarfstack/32/dwarfstack.dll \ build/bin/indep-32/inject.exe \ build/bin/indep-32/security-id-test.exe \ build/bin/indep-32/security-mcode-test.exe \ diff --git a/dist/bst/gamestart1.bat b/dist/bst/gamestart1.bat index 5a3a9155..5bca8eb1 100644 --- a/dist/bst/gamestart1.bat +++ b/dist/bst/gamestart1.bat @@ -1,10 +1,34 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist dev\nvram mkdir dev\nvram -if not exist dev\nvram\coin.xml copy prop\defaults\coin.xml dev\nvram\coin.xml -if not exist dev\nvram\eacoin.xml copy prop\defaults\eacoin.xml dev\nvram\eacoin.xml -if not exist dev\raw mkdir dev\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -launcher -K bsthook.dll -E prop/ea3-config-1.xml beatstream1.dll %* +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-01.xml %* \ No newline at end of file diff --git a/dist/bst/gamestart2.bat b/dist/bst/gamestart2.bat index 8b6f53f8..55340c37 100644 --- a/dist/bst/gamestart2.bat +++ b/dist/bst/gamestart2.bat @@ -1,10 +1,34 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist dev\nvram mkdir dev\nvram -if not exist dev\nvram\coin.xml copy prop\defaults\coin.xml dev\nvram\coin.xml -if not exist dev\nvram\eacoin.xml copy prop\defaults\eacoin.xml dev\nvram\eacoin.xml -if not exist dev\raw mkdir dev\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -launcher -K bsthook.dll -E prop/ea3-config-2.xml beatstream2.dll %* +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-02.xml %* \ No newline at end of file diff --git a/dist/bst/launcher-01.xml b/dist/bst/launcher-01.xml new file mode 100644 index 00000000..b62630ef --- /dev/null +++ b/dist/bst/launcher-01.xml @@ -0,0 +1,75 @@ + + + + bemanitools_local_fs + + + + + + + + + + + prop/avs-config.xml + 16777216 + 16777216 + ∂ + + info + + + /dev/nvram/ea3-config.xml + + + beatstream1.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-server.xml + + + + bsthook.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/bst/launcher-02.xml b/dist/bst/launcher-02.xml new file mode 100644 index 00000000..fdad81d9 --- /dev/null +++ b/dist/bst/launcher-02.xml @@ -0,0 +1,75 @@ + + + + bemanitools_local_fs + + + + + + + + + + + prop/avs-config.xml + 16777216 + 16777216 + + + info + + + /dev/nvram/ea3-config.xml + + + beatstream2.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + bsthook.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/ddr/gamestart-12.bat b/dist/ddr/gamestart-12.bat index 7df3fa33..03daa9c3 100644 --- a/dist/ddr/gamestart-12.bat +++ b/dist/ddr/gamestart-12.bat @@ -1,11 +1,42 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist conf\nvram mkdir conf\nvram -if not exist conf\raw mkdir conf\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set COM_DIR=%CONTENT_DIR%\com +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Register video codecs, e.g. required for background videos +regsvr32 /s %COM_DIR%\k-clvsd.dll + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -regsvr32 /s k-clvsd.dll -regsvr32 /s xactengine2_10.dll +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-13.xml %* -.\launcher.exe -K .\ddrhook2.dll .\ddr.dll %* +:: Unregister video codec to avoid conflicts with other/older video codecs crashing different +:: game versions, e.g. DDR X +regsvr32 /u /s %COM_DIR%\k-clvsd.dll \ No newline at end of file diff --git a/dist/ddr/gamestart-13.bat b/dist/ddr/gamestart-13.bat index 7df3fa33..03daa9c3 100644 --- a/dist/ddr/gamestart-13.bat +++ b/dist/ddr/gamestart-13.bat @@ -1,11 +1,42 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist conf\nvram mkdir conf\nvram -if not exist conf\raw mkdir conf\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set COM_DIR=%CONTENT_DIR%\com +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Register video codecs, e.g. required for background videos +regsvr32 /s %COM_DIR%\k-clvsd.dll + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -regsvr32 /s k-clvsd.dll -regsvr32 /s xactengine2_10.dll +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-13.xml %* -.\launcher.exe -K .\ddrhook2.dll .\ddr.dll %* +:: Unregister video codec to avoid conflicts with other/older video codecs crashing different +:: game versions, e.g. DDR X +regsvr32 /u /s %COM_DIR%\k-clvsd.dll \ No newline at end of file diff --git a/dist/ddr/gamestart-14.bat b/dist/ddr/gamestart-14.bat index 2ad52121..2364f521 100644 --- a/dist/ddr/gamestart-14.bat +++ b/dist/ddr/gamestart-14.bat @@ -1,14 +1,44 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist conf\nvram mkdir conf\nvram -if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml -if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml -if not exist conf\nvram\share-config.xml copy prop\share-config.xml conf\nvram\share-config.xml -if not exist conf\raw mkdir conf\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set COM_DIR=%CONTENT_DIR%\com +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Register video codecs, e.g. required for background videos +regsvr32 /s %COM_DIR%\k-clvsd.dll +regsvr32 /s %COM_DIR%\xactengine2_10.dll + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -regsvr32 /s k-clvsd.dll -regsvr32 /s xactengine2_10.dll +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-14.xml %* -.\launcher.exe -K .\ddrhook2.dll .\mdxja_945.dll %* +:: Unregister video codec to avoid conflicts with other/older video codecs crashing different +:: game versions, e.g. DDR X +regsvr32 /u /s %COM_DIR%\k-clvsd.dll +regsvr32 /u /s %COM_DIR%\xactengine2_10.dll \ No newline at end of file diff --git a/dist/ddr/gamestart-15.bat b/dist/ddr/gamestart-15.bat index 2ad52121..97301177 100644 --- a/dist/ddr/gamestart-15.bat +++ b/dist/ddr/gamestart-15.bat @@ -1,14 +1,44 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist conf\nvram mkdir conf\nvram -if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml -if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml -if not exist conf\nvram\share-config.xml copy prop\share-config.xml conf\nvram\share-config.xml -if not exist conf\raw mkdir conf\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set COM_DIR=%CONTENT_DIR%\com +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Register video codecs, e.g. required for background videos +regsvr32 /s %COM_DIR%\k-clvsd.dll +regsvr32 /s %COM_DIR%\xactengine2_10.dll + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -regsvr32 /s k-clvsd.dll -regsvr32 /s xactengine2_10.dll +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-15.xml %* -.\launcher.exe -K .\ddrhook2.dll .\mdxja_945.dll %* +:: Unregister video codec to avoid conflicts with other/older video codecs crashing different +:: game versions, e.g. DDR X +regsvr32 /u /s %COM_DIR%\k-clvsd.dll +regsvr32 /u /s %COM_DIR%\xactengine2_10.dll \ No newline at end of file diff --git a/dist/ddr/gamestart-16.bat b/dist/ddr/gamestart-16.bat index d108118b..b176cb3a 100644 --- a/dist/ddr/gamestart-16.bat +++ b/dist/ddr/gamestart-16.bat @@ -1,15 +1,44 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist conf\nvram mkdir conf\nvram -if not exist conf\nvram\ea3-config.xml copy prop\eamuse-config.xml conf\nvram\ea3-config.xml -if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml -if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml -if not exist conf\nvram\testmode-v.xml copy prop\testmode-v.xml conf\nvram\testmode-v.xml -if not exist conf\raw mkdir conf\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set COM_DIR=%CONTENT_DIR%\com +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Register video codecs, e.g. required for background videos +regsvr32 /s %COM_DIR%\k-clvsd.dll +regsvr32 /s %COM_DIR%\xactengine2_10.dll + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -regsvr32 /s com\k-clvsd.dll -regsvr32 /s com\xactengine2_10.dll +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-17.xml %* -.\launcher.exe -H 33554432 -K .\ddrhook2.dll .\arkmdxp3.dll %* +:: Unregister video codec to avoid conflicts with other/older video codecs crashing different +:: game versions, e.g. DDR X +regsvr32 /u /s %COM_DIR%\k-clvsd.dll +regsvr32 /u /s %COM_DIR%\xactengine2_10.dll \ No newline at end of file diff --git a/dist/ddr/gamestart-17.bat b/dist/ddr/gamestart-17.bat index d108118b..b176cb3a 100644 --- a/dist/ddr/gamestart-17.bat +++ b/dist/ddr/gamestart-17.bat @@ -1,15 +1,44 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist conf\nvram mkdir conf\nvram -if not exist conf\nvram\ea3-config.xml copy prop\eamuse-config.xml conf\nvram\ea3-config.xml -if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml -if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml -if not exist conf\nvram\testmode-v.xml copy prop\testmode-v.xml conf\nvram\testmode-v.xml -if not exist conf\raw mkdir conf\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set COM_DIR=%CONTENT_DIR%\com +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Register video codecs, e.g. required for background videos +regsvr32 /s %COM_DIR%\k-clvsd.dll +regsvr32 /s %COM_DIR%\xactengine2_10.dll + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -regsvr32 /s com\k-clvsd.dll -regsvr32 /s com\xactengine2_10.dll +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-17.xml %* -.\launcher.exe -H 33554432 -K .\ddrhook2.dll .\arkmdxp3.dll %* +:: Unregister video codec to avoid conflicts with other/older video codecs crashing different +:: game versions, e.g. DDR X +regsvr32 /u /s %COM_DIR%\k-clvsd.dll +regsvr32 /u /s %COM_DIR%\xactengine2_10.dll \ No newline at end of file diff --git a/dist/ddr/gamestart-18.bat b/dist/ddr/gamestart-18.bat index d108118b..95a92ed0 100644 --- a/dist/ddr/gamestart-18.bat +++ b/dist/ddr/gamestart-18.bat @@ -1,15 +1,44 @@ @echo off +:: Game doesn't work properly when not run with administrator privileges +>nul 2>&1 net session + +if %errorlevel% neq 0 ( + echo This script requires administrative privileges. + echo Please run the script as an administrator. + pause + exit 1 +) + +:: Script expects to be located in a subfolder "bemanitools" in the root folder +:: (contents/) next to the folders modules, data etc. cd /d %~dp0 -if not exist conf\nvram mkdir conf\nvram -if not exist conf\nvram\ea3-config.xml copy prop\eamuse-config.xml conf\nvram\ea3-config.xml -if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml -if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml -if not exist conf\nvram\testmode-v.xml copy prop\testmode-v.xml conf\nvram\testmode-v.xml -if not exist conf\raw mkdir conf\raw +:: Script expects to be located in the root folder (contents/) next to the +:: folders modules, data etc. +set CONTENT_DIR=%CD%\.. +set BEMANITOOLS_DIR=%CONTENT_DIR%\bemanitools +set COM_DIR=%CONTENT_DIR%\com +set MODULES_DIR=%CONTENT_DIR%\modules + +:: Register video codecs, e.g. required for background videos +regsvr32 /s %COM_DIR%\k-clvsd.dll +regsvr32 /s %COM_DIR%\xactengine2_10.dll + +:: Keep that data vanilla, no need to copy these around anymore +:: Just add them to the env PATH so launcher can find the libs and game executable +:: Remark: This also requires admin privileges to propage correctly to launcher +set PATH=^ +%MODULES_DIR%;^ +%BEMANITOOLS_DIR%;^ +%PATH% + +:: Current working dir is the game's root folder +cd /d %CONTENT_DIR% -regsvr32 /s com\k-clvsd.dll -regsvr32 /s com\xactengine2_10.dll +%BEMANITOOLS_DIR%\launcher %BEMANITOOLS_DIR%\launcher-18.xml %* -.\launcher.exe -H 33554432 -K .\ddrhook2.dll .\arkmdxp3.dll %* +:: Unregister video codec to avoid conflicts with other/older video codecs crashing different +:: game versions, e.g. DDR X +regsvr32 /u /s %COM_DIR%\k-clvsd.dll +regsvr32 /u /s %COM_DIR%\xactengine2_10.dll \ No newline at end of file diff --git a/dist/ddr/launcher-12.xml b/dist/ddr/launcher-12.xml new file mode 100644 index 00000000..54c0284b --- /dev/null +++ b/dist/ddr/launcher-12.xml @@ -0,0 +1,75 @@ + + + + bemanitools_local_fs + + + + + + + + + + + prop/avs-config.xml + 31457280 + 31457280 + + + info + + + /dev/nvram/ea3-config.xml + + + ddr.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + ddrhook2.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/ddr/launcher-13.xml b/dist/ddr/launcher-13.xml new file mode 100644 index 00000000..54c0284b --- /dev/null +++ b/dist/ddr/launcher-13.xml @@ -0,0 +1,75 @@ + + + + bemanitools_local_fs + + + + + + + + + + + prop/avs-config.xml + 31457280 + 31457280 + + + info + + + /dev/nvram/ea3-config.xml + + + ddr.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + ddrhook2.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/ddr/launcher-14.xml b/dist/ddr/launcher-14.xml new file mode 100644 index 00000000..9ae27ac3 --- /dev/null +++ b/dist/ddr/launcher-14.xml @@ -0,0 +1,75 @@ + + + + bemanitools_local_fs + + + + + + + + + + + prop/avs-config.xml + 31457280 + 31457280 + + + info + + + /dev/nvram/ea3-config.xml + + + mdxja_945.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + ddrhook2.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/ddr/launcher-15.xml b/dist/ddr/launcher-15.xml new file mode 100644 index 00000000..9ae27ac3 --- /dev/null +++ b/dist/ddr/launcher-15.xml @@ -0,0 +1,75 @@ + + + + bemanitools_local_fs + + + + + + + + + + + prop/avs-config.xml + 31457280 + 31457280 + + + info + + + /dev/nvram/ea3-config.xml + + + mdxja_945.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + ddrhook2.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/ddr/launcher-16.xml b/dist/ddr/launcher-16.xml new file mode 100644 index 00000000..cdf16d55 --- /dev/null +++ b/dist/ddr/launcher-16.xml @@ -0,0 +1,76 @@ + + + + bemanitools_local_fs + + + + + + + + + + + + prop/avs-config.xml + 33554432 + 1048576 + + + info + + + /dev/nvram/ea3-config.xml + + + arkmdxp3.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + ddrhook2.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/ddr/launcher-17.xml b/dist/ddr/launcher-17.xml new file mode 100644 index 00000000..cdf16d55 --- /dev/null +++ b/dist/ddr/launcher-17.xml @@ -0,0 +1,76 @@ + + + + bemanitools_local_fs + + + + + + + + + + + + prop/avs-config.xml + 33554432 + 1048576 + + + info + + + /dev/nvram/ea3-config.xml + + + arkmdxp3.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + ddrhook2.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/ddr/launcher-18.xml b/dist/ddr/launcher-18.xml new file mode 100644 index 00000000..cdf16d55 --- /dev/null +++ b/dist/ddr/launcher-18.xml @@ -0,0 +1,76 @@ + + + + bemanitools_local_fs + + + + + + + + + + + + prop/avs-config.xml + 33554432 + 1048576 + + + info + + + /dev/nvram/ea3-config.xml + + + arkmdxp3.dll + + + + + + + + + bemanitools/ea3-ident.xml + bemanitools/ea3-license.xml + + + + + + + . + + + dev/nvram + fs + + + + dev/raw + fs + + + + + + + + bemanitools/ea3-service.xml + + + + ddrhook2.dll + + + + + + + + 0 + 0 + + \ No newline at end of file diff --git a/dist/dwarfstack/32/dwarfstack.dll b/dist/dwarfstack/32/dwarfstack.dll new file mode 100644 index 0000000000000000000000000000000000000000..74b9796d0740d80ab1771704689e9e960ca6db70 GIT binary patch literal 555008 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

KBB7MRsob6CzwA@ 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≦ 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