about summary refs log tree commit diff
path: root/pkgs/applications/emulators/retroarch
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/applications/emulators/retroarch')
-rw-r--r--pkgs/applications/emulators/retroarch/0001-Disable-menu_show_core_updater.patch25
-rw-r--r--pkgs/applications/emulators/retroarch/0002-Use-fixed-paths-on-libretro_info_path.patch80
-rw-r--r--pkgs/applications/emulators/retroarch/cores.nix919
-rw-r--r--pkgs/applications/emulators/retroarch/default.nix122
-rw-r--r--pkgs/applications/emulators/retroarch/disable-menu-show-core-updater.patch13
-rw-r--r--pkgs/applications/emulators/retroarch/fix-config.patch26
-rw-r--r--pkgs/applications/emulators/retroarch/fix-libretro-paths.patch28
-rw-r--r--pkgs/applications/emulators/retroarch/hashes.json499
-rw-r--r--pkgs/applications/emulators/retroarch/kodi-advanced-launchers.nix36
-rwxr-xr-xpkgs/applications/emulators/retroarch/update.py179
-rw-r--r--pkgs/applications/emulators/retroarch/wrapper.nix37
11 files changed, 1964 insertions, 0 deletions
diff --git a/pkgs/applications/emulators/retroarch/0001-Disable-menu_show_core_updater.patch b/pkgs/applications/emulators/retroarch/0001-Disable-menu_show_core_updater.patch
new file mode 100644
index 0000000000000..75018dc8c4d1e
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/0001-Disable-menu_show_core_updater.patch
@@ -0,0 +1,25 @@
+From 546b343294209abbb193883ab76b679b7f99c6d3 Mon Sep 17 00:00:00 2001
+From: Thiago Kenji Okada <thiagokokada@gmail.com>
+Date: Sat, 20 Nov 2021 16:03:50 -0300
+Subject: [PATCH 1/2] Disable "menu_show_core_updater"
+
+---
+ retroarch.cfg | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/retroarch.cfg b/retroarch.cfg
+index cdcb199c9f..ab72f3920f 100644
+--- a/retroarch.cfg
++++ b/retroarch.cfg
+@@ -681,7 +681,7 @@
+ # menu_show_online_updater = true
+ 
+ # If disabled, will hide the ability to update cores (and core info files) inside the menu.
+-# menu_show_core_updater = true
++menu_show_core_updater = false
+ 
+ # If disabled, the libretro core will keep running in the background when we
+ # are in the menu.
+-- 
+2.31.1
+
diff --git a/pkgs/applications/emulators/retroarch/0002-Use-fixed-paths-on-libretro_info_path.patch b/pkgs/applications/emulators/retroarch/0002-Use-fixed-paths-on-libretro_info_path.patch
new file mode 100644
index 0000000000000..9aa8db6ab048b
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/0002-Use-fixed-paths-on-libretro_info_path.patch
@@ -0,0 +1,80 @@
+From 6788718299e1aba3ff8b31cd6ef012e8d3643bd3 Mon Sep 17 00:00:00 2001
+From: Thiago Kenji Okada <thiagokokada@gmail.com>
+Date: Sat, 20 Nov 2021 15:59:23 -0300
+Subject: [PATCH 2/2] Use fixed paths on "libretro_info_path"
+
+This patch sets "libretro_info_path" to `handle = false`, so instead of
+using the values from `retroarch.cfg`, it will always use the default.
+
+Also, it patches the default "libretro_info_path" to the
+`@libretro_info_path` string, so we can substitute it with the full path
+to it during build.
+---
+ configuration.c                    | 2 +-
+ frontend/drivers/platform_darwin.m | 9 ++-------
+ frontend/drivers/platform_unix.c   | 8 ++++----
+ 3 files changed, 7 insertions(+), 12 deletions(-)
+
+diff --git a/configuration.c b/configuration.c
+index e6a3841324..afb1d6e2ce 100644
+--- a/configuration.c
++++ b/configuration.c
+@@ -1456,7 +1456,7 @@ static struct config_path_setting *populate_settings_path(
+    SETTING_PATH("core_options_path",
+          settings->paths.path_core_options, false, NULL, true);
+    SETTING_PATH("libretro_info_path",
+-         settings->paths.path_libretro_info, false, NULL, true);
++         settings->paths.path_libretro_info, false, NULL, false);
+    SETTING_PATH("content_database_path",
+          settings->paths.path_content_database, false, NULL, true);
+    SETTING_PATH("cheat_database_path",
+diff --git a/frontend/drivers/platform_darwin.m b/frontend/drivers/platform_darwin.m
+index f922e50c55..52732f65ae 100644
+--- a/frontend/drivers/platform_darwin.m
++++ b/frontend/drivers/platform_darwin.m
+@@ -383,14 +383,9 @@ static void frontend_darwin_get_env(int *argc, char *argv[],
+          home_dir_buf, "shaders_glsl",
+          sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
+ #endif
+-#ifdef HAVE_UPDATE_CORES
+     fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE],
+-		    home_dir_buf, "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
+-#else
+-    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE],
+-		    bundle_path_buf, "modules", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
+-#endif
+-   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], home_dir_buf, "info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
++		    "@libretro_directory@", "", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
++   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], "@libretro_info_path@", "", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
+    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_OVERLAY], home_dir_buf, "overlays", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY]));
+ #ifdef HAVE_VIDEO_LAYOUT
+    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT], home_dir_buf, "layouts", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT]));
+diff --git a/frontend/drivers/platform_unix.c b/frontend/drivers/platform_unix.c
+index 722e1c595c..d87e01cf12 100644
+--- a/frontend/drivers/platform_unix.c
++++ b/frontend/drivers/platform_unix.c
+@@ -1815,8 +1815,8 @@ static void frontend_unix_get_env(int *argc,
+       strcpy_literal(base_path, "retroarch");
+ #endif
+ 
+-   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], base_path,
+-         "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
++   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], "@libretro_directory@",
++         "", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
+ #if defined(DINGUX)
+    /* On platforms that require manual core installation/
+     * removal, placing core info files in the same directory
+@@ -1825,8 +1825,8 @@ static void frontend_unix_get_env(int *argc,
+    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], base_path,
+          "core_info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
+ #else
+-   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], base_path,
+-         "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
++   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], "@libretro_info_path@",
++         "", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
+ #endif
+    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG], base_path,
+          "autoconfig", sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG]));
+-- 
+2.31.1
+
diff --git a/pkgs/applications/emulators/retroarch/cores.nix b/pkgs/applications/emulators/retroarch/cores.nix
new file mode 100644
index 0000000000000..e0045a3827d18
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/cores.nix
@@ -0,0 +1,919 @@
+{ lib
+, stdenv
+, SDL
+, alsa-lib
+, boost
+, buildPackages
+, bzip2
+, cmake
+, curl
+, fetchFromGitHub
+, ffmpeg
+, fluidsynth
+, gettext
+, hexdump
+, hidapi
+, icu
+, libaio
+, libGL
+, libGLU
+, libevdev
+, libjpeg
+, libpcap
+, libpng
+, libvorbis
+, libxml2
+, libzip
+, makeWrapper
+, nasm
+, openssl
+, pcre
+, pkg-config
+, portaudio
+, python3
+, retroarch
+, sfml
+, snappy
+, udev
+, which
+, xorg
+, xxd
+, xz
+, zlib
+}:
+
+let
+  hashesFile = builtins.fromJSON (builtins.readFile ./hashes.json);
+
+  getCoreSrc = core:
+    fetchFromGitHub (builtins.getAttr core hashesFile);
+
+  mkLibRetroCore =
+    { core
+    , description
+      # Check https://github.com/libretro/libretro-core-info for license information
+    , license
+    , src ? (getCoreSrc core)
+    , broken ? false
+    , version ? "unstable-2022-01-21"
+    , platforms ? retroarch.meta.platforms
+      # The resulting core file is based on core name
+      # Setting `normalizeCore` to `true` will convert `-` to `_` on the core filename
+    , normalizeCore ? true
+    , ...
+    }@args:
+    stdenv.mkDerivation (
+      let
+        d2u = if normalizeCore then (lib.replaceChars [ "-" ] [ "_" ]) else (x: x);
+      in
+      (rec {
+        pname = "libretro-${core}";
+        inherit version src;
+
+        buildInputs = [ zlib ] ++ args.extraBuildInputs or [ ];
+        nativeBuildInputs = [ makeWrapper ] ++ args.extraNativeBuildInputs or [ ];
+
+        makefile = "Makefile.libretro";
+        makeFlags = [
+          "platform=${{
+            linux = "unix";
+            darwin = "osx";
+            windows = "win";
+          }.${stdenv.hostPlatform.parsed.kernel.name} or stdenv.hostPlatform.parsed.kernel.name}"
+          "ARCH=${{
+            armv7l = "arm";
+            armv6l = "arm";
+            i686 = "x86";
+          }.${stdenv.hostPlatform.parsed.cpu.name} or stdenv.hostPlatform.parsed.cpu.name}"
+        ] ++ (args.makeFlags or [ ]);
+
+        coreDir = "${placeholder "out"}/lib/retroarch/cores";
+
+        installPhase = ''
+          runHook preInstall
+
+          mkdir -p $out/bin
+          mkdir -p $coreDir
+          mv ${d2u args.core}_libretro${stdenv.hostPlatform.extensions.sharedLibrary} $coreDir
+          makeWrapper ${retroarch}/bin/retroarch $out/bin/retroarch-${core} \
+            --add-flags "-L $coreDir/${d2u core}_libretro${stdenv.hostPlatform.extensions.sharedLibrary} $@"
+
+          runHook postInstall
+        '';
+
+        enableParallelBuilding = true;
+
+        passthru = {
+          inherit core;
+          libretroCore = "/lib/retroarch/cores";
+        };
+
+        meta = with lib; {
+          inherit broken description license platforms;
+          homepage = "https://www.libretro.com/";
+          maintainers = with maintainers; [ edwtjo hrdinka MP2E thiagokokada ];
+        };
+      }) // builtins.removeAttrs args [ "core" "src" "description" "license" "makeFlags" ]
+    );
+in
+{
+  inherit mkLibRetroCore;
+
+  atari800 = mkLibRetroCore {
+    core = "atari800";
+    description = "Port of Atari800 to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+    makeFlags = [ "GIT_VERSION=" ];
+  };
+
+  beetle-gba = mkLibRetroCore {
+    core = "mednafen-gba";
+    src = getCoreSrc "beetle-gba";
+    description = "Port of Mednafen's GameBoy Advance core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-lynx = mkLibRetroCore {
+    core = "mednafen-lynx";
+    src = getCoreSrc "beetle-lynx";
+    description = "Port of Mednafen's Lynx core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-ngp = mkLibRetroCore {
+    core = "mednafen-ngp";
+    src = getCoreSrc "beetle-ngp";
+    description = "Port of Mednafen's NeoGeo Pocket core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-pce-fast = mkLibRetroCore {
+    core = "mednafen-pce-fast";
+    src = getCoreSrc "beetle-pce-fast";
+    description = "Port of Mednafen's PC Engine core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-pcfx = mkLibRetroCore {
+    core = "mednafen-pcfx";
+    src = getCoreSrc "beetle-pcfx";
+    description = "Port of Mednafen's PCFX core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-psx = mkLibRetroCore {
+    core = "mednafen-psx";
+    src = getCoreSrc "beetle-psx";
+    description = "Port of Mednafen's PSX Engine core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+    makeFlags = [ "HAVE_HW=0" "HAVE_LIGHTREC=1" ];
+  };
+
+  beetle-psx-hw = mkLibRetroCore {
+    core = "mednafen-psx-hw";
+    src = getCoreSrc "beetle-psx";
+    description = "Port of Mednafen's PSX Engine (with HW accel) core to libretro";
+    license = lib.licenses.gpl2Only;
+    extraBuildInputs = [ libGL libGLU ];
+    makefile = "Makefile";
+    makeFlags = [ "HAVE_VULKAN=1" "HAVE_OPENGL=1" "HAVE_HW=1" "HAVE_LIGHTREC=1" ];
+  };
+
+  beetle-saturn = mkLibRetroCore {
+    core = "mednafen-saturn";
+    src = getCoreSrc "beetle-saturn";
+    description = "Port of Mednafen's Saturn core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+    platforms = [ "x86_64-linux" "aarch64-linux" ];
+  };
+
+  beetle-snes = mkLibRetroCore {
+    core = "mednafen-snes";
+    src = getCoreSrc "beetle-snes";
+    description = "Port of Mednafen's SNES core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-supergrafx = mkLibRetroCore {
+    core = "mednafen-supergrafx";
+    src = getCoreSrc "beetle-supergrafx";
+    description = "Port of Mednafen's SuperGrafx core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-vb = mkLibRetroCore {
+    core = "mednafen-vb";
+    src = getCoreSrc "beetle-vb";
+    description = "Port of Mednafen's VirtualBoy core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  beetle-wswan = mkLibRetroCore {
+    core = "mednafen-wswan";
+    src = getCoreSrc "beetle-wswan";
+    description = "Port of Mednafen's WonderSwan core to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  blastem = mkLibRetroCore {
+    core = "blastem";
+    description = "Port of BlastEm to libretro";
+    license = lib.licenses.gpl3Only;
+  };
+
+  bluemsx = mkLibRetroCore {
+    core = "bluemsx";
+    description = "Port of BlueMSX to libretro";
+    license = lib.licenses.gpl2Only;
+  };
+
+  bsnes = mkLibRetroCore {
+    core = "bsnes";
+    description = "Port of bsnes to libretro";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+  };
+
+  bsnes-hd =
+    let
+      # linux = bsd
+      # https://github.com/DerKoun/bsnes-hd/blob/f0b6cf34e9780d53516977ed2de64137a8bcc3c5/bsnes/GNUmakefile#L37
+      platform = if stdenv.isDarwin then "macos" else "linux";
+    in
+    mkLibRetroCore {
+      core = "bsnes-hd-beta";
+      src = getCoreSrc "bsnes-hd";
+      description = "Port of bsnes-hd to libretro";
+      license = lib.licenses.gpl3Only;
+      makefile = "GNUmakefile";
+      makeFlags = [
+        "-C"
+        "bsnes"
+        "target=libretro"
+        "platform=${platform}"
+      ];
+      extraBuildInputs = [ xorg.libX11 xorg.libXext ];
+      postBuild = "cd bsnes/out";
+    };
+
+  bsnes-mercury = mkLibRetroCore {
+    core = "bsnes-mercury-accuracy";
+    src = getCoreSrc "bsnes-mercury";
+    description = "Fork of bsnes with HLE DSP emulation restored";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+    makeFlags = [ "PROFILE=accuracy" ];
+  };
+
+  bsnes-mercury-balanced = mkLibRetroCore {
+    core = "bsnes-mercury-balanced";
+    src = getCoreSrc "bsnes-mercury";
+    description = "Fork of bsnes with HLE DSP emulation restored";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+    makeFlags = [ "PROFILE=balanced" ];
+  };
+
+  bsnes-mercury-performance = mkLibRetroCore {
+    core = "bsnes-mercury-performance";
+    src = getCoreSrc "bsnes-mercury";
+    description = "Fork of bsnes with HLE DSP emulation restored";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+    makeFlags = [ "PROFILE=performance" ];
+  };
+
+  citra = mkLibRetroCore {
+    core = "citra";
+    description = "Port of Citra to libretro";
+    license = lib.licenses.gpl2Plus;
+    extraNativeBuildInputs = [ cmake pkg-config ];
+    extraBuildInputs = [ libGLU libGL boost ];
+    makefile = "Makefile";
+    cmakeFlags = [
+      "-DENABLE_LIBRETRO=ON"
+      "-DENABLE_QT=OFF"
+      "-DENABLE_SDL2=OFF"
+      "-DENABLE_WEB_SERVICE=OFF"
+      "-DENABLE_DISCORD_PRESENCE=OFF"
+    ];
+    preConfigure = "sed -e '77d' -i externals/cmake-modules/GetGitRevisionDescription.cmake";
+    postBuild = "cd src/citra_libretro";
+  };
+
+  citra-canary = mkLibRetroCore {
+    core = "citra-canary";
+    description = "Port of Citra Canary/Experimental to libretro";
+    license = lib.licenses.gpl2Plus;
+    extraNativeBuildInputs = [ cmake pkg-config ];
+    extraBuildInputs = [ libGLU libGL boost ];
+    makefile = "Makefile";
+    cmakeFlags = [
+      "-DENABLE_LIBRETRO=ON"
+      "-DENABLE_QT=OFF"
+      "-DENABLE_SDL2=OFF"
+      "-DENABLE_WEB_SERVICE=OFF"
+      "-DENABLE_DISCORD_PRESENCE=OFF"
+    ];
+    preConfigure = "sed -e '77d' -i externals/cmake-modules/GetGitRevisionDescription.cmake";
+    postBuild = "cd src/citra_libretro";
+  };
+
+  desmume = mkLibRetroCore {
+    core = "desmume";
+    description = "libretro wrapper for desmume NDS emulator";
+    license = lib.licenses.gpl2Plus;
+    extraBuildInputs = [ libpcap libGLU libGL xorg.libX11 ];
+    preBuild = "cd desmume/src/frontend/libretro";
+    makeFlags = lib.optional stdenv.hostPlatform.isAarch32 "platform=armv-unix"
+      ++ lib.optional (!stdenv.hostPlatform.isx86) "DESMUME_JIT=0";
+  };
+
+  desmume2015 = mkLibRetroCore {
+    core = "desmume2015";
+    description = "libretro wrapper for desmume NDS emulator from 2015";
+    license = lib.licenses.gpl2Plus;
+    extraBuildInputs = [ libpcap libGLU libGL xorg.libX11 ];
+    makeFlags = lib.optional stdenv.hostPlatform.isAarch32 "platform=armv-unix"
+      ++ lib.optional (!stdenv.hostPlatform.isx86) "DESMUME_JIT=0";
+    preBuild = "cd desmume";
+  };
+
+  dolphin = mkLibRetroCore {
+    core = "dolphin";
+    description = "Port of Dolphin to libretro";
+    license = lib.licenses.gpl2Plus;
+
+    extraNativeBuildInputs = [ cmake curl pkg-config ];
+    extraBuildInputs = [
+      libGLU
+      libGL
+      pcre
+      sfml
+      gettext
+      hidapi
+      libevdev
+      udev
+    ] ++ (with xorg; [ libSM libX11 libXi libpthreadstubs libxcb xcbutil libXext libXrandr libXinerama libXxf86vm ]);
+    makefile = "Makefile";
+    cmakeFlags = [
+      "-DLIBRETRO=ON"
+      "-DLIBRETRO_STATIC=1"
+      "-DENABLE_QT=OFF"
+      "-DENABLE_LTO=OFF"
+      "-DUSE_UPNP=OFF"
+      "-DUSE_DISCORD_PRESENCE=OFF"
+    ];
+    dontUseCmakeBuildDir = true;
+  };
+
+  dosbox = mkLibRetroCore {
+    core = "dosbox";
+    description = "Port of DOSBox to libretro";
+    license = lib.licenses.gpl2Only;
+  };
+
+  eightyone = mkLibRetroCore {
+    core = "81";
+    src = getCoreSrc "eightyone";
+    description = "Port of EightyOne to libretro";
+    license = lib.licenses.gpl3Only;
+  };
+
+  fbalpha2012 = mkLibRetroCore {
+    core = "fbalpha2012";
+    description = "Port of Final Burn Alpha ~2012 to libretro";
+    license = "Non-commercial";
+    makefile = "makefile.libretro";
+    preBuild = "cd svn-current/trunk";
+  };
+
+  fbneo = mkLibRetroCore {
+    core = "fbneo";
+    description = "Port of FBNeo to libretro";
+    license = "Non-commercial";
+    makefile = "Makefile";
+    preBuild = "cd src/burner/libretro";
+  };
+
+  fceumm = mkLibRetroCore {
+    core = "fceumm";
+    description = "FCEUmm libretro port";
+    license = lib.licenses.gpl2Only;
+  };
+
+  flycast = mkLibRetroCore {
+    core = "flycast";
+    description = "Flycast libretro port";
+    license = lib.licenses.gpl2Only;
+    extraBuildInputs = [ libGL libGLU ];
+    makefile = "Makefile";
+    makeFlags = lib.optional stdenv.hostPlatform.isAarch64 [ "platform=arm64" ];
+    platforms = [ "aarch64-linux" "x86_64-linux" ];
+  };
+
+  fmsx = mkLibRetroCore {
+    core = "fmsx";
+    description = "FMSX libretro port";
+    license = "Non-commercial";
+    makefile = "Makefile";
+  };
+
+  freeintv = mkLibRetroCore {
+    core = "freeintv";
+    description = "FreeIntv libretro port";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+  };
+
+  gambatte = mkLibRetroCore {
+    core = "gambatte";
+    description = "Gambatte libretro port";
+    license = lib.licenses.gpl2Only;
+  };
+
+  genesis-plus-gx = mkLibRetroCore {
+    core = "genesis-plus-gx";
+    description = "Enhanced Genesis Plus libretro port";
+    license = "Non-commercial";
+  };
+
+  gpsp = mkLibRetroCore {
+    core = "gpsp";
+    description = "Port of gpSP to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  gw = mkLibRetroCore {
+    core = "gw";
+    description = "Port of Game and Watch to libretro";
+    license = lib.licenses.zlib;
+    makefile = "Makefile";
+  };
+
+  handy = mkLibRetroCore {
+    core = "handy";
+    description = "Port of Handy to libretro";
+    license = lib.licenses.zlib;
+    makefile = "Makefile";
+  };
+
+  hatari = mkLibRetroCore {
+    core = "hatari";
+    description = "Port of Hatari to libretro";
+    license = lib.licenses.gpl2Only;
+    extraBuildInputs = [ SDL zlib ];
+    extraNativeBuildInputs = [ cmake which ];
+    dontUseCmakeConfigure = true;
+    dontConfigure = true;
+    makeFlags = [ "EXTERNAL_ZLIB=1" ];
+    depsBuildBuild = [ buildPackages.stdenv.cc ];
+  };
+
+  mame = mkLibRetroCore {
+    core = "mame";
+    description = "Port of MAME to libretro";
+    license = with lib.licenses; [ bsd3 gpl2Plus ];
+    extraBuildInputs = [ alsa-lib libGLU libGL portaudio python3 xorg.libX11 ];
+    makefile = "Makefile.libretro";
+  };
+
+  mame2000 = mkLibRetroCore {
+    core = "mame2000";
+    description = "Port of MAME ~2000 to libretro";
+    license = "MAME";
+    makefile = "Makefile";
+    makeFlags = lib.optional (!stdenv.hostPlatform.isx86) "IS_X86=0";
+    enableParallelBuilding = false;
+  };
+
+  mame2003 = mkLibRetroCore {
+    core = "mame2003";
+    description = "Port of MAME ~2003 to libretro";
+    license = "MAME";
+    makefile = "Makefile";
+    enableParallelBuilding = false;
+  };
+
+  mame2003-plus = mkLibRetroCore {
+    core = "mame2003-plus";
+    description = "Port of MAME ~2003+ to libretro";
+    license = "MAME";
+    makefile = "Makefile";
+    enableParallelBuilding = false;
+  };
+
+  mame2010 = mkLibRetroCore {
+    core = "mame2010";
+    description = "Port of MAME ~2010 to libretro";
+    license = "MAME";
+    makefile = "Makefile";
+    makeFlags = lib.optionals stdenv.hostPlatform.isAarch64 [ "PTR64=1" "ARM_ENABLED=1" "X86_SH2DRC=0" "FORCE_DRC_C_BACKEND=1" ];
+    enableParallelBuilding = false;
+  };
+
+  mame2015 = mkLibRetroCore {
+    core = "mame2015";
+    description = "Port of MAME ~2015 to libretro";
+    license = "MAME";
+    makeFlags = [ "PYTHON=python3" ];
+    extraNativeBuildInputs = [ python3 ];
+    extraBuildInputs = [ alsa-lib ];
+    makefile = "Makefile";
+    enableParallelBuilding = false;
+  };
+
+  mame2016 = mkLibRetroCore {
+    core = "mame2016";
+    description = "Port of MAME ~2016 to libretro";
+    license = with lib.licenses; [ bsd3 gpl2Plus ];
+    extraNativeBuildInputs = [ python3 ];
+    extraBuildInputs = [ alsa-lib ];
+    makeFlags = [ "PYTHON_EXECUTABLE=python3" ];
+    postPatch = ''
+      # Prevent the failure during the parallel building of:
+      # make -C 3rdparty/genie/build/gmake.linux -f genie.make obj/Release/src/host/lua-5.3.0/src/lgc.o
+      mkdir -p 3rdparty/genie/build/gmake.linux/obj/Release/src/host/lua-5.3.0/src
+    '';
+    enableParallelBuilding = false;
+  };
+
+  melonds = mkLibRetroCore {
+    core = "melonds";
+    description = "Port of MelonDS to libretro";
+    license = lib.licenses.gpl3Only;
+    extraBuildInputs = [ libGL libGLU ];
+    makefile = "Makefile";
+  };
+
+  mesen = mkLibRetroCore {
+    core = "mesen";
+    description = "Port of Mesen to libretro";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+    preBuild = "cd Libretro";
+  };
+
+  mesen-s = mkLibRetroCore {
+    core = "mesen-s";
+    description = "Port of Mesen-S to libretro";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+    preBuild = "cd Libretro";
+    normalizeCore = false;
+  };
+
+  meteor = mkLibRetroCore {
+    core = "meteor";
+    description = "Port of Meteor to libretro";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+    preBuild = "cd libretro";
+  };
+
+  mgba = mkLibRetroCore {
+    core = "mgba";
+    description = "Port of mGBA to libretro";
+    license = lib.licenses.mpl20;
+  };
+
+  mupen64plus = mkLibRetroCore {
+    core = "mupen64plus-next";
+    src = getCoreSrc "mupen64plus";
+    description = "Libretro port of Mupen64 Plus, GL only";
+    license = lib.licenses.gpl3Only;
+    extraBuildInputs = [ libGLU libGL libpng nasm xorg.libX11 ];
+    makefile = "Makefile";
+  };
+
+  neocd = mkLibRetroCore {
+    core = "neocd";
+    description = "NeoCD libretro port";
+    license = lib.licenses.lgpl3Only;
+    makefile = "Makefile";
+  };
+
+  nestopia = mkLibRetroCore {
+    core = "nestopia";
+    description = "Nestopia libretro port";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+    preBuild = "cd libretro";
+  };
+
+  np2kai = mkLibRetroCore rec {
+    core = "np2kai";
+    src = getCoreSrc core;
+    description = "Neko Project II kai libretro port";
+    license = lib.licenses.mit;
+    makefile = "Makefile.libretro";
+    makeFlags = [
+      # See https://github.com/AZO234/NP2kai/tags
+      "NP2KAI_VERSION=rev.22"
+      "NP2KAI_HASH=${src.rev}"
+    ];
+    preBuild = "cd sdl";
+  };
+
+  o2em = mkLibRetroCore {
+    core = "o2em";
+    description = "Port of O2EM to libretro";
+    license = lib.licenses.artistic1;
+    makefile = "Makefile";
+  };
+
+  opera = mkLibRetroCore {
+    core = "opera";
+    description = "Opera is a port of 4DO/libfreedo to libretro";
+    license = "Non-commercial";
+    makefile = "Makefile";
+    makeFlags = [ "CC_PREFIX=${stdenv.cc.targetPrefix}" ];
+  };
+
+  parallel-n64 = mkLibRetroCore {
+    core = "parallel-n64";
+    description = "Parallel Mupen64plus rewrite for libretro.";
+    license = lib.licenses.gpl3Only;
+    extraBuildInputs = [ libGLU libGL libpng ];
+    makefile = "Makefile";
+    postPatch = lib.optionalString stdenv.hostPlatform.isAarch64 ''
+      sed -i -e '1 i\CPUFLAGS += -DARM_FIX -DNO_ASM -DARM_ASM -DDONT_WANT_ARM_OPTIMIZATIONS -DARM64' Makefile \
+      && sed -i -e 's,CPUFLAGS  :=,,g' Makefile
+    '';
+  };
+
+  pcsx2 = mkLibRetroCore {
+    core = "pcsx2";
+    description = "Port of PCSX2 to libretro";
+    license = lib.licenses.gpl3Plus;
+    extraNativeBuildInputs = [
+      cmake
+      gettext
+      pkg-config
+    ];
+    extraBuildInputs = [
+      libaio
+      libGL
+      libGLU
+      libpcap
+      libpng
+      libxml2
+      xz
+      xxd
+    ];
+    makefile = "Makefile";
+    cmakeFlags = [
+      "-DLIBRETRO=ON"
+    ];
+    postPatch = ''
+      # remove ccache
+      substituteInPlace CMakeLists.txt --replace "ccache" ""
+    '';
+    postBuild = "cd /build/source/build/pcsx2";
+    platforms = lib.platforms.x86;
+  };
+
+  pcsx_rearmed = mkLibRetroCore {
+    core = "pcsx_rearmed";
+    description = "Port of PCSX ReARMed with GNU lightning to libretro";
+    license = lib.licenses.gpl2Only;
+    dontConfigure = true;
+  };
+
+  picodrive = mkLibRetroCore {
+    core = "picodrive";
+    description = "Fast MegaDrive/MegaCD/32X emulator";
+    license = "MAME";
+
+    extraBuildInputs = [ libpng SDL ];
+    SDL_CONFIG = "${SDL.dev}/bin/sdl-config";
+    dontAddPrefix = true;
+    configurePlatforms = [ ];
+    makeFlags = lib.optional stdenv.hostPlatform.isAarch64 [ "platform=aarch64" ];
+  };
+
+  play = mkLibRetroCore {
+    core = "play";
+    description = "Port of Play! to libretro";
+    license = lib.licenses.bsd2;
+    extraBuildInputs = [ boost bzip2 curl openssl icu libGL libGLU xorg.libX11 ];
+    extraNativeBuildInputs = [ cmake ];
+    makefile = "Makefile";
+    cmakeFlags = [ "-DBUILD_PLAY=OFF" "-DBUILD_LIBRETRO_CORE=ON" ];
+    postBuild = "cd Source/ui_libretro";
+  };
+
+  ppsspp = mkLibRetroCore {
+    core = "ppsspp";
+    description = "ppsspp libretro port";
+    license = lib.licenses.gpl2Plus;
+    extraNativeBuildInputs = [ cmake pkg-config python3 ];
+    extraBuildInputs = [ libGLU libGL libzip ffmpeg snappy xorg.libX11 ];
+    makefile = "Makefile";
+    cmakeFlags = [
+      "-DLIBRETRO=ON"
+      "-DUSE_SYSTEM_FFMPEG=ON"
+      "-DUSE_SYSTEM_SNAPPY=ON"
+      "-DUSE_SYSTEM_LIBZIP=ON"
+      "-DOpenGL_GL_PREFERENCE=GLVND"
+    ];
+    postBuild = "cd lib";
+  };
+
+  prboom = mkLibRetroCore {
+    core = "prboom";
+    description = "Prboom libretro port";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  prosystem = mkLibRetroCore {
+    core = "prosystem";
+    description = "Port of ProSystem to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  quicknes = mkLibRetroCore {
+    core = "quicknes";
+    description = "QuickNES libretro port";
+    license = lib.licenses.lgpl21Plus;
+    makefile = "Makefile";
+  };
+
+  sameboy = mkLibRetroCore {
+    core = "sameboy";
+    description = "SameBoy libretro port";
+    license = lib.licenses.mit;
+    extraNativeBuildInputs = [ which hexdump ];
+    preBuild = "cd libretro";
+    makefile = "Makefile";
+  };
+
+  scummvm = mkLibRetroCore {
+    core = "scummvm";
+    description = "Libretro port of ScummVM";
+    license = lib.licenses.gpl2Only;
+    extraBuildInputs = [ fluidsynth libjpeg libvorbis libGLU libGL SDL ];
+    makefile = "Makefile";
+    preConfigure = "cd backends/platform/libretro/build";
+  };
+
+  smsplus-gx = mkLibRetroCore {
+    core = "smsplus";
+    src = getCoreSrc "smsplus-gx";
+    description = "SMS Plus GX libretro port";
+    license = lib.licenses.gpl2Plus;
+  };
+
+  snes9x = mkLibRetroCore {
+    core = "snes9x";
+    description = "Port of SNES9x git to libretro";
+    license = "Non-commercial";
+    makefile = "Makefile";
+    preBuild = "cd libretro";
+  };
+
+  snes9x2002 = mkLibRetroCore {
+    core = "snes9x2002";
+    description = "Optimized port/rewrite of SNES9x 1.39 to Libretro";
+    license = "Non-commercial";
+    makefile = "Makefile";
+  };
+
+  snes9x2005 = mkLibRetroCore {
+    core = "snes9x2005";
+    description = "Optimized port/rewrite of SNES9x 1.43 to Libretro";
+    license = "Non-commercial";
+    makefile = "Makefile";
+  };
+
+  snes9x2005-plus = mkLibRetroCore {
+    core = "snes9x2005-plus";
+    src = getCoreSrc "snes9x2005";
+    description = "Optimized port/rewrite of SNES9x 1.43 to Libretro, with Blargg's APU";
+    license = "Non-commercial";
+    makefile = "Makefile";
+    makeFlags = [ "USE_BLARGG_APU=1" ];
+  };
+
+  snes9x2010 = mkLibRetroCore {
+    core = "snes9x2010";
+    description = "Optimized port/rewrite of SNES9x 1.52+ to Libretro";
+    license = "Non-commercial";
+  };
+
+  stella = mkLibRetroCore {
+    core = "stella";
+    description = "Port of Stella to libretro";
+    license = lib.licenses.gpl2Only;
+    extraBuildInputs = [ libpng pkg-config SDL ];
+    makefile = "Makefile";
+    preBuild = "cd src/libretro";
+    dontConfigure = true;
+  };
+
+  stella2014 = mkLibRetroCore {
+    core = "stella2014";
+    description = "Port of Stella to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  swanstation = mkLibRetroCore {
+    core = "swanstation";
+    description = "Port of SwanStation (a fork of DuckStation) to libretro";
+    license = lib.licenses.gpl3Only;
+    extraNativeBuildInputs = [ cmake ];
+    makefile = "Makefile";
+    cmakeFlags = [
+      "-DBUILD_LIBRETRO_CORE=ON"
+    ];
+  };
+
+  tgbdual = mkLibRetroCore {
+    core = "tgbdual";
+    description = "Port of TGBDual to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+  };
+
+  thepowdertoy = mkLibRetroCore {
+    core = "thepowdertoy";
+    description = "Port of The Powder Toy to libretro";
+    license = lib.licenses.gpl3Only;
+    extraNativeBuildInputs = [ cmake ];
+    makefile = "Makefile";
+    postBuild = "cd src";
+  };
+
+  tic80 = mkLibRetroCore {
+    core = "tic80";
+    description = "Port of TIC-80 to libretro";
+    license = lib.licenses.mit;
+    extraNativeBuildInputs = [ cmake pkg-config libGL libGLU ];
+    makefile = "Makefile";
+    cmakeFlags = [
+      "-DBUILD_LIBRETRO=ON"
+      "-DBUILD_DEMO_CARTS=OFF"
+      "-DBUILD_PRO=OFF"
+      "-DBUILD_PLAYER=OFF"
+      "-DBUILD_SDL=OFF"
+      "-DBUILD_SOKOL=OFF"
+    ];
+    preConfigure = "cd core";
+    postBuild = "cd lib";
+  };
+
+  vba-m = mkLibRetroCore {
+    core = "vbam";
+    src = getCoreSrc "vba-m";
+    description = "vanilla VBA-M libretro port";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+    preBuild = "cd src/libretro";
+  };
+
+  vba-next = mkLibRetroCore {
+    core = "vba-next";
+    description = "VBA-M libretro port with modifications for speed";
+    license = lib.licenses.gpl2Only;
+  };
+
+  vecx = mkLibRetroCore {
+    core = "vecx";
+    description = "Port of Vecx to libretro";
+    license = lib.licenses.gpl3Only;
+    extraBuildInputs = [ libGL libGLU ];
+  };
+
+  virtualjaguar = mkLibRetroCore {
+    core = "virtualjaguar";
+    description = "Port of VirtualJaguar to libretro";
+    license = lib.licenses.gpl3Only;
+    makefile = "Makefile";
+  };
+
+  yabause = mkLibRetroCore {
+    core = "yabause";
+    description = "Port of Yabause to libretro";
+    license = lib.licenses.gpl2Only;
+    makefile = "Makefile";
+    # Disable SSE for non-x86. DYNAREC doesn't build on aarch64.
+    makeFlags = lib.optional (!stdenv.hostPlatform.isx86) "HAVE_SSE=0";
+    preBuild = "cd yabause/src/libretro";
+  };
+}
diff --git a/pkgs/applications/emulators/retroarch/default.nix b/pkgs/applications/emulators/retroarch/default.nix
new file mode 100644
index 0000000000000..2c49874e54468
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/default.nix
@@ -0,0 +1,122 @@
+{ lib
+, stdenv
+, enableNvidiaCgToolkit ? false
+, withGamemode ? stdenv.isLinux
+, withVulkan ? stdenv.isLinux
+, alsa-lib
+, AppKit
+, fetchFromGitHub
+, ffmpeg_4
+, Foundation
+, freetype
+, gamemode
+, libdrm
+, libGL
+, libGLU
+, libobjc
+, libpulseaudio
+, libv4l
+, libX11
+, libXdmcp
+, libXext
+, libxkbcommon
+, libxml2
+, libXxf86vm
+, makeWrapper
+, mesa
+, nvidia_cg_toolkit
+, pkg-config
+, python3
+, SDL2
+, udev
+, vulkan-loader
+, wayland
+, which
+}:
+
+let
+  version = "1.10.0";
+  libretroCoreInfo = fetchFromGitHub {
+    owner = "libretro";
+    repo = "libretro-core-info";
+    sha256 = "sha256-3j7fvcfbgyk71MmbUUKYi+/0cpQFNbYXO+DMDUjDqkQ=";
+    rev = "v${version}";
+  };
+  runtimeLibs = lib.optional withVulkan vulkan-loader
+    ++ lib.optional withGamemode gamemode.lib;
+in
+stdenv.mkDerivation rec {
+  pname = "retroarch-bare";
+  inherit version;
+
+  src = fetchFromGitHub {
+    owner = "libretro";
+    repo = "RetroArch";
+    sha256 = "sha256-bpTSzODVRKRs1OW6JafjbU3e/AqdQeGzWcg1lb9SIyo=";
+    rev = "v${version}";
+  };
+
+  patches = [
+    ./0001-Disable-menu_show_core_updater.patch
+    ./0002-Use-fixed-paths-on-libretro_info_path.patch
+  ];
+
+  postPatch = ''
+    substituteInPlace "frontend/drivers/platform_unix.c" \
+      --replace "@libretro_directory@" "$out/lib" \
+      --replace "@libretro_info_path@" "$out/share/libretro/info"
+    substituteInPlace "frontend/drivers/platform_darwin.m" \
+      --replace "@libretro_directory@" "$out/lib" \
+      --replace "@libretro_info_path@" "$out/share/libretro/info"
+  '';
+
+  nativeBuildInputs = [ pkg-config ] ++
+    lib.optional stdenv.isLinux wayland ++
+    lib.optional (runtimeLibs != [ ]) makeWrapper;
+
+  buildInputs = [ ffmpeg_4 freetype libxml2 libGLU libGL python3 SDL2 which ] ++
+    lib.optional enableNvidiaCgToolkit nvidia_cg_toolkit ++
+    lib.optional withVulkan vulkan-loader ++
+    lib.optionals stdenv.isDarwin [ libobjc AppKit Foundation ] ++
+    lib.optionals stdenv.isLinux [
+      alsa-lib
+      libX11
+      libXdmcp
+      libXext
+      libXxf86vm
+      libdrm
+      libpulseaudio
+      libv4l
+      libxkbcommon
+      mesa
+      udev
+      wayland
+    ];
+
+  enableParallelBuilding = true;
+
+  configureFlags = lib.optionals stdenv.isLinux [ "--enable-kms" "--enable-egl" ];
+
+  postInstall = ''
+    mkdir -p $out/share/libretro/info
+    # TODO: ideally each core should have its own core information
+    cp -r ${libretroCoreInfo}/* $out/share/libretro/info
+  '' + lib.optionalString (runtimeLibs != [ ]) ''
+    wrapProgram $out/bin/retroarch \
+      --prefix LD_LIBRARY_PATH ':' ${lib.makeLibraryPath runtimeLibs}
+  '';
+
+  preFixup = "rm $out/bin/retroarch-cg2glsl";
+
+  meta = with lib; {
+    homepage = "https://libretro.com";
+    description = "Multi-platform emulator frontend for libretro cores";
+    license = licenses.gpl3Plus;
+    platforms = platforms.unix;
+    changelog = "https://github.com/libretro/RetroArch/blob/v${version}/CHANGES.md";
+    maintainers = with maintainers; [ MP2E edwtjo matthewbauer kolbycrouch thiagokokada ];
+    # FIXME: exits with error on macOS:
+    # No Info.plist file in application bundle or no NSPrincipalClass in the Info.plist file, exiting
+    broken = stdenv.isDarwin;
+  };
+}
diff --git a/pkgs/applications/emulators/retroarch/disable-menu-show-core-updater.patch b/pkgs/applications/emulators/retroarch/disable-menu-show-core-updater.patch
new file mode 100644
index 0000000000000..34fea554ef71e
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/disable-menu-show-core-updater.patch
@@ -0,0 +1,13 @@
+diff --git a/retroarch.cfg b/retroarch.cfg
+index cdcb199c9f..ab72f3920f 100644
+--- a/retroarch.cfg
++++ b/retroarch.cfg
+@@ -681,7 +681,7 @@
+ # menu_show_online_updater = true
+ 
+ # If disabled, will hide the ability to update cores (and core info files) inside the menu.
+-# menu_show_core_updater = true
++menu_show_core_updater = false
+ 
+ # If disabled, the libretro core will keep running in the background when we
+ # are in the menu.
diff --git a/pkgs/applications/emulators/retroarch/fix-config.patch b/pkgs/applications/emulators/retroarch/fix-config.patch
new file mode 100644
index 0000000000000..1a71bf43cb11f
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/fix-config.patch
@@ -0,0 +1,26 @@
+diff --git a/retroarch.cfg b/retroarch.cfg
+index cdcb199c9f..08b9b1cf10 100644
+--- a/retroarch.cfg
++++ b/retroarch.cfg
+@@ -681,7 +681,7 @@
+ # menu_show_online_updater = true
+ 
+ # If disabled, will hide the ability to update cores (and core info files) inside the menu.
+-# menu_show_core_updater = true
++menu_show_core_updater = false
+ 
+ # If disabled, the libretro core will keep running in the background when we
+ # are in the menu.
+@@ -823,10 +823,10 @@
+ # rgui_browser_directory =
+ 
+ # Core directory for libretro core implementations.
+-# libretro_directory =
++libretro_directory = @libretro_directory@
+ 
+ # Core info directory for libretro core information.
+-# libretro_info_path =
++libretro_info_path = @libretro_info_path@
+ 
+ # Path to content database directory.
+ # content_database_path =
diff --git a/pkgs/applications/emulators/retroarch/fix-libretro-paths.patch b/pkgs/applications/emulators/retroarch/fix-libretro-paths.patch
new file mode 100644
index 0000000000000..203ce836533d5
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/fix-libretro-paths.patch
@@ -0,0 +1,28 @@
+diff --git a/configuration.c b/configuration.c
+index e6a3841324..afb1d6e2ce 100644
+--- a/configuration.c
++++ b/configuration.c
+@@ -1456,7 +1456,7 @@ static struct config_path_setting *populate_settings_path(
+    SETTING_PATH("core_options_path",
+          settings->paths.path_core_options, false, NULL, true);
+    SETTING_PATH("libretro_info_path",
+-         settings->paths.path_libretro_info, false, NULL, true);
++         settings->paths.path_libretro_info, false, NULL, false);
+    SETTING_PATH("content_database_path",
+          settings->paths.path_content_database, false, NULL, true);
+    SETTING_PATH("cheat_database_path",
+diff --git a/frontend/drivers/platform_unix.c b/frontend/drivers/platform_unix.c
+index 722e1c595c..e7313ee038 100644
+--- a/frontend/drivers/platform_unix.c
++++ b/frontend/drivers/platform_unix.c
+@@ -1825,8 +1825,8 @@ static void frontend_unix_get_env(int *argc,
+    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], base_path,
+          "core_info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
+ #else
+-   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], base_path,
+-         "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
++   fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], "@libretro_info_path@",
++         "", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
+ #endif
+    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG], base_path,
+          "autoconfig", sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG]));
diff --git a/pkgs/applications/emulators/retroarch/hashes.json b/pkgs/applications/emulators/retroarch/hashes.json
new file mode 100644
index 0000000000000..1280f25047d89
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/hashes.json
@@ -0,0 +1,499 @@
+{
+    "atari800": {
+        "owner": "libretro",
+        "repo": "libretro-atari800",
+        "rev": "478a8ec99a7f8436a39d5ac193c5fe313233ee7b",
+        "sha256": "LJpRegJVR2+sS1UmTTpVest0rMrNDBMXmj/jRFVglWI="
+    },
+    "beetle-gba": {
+        "owner": "libretro",
+        "repo": "beetle-gba-libretro",
+        "rev": "38182572571a48cb58057cde64b915237c4e2d58",
+        "sha256": "4xnXWswozlcXBNI1lbGSNW/gAdIeLLO9Bf1SxOFLhSo="
+    },
+    "beetle-lynx": {
+        "owner": "libretro",
+        "repo": "beetle-lynx-libretro",
+        "rev": "8930e88a4342945c023cbf713031a65de11a8e75",
+        "sha256": "bg/a+9ZJNTUIuEHKrFIss8sia3JWMWXIXbxha5qKVeI="
+    },
+    "beetle-ngp": {
+        "owner": "libretro",
+        "repo": "beetle-ngp-libretro",
+        "rev": "f7c393184e5228c3d3807ee74c951c4c549107d8",
+        "sha256": "7vki8VkwOzxwMZcUxekg1DFSskV7VNQ1SRaU3M1xHZ0="
+    },
+    "beetle-pce-fast": {
+        "owner": "libretro",
+        "repo": "beetle-pce-fast-libretro",
+        "rev": "0f43fd4dc406e7da6bbdc13b6eb1c105d6072f8a",
+        "sha256": "u1lOgXEYuGAF4sOLdsBzcA4/A5Yz1b82TjFBiM57yE4="
+    },
+    "beetle-pcfx": {
+        "owner": "libretro",
+        "repo": "beetle-pcfx-libretro",
+        "rev": "6d2b11e17ad5a95907c983e7c8a70e75508c2d41",
+        "sha256": "WG2YpCYdL/MxW5EbiP2+1VtAjbX7yYDIcLXhb+YySI4="
+    },
+    "beetle-psx": {
+        "owner": "libretro",
+        "repo": "beetle-psx-libretro",
+        "rev": "297970e4ff080ea80a5670209aeea4fde8059020",
+        "sha256": "6kZher3/+5ywXyC3n9R9JVA4IVLZBaSfAcWEKp2SsDE="
+    },
+    "beetle-saturn": {
+        "owner": "libretro",
+        "repo": "beetle-saturn-libretro",
+        "rev": "e6ba71f8bcc647b646d94dec812b24d00c41cf3f",
+        "sha256": "tDbV+CsDr4bowBbJ/C8J9scfCryTAXxz58pGaUHU5yU="
+    },
+    "beetle-snes": {
+        "owner": "libretro",
+        "repo": "beetle-bsnes-libretro",
+        "rev": "bc867656d7438aaffc6818b3b92350587bc78a47",
+        "sha256": "TyUCRGK+uyXowDjXW9/4m+zL8Vh/3GGsX1eznrTCbAg="
+    },
+    "beetle-supergrafx": {
+        "owner": "libretro",
+        "repo": "beetle-supergrafx-libretro",
+        "rev": "7bae6fb1a238f1e66b129c7c70c7cb6dbdc09fa1",
+        "sha256": "OAJ86XrwjDrgCjrk0RHMn8sHYaJFhJhLaQnhaEVXN38="
+    },
+    "beetle-vb": {
+        "owner": "libretro",
+        "repo": "beetle-vb-libretro",
+        "rev": "aa77198c6c60b935503b5ea2149b8ff7598344da",
+        "sha256": "ShsMYc2vjDoiN1yCCoSl91P5ecYJDj/V+VWUYuYVxas="
+    },
+    "beetle-wswan": {
+        "owner": "libretro",
+        "repo": "beetle-wswan-libretro",
+        "rev": "5717c101b314f64d4c384c23b1934d09fcbf82bb",
+        "sha256": "Nfezb6hja1qHv1fMGU9HMbbb56GHAfe/zIgRqrzz334="
+    },
+    "blastem": {
+        "owner": "libretro",
+        "repo": "blastem",
+        "rev": "0786858437ed71996f43b7af0fbe627eb88152fc",
+        "sha256": "uEP5hSgLAle1cLv/EM7D11TJMAggu7pqWxfrUt3rhEg="
+    },
+    "bluemsx": {
+        "owner": "libretro",
+        "repo": "bluemsx-libretro",
+        "rev": "5dfdb75106e10ef8bc21b8bcea1432ecbd590b2a",
+        "sha256": "0D0xufIt3qmQ+/UjyWynoLyLDSza8cTrFp3UwGWBXko="
+    },
+    "bsnes": {
+        "owner": "libretro",
+        "repo": "bsnes-libretro",
+        "rev": "1b2987ab1e9caf5c8d7550da01ffa08edff2f128",
+        "sha256": "l6Jvn0ZgFaKSWjiV2bN9aemxLyfnNEQFc+HS1/MuiaY="
+    },
+    "bsnes-hd": {
+        "owner": "DerKoun",
+        "repo": "bsnes-hd",
+        "rev": "65f24e56c37f46bb752190024bd4058e64ad77d1",
+        "sha256": "1dk2i71NOLeTTOZjVll8wrkr5dIH5bGSGUeeHqWjZHE="
+    },
+    "bsnes-mercury": {
+        "owner": "libretro",
+        "repo": "bsnes-mercury",
+        "rev": "d232c6ea90552f5921fec33a06626f08d3e18b24",
+        "sha256": "fpl7hmqz+Ca+9ZeM6E1JSikbiu+NJUU8xXtyl6Dd9Gg="
+    },
+    "citra": {
+        "owner": "libretro",
+        "repo": "citra",
+        "rev": "b1959d07a340bfd9af65ad464fd19eb6799a96ef",
+        "sha256": "bwnYkMvbtRF5bGZRYVtMWxnCu9P45qeX4+ntOj9eRds=",
+        "fetchSubmodules": true,
+        "leaveDotGit": true,
+        "deepClone": true
+    },
+    "citra-canary": {
+        "owner": "libretro",
+        "repo": "citra",
+        "rev": "5401990a9be46e4497abc92db3d5f2042674303d",
+        "sha256": "JKKJBa840i7ESwMrB5tKamCBmrYvvoEUdibqxkWg5Gc=",
+        "fetchSubmodules": true,
+        "leaveDotGit": true,
+        "deepClone": true
+    },
+    "desmume": {
+        "owner": "libretro",
+        "repo": "desmume",
+        "rev": "7ea0fc96804fcd9c8d33e8f76cf64b1be50cc5ea",
+        "sha256": "4S/CirRVOBN6PVbato5X5fu0tBn3Fu5FEAbdf3TBqng="
+    },
+    "desmume2015": {
+        "owner": "libretro",
+        "repo": "desmume2015",
+        "rev": "cd89fb7c48c735cb321311fbce7e6e9889dda1ce",
+        "sha256": "9Ou/n6pxRjJOp/Ybpyg4+Simosj2X26kLZCMEqeVL6U="
+    },
+    "dolphin": {
+        "owner": "libretro",
+        "repo": "dolphin",
+        "rev": "3b19e6d1781584f3e1fd2922b48b8ae6b3bcb686",
+        "sha256": "EcgJhkMzdZfYRwSpU1OcsJqQyq4V8dq5PndVufZFy7k="
+    },
+    "dosbox": {
+        "owner": "libretro",
+        "repo": "dosbox-libretro",
+        "rev": "aa71b67d54eaaf9e41cdd3cb5153d9cff0ad116e",
+        "sha256": "L0Y67UROjldnXUlLQ+Xbd7RHLb96jDxlB/k+LR9Kbas="
+    },
+    "eightyone": {
+        "owner": "libretro",
+        "repo": "81-libretro",
+        "rev": "86d7d5afe98f16006d4b1fdb99d281f1d7ea6b2f",
+        "sha256": "QN7anzqv1z8SgY8dlkjr8Ns7reGWc7hTneiRmorXZSk="
+    },
+    "fbalpha2012": {
+        "owner": "libretro",
+        "repo": "fbalpha2012",
+        "rev": "23f98fc7cf4f2f216149c263cf5913d2e28be8d4",
+        "sha256": "dAInW6tTV7oXcPhKMnHWcmQaWQCTqRrYHD2yuaI1I1w="
+    },
+    "fbneo": {
+        "owner": "libretro",
+        "repo": "fbneo",
+        "rev": "4ecf2782a4eee042d1e126d1671e5231b6437b6e",
+        "sha256": "15MYI03r45mmRsXCwzWnjfBdtzSaHLp7DfmcACQFTvU="
+    },
+    "fceumm": {
+        "owner": "libretro",
+        "repo": "libretro-fceumm",
+        "rev": "eb06d17e7912780a3ee117ae73bc50c3948c761c",
+        "sha256": "aBqskJtK1bFBjwaoo9hilr33fyAWsdj5+hFC3WY3sKk="
+    },
+    "flycast": {
+        "owner": "libretro",
+        "repo": "flycast",
+        "rev": "0d8c6a2e717c002bc76ce26a152353b004fb15e7",
+        "sha256": "t2RGHAyYXeHVqTqqhayOUWx/msFN9q/Z9P2wXJUtQTI="
+    },
+    "fmsx": {
+        "owner": "libretro",
+        "repo": "fmsx-libretro",
+        "rev": "dfcda056896576c6a1c75c002a82d0e6c1160ccc",
+        "sha256": "9ANZ1suAQcYOhqSchQ20Yuqvgw06j5Sd3Z1fjrp2UFc="
+    },
+    "freeintv": {
+        "owner": "libretro",
+        "repo": "freeintv",
+        "rev": "d58caf23ed1438a1db58f8d6ac24ca521b411d3b",
+        "sha256": "nUV+A3Zh66M1K5NDK0ksNF5H1HS3AQdeYLaGfaA34n4="
+    },
+    "gambatte": {
+        "owner": "libretro",
+        "repo": "gambatte-libretro",
+        "rev": "79bb2e56d034c30d8dcac02b6c34a59ec8fe91bc",
+        "sha256": "H+Hkeep18whaSYbyG8DcaJqsVVu7DEX9T28pkfXfyCg="
+    },
+    "genesis-plus-gx": {
+        "owner": "libretro",
+        "repo": "Genesis-Plus-GX",
+        "rev": "88c9ad000ba553b9c819d9eb259f741fabd877bb",
+        "sha256": "8ZCMq8/sk5TqwTNWMfDevZHRPSOM1PJ57kiZZ7qfQxA="
+    },
+    "gpsp": {
+        "owner": "libretro",
+        "repo": "gpsp",
+        "rev": "e554360dd3ed283696fc607877024a219248b735",
+        "sha256": "ImsqB89XmjF8nvs7j8IZVvFltgZRYvF2L7LTcJG/xCU="
+    },
+    "gw": {
+        "owner": "libretro",
+        "repo": "gw-libretro",
+        "rev": "0f1ccca156388880bf4507ad44741f80945dfc6f",
+        "sha256": "BVpx8pL224J2u9W6UDrxzfEv4qIsh6wrf3bDdd1R850="
+    },
+    "handy": {
+        "owner": "libretro",
+        "repo": "libretro-handy",
+        "rev": "3b02159ba32aa37c1b93d7f7eac56b28e3715645",
+        "sha256": "mBKK+pdWgkxYkV4OOiBrlWbLAMugDX0fd6QRh0D7JYU="
+    },
+    "hatari": {
+        "owner": "libretro",
+        "repo": "hatari",
+        "rev": "79d128888ca3efdd27d639a35edf72a9bc81a798",
+        "sha256": "du2xORgAXTSQArqNuFa5gjticgZ+weqySFHVz2Y2qzI="
+    },
+    "mame": {
+        "owner": "libretro",
+        "repo": "mame",
+        "rev": "2f9c793a77222ae46266c71f64d491cf7870dc1e",
+        "sha256": "WAhm6QMMVbnuSIK4PW7Ek+AAkMs7s95gGb6ERzlon0w="
+    },
+    "mame2000": {
+        "owner": "libretro",
+        "repo": "mame2000-libretro",
+        "rev": "4793742b457945afb74053c8a895e6ff0b36b033",
+        "sha256": "DA9fZTic/jlYzSAIiOjfhohyEyQZiBNdIa8YCZoKZNs="
+    },
+    "mame2003": {
+        "owner": "libretro",
+        "repo": "mame2003-libretro",
+        "rev": "dbda6ddacdd8962cfea25000421dba398e551aef",
+        "sha256": "RSL3iZZEJCxOtsJqjnM5ZiT0yM2nAgg/Ujq6FBLMHkk="
+    },
+    "mame2003-plus": {
+        "owner": "libretro",
+        "repo": "mame2003-plus-libretro",
+        "rev": "9c0c954f0f88730f44abdd4d414691fef6b1cd7c",
+        "sha256": "NLdHc0VuZhqQhAzv+8kipc0mhqT2BNaJeLYZUx7DwRU="
+    },
+    "mame2010": {
+        "owner": "libretro",
+        "repo": "mame2010-libretro",
+        "rev": "932e6f2c4f13b67b29ab33428a4037dee9a236a8",
+        "sha256": "HSZRSnc+0300UE9fPcUOMrXABlxHhTewkFPTqQ4Srxs="
+    },
+    "mame2015": {
+        "owner": "libretro",
+        "repo": "mame2015-libretro",
+        "rev": "e6a7aa4d53726e61498f68d6b8e2c092a2169fa2",
+        "sha256": "IgiLxYYuUIn3YE+kQCXzgshES2VNpUHn0Qjsorw0w/s="
+    },
+    "mame2016": {
+        "owner": "libretro",
+        "repo": "mame2016-libretro",
+        "rev": "bcff8046328da388d100b1634718515e1b15415d",
+        "sha256": "XxnX39+0VUbG9TF8+wFEFVxHCm2rzrJsIQryyNsF6zU="
+    },
+    "melonds": {
+        "owner": "libretro",
+        "repo": "melonds",
+        "rev": "0053daa700018657bf2e47562b3b4eb86f9b9d03",
+        "sha256": "K6ZYuk7cE+ioq1rLRyAKNQxddCYIOXLU5SXT7sYgGnc="
+    },
+    "mesen": {
+        "owner": "libretro",
+        "repo": "mesen",
+        "rev": "094d82bf724448426acbaad45e83bc38994e32f6",
+        "sha256": "9+AqZRv8lugNNa+ZZzIPJNO87J1aBUEiOggL8aYno1M="
+    },
+    "mesen-s": {
+        "owner": "libretro",
+        "repo": "mesen-s",
+        "rev": "42eb0e8ad346608dae86feb8a04833d16ad21541",
+        "sha256": "q6zeoNiZtFy8ZYls9/E+O7o9BYTcVcmYjbJA48qiraU="
+    },
+    "meteor": {
+        "owner": "libretro",
+        "repo": "meteor-libretro",
+        "rev": "e533d300d0561564451bde55a2b73119c768453c",
+        "sha256": "zMkgzUz2rk0SD5ojY4AqaDlNM4k4QxuUxVBRBcn6TqQ="
+    },
+    "mgba": {
+        "owner": "libretro",
+        "repo": "mgba",
+        "rev": "43da6e1d54ad0395f474346db88fe59a4c0aa451",
+        "sha256": "JxiWIBQi1fZoBV2lvx2r7iIvlQm0BYuJFz0TsxngUT8="
+    },
+    "mupen64plus": {
+        "owner": "libretro",
+        "repo": "mupen64plus-libretro-nx",
+        "rev": "350f90a73cf0f5d65357ce982ccbaa3b22fc3569",
+        "sha256": "9Hq93+dvO60LBbcXLIHsTq243QThicI0rVJW3tou/5Y="
+    },
+    "neocd": {
+        "owner": "libretro",
+        "repo": "neocd_libretro",
+        "rev": "83d10f3be10fff2f28aa56fc674c687528cb7f5c",
+        "sha256": "yYZGoMsUfE8cpU9i826UWQGi1l0zPJPcBDb2CINxGeQ="
+    },
+    "nestopia": {
+        "owner": "libretro",
+        "repo": "nestopia",
+        "rev": "8af07b7ab49e45495cbc4ba73cd2f879d9908b55",
+        "sha256": "Z447flP1L/7gWEovWhbBearPKzsZNnGE2cz7jH7kEnY="
+    },
+    "np2kai": {
+        "owner": "AZO234",
+        "repo": "NP2kai",
+        "rev": "30d4b6959c48db039207a37e278c868c7737ed69",
+        "sha256": "uIcgbpcEz6yUKrBe0r84Yq2ihWfT0+TdUTIF5kMT5mI=",
+        "fetchSubmodules": true
+    },
+    "o2em": {
+        "owner": "libretro",
+        "repo": "libretro-o2em",
+        "rev": "f1050243e0d5285e7769e94a882b0cf39d2b7370",
+        "sha256": "wD+iJ8cKC8jYFZ6OVvX71uO7sSh5b/LLoc5+g7f3Yyg="
+    },
+    "opera": {
+        "owner": "libretro",
+        "repo": "opera-libretro",
+        "rev": "3849c969c64b82e622a7655b327fa94bc5a4c7cc",
+        "sha256": "McSrvjrYTemqAAnfHELf9qXC6n6Dg4kNsUDA7e2DvkE="
+    },
+    "parallel-n64": {
+        "owner": "libretro",
+        "repo": "parallel-n64",
+        "rev": "28c4572c9a09447b3bf5ed5fbd3594a558bc210d",
+        "sha256": "by8NvKjVT9OrgVhNtv5E4Fqmdva42lWV8UQi0SKfBL8="
+    },
+    "pcsx2": {
+        "owner": "libretro",
+        "repo": "pcsx2",
+        "rev": "3ef2a36b0608e9dcae808c7ef01c7a760d628735",
+        "sha256": "ezqVntonhGfejiGx9cxQEnjsXEHqT++M1fO0Jz1t/Us="
+    },
+    "pcsx_rearmed": {
+        "owner": "libretro",
+        "repo": "pcsx_rearmed",
+        "rev": "12fc12797064599dfca2d44043d5c02a949711ef",
+        "sha256": "SXmNfHGyk+KChiwkKlA+d/oezzp/7p1DJY+w2bES6kg="
+    },
+    "picodrive": {
+        "owner": "libretro",
+        "repo": "picodrive",
+        "rev": "50b8b47838fea8096535d543caaacdcc56aa7df2",
+        "sha256": "C1Htwel5PHZcjkKmjiiN/QgRofMhqlArxktOSqoTxTc=",
+        "fetchSubmodules": true
+    },
+    "play": {
+        "owner": "jpd002",
+        "repo": "Play-",
+        "rev": "fd6a5161030215090d48a8036680f57914c71bb0",
+        "sha256": "g6UBRV7biLjPBXdlejjXUSk3v1wrsYWA3quZlpPj23U=",
+        "fetchSubmodules": true
+    },
+    "ppsspp": {
+        "owner": "hrydgard",
+        "repo": "ppsspp",
+        "rev": "54d63cc1daf2a0cdc812e9af85854bb4ae5ef399",
+        "sha256": "iB/8zf4FYdsbiKZVq/YISTEQSoo1kme1uZsyuhbOcoc=",
+        "fetchSubmodules": true
+    },
+    "prboom": {
+        "owner": "libretro",
+        "repo": "libretro-prboom",
+        "rev": "af1b5bf89d01095326ee27e178f9257f9e728873",
+        "sha256": "pvTUv4E+wBOYfjz8Ph11CK4E7rIm1T+u90TWDNXEBIU="
+    },
+    "prosystem": {
+        "owner": "libretro",
+        "repo": "prosystem-libretro",
+        "rev": "89e6df7b60d151310fedbe118fb472959a9dcd61",
+        "sha256": "uxgKddS53X7ntPClE8MGezBAG+7OAFvMXTnyKpOOau0="
+    },
+    "quicknes": {
+        "owner": "libretro",
+        "repo": "QuickNES_Core",
+        "rev": "743e6e06db246c5edab27c738c7a573d83140485",
+        "sha256": "NYmP+HFeZGUeIRaT3bzdpWw9cmEAaBkA3EGnw/zpDXA="
+    },
+    "sameboy": {
+        "owner": "libretro",
+        "repo": "sameboy",
+        "rev": "b154b7d3d885a3cf31203f0b8f50d3b37c8b742b",
+        "sha256": "tavGHiNpRiPkibi66orMf93cnCqQCD8XhSl/36nl/9M="
+    },
+    "scummvm": {
+        "owner": "libretro",
+        "repo": "scummvm",
+        "rev": "80cb7269a33b233dcea27d8d01df084b0d35c80a",
+        "sha256": "5kMWM8d5aBbT7TseNyaYxw7VDkrLL0G+KUvJcUboQgA="
+    },
+    "smsplus-gx": {
+        "owner": "libretro",
+        "repo": "smsplus-gx",
+        "rev": "3f1ffede55bcfe0168caa484a00bf041ab591abf",
+        "sha256": "fD+grzMPk4uXvmzGf+f9Mor0eefBLHIumCydsSHUsck="
+    },
+    "snes9x": {
+        "owner": "snes9xgit",
+        "repo": "snes9x",
+        "rev": "34b6160805c4995a8edf5f9b3328f5e492ae4c44",
+        "sha256": "YRRqtd5iu2evRk+7SyQpqpxqTaEFOkDZ/XQHEjpSBcM="
+    },
+    "snes9x2002": {
+        "owner": "libretro",
+        "repo": "snes9x2002",
+        "rev": "e16cb16efa00765b1bc3b8fee195680efb1542c7",
+        "sha256": "0dhLpNy+NUE3mE/ejEwbq3G28/a2HONS5NPslI5LOEc="
+    },
+    "snes9x2005": {
+        "owner": "libretro",
+        "repo": "snes9x2005",
+        "rev": "77e9cd293c791b47f4397da0a47242b329243cb5",
+        "sha256": "iHGfZIGzE4n3EHrVRxTULuYKsOse5NcJftmasoJFwFo="
+    },
+    "snes9x2010": {
+        "owner": "libretro",
+        "repo": "snes9x2010",
+        "rev": "714b1c8e08c7580430190119b07e793405773ac2",
+        "sha256": "yKSQEE+lT4V2V1XqemfziHuIt79TcvC0ranU9ounTXo="
+    },
+    "stella": {
+        "owner": "stella-emu",
+        "repo": "stella",
+        "rev": "1db9de390a331a7d55c35591c93d9e89184cce5f",
+        "sha256": "vICKxx+UBYvMzZ3a3F86yzJRKfdo0jMxa27wsUX0KZw="
+    },
+    "stella2014": {
+        "owner": "libretro",
+        "repo": "stella2014-libretro",
+        "rev": "934c7a2a44ef038af529b68950ddba4f7ea3478e",
+        "sha256": "s7LQ47sAPTyk4COONk4qnebxCq78zGLIjh3Y2+1fIak="
+    },
+    "swanstation": {
+        "owner": "libretro",
+        "repo": "swanstation",
+        "rev": "61c5debe60192b0fecd8c15310b2e4c4473f9438",
+        "sha256": "DZJApJnGDMsUhjO35TBc7tMldCGKDPPtrwxPLe0Ey1s="
+    },
+    "tgbdual": {
+        "owner": "libretro",
+        "repo": "tgbdual-libretro",
+        "rev": "1e0c4f931d8c5e859e6d3255d67247d7a2987434",
+        "sha256": "0wHv9DpKuzJ/q5vERqCo4GBLre2ggClBIWSjGnMLQq8="
+    },
+    "thepowdertoy": {
+        "owner": "libretro",
+        "repo": "ThePowderToy",
+        "rev": "ac620c0a89a18774c3ad176a8a1bc596df23ff57",
+        "sha256": "C/X1DbmnucRddemEYml2zN3qr5yoXY3b+nvqfpboS0M="
+    },
+    "tic80": {
+        "owner": "libretro",
+        "repo": "tic-80",
+        "rev": "967eb78c3610385a0e6cba8bb5c60ebc3b886d3e",
+        "sha256": "N0QFNTYFVbhWwt2yx5fLM7Dl6pJZPYrt9o3+6rjnWa8=",
+        "fetchSubmodules": true
+    },
+    "vba-m": {
+        "owner": "libretro",
+        "repo": "vbam-libretro",
+        "rev": "254f6effebe882b7d3d29d9e417c6aeeabc08026",
+        "sha256": "vJWjdqJ913NLGL4G15sRPqO/wp9xPsuhUMLUuAbDRKk="
+    },
+    "vba-next": {
+        "owner": "libretro",
+        "repo": "vba-next",
+        "rev": "b218f48bb27b5d3885fa4076ff325922b5acd817",
+        "sha256": "idqGMbMA9mZlIh0QAba3BxpPDi/bFJJkUbnxV3xMOCo="
+    },
+    "vecx": {
+        "owner": "libretro",
+        "repo": "libretro-vecx",
+        "rev": "28d6efc8972313903d0802a736ff8c3bc115e78f",
+        "sha256": "VYa8s+HB8IYF+HS6SA+sO5DzpgCtnMGrh88KTVNGICY="
+    },
+    "virtualjaguar": {
+        "owner": "libretro",
+        "repo": "virtualjaguar-libretro",
+        "rev": "d1b1b28a6ad2518b746e3f7537ec6d66db96ec57",
+        "sha256": "Io25dt80fqIqIxwzF2DK9J5UFz6YCUQoqThcIuxdEBo="
+    },
+    "yabause": {
+        "owner": "libretro",
+        "repo": "yabause",
+        "rev": "f30153ff9e534b96049c6f1ac3075b572642ceb5",
+        "sha256": "AdqCr5X3Bq8ic2jkIestmYi+CBByZ5Fyf0BUYwBkWnA="
+    }
+}
diff --git a/pkgs/applications/emulators/retroarch/kodi-advanced-launchers.nix b/pkgs/applications/emulators/retroarch/kodi-advanced-launchers.nix
new file mode 100644
index 0000000000000..9d83c46396c37
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/kodi-advanced-launchers.nix
@@ -0,0 +1,36 @@
+{ stdenv, pkgs, lib, runtimeShell, cores ? [ ] }:
+
+let
+
+  script = exec: ''
+    #!${runtimeShell}
+    nohup sh -c "pkill -SIGTSTP kodi" &
+    # https://forum.kodi.tv/showthread.php?tid=185074&pid=1622750#pid1622750
+    nohup sh -c "sleep 10 && ${exec} '$@' -f;pkill -SIGCONT kodi"
+  '';
+  scriptSh = exec: pkgs.writeScript ("kodi-"+exec.name) (script exec.path);
+  execs = map (core: rec { name = core.core; path = core+"/bin/retroarch-"+name;}) cores;
+
+in
+
+stdenv.mkDerivation {
+  pname = "kodi-retroarch-advanced-launchers";
+  version = "0.2";
+
+  dontBuild = true;
+
+  buildCommand = ''
+    mkdir -p $out/bin
+    ${lib.concatMapStrings (exec: "ln -s ${scriptSh exec} $out/bin/kodi-${exec.name};") execs}
+  '';
+
+  meta = {
+    description = "Kodi retroarch advanced launchers";
+    longDescription = ''
+      These retroarch launchers are intended to be used with
+      advanced (emulation) launcher for Kodi since device input is
+      otherwise caught by both Kodi and the retroarch process.
+    '';
+    license = lib.licenses.gpl3;
+  };
+}
diff --git a/pkgs/applications/emulators/retroarch/update.py b/pkgs/applications/emulators/retroarch/update.py
new file mode 100755
index 0000000000000..68f72103168e8
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/update.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -I nixpkgs=../../../../ -i python3 -p "python3.withPackages (ps: with ps; [ requests nix-prefetch-github ])" -p "git"
+
+import json
+import sys
+import subprocess
+from pathlib import Path
+
+SCRIPT_PATH = Path(__file__).absolute().parent
+HASHES_PATH = SCRIPT_PATH / "hashes.json"
+CORES = {
+    "atari800": {"repo": "libretro-atari800"},
+    "beetle-gba": {"repo": "beetle-gba-libretro"},
+    "beetle-lynx": {"repo": "beetle-lynx-libretro"},
+    "beetle-ngp": {"repo": "beetle-ngp-libretro"},
+    "beetle-pce-fast": {"repo": "beetle-pce-fast-libretro"},
+    "beetle-pcfx": {"repo": "beetle-pcfx-libretro"},
+    "beetle-psx": {"repo": "beetle-psx-libretro"},
+    "beetle-saturn": {"repo": "beetle-saturn-libretro"},
+    "beetle-snes": {"repo": "beetle-bsnes-libretro"},
+    "beetle-supergrafx": {"repo": "beetle-supergrafx-libretro"},
+    "beetle-vb": {"repo": "beetle-vb-libretro"},
+    "beetle-wswan": {"repo": "beetle-wswan-libretro"},
+    "blastem": {"repo": "blastem"},
+    "bluemsx": {"repo": "bluemsx-libretro"},
+    "bsnes": {"repo": "bsnes-libretro"},
+    "bsnes-hd": {"repo": "bsnes-hd", "owner": "DerKoun"},
+    "bsnes-mercury": {"repo": "bsnes-mercury"},
+    "citra": {
+        "repo": "citra",
+        "fetch_submodules": True,
+        "deep_clone": True,
+        "leave_dot_git": True,
+    },
+    "citra-canary": {
+        "repo": "citra",
+        "fetch_submodules": True,
+        "deep_clone": True,
+        "leave_dot_git": True,
+        "rev": "canary",
+    },
+    "desmume": {"repo": "desmume"},
+    "desmume2015": {"repo": "desmume2015"},
+    "dolphin": {"repo": "dolphin"},
+    "dosbox": {"repo": "dosbox-libretro"},
+    "eightyone": {"repo": "81-libretro"},
+    "fbalpha2012": {"repo": "fbalpha2012"},
+    "fbneo": {"repo": "fbneo"},
+    "fceumm": {"repo": "libretro-fceumm"},
+    "flycast": {"repo": "flycast"},
+    "fmsx": {"repo": "fmsx-libretro"},
+    "freeintv": {"repo": "freeintv"},
+    "gambatte": {"repo": "gambatte-libretro"},
+    "genesis-plus-gx": {"repo": "Genesis-Plus-GX"},
+    "gpsp": {"repo": "gpsp"},
+    "gw": {"repo": "gw-libretro"},
+    "handy": {"repo": "libretro-handy"},
+    "hatari": {"repo": "hatari"},
+    "mame": {"repo": "mame"},
+    "mame2000": {"repo": "mame2000-libretro"},
+    "mame2003": {"repo": "mame2003-libretro"},
+    "mame2003-plus": {"repo": "mame2003-plus-libretro"},
+    "mame2010": {"repo": "mame2010-libretro"},
+    "mame2015": {"repo": "mame2015-libretro"},
+    "mame2016": {"repo": "mame2016-libretro"},
+    "melonds": {"repo": "melonds"},
+    "mesen": {"repo": "mesen"},
+    "mesen-s": {"repo": "mesen-s"},
+    "meteor": {"repo": "meteor-libretro"},
+    "mgba": {"repo": "mgba"},
+    "mupen64plus": {"repo": "mupen64plus-libretro-nx"},
+    "neocd": {"repo": "neocd_libretro"},
+    "nestopia": {"repo": "nestopia"},
+    "np2kai": {"repo": "NP2kai", "owner": "AZO234", "fetch_submodules": True},
+    "o2em": {"repo": "libretro-o2em"},
+    "opera": {"repo": "opera-libretro"},
+    "parallel-n64": {"repo": "parallel-n64"},
+    "pcsx2": {"repo": "pcsx2"},
+    "pcsx_rearmed": {"repo": "pcsx_rearmed"},
+    "picodrive": {"repo": "picodrive", "fetch_submodules": True},
+    "play": {"repo": "Play-", "owner": "jpd002", "fetch_submodules": True},
+    "ppsspp": {"repo": "ppsspp", "owner": "hrydgard", "fetch_submodules": True},
+    "prboom": {"repo": "libretro-prboom"},
+    "prosystem": {"repo": "prosystem-libretro"},
+    "quicknes": {"repo": "QuickNES_Core"},
+    "sameboy": {"repo": "sameboy"},
+    "scummvm": {"repo": "scummvm"},
+    "smsplus-gx": {"repo": "smsplus-gx"},
+    "snes9x": {"repo": "snes9x", "owner": "snes9xgit"},
+    "snes9x2002": {"repo": "snes9x2002"},
+    "snes9x2005": {"repo": "snes9x2005"},
+    "snes9x2010": {"repo": "snes9x2010"},
+    "stella": {"repo": "stella", "owner": "stella-emu"},
+    "stella2014": {"repo": "stella2014-libretro"},
+    "swanstation": {"repo": "swanstation"},
+    "tgbdual": {"repo": "tgbdual-libretro"},
+    "thepowdertoy": {"repo": "ThePowderToy"},
+    "tic80": {"repo": "tic-80", "fetch_submodules": True},
+    "vba-m": {"repo": "vbam-libretro"},
+    "vba-next": {"repo": "vba-next"},
+    "vecx": {"repo": "libretro-vecx"},
+    "virtualjaguar": {"repo": "virtualjaguar-libretro"},
+    "yabause": {"repo": "yabause"},
+}
+
+
+def info(*msg):
+    print(*msg, file=sys.stderr)
+
+
+def get_repo_hash_fetchFromGitHub(
+    repo,
+    owner="libretro",
+    deep_clone=False,
+    fetch_submodules=False,
+    leave_dot_git=False,
+    rev=None,
+):
+    extra_args = []
+    if deep_clone:
+        extra_args.append("--deep-clone")
+    if fetch_submodules:
+        extra_args.append("--fetch-submodules")
+    if leave_dot_git:
+        extra_args.append("--leave-dot-git")
+    if rev:
+        extra_args.append("--rev")
+        extra_args.append(rev)
+    result = subprocess.run(
+        ["nix-prefetch-github", owner, repo, *extra_args],
+        check=True,
+        capture_output=True,
+        text=True,
+    )
+    j = json.loads(result.stdout)
+    # Remove False values
+    return {k: v for k, v in j.items() if v}
+
+
+def get_repo_hash(fetcher="fetchFromGitHub", **kwargs):
+    if fetcher == "fetchFromGitHub":
+        return get_repo_hash_fetchFromGitHub(**kwargs)
+    else:
+        raise ValueError(f"Unsupported fetcher: {fetcher}")
+
+
+def get_repo_hashes(cores_to_update=[]):
+    with open(HASHES_PATH) as f:
+        repo_hashes = json.loads(f.read())
+
+    for core, repo in CORES.items():
+        if core in cores_to_update:
+            info(f"Getting repo hash for '{core}'...")
+            repo_hashes[core] = get_repo_hash(**repo)
+        else:
+            info(f"Skipping '{core}'...")
+
+    return repo_hashes
+
+
+def main():
+    # If you don't want to update all cores, pass the name of the cores you
+    # want to update on the command line. E.g.:
+    # $ ./update.py citra snes9x
+    if len(sys.argv) > 1:
+        cores_to_update = sys.argv[1:]
+    else:
+        cores_to_update = CORES.keys()
+
+    repo_hashes = get_repo_hashes(cores_to_update)
+    info(f"Generating '{HASHES_PATH}'...")
+    with open(HASHES_PATH, "w") as f:
+        f.write(json.dumps(dict(sorted(repo_hashes.items())), indent=4))
+        f.write("\n")
+    info("Finished!")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/pkgs/applications/emulators/retroarch/wrapper.nix b/pkgs/applications/emulators/retroarch/wrapper.nix
new file mode 100644
index 0000000000000..e667afdf079c4
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/wrapper.nix
@@ -0,0 +1,37 @@
+{ stdenv, lib, makeWrapper, retroarch, cores ? [ ] }:
+
+stdenv.mkDerivation {
+  pname = "retroarch";
+  version = lib.getVersion retroarch;
+
+  nativeBuildInputs = [ makeWrapper ];
+
+  buildCommand = ''
+    mkdir -p $out/lib
+    for coreDir in $cores
+    do
+      ln -s $coreDir/* $out/lib/.
+    done
+
+    ln -s -t $out ${retroarch}/share
+
+    if [ -d ${retroarch}/Applications ]; then
+      ln -s -t $out ${retroarch}/Applications
+    fi
+
+    makeWrapper ${retroarch}/bin/retroarch $out/bin/retroarch \
+      --suffix-each LD_LIBRARY_PATH ':' "$cores" \
+      --add-flags "-L $out/lib/" \
+  '';
+
+  cores = map (x: x + x.libretroCore) cores;
+  preferLocalBuild = true;
+
+  meta = with retroarch.meta; {
+    inherit changelog license homepage platforms maintainers;
+    description = description
+      + " (with cores: "
+      + lib.concatStringsSep ", " (map (x: "${x.name}") cores)
+      + ")";
+  };
+}