From 93307ffe3a2ecab90625a8073fad09ebfc67d0ba Mon Sep 17 00:00:00 2001 From: Arignir Date: Thu, 28 Dec 2023 12:26:46 +0100 Subject: [PATCH] Add support for loading embedded games into an archive. --- .github/workflows/build.yml | 2 +- .gitmodules | 4 +- external/meson.build | 23 ++++++++ meson.build | 2 + source/app/emulator.c | 101 +++++++++++++++++++++++++++++++++++- source/app/meson.build | 5 +- subprojects/libarchive | 1 + 7 files changed, 134 insertions(+), 4 deletions(-) create mode 160000 subprojects/libarchive diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54ed21d6..9df089b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: - name: Install Dependencies uses: msys2/setup-msys2@v2 with: - install: make mingw-w64-x86_64-meson mingw-w64-x86_64-ninja mingw-w64-x86_64-pkg-config mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 mingw-w64-x86_64-glew + install: make mingw-w64-x86_64-meson mingw-w64-x86_64-ninja mingw-w64-x86_64-pkg-config mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 mingw-w64-x86_64-glew mingw-w64-x86_64-cmake mingw-w64-x86_64-cmake libarchive-devel - name: Build Hades run: | meson build -Dstatic_executable=true diff --git a/.gitmodules b/.gitmodules index b03b45b5..61e5f3a4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,6 @@ [submodule "external/cimgui"] path = external/cimgui url = https://github.com/cimgui/cimgui.git - branch = Lib_Only [submodule "external/stb"] path = external/stb url = https://github.com/nothings/stb.git @@ -11,3 +10,6 @@ [submodule "external/mjson"] path = external/mjson url = https://github.com/cesanta/mjson.git +[submodule "subprojects/libarchive"] + path = subprojects/libarchive + url = https://github.com/libarchive/libarchive.git diff --git a/external/meson.build b/external/meson.build index 3130184a..4e4002bf 100644 --- a/external/meson.build +++ b/external/meson.build @@ -126,3 +126,26 @@ nfde = static_library( cpp_args: cflags, link_args: ldflags, ) + +############################### +## libarchive ## +############################### + +libarchive = cmake.subproject( + 'libarchive', + cmake_options: [ + '-DCMAKE_BUILD_TYPE=Release', + '-DCMAKE_INSTALL_LIBDIR=lib', + '-DCMAKE_POSITION_INDEPENDENT_CODE=ON', + '-DBUILD_SHARED_LIBS=OFF', + '-DENABLE_ACL=OFF', + '-DENABLE_CPIO=OFF', + '-DENABLE_TAR=OFF', + '-DENABLE_CAT=OFF', + '-DENABLE_UNZIP=OFF', + '-DENABLE_TEST=OFF', + '-DENABLE_XATTR=OFF', + '-DENABLE_CNG=OFF', + '-DLIBARCHIVE_STATIC=ON', + ] +).dependency('archive_static') diff --git a/meson.build b/meson.build index d35bbe8c..bece51c0 100644 --- a/meson.build +++ b/meson.build @@ -16,6 +16,8 @@ project( default_options: ['c_std=gnu17', 'cpp_std=c++11', 'buildtype=release'], ) +cmake = import('cmake') + incdir = include_directories('include', 'source') cflags = [ '-fms-extensions', diff --git a/source/app/emulator.c b/source/app/emulator.c index 3c2aabe9..bb504c57 100644 --- a/source/app/emulator.c +++ b/source/app/emulator.c @@ -10,6 +10,8 @@ #define _GNU_SOURCE #define STB_IMAGE_WRITE_IMPLEMENTATION +#include +#include #include #include #include "app/app.h" @@ -262,6 +264,94 @@ app_emulator_configure_bios( return (false); } +static +bool +app_emulator_configure_rom_archive( + struct app *app, + char const *archive_path +) { + struct archive *archive; + struct archive_entry *entry; + int err; + bool game_found; + + logln(HS_INFO, "Path given identified as an archived."); + + game_found = false; + archive = archive_read_new(); + hs_assert(archive); + + archive_read_support_filter_all(archive); + archive_read_support_format_all(archive); + + err = archive_read_open_filename(archive, archive_path, 4096); + if (err != ARCHIVE_OK) { + app_new_notification( + app, + UI_NOTIFICATION_ERROR, + "Failed to open the path as an archive: %s.", + archive_path, + archive_error_string(archive) + ); + return (true); + } + + while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + char const *entry_name; + char const *ext; + + entry_name = archive_entry_pathname(entry); + ext = strrchr(entry_name, '.'); + if (ext && !strcmp(ext, ".gba")) { + size_t file_len; + ssize_t read_len; + void *data; + + file_len = 0; + data = NULL; + do { + data = realloc(data, file_len + 4096); + hs_assert(data); + + read_len = archive_read_data(archive, data + file_len, 4096); + if (read_len < 0) { + app_new_notification( + app, + UI_NOTIFICATION_ERROR, + "Failed to archive's entry %s: %s.", + entry_name, + archive_error_string(archive) + ); + free(data); + goto cleanup; + } + file_len += read_len; + } while (read_len > 0); + + game_found = true; + + app->emulation.launch_config->rom.data = data; + app->emulation.launch_config->rom.size = file_len; + + goto cleanup; + } + + archive_read_data_skip(archive); + } + + app_new_notification( + app, + UI_NOTIFICATION_ERROR, + "No valid GBA game found in the archive.", + archive_path, + archive_error_string(archive) + ); + +cleanup: + archive_read_free(archive); + return (!game_found); +} + static bool app_emulator_configure_rom( @@ -387,21 +477,28 @@ app_emulator_configure( struct message_reset event; char *backup_path; char *extension; + bool is_archive; size_t basename_len; size_t i; uint8_t *code; app_emulator_unconfigure(app); + logln(HS_INFO, "Loading game at \"%s%s%s\".", g_light_green, rom_path, g_reset); + app->emulation.launch_config = calloc(1, sizeof(struct launch_config)); hs_assert(app->emulation.launch_config); extension = strrchr(rom_path, '.'); + // We consider anything that isn't ending with `.gba` or `.bin` as an archive. + // XXX: Should we build a hard-coded list instead? if (extension) { basename_len = extension - rom_path; + is_archive = (bool)(strcmp(extension, ".gba") && strcmp(extension, ".bin")); } else { basename_len = strlen(rom_path); + is_archive = false; } for (i = 0; i < MAX_QUICKSAVES; ++i) { @@ -426,7 +523,7 @@ app_emulator_configure( ); if (app_emulator_configure_bios(app) - || app_emulator_configure_rom(app, rom_path) + || (is_archive ? app_emulator_configure_rom_archive(app, rom_path) : app_emulator_configure_rom(app, rom_path)) || app_emulator_configure_backup(app, backup_path) ) { app_emulator_unconfigure(app); @@ -502,6 +599,8 @@ app_emulator_configure( app_config_push_recent_rom(app, rom_path); + logln(HS_INFO, "Game successfully loaded."); + return (false); } diff --git a/source/app/meson.build b/source/app/meson.build index db7f9c28..208d618f 100644 --- a/source/app/meson.build +++ b/source/app/meson.build @@ -76,9 +76,12 @@ libapp = static_library( 'main.c', dependencies: [ dependency('threads', required: true, static: get_option('static_executable')), + libarchive, ], link_with: [libgba, imgui, nfde, mjson] + libapp_extra_deps, include_directories: [incdir, imgui_inc, nfde_inc, mjson_inc, stb_inc], - c_args: cflags, + c_args: cflags + [ + '-DLIBARCHIVE_STATIC=ON', + ], link_args: ldflags, ) diff --git a/subprojects/libarchive b/subprojects/libarchive new file mode 160000 index 00000000..6468cd1f --- /dev/null +++ b/subprojects/libarchive @@ -0,0 +1 @@ +Subproject commit 6468cd1f5c9b76e2c3b10cdd6938faf6b82823b6