From 4c5f7c4745eb192eb7f70f05c1df0cdddfe4cc02 Mon Sep 17 00:00:00 2001
From: techmetx11
Date: Tue, 3 Feb 2026 01:35:57 +0100
Subject: [PATCH 1/4] Update to latest LATEST nightly version of BlastEm, and
patch it to remove home directory access
---
...ve-file-chooser-into-BlastEm-using-X.patch | 129 ++++++++++++++++++
com.retrodev.blastem.appdata.xml | 2 +-
com.retrodev.blastem.json | 29 +++-
3 files changed, 154 insertions(+), 6 deletions(-)
create mode 100644 0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch
diff --git a/0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch b/0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch
new file mode 100644
index 0000000..6a329b5
--- /dev/null
+++ b/0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch
@@ -0,0 +1,129 @@
+From 464bbad5dea3c481600b206f0bcaa5ace7861650 Mon Sep 17 00:00:00 2001
+From: techmetx11
+Date: Mon, 2 Feb 2026 23:51:58 +0100
+Subject: [PATCH] Integrate a native file chooser into BlastEm using XDG
+ desktop portal API (for Flatpak)
+
+---
+ Makefile | 15 ++------
+ default.cfg | 2 +-
+ nuklear_ui/filechooser_xdg.c | 66 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 69 insertions(+), 14 deletions(-)
+ create mode 100644 nuklear_ui/filechooser_xdg.c
+
+diff --git a/Makefile b/Makefile
+index 6bb9be0..66d0204 100644
+--- a/Makefile
++++ b/Makefile
+@@ -93,21 +93,10 @@ Z80_DISPATCH:=call
+ else #CPU=wasm
+ FONT:=nuklear_ui/font.o
+ ifneq ($(MAKECMDGOALS),libblastem.$(SO))
+-CHOOSER:=nuklear_ui/filechooser_gtk.o
+-GTKFLAGS:=$(shell pkg-config --cflags gtk+-3.0 2>/dev/null)
+-ifeq ($(GTKFLAGS),)
+-GTKFLAGS:=$(shell pkg-config --cflags gtk+-2.0 2>/dev/null)
+-ifeq ($(GTKFLAGS),)
+-CHOOSER:=nuklear_ui/filechooser_null.o
+-endif
+-endif
++CHOOSER:=nuklear_ui/filechooser_xdg.o
++LIBS+= libportal
+ endif #neq ($(MAKECMDGOALS),libblastem.$(SO))
+ endif #CPU=wasm
+-ifeq ($(GTKFLAGS),)
+-else
+-EXTRA_NUKLEAR_LDFLAGS:=-ldl
+-endif
+-CFLAGS+= $(GTKFLAGS)
+ endif #Darwin
+
+ ifdef HOST_ZLIB
+diff --git a/default.cfg b/default.cfg
+index 73df736..7a22f1c 100644
+--- a/default.cfg
++++ b/default.cfg
+@@ -412,7 +412,7 @@ ui {
+ #specifies the preferred save-state format, set to gst for Genecyst compatible states
+ state_format native
+ #set to on to use the native file picker on your OS instead of the builtin one
+- use_native_filechooser off
++ use_native_filechooser on
+ }
+
+ system {
+diff --git a/nuklear_ui/filechooser_xdg.c b/nuklear_ui/filechooser_xdg.c
+new file mode 100644
+index 0000000..080dce3
+--- /dev/null
++++ b/nuklear_ui/filechooser_xdg.c
+@@ -0,0 +1,66 @@
++#include
++#include
++
++#include
++#include
++
++static XdpPortal *portal = NULL;
++typedef struct {
++ GMainLoop *loop;
++ char *uri;
++} FileChooserData;
++
++uint8_t native_filechooser_available(void)
++{
++ return 1;
++}
++
++void native_filechooser_callback(GObject *obj, GAsyncResult *res, gpointer data) {
++ FileChooserData *fcdata = data;
++ GVariant *var = xdp_portal_open_file_finish(portal, res, NULL);
++ if (!var) {
++ fcdata->uri = NULL;
++ g_main_loop_quit(fcdata->loop);
++ return;
++ }
++
++ GVariant *uris = g_variant_lookup_value(var, "uris", G_VARIANT_TYPE_STRING_ARRAY);
++
++ GVariant *first_uri = g_variant_get_child_value(uris, 0);
++ gsize length = 0;
++
++ const gchar *uri_string = g_variant_dup_string(first_uri, &length);
++
++ gchar *unespaced_uri = g_filename_from_uri(uri_string, NULL, NULL);
++ fcdata->uri = strndup(unespaced_uri, length);
++
++ g_free(unespaced_uri);
++ g_main_loop_quit(fcdata->loop);
++}
++
++char* native_filechooser_pick(const char *title, const char *start_directory)
++{
++ if (!portal) {
++ portal = xdp_portal_new();
++ }
++ FileChooserData data;
++ GMainContext *context = g_main_context_new();
++ GMainLoop *loop = g_main_loop_new(context, FALSE);
++ GCancellable *cancellable = g_cancellable_new();
++
++ g_main_context_push_thread_default(context);
++
++ data.loop = loop;
++ //data.cancellable = cancellable;
++
++ xdp_portal_open_file(portal, NULL, title, NULL, NULL, NULL, 0, cancellable, native_filechooser_callback, &data);
++
++ g_main_loop_run(loop);
++ g_main_context_pop_thread_default(context);
++
++ if (g_cancellable_is_cancelled(cancellable)) {
++ return NULL;
++ }
++
++ return data.uri;
++}
+--
+2.52.0
+
diff --git a/com.retrodev.blastem.appdata.xml b/com.retrodev.blastem.appdata.xml
index e33f866..50e6390 100644
--- a/com.retrodev.blastem.appdata.xml
+++ b/com.retrodev.blastem.appdata.xml
@@ -3,7 +3,7 @@
com.retrodev.blastem
com.retrodev.blastem.desktop
- Blastem
+ BlastEm
A Sega Mega Drive/Genesis, and Master System emulator
CC0-1.0
GPL-3.0+
diff --git a/com.retrodev.blastem.json b/com.retrodev.blastem.json
index d9a4b29..5c57195 100644
--- a/com.retrodev.blastem.json
+++ b/com.retrodev.blastem.json
@@ -12,12 +12,30 @@
/* Joystick and GPU access */
"--device=all",
/* Get access to the files */
- "--filesystem=home:ro",
"--filesystem=xdg-run/gvfs:ro"
],
"modules" : [
"shared-modules/glu/glu-9.json",
"shared-modules/glew/glew.json",
+ {
+ "name": "libportal",
+ "buildsystem": "meson",
+ "config-opts": [
+ "-Dbackend-gtk3=disabled",
+ "-Dbackend-gtk4=disabled",
+ "-Dbackend-qt5=disabled",
+ "-Dbackend-qt6=disabled",
+ "-Dvapi=false",
+ "-Ddocs=false",
+ "-Dtests=false"
+ ],
+ "sources": [
+ {
+ "type": "git",
+ "url": "https://github.com/flatpak/libportal.git",
+ "commit": "086cc57372989117cfb6ef4c565bdd316db2e536"
+ }]
+ },
{
"name" : "blastem",
"buildsystem" : "simple",
@@ -26,7 +44,7 @@
"install -m0755 -D blastem /app/bin/blastem.bin",
"install -m0755 -D blastem.sh /app/bin/blastem",
"install -d /app/share/blastem/",
- "cp -r shaders/ images/ gamecontrollerdb.txt rom.db /app/share/blastem/",
+ "cp -r shaders/ images/ gamecontrollerdb.txt rom.db systems.cfg /app/share/blastem/",
"install -m0644 -D default.cfg /app/etc/default.cfg",
"install -m0644 -D ${FLATPAK_ID}.png /app/share/icons/hicolor/256x256/apps/${FLATPAK_ID}.png",
"install -m0644 -D com.retrodev.blastem.desktop /app/share/applications/com.retrodev.blastem.desktop",
@@ -35,8 +53,8 @@
"sources" : [
{
"type" : "archive",
- "url" : "https://www.retrodev.com/repos/blastem/archive/0013362c320c.tar.bz2",
- "sha256" : "4e6a937097c868471507d59e4a5a6f0da8b06af44ee9f3d05d530c5348b2ad9b"
+ "url" : "https://www.retrodev.com/repos/blastem/archive/0b220095659f.tar.bz2",
+ "sha256" : "7e02e3ee6547ddb81f049f8b99d1a17288c916cdb169365e60ae91bad33b0f0c"
},
{
"type" : "script",
@@ -49,7 +67,8 @@
{
"type" : "patch",
"paths" : [
- "0001-Add-support-for-Flatpak-config-data-dir-variables.patch"
+ "0001-Add-support-for-Flatpak-config-data-dir-variables.patch",
+ "0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch"
]
},
{
From 775c2bd66aa8e54bb14b8bbe4df078d6b70bb68d Mon Sep 17 00:00:00 2001
From: techmetx11
Date: Tue, 3 Feb 2026 01:45:42 +0100
Subject: [PATCH 2/4] Remove i386 architecture to make the build pass
---
flathub.json | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/flathub.json b/flathub.json
index c22dc7d..e461637 100644
--- a/flathub.json
+++ b/flathub.json
@@ -1,6 +1,5 @@
{
"only-arches": [
- "x86_64",
- "i386"
+ "x86_64"
]
}
From bc5670c1dbc75ed1ddc820918f27cd959c70b91d Mon Sep 17 00:00:00 2001
From: techmetx11
Date: Tue, 3 Feb 2026 11:37:28 +0100
Subject: [PATCH 3/4] Add vcs-browser in AppData
---
com.retrodev.blastem.appdata.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/com.retrodev.blastem.appdata.xml b/com.retrodev.blastem.appdata.xml
index 50e6390..3a9b856 100644
--- a/com.retrodev.blastem.appdata.xml
+++ b/com.retrodev.blastem.appdata.xml
@@ -17,6 +17,7 @@
https://www.retrodev.com/blastem/
+ https://www.retrodev.com/repos/blastem
https://raw.githubusercontent.com/flathub/com.retrodev.blastem/master/screenshots/blastem-1.png
From 6978ce2ff994f5dde49382976829203ea512b687 Mon Sep 17 00:00:00 2001
From: techmetx11
Date: Tue, 3 Feb 2026 14:46:28 +0100
Subject: [PATCH 4/4] Add filters to the file chooser
---
...ve-file-chooser-into-BlastEm-using-X.patch | 100 ++++++++++++++++--
1 file changed, 90 insertions(+), 10 deletions(-)
diff --git a/0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch b/0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch
index 6a329b5..85a10aa 100644
--- a/0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch
+++ b/0002-Integrate-a-native-file-chooser-into-BlastEm-using-X.patch
@@ -1,14 +1,15 @@
-From 464bbad5dea3c481600b206f0bcaa5ace7861650 Mon Sep 17 00:00:00 2001
+From e0ad37e6fe205d0ff701458403e1c83e5000c912 Mon Sep 17 00:00:00 2001
From: techmetx11
Date: Mon, 2 Feb 2026 23:51:58 +0100
Subject: [PATCH] Integrate a native file chooser into BlastEm using XDG
desktop portal API (for Flatpak)
+Add filters to the file chooser
---
- Makefile | 15 ++------
- default.cfg | 2 +-
- nuklear_ui/filechooser_xdg.c | 66 ++++++++++++++++++++++++++++++++++++
- 3 files changed, 69 insertions(+), 14 deletions(-)
+ Makefile | 15 +---
+ default.cfg | 2 +-
+ nuklear_ui/filechooser_xdg.c | 145 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 148 insertions(+), 14 deletions(-)
create mode 100644 nuklear_ui/filechooser_xdg.c
diff --git a/Makefile b/Makefile
@@ -54,10 +55,10 @@ index 73df736..7a22f1c 100644
system {
diff --git a/nuklear_ui/filechooser_xdg.c b/nuklear_ui/filechooser_xdg.c
new file mode 100644
-index 0000000..080dce3
+index 0000000..bd90286
--- /dev/null
+++ b/nuklear_ui/filechooser_xdg.c
-@@ -0,0 +1,66 @@
+@@ -0,0 +1,145 @@
+#include
+#include
+
@@ -111,16 +112,95 @@ index 0000000..080dce3
+ g_main_context_push_thread_default(context);
+
+ data.loop = loop;
-+ //data.cancellable = cancellable;
+
-+ xdp_portal_open_file(portal, NULL, title, NULL, NULL, NULL, 0, cancellable, native_filechooser_callback, &data);
++ // Filter builder
++ GVariantBuilder filters;
++ GVariantBuilder allfiles_filter;
++ GVariantBuilder allsupported_filter;
++ GVariantBuilder gen_filter;
++ GVariantBuilder scd_filter;
++ GVariantBuilder sms_filter;
++ GVariantBuilder vgm_filter;
++
++ g_variant_builder_init(&filters, G_VARIANT_TYPE("a(sa(us))"));
++ g_variant_builder_init(&allfiles_filter, G_VARIANT_TYPE("a(us)"));
++
++ g_variant_builder_add(&allfiles_filter, "(us)", 0, "*");
++
++ g_variant_builder_init(&allsupported_filter, G_VARIANT_TYPE("a(us)"));
++
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.zip");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.bin");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.bin.gz");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.gen");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.gen.gz");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.md");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.md.gz");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.sms");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.sms.gz");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.gg");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.gg.gz");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.sg");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.sg.gz");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.cue");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.toc");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.flac");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.vgm");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.vgz");
++ g_variant_builder_add(&allsupported_filter, "(us)", 0, "*.vgm.gz");
++
++ g_variant_builder_init(&gen_filter, G_VARIANT_TYPE("a(us)"));
++
++ g_variant_builder_add(&gen_filter, "(us)", 0, "*.zip");
++ g_variant_builder_add(&gen_filter, "(us)", 0, "*.bin");
++ g_variant_builder_add(&gen_filter, "(us)", 0, "*.bin.gz");
++ g_variant_builder_add(&gen_filter, "(us)", 0, "*.gen");
++ g_variant_builder_add(&gen_filter, "(us)", 0, "*.gen.gz");
++ g_variant_builder_add(&gen_filter, "(us)", 0, "*.md");
++ g_variant_builder_add(&gen_filter, "(us)", 0, "*.md.gz");
++
++ g_variant_builder_init(&scd_filter, G_VARIANT_TYPE("a(us)"));
++
++ g_variant_builder_add(&scd_filter, "(us)", 0, "*.cue");
++ g_variant_builder_add(&scd_filter, "(us)", 0, "*.toc");
++
++ g_variant_builder_init(&sms_filter, G_VARIANT_TYPE("a(us)"));
++
++ g_variant_builder_add(&sms_filter, "(us)", 0, "*.sms");
++ g_variant_builder_add(&sms_filter, "(us)", 0, "*.sms.gz");
++ g_variant_builder_add(&sms_filter, "(us)", 0, "*.gg");
++ g_variant_builder_add(&sms_filter, "(us)", 0, "*.gg.gz");
++ g_variant_builder_add(&sms_filter, "(us)", 0, "*.sg");
++ g_variant_builder_add(&sms_filter, "(us)", 0, "*.sg.gz");
++
++ g_variant_builder_init(&vgm_filter, G_VARIANT_TYPE("a(us)"));
++
++ g_variant_builder_add(&vgm_filter, "(us)", 0, "*.flac");
++ g_variant_builder_add(&vgm_filter, "(us)", 0, "*.vgm");
++ g_variant_builder_add(&vgm_filter, "(us)", 0, "*.vgz");
++ g_variant_builder_add(&vgm_filter, "(us)", 0, "*.vgm.gz");
++
++ g_variant_builder_add(&filters, "(sa(us))", "All Files", &allfiles_filter);
++ g_variant_builder_add(&filters, "(sa(us))", "All Supported Files", &allsupported_filter);
++ g_variant_builder_add(&filters, "(sa(us))", "Genesis/MD", &gen_filter);
++ g_variant_builder_add(&filters, "(sa(us))", "Sega/Mega CD", &scd_filter);
++ g_variant_builder_add(&filters, "(sa(us))", "Sega 8-bit", &sms_filter);
++ g_variant_builder_add(&filters, "(sa(us))", "Audio/VGM", &vgm_filter);
++
++ GVariant *filters_var = g_variant_builder_end(&filters);
++
++ // Set Genesis/MD as the default filter
++ xdp_portal_open_file(portal, NULL, title, filters_var, g_variant_get_child_value(filters_var, 2), NULL, 0, cancellable, native_filechooser_callback, &data);
+
+ g_main_loop_run(loop);
+ g_main_context_pop_thread_default(context);
+
+ if (g_cancellable_is_cancelled(cancellable)) {
-+ return NULL;
++ data.uri = NULL;
+ }
++
++ g_main_loop_unref(loop);
++ g_main_context_unref(context);
+
+ return data.uri;
+}