Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for loading games from an archive #65

Merged
merged 1 commit into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
49 changes: 40 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,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
Expand Down Expand Up @@ -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
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 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.

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
2 changes: 1 addition & 1 deletion external/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)

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,
)
Loading