From 5fafafb784b08ae59b58a99209015cfc2a36296b Mon Sep 17 00:00:00 2001 From: Arignir Date: Thu, 28 Dec 2023 12:26:46 +0100 Subject: [PATCH] Add support for loading games from an archive. --- .github/workflows/accuracy.yml | 2 +- .github/workflows/build.yml | 49 +++++++++++++--- .gitmodules | 1 - README.md | 8 ++- external/meson.build | 2 +- include/app/app.h | 3 - source/app/bindings.c | 3 - source/app/emulator.c | 101 ++++++++++++++++++++++++++++++++- source/app/main.c | 22 +------ source/app/meson.build | 10 +++- source/app/sdl/event.c | 3 - source/app/sdl/init.c | 3 - source/app/sdl/video.c | 5 +- source/app/windows/game.c | 1 - source/app/windows/keybinds.c | 1 - source/app/windows/menubar.c | 3 +- source/app/windows/notif.c | 1 - source/gba/core/core.c | 2 +- 18 files changed, 159 insertions(+), 61 deletions(-) diff --git a/.github/workflows/accuracy.yml b/.github/workflows/accuracy.yml index 2ad9e706..a1c354e2 100644 --- a/.github/workflows/accuracy.yml +++ b/.github/workflows/accuracy.yml @@ -13,7 +13,7 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y meson ninja-build libsdl2-dev libglew-dev libgtk-3-dev libreadline-dev libedit-dev libcapstone-dev + sudo apt-get install -y meson ninja-build libsdl2-dev libglew-dev libgtk-3-dev libreadline-dev libedit-dev libcapstone-dev libarchive-dev - name: Build Hades w/ Debugger run: | meson build --werror -Dwith_debugger=true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54ed21d6..654c54b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,10 +16,10 @@ 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-libarchive - name: Build Hades run: | - meson build -Dstatic_executable=true + meson --buildtype=release build -Dstatic_executable=true cd build ninja - name: Test Hades @@ -41,9 +41,11 @@ jobs: submodules: recursive - name: Install Dependencies run: | - brew install meson ninja sdl2 glew create-dmg + brew install meson ninja sdl2 glew libarchive create-dmg - name: Build Hades run: | + export PKG_CONFIG_PATH=/usr/local/opt/libarchive/lib/pkgconfig + rm -rf /tmp/build/ mkdir -p /tmp/build/Hades.app @@ -52,13 +54,42 @@ jobs: chmod +x /tmp/build/Hades.app/Contents/MacOS/hades /tmp/build/Hades.app/Contents/MacOS/run.sh + echo "Hades dependencies, before fixing:" otool -L /tmp/build/Hades.app/Contents/MacOS/hades + echo "End of dependencies" + + # Fix the dependencies of Hades + + declare libraries=(sdl2 glew libarchive) + declare dylibs=(libSDL2-2.0.0.dylib libGLEW.2.2.dylib libarchive.13.dylib) + + for i in "${!libraries[@]}"; do + declare lib="${libraries[$i]}" + declare dylib="${dylibs[$i]}" + + echo "Fixing ${lib} for hades" + cp /usr/local/opt/${lib}/lib/${dylib} /tmp/build/Hades.app/Contents/MacOS/ + install_name_tool -change /usr/local/opt/${lib}/lib/${dylib} @executable_path/${dylib} /tmp/build/Hades.app/Contents/MacOS/hades + done + + # Fix the dependencies of libarchive + + declare libraries=(xz zstd lz4 libb2) + declare dylibs=(liblzma.5.dylib libzstd.1.dylib liblz4.1.dylib libb2.1.dylib) + + for i in "${!libraries[@]}"; do + declare lib="${libraries[$i]}" + declare dylib="${dylibs[$i]}" + + echo "Fixing ${lib} for libarchive.13.dylib" + cp /usr/local/opt/${lib}/lib/${dylib} /tmp/build/Hades.app/Contents/MacOS/ + install_name_tool -change /usr/local/opt/${lib}/lib/${dylib} @executable_path/${dylib} /tmp/build/Hades.app/Contents/MacOS/libarchive.13.dylib + done - cp /usr/local/opt/sdl2/lib/libSDL2-2.0.0.dylib /tmp/build/Hades.app/Contents/MacOS/ - install_name_tool -change /usr/local/opt/sdl2/lib/libSDL2-2.0.0.dylib @executable_path/libSDL2-2.0.0.dylib /tmp/build/Hades.app/Contents/MacOS/hades + echo "All dependencies, after fixing:" + otool -L /tmp/build/Hades.app/Contents/MacOS/{hades,*.dylib} + echo "End of dependencies" - cp /usr/local/opt/glew/lib/libGLEW.2.2.dylib /tmp/build/Hades.app/Contents/MacOS/ - install_name_tool -change /usr/local/opt/glew/lib/libGLEW.2.2.dylib @executable_path/libGLEW.2.2.dylib /tmp/build/Hades.app/Contents/MacOS/hades - name: Test Hades run: | /tmp/build/Hades.app/Contents/MacOS/hades --help @@ -137,10 +168,10 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y meson ninja-build libsdl2-dev libglew-dev libgtk-3-dev + sudo apt-get install -y meson ninja-build libsdl2-dev libglew-dev libgtk-3-dev libarchive-dev - name: Build Hades run: | - meson build --werror + meson --buildtype=release build --werror cd build ninja - name: Test Hades diff --git a/.gitmodules b/.gitmodules index b03b45b5..6b03e87a 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 diff --git a/README.md b/README.md index d9ecbf6e..c29c93d3 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Currently, Hades features: - Quick Saves (also known as Save State) - Real Time Clock (RTC) support - Color correction & LCD effects + - Loading games from common archive formats (`.zip`, `.7z`, `.rar`, etc.) It is the third 🥉 software emulator to pass the AGS Aging Cartridge used to test Game Boy Advance systems. @@ -56,17 +57,18 @@ The build dependencies are: - `OpenGL` - `glew` - `gtk3` + - `libarchive` On Ubuntu, you can install all those dependencies with: ```bash -$ apt install meson ninja-build gcc libsdl2-dev libglew-dev libgtk-3-dev +$ apt install meson ninja-build gcc libsdl2-dev libglew-dev libgtk-3-dev libarchive-dev ``` On Fedora, you can install all those dependencies with: ```bash -$ dnf install meson ninja-build gcc SDL2-devel glew-devel gtk3-devel +$ dnf install meson ninja-build gcc SDL2-devel glew-devel gtk3-devel libarchive-devel ``` To build Hades, run: @@ -84,9 +86,9 @@ Special thanks to some invaluable resources while writing Hades: - [GBATEK](https://problemkaputt.de/gbatek.htm) by Martin Korth - [NanoBoyAdvance](https://github.com/fleroviux/NanoBoyAdvance/) by Fleroviux + - [mGBA](https://mgba.io/) and [mgba-emu/suite](https://github.com/mgba-emu/suite) by Endrift - [gba-tests](https://github.com/jsmolka/gba-tests) by Jsmolka - [Cowbite](https://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm) by Tom Happ - - [mGBA](https://mgba.io/) and [mgba-emu/suite](https://github.com/mgba-emu/suite) by Endrift - [gdkGBA](https://github.com/gdkchan/gdkGBA/) by gdkChan - [Tonc](https://www.coranac.com/tonc/text/toc.htm) by Cearn - [GBA Cartridge Backup Storage](https://dillonbeliveau.com/2020/06/05/GBA-FLASH.html) by Dillon Beliveau diff --git a/external/meson.build b/external/meson.build index 3130184a..22ee26e1 100644 --- a/external/meson.build +++ b/external/meson.build @@ -79,7 +79,7 @@ mjson = static_library( 'mjson', 'mjson/src/mjson.c', include_directories: mjson_inc, - c_args: cflags, + c_args: cflags + ['-Wno-unused-but-set-variable'], link_args: ldflags, ) diff --git a/include/app/app.h b/include/app/app.h index 226ccb3b..946c75b2 100644 --- a/include/app/app.h +++ b/include/app/app.h @@ -9,9 +9,6 @@ #pragma once -#define SDL_MAIN_HANDLED -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS - #if WITH_DEBUGGER #include #endif diff --git a/source/app/bindings.c b/source/app/bindings.c index f00a5c0c..23cb2029 100644 --- a/source/app/bindings.c +++ b/source/app/bindings.c @@ -7,9 +7,6 @@ ** \******************************************************************************/ -#define SDL_MAIN_HANDLED -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS - #include #include #include diff --git a/source/app/emulator.c b/source/app/emulator.c index 3c2aabe9..7abcbb27 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, 1024 * 1024); // 1MiB + 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 + 1024 * 1024); // 1MiB + hs_assert(data); + + read_len = archive_read_data(archive, data + file_len, 1024 * 1024); // 1MiB + 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/main.c b/source/app/main.c index 47f62bc4..d7ee68b6 100644 --- a/source/app/main.c +++ b/source/app/main.c @@ -7,33 +7,15 @@ ** \******************************************************************************/ -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS +#include #include - #include #include - -#define SDL_MAIN_HANDLED -#include - -#ifdef _MSC_VER -# include -#endif - #include #include #include #include #include - -#ifdef IMGUI_HAS_IMSTR -# define igBegin igBegin_Str -# define igSliderFloat igSliderFloat_Str -# define igCheckbox igCheckbox_Str -# define igColorEdit3 igColorEdit3_Str -# define igButton igButton_Str -#endif - #include #include #include "hades.h" @@ -63,7 +45,7 @@ sighandler( int main( int argc, - char * const argv[] + char *argv[] ) { struct app app; pthread_t gba_thread; diff --git a/source/app/meson.build b/source/app/meson.build index db7f9c28..4fb3eca4 100644 --- a/source/app/meson.build +++ b/source/app/meson.build @@ -7,6 +7,9 @@ ## ################################################################################ +libapp_extra_cflags = [ + '-DCIMGUI_DEFINE_ENUMS_AND_STRUCTS', +] libapp_extra_deps = [] ############################### @@ -45,7 +48,7 @@ if get_option('with_debugger') dependency('capstone', required: true, static: get_option('static_executable')), ], include_directories: [incdir, imgui_inc], - c_args: cflags, + c_args: cflags + libapp_extra_cflags, link_args: ldflags, ) @@ -76,9 +79,10 @@ libapp = static_library( 'main.c', dependencies: [ dependency('threads', required: true, static: get_option('static_executable')), - ], + dependency('libarchive', version: '>=3.0', required: true, static: get_option('static_executable')), + ] + imgui_dep, 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 + libapp_extra_cflags, link_args: ldflags, ) diff --git a/source/app/sdl/event.c b/source/app/sdl/event.c index ce03971a..5350331b 100644 --- a/source/app/sdl/event.c +++ b/source/app/sdl/event.c @@ -7,9 +7,6 @@ ** \******************************************************************************/ -#define SDL_MAIN_HANDLED -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS - #include #include #include diff --git a/source/app/sdl/init.c b/source/app/sdl/init.c index bb8c47ff..48de1b73 100644 --- a/source/app/sdl/init.c +++ b/source/app/sdl/init.c @@ -7,7 +7,6 @@ ** \******************************************************************************/ -#define SDL_MAIN_HANDLED #include #include #include "app/app.h" @@ -16,8 +15,6 @@ void app_sdl_init( struct app *app ) { - SDL_SetMainReady(); - /* Initialize the SDL */ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) < 0) { logln(HS_ERROR, "Failed to init the SDL: %s", SDL_GetError()); diff --git a/source/app/sdl/video.c b/source/app/sdl/video.c index 24f60eee..4c1d548e 100644 --- a/source/app/sdl/video.c +++ b/source/app/sdl/video.c @@ -7,14 +7,11 @@ ** \******************************************************************************/ -#define SDL_MAIN_HANDLED -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS - -#include #include #include #include #include +#include #include "hades.h" #include "app/app.h" #include "gba/gba.h" diff --git a/source/app/windows/game.c b/source/app/windows/game.c index 6f11ecfb..87086778 100644 --- a/source/app/windows/game.c +++ b/source/app/windows/game.c @@ -8,7 +8,6 @@ \******************************************************************************/ #define _GNU_SOURCE -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS #include #include "hades.h" diff --git a/source/app/windows/keybinds.c b/source/app/windows/keybinds.c index bda565fc..70af28fe 100644 --- a/source/app/windows/keybinds.c +++ b/source/app/windows/keybinds.c @@ -7,7 +7,6 @@ ** \******************************************************************************/ -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS #include #include "hades.h" #include "app/app.h" diff --git a/source/app/windows/menubar.c b/source/app/windows/menubar.c index a293ad8c..034079aa 100644 --- a/source/app/windows/menubar.c +++ b/source/app/windows/menubar.c @@ -8,7 +8,6 @@ \******************************************************************************/ #define _GNU_SOURCE -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS #include #include @@ -31,7 +30,7 @@ app_win_menubar_file( result = NFD_OpenDialog( &path, - (nfdfilteritem_t[1]){(nfdfilteritem_t){ .name = "GBA Rom", .spec = "gba"}}, + (nfdfilteritem_t[1]){(nfdfilteritem_t){ .name = "GBA Rom", .spec = "gba,zip,7z,rar"}}, 1, NULL ); diff --git a/source/app/windows/notif.c b/source/app/windows/notif.c index f93ffedb..cb705398 100644 --- a/source/app/windows/notif.c +++ b/source/app/windows/notif.c @@ -8,7 +8,6 @@ \******************************************************************************/ #define _GNU_SOURCE -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS #include #include "hades.h" diff --git a/source/gba/core/core.c b/source/gba/core/core.c index ea9862b0..e917b686 100644 --- a/source/gba/core/core.c +++ b/source/gba/core/core.c @@ -543,7 +543,7 @@ core_compute_shift( if (bits == 0) { carry_out = value & 0b1; value >>= 1; - value |= core->cpsr.carry << 31; + value |= (uint32_t)core->cpsr.carry << 31; } else { carry_out = (value >> (bits - 1)) & 0b1; // Save the carry value = ror32(value, bits & 0x1F);