Skip to content

Commit

Permalink
Add support for loading games from an archive.
Browse files Browse the repository at this point in the history
  • Loading branch information
Arignir committed Jan 3, 2024
1 parent 50e833b commit 3a5951c
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/accuracy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 39 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -52,13 +54,41 @@ 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]}"
cp -v /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=(libarchive xz zstd lz4 libb2)
declare dylibs=(libarchive.13.dylib 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]}"
cp -v /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
Expand Down Expand Up @@ -137,10 +167,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
Expand Down
1 change: 0 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 fron common archive format (`.zip`, `.7z`, `.rar`, etc.)

It is the third 🥉 software emulator to pass the AGS Aging Cartridge used to test Game Boy Advance systems.

Expand All @@ -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:
Expand All @@ -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
Expand Down
3 changes: 0 additions & 3 deletions include/app/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

#pragma once

#define SDL_MAIN_HANDLED
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS

#if WITH_DEBUGGER
#include <capstone/capstone.h>
#endif
Expand Down
3 changes: 0 additions & 3 deletions source/app/bindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
**
\******************************************************************************/

#define SDL_MAIN_HANDLED
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS

#include <SDL2/SDL.h>
#include <cimgui.h>
#include <cimgui_impl.h>
Expand Down
101 changes: 100 additions & 1 deletion source/app/emulator.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#define _GNU_SOURCE
#define STB_IMAGE_WRITE_IMPLEMENTATION

#include <archive.h>
#include <archive_entry.h>
#include <stb_image_write.h>
#include <errno.h>
#include "app/app.h"
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -502,6 +599,8 @@ app_emulator_configure(

app_config_push_recent_rom(app, rom_path);

logln(HS_INFO, "Game successfully loaded.");

return (false);
}

Expand Down
22 changes: 2 additions & 20 deletions source/app/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,15 @@
**
\******************************************************************************/

#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#include <SDL2/SDL.h>
#include <GL/glew.h>

#include <cimgui.h>
#include <cimgui_impl.h>

#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>

#ifdef _MSC_VER
# include <windows.h>
#endif

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>

#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 <stdlib.h>
#include <string.h>
#include "hades.h"
Expand Down Expand Up @@ -63,7 +45,7 @@ sighandler(
int
main(
int argc,
char * const argv[]
char *argv[]
) {
struct app app;
pthread_t gba_thread;
Expand Down
10 changes: 7 additions & 3 deletions source/app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
##
################################################################################

libapp_extra_cflags = [
'-DCIMGUI_DEFINE_ENUMS_AND_STRUCTS',
]
libapp_extra_deps = []

###############################
Expand Down Expand Up @@ -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,
)

Expand Down Expand Up @@ -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,
)
3 changes: 0 additions & 3 deletions source/app/sdl/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
**
\******************************************************************************/

#define SDL_MAIN_HANDLED
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS

#include <SDL2/SDL.h>
#include <cimgui.h>
#include <cimgui_impl.h>
Expand Down
Loading

0 comments on commit 3a5951c

Please sign in to comment.