about summary refs log tree commit diff
path: root/pkgs/applications/emulators
diff options
context:
space:
mode:
authorThiago Kenji Okada <thiagokokada@gmail.com>2022-10-29 20:09:25 +0100
committerGitHub <noreply@github.com>2022-10-29 20:09:25 +0100
commit1ec96f776b2c5a5789005ecc6afedcb4b30e8fef (patch)
treeec314d222883cc1a561c64ab209339ca5761ddcf /pkgs/applications/emulators
parent6ddd77b48ff5af9e94911c316f14d6ff62d6d74e (diff)
parent784c363bd0fad7249e793c808e3883ff59d936ab (diff)
downloadnixlib-1ec96f776b2c5a5789005ecc6afedcb4b30e8fef.tar
nixlib-1ec96f776b2c5a5789005ecc6afedcb4b30e8fef.tar.gz
nixlib-1ec96f776b2c5a5789005ecc6afedcb4b30e8fef.tar.bz2
nixlib-1ec96f776b2c5a5789005ecc6afedcb4b30e8fef.tar.lz
nixlib-1ec96f776b2c5a5789005ecc6afedcb4b30e8fef.tar.xz
nixlib-1ec96f776b2c5a5789005ecc6afedcb4b30e8fef.tar.zst
nixlib-1ec96f776b2c5a5789005ecc6afedcb4b30e8fef.zip
Merge pull request #198271 from thiagokokada/retroarch-improvements
retroarch,libretro: general improvements
Diffstat (limited to 'pkgs/applications/emulators')
-rw-r--r--pkgs/applications/emulators/retroarch/cores.nix809
-rw-r--r--pkgs/applications/emulators/retroarch/default.nix78
-rw-r--r--pkgs/applications/emulators/retroarch/libretro-core-info.nix28
-rw-r--r--pkgs/applications/emulators/retroarch/mkLibretroCore.nix80
-rw-r--r--pkgs/applications/emulators/retroarch/use-fixed-path-for-libretro_core_info.patch41
-rw-r--r--pkgs/applications/emulators/retroarch/use-fixed-paths.patch154
-rw-r--r--pkgs/applications/emulators/retroarch/wrapper.nix61
7 files changed, 668 insertions, 583 deletions
diff --git a/pkgs/applications/emulators/retroarch/cores.nix b/pkgs/applications/emulators/retroarch/cores.nix
index 65b117629306..a738432a8ec3 100644
--- a/pkgs/applications/emulators/retroarch/cores.nix
+++ b/pkgs/applications/emulators/retroarch/cores.nix
@@ -6,6 +6,7 @@
 , cmake
 , curl
 , fetchFromGitHub
+, fetchpatch
 , ffmpeg
 , fluidsynth
 , gettext
@@ -38,7 +39,6 @@
 , xxd
 , xz
 , zlib
-, fetchpatch
 }:
 
 let
@@ -47,297 +47,283 @@ let
   getCoreSrc = core:
     fetchFromGitHub (builtins.getAttr core hashesFile);
 
-  mkLibRetroCore =
+  mkLibretroCore =
     { core
-    , description
-      # Check https://github.com/libretro/libretro-core-info for license information
-    , license
-    , stdenvOverride ? stdenv
     , src ? (getCoreSrc core)
-    , broken ? false
     , version ? "unstable-2022-10-18"
-    , 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:
-    stdenvOverride.mkDerivation (
-      let
-        inherit (stdenvOverride) hostPlatform;
-        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";
-          }.${hostPlatform.parsed.kernel.name} or hostPlatform.parsed.kernel.name}"
-          "ARCH=${{
-            armv7l = "arm";
-            armv6l = "arm";
-            i686 = "x86";
-          }.${hostPlatform.parsed.cpu.name} or 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${hostPlatform.extensions.sharedLibrary} $coreDir
-          makeWrapper ${retroarch}/bin/retroarch $out/bin/retroarch-${core} \
-            --add-flags "-L $coreDir/${d2u core}_libretro${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; teams.libretro.members ++ [ hrdinka ];
-        };
-      }) // builtins.removeAttrs args [ "core" "src" "description" "license" "makeFlags" ]
-    );
+    import ./mkLibretroCore.nix ({
+      inherit lib stdenv core src version makeWrapper retroarch zlib;
+    } // args);
 in
 {
-  inherit mkLibRetroCore;
+  inherit mkLibretroCore;
 
-  atari800 = mkLibRetroCore {
+  atari800 = mkLibretroCore {
     core = "atari800";
-    description = "Port of Atari800 to libretro";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
     makeFlags = [ "GIT_VERSION=" ];
+    meta = {
+      description = "Port of Atari800 to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-gba = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's GameBoy Advance core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-lynx = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's Lynx core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-ngp = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's NeoGeo Pocket core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-pce-fast = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's PC Engine core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-pcfx = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's PCFX core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-psx = mkLibRetroCore {
+  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" ];
+    meta = {
+      description = "Port of Mednafen's PSX Engine core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-psx-hw = mkLibRetroCore {
+  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" ];
+    meta = {
+      description = "Port of Mednafen's PSX Engine (with HW accel) core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-saturn = mkLibRetroCore {
+  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" ];
+    meta = {
+      description = "Port of Mednafen's Saturn core to libretro";
+      license = lib.licenses.gpl2Only;
+      platforms = [ "aarch64-linux" "x86_64-linux" ];
+    };
   };
 
-  beetle-snes = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's SNES core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-supafaust = mkLibRetroCore {
+  beetle-supafaust = mkLibretroCore {
     core = "mednafen-supafaust";
     src = getCoreSrc "beetle-supafaust";
-    description = "Port of Mednafen's experimental snes_faust core to libretro";
-    license = lib.licenses.gpl2Plus;
     makefile = "Makefile";
+    meta = {
+      description = "Port of Mednafen's experimental snes_faust core to libretro";
+      license = lib.licenses.gpl2Plus;
+    };
   };
 
-  beetle-supergrafx = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's SuperGrafx core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-vb = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's VirtualBoy core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  beetle-wswan = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Mednafen's WonderSwan core to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  blastem = mkLibRetroCore {
+  blastem = mkLibretroCore {
     core = "blastem";
-    description = "Port of BlastEm to libretro";
-    license = lib.licenses.gpl3Only;
-    platforms = lib.platforms.x86;
+    meta = {
+      description = "Port of BlastEm to libretro";
+      license = lib.licenses.gpl3Only;
+      platforms = lib.platforms.x86;
+    };
   };
 
-  bluemsx = mkLibRetroCore {
+  bluemsx = mkLibretroCore {
     core = "bluemsx";
-    description = "Port of BlueMSX to libretro";
-    license = lib.licenses.gpl2Only;
+    meta = {
+      description = "Port of BlueMSX to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  bsnes = mkLibRetroCore {
+  bsnes = mkLibretroCore {
     core = "bsnes";
-    description = "Port of bsnes to libretro";
-    license = lib.licenses.gpl3Only;
     makefile = "Makefile";
+    meta = {
+      description = "Port of bsnes to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  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 = [
+  bsnes-hd = mkLibretroCore {
+    core = "bsnes-hd-beta";
+    src = getCoreSrc "bsnes-hd";
+    makefile = "GNUmakefile";
+    makeFlags =
+      let
+        # linux = bsd
+        # https://github.com/DerKoun/bsnes-hd/blob/f0b6cf34e9780d53516977ed2de64137a8bcc3c5/bsnes/GNUmakefile#L37
+        platform = if stdenv.isDarwin then "macos" else "linux";
+      in
+      [
         "-C"
         "bsnes"
         "target=libretro"
         "platform=${platform}"
       ];
-      extraBuildInputs = [ xorg.libX11 xorg.libXext ];
-      postBuild = "cd bsnes/out";
+    extraBuildInputs = [ xorg.libX11 xorg.libXext ];
+    postBuild = "cd bsnes/out";
+    meta = {
+      description = "Port of bsnes-hd to libretro";
+      license = lib.licenses.gpl3Only;
     };
+  };
 
-  bsnes-mercury = mkLibRetroCore {
+  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" ];
+    meta = {
+      description = "Fork of bsnes with HLE DSP emulation restored (accuracy profile)";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  bsnes-mercury-balanced = mkLibRetroCore {
+  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" ];
+    meta = {
+      description = "Fork of bsnes with HLE DSP emulation restored (balanced profile)";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  bsnes-mercury-performance = mkLibRetroCore {
+  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" ];
+    meta = {
+      description = "Fork of bsnes with HLE DSP emulation restored (performance profile)";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  citra = mkLibRetroCore {
+  citra = mkLibretroCore {
     core = "citra";
-    description = "Port of Citra to libretro";
-    license = lib.licenses.gpl2Plus;
     extraBuildInputs = [ libGLU libGL boost ffmpeg nasm ];
     makefile = "Makefile";
     makeFlags = [ "HAVE_FFMPEG_STATIC=0" ];
+    meta = {
+      description = "Port of Citra to libretro";
+      license = lib.licenses.gpl2Plus;
+    };
   };
 
-  desmume = mkLibRetroCore {
+  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";
+    extraBuildInputs = [ libpcap libGLU libGL xorg.libX11 ];
     makeFlags = lib.optional stdenv.hostPlatform.isAarch32 "platform=armv-unix"
       ++ lib.optional (!stdenv.hostPlatform.isx86) "DESMUME_JIT=0";
+    meta = {
+      description = "Port of DeSmuME to libretro";
+      license = lib.licenses.gpl2Plus;
+    };
   };
 
-  desmume2015 = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of DeSmuME ~2015 to libretro";
+      license = lib.licenses.gpl2Plus;
+    };
   };
 
-  dolphin = mkLibRetroCore {
+  dolphin = mkLibretroCore {
     core = "dolphin";
-    description = "Port of Dolphin to libretro";
-    license = lib.licenses.gpl2Plus;
     extraNativeBuildInputs = [ cmake curl pkg-config ];
     extraBuildInputs = [
       libGLU
@@ -359,284 +345,354 @@ in
       "-DUSE_DISCORD_PRESENCE=OFF"
     ];
     dontUseCmakeBuildDir = true;
+    meta = {
+      description = "Port of Dolphin to libretro";
+      license = lib.licenses.gpl2Plus;
+    };
   };
 
-  dosbox = mkLibRetroCore {
+  dosbox = mkLibretroCore {
     core = "dosbox";
-    description = "Port of DOSBox to libretro";
-    license = lib.licenses.gpl2Only;
     CXXFLAGS = "-std=gnu++11";
+    meta = {
+      description = "Port of DOSBox to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  eightyone = mkLibRetroCore {
+  eightyone = mkLibretroCore {
     core = "81";
     src = getCoreSrc "eightyone";
-    description = "Port of EightyOne to libretro";
-    license = lib.licenses.gpl3Only;
+    meta = {
+      description = "Port of EightyOne to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  fbalpha2012 = mkLibRetroCore {
+  fbalpha2012 = mkLibretroCore {
     core = "fbalpha2012";
-    description = "Port of Final Burn Alpha ~2012 to libretro";
-    license = "Non-commercial";
     makefile = "makefile.libretro";
     preBuild = "cd svn-current/trunk";
+    meta = {
+      description = "Port of Final Burn Alpha ~2012 to libretro";
+      license = "Non-commercial";
+    };
   };
 
-  fbneo = mkLibRetroCore {
+  fbneo = mkLibretroCore {
     core = "fbneo";
-    description = "Port of FBNeo to libretro";
-    license = "Non-commercial";
     makefile = "Makefile";
     preBuild = "cd src/burner/libretro";
+    meta = {
+      description = "Port of FBNeo to libretro";
+      license = "Non-commercial";
+    };
   };
 
-  fceumm = mkLibRetroCore {
+  fceumm = mkLibretroCore {
     core = "fceumm";
-    description = "FCEUmm libretro port";
-    license = lib.licenses.gpl2Only;
+    meta = {
+      description = "FCEUmm libretro port";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  flycast = mkLibRetroCore {
+  flycast = mkLibretroCore {
     core = "flycast";
-    description = "Flycast libretro port";
-    license = lib.licenses.gpl2Only;
     extraBuildInputs = [ libGL libGLU ];
     makefile = "Makefile";
     makeFlags = lib.optionals stdenv.hostPlatform.isAarch64 [ "platform=arm64" ];
     patches = [ ./fix-flycast-makefile.patch ];
-    platforms = [ "aarch64-linux" "x86_64-linux" ];
+    meta = {
+      description = "Flycast libretro port";
+      license = lib.licenses.gpl2Only;
+      platforms = [ "aarch64-linux" "x86_64-linux" ];
+    };
   };
 
-  fmsx = mkLibRetroCore {
+  fmsx = mkLibretroCore {
     core = "fmsx";
-    description = "FMSX libretro port";
-    license = "Non-commercial";
     makefile = "Makefile";
+    meta = {
+      description = "FMSX libretro port";
+      license = "Non-commercial";
+    };
   };
 
-  freeintv = mkLibRetroCore {
+  freeintv = mkLibretroCore {
     core = "freeintv";
-    description = "FreeIntv libretro port";
-    license = lib.licenses.gpl3Only;
     makefile = "Makefile";
+    meta = {
+      description = "FreeIntv libretro port";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  gambatte = mkLibRetroCore {
+  gambatte = mkLibretroCore {
     core = "gambatte";
-    description = "Gambatte libretro port";
-    license = lib.licenses.gpl2Only;
+    meta = {
+      description = "Gambatte libretro port";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  genesis-plus-gx = mkLibRetroCore {
+  genesis-plus-gx = mkLibretroCore {
     core = "genesis-plus-gx";
-    description = "Enhanced Genesis Plus libretro port";
-    license = "Non-commercial";
+    meta = {
+      description = "Enhanced Genesis Plus libretro port";
+      license = "Non-commercial";
+    };
   };
 
-  gpsp = mkLibRetroCore {
+  gpsp = mkLibretroCore {
     core = "gpsp";
-    description = "Port of gpSP to libretro";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
+    meta = {
+      description = "Port of gpSP to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  gw = mkLibRetroCore {
+  gw = mkLibretroCore {
     core = "gw";
-    description = "Port of Game and Watch to libretro";
-    license = lib.licenses.zlib;
     makefile = "Makefile";
+    meta = {
+      description = "Port of Game and Watch to libretro";
+      license = lib.licenses.zlib;
+    };
   };
 
-  handy = mkLibRetroCore {
+  handy = mkLibretroCore {
     core = "handy";
-    description = "Port of Handy to libretro";
-    license = lib.licenses.zlib;
     makefile = "Makefile";
+    meta = {
+      description = "Port of Handy to libretro";
+      license = lib.licenses.zlib;
+    };
   };
 
-  hatari = mkLibRetroCore {
+  hatari = mkLibretroCore {
     core = "hatari";
-    description = "Port of Hatari to libretro";
-    license = lib.licenses.gpl2Only;
     extraNativeBuildInputs = [ which ];
     dontConfigure = true;
-    # zlib is already included in mkLibRetroCore as buildInputs
+    # zlib is already included in mkLibretroCore as buildInputs
     makeFlags = [ "EXTERNAL_ZLIB=1" ];
+    meta = {
+      description = "Port of Hatari to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  mame = mkLibRetroCore {
+  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 ];
+    meta = {
+      description = "Port of MAME to libretro";
+      license = with lib.licenses; [ bsd3 gpl2Plus ];
+    };
   };
 
-  mame2000 = mkLibRetroCore {
+  mame2000 = mkLibretroCore {
     core = "mame2000";
-    description = "Port of MAME ~2000 to libretro, compatible with MAME 0.37b5 sets";
-    license = "MAME";
     makefile = "Makefile";
     makeFlags = lib.optional (!stdenv.hostPlatform.isx86) "IS_X86=0";
+    meta = {
+      description = "Port of MAME ~2000 to libretro, compatible with MAME 0.37b5 sets";
+      license = "MAME";
+    };
   };
 
-  mame2003 = mkLibRetroCore {
+  mame2003 = mkLibretroCore {
     core = "mame2003";
-    description = "Port of MAME ~2003 to libretro, compatible with MAME 0.78 sets";
-    license = "MAME";
     makefile = "Makefile";
+    meta = {
+      description = "Port of MAME ~2003 to libretro, compatible with MAME 0.78 sets";
+      license = "MAME";
+    };
   };
 
-  mame2003-plus = mkLibRetroCore {
+  mame2003-plus = mkLibretroCore {
     core = "mame2003-plus";
-    description = "Port of MAME ~2003+ to libretro, compatible with MAME 0.78 sets";
-    license = "MAME";
     makefile = "Makefile";
+    meta = {
+      description = "Port of MAME ~2003+ to libretro, compatible with MAME 0.78 sets";
+      license = "MAME";
+    };
   };
 
-  mame2010 = mkLibRetroCore {
+  mame2010 = mkLibretroCore {
     core = "mame2010";
-    description = "Port of MAME ~2010 to libretro, compatible with MAME 0.139 sets";
-    license = "MAME";
     makefile = "Makefile";
     makeFlags = lib.optionals stdenv.hostPlatform.isAarch64 [ "PTR64=1" "ARM_ENABLED=1" "X86_SH2DRC=0" "FORCE_DRC_C_BACKEND=1" ];
+    meta = {
+      description = "Port of MAME ~2010 to libretro, compatible with MAME 0.139 sets";
+      license = "MAME";
+    };
   };
 
-  mame2015 = mkLibRetroCore {
+  mame2015 = mkLibretroCore {
     core = "mame2015";
-    description = "Port of MAME ~2015 to libretro, compatible with MAME 0.160 sets";
-    license = "MAME";
     makeFlags = [ "PYTHON=python3" ];
     extraNativeBuildInputs = [ python3 ];
     extraBuildInputs = [ alsa-lib ];
     makefile = "Makefile";
     enableParallelBuilding = false;
+    meta = {
+      description = "Port of MAME ~2015 to libretro, compatible with MAME 0.160 sets";
+      license = "MAME";
+    };
   };
 
-  mame2016 = mkLibRetroCore {
+  mame2016 = mkLibretroCore {
     core = "mame2016";
-    description = "Port of MAME ~2016 to libretro, compatible with MAME 0.174 sets";
-    license = with lib.licenses; [ bsd3 gpl2Plus ];
     extraNativeBuildInputs = [ python3 ];
     extraBuildInputs = [ alsa-lib ];
     makeFlags = [ "PYTHON_EXECUTABLE=python3" ];
     enableParallelBuilding = false;
+    meta = {
+      description = "Port of MAME ~2016 to libretro, compatible with MAME 0.174 sets";
+      license = with lib.licenses; [ bsd3 gpl2Plus ];
+    };
   };
 
-  melonds = mkLibRetroCore {
+  melonds = mkLibretroCore {
     core = "melonds";
-    description = "Port of MelonDS to libretro";
-    license = lib.licenses.gpl3Only;
     extraBuildInputs = [ libGL libGLU ];
     makefile = "Makefile";
+    meta = {
+      description = "Port of MelonDS to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  mesen = mkLibRetroCore {
+  mesen = mkLibretroCore {
     core = "mesen";
-    description = "Port of Mesen to libretro";
-    license = lib.licenses.gpl3Only;
     makefile = "Makefile";
     preBuild = "cd Libretro";
+    meta = {
+      description = "Port of Mesen to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  mesen-s = mkLibRetroCore {
+  mesen-s = mkLibretroCore {
     core = "mesen-s";
-    description = "Port of Mesen-S to libretro";
-    license = lib.licenses.gpl3Only;
     makefile = "Makefile";
     preBuild = "cd Libretro";
     normalizeCore = false;
+    meta = {
+      description = "Port of Mesen-S to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  meteor = mkLibRetroCore {
+  meteor = mkLibretroCore {
     core = "meteor";
-    description = "Port of Meteor to libretro";
-    license = lib.licenses.gpl3Only;
     makefile = "Makefile";
     preBuild = "cd libretro";
+    meta = {
+      description = "Port of Meteor to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  mgba = mkLibRetroCore {
+  mgba = mkLibretroCore {
     core = "mgba";
-    description = "Port of mGBA to libretro";
-    license = lib.licenses.mpl20;
+    meta = {
+      description = "Port of mGBA to libretro";
+      license = lib.licenses.mpl20;
+    };
   };
 
-  mupen64plus = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Libretro port of Mupen64 Plus, GL only";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  neocd = mkLibRetroCore {
+  neocd = mkLibretroCore {
     core = "neocd";
-    description = "NeoCD libretro port";
-    license = lib.licenses.lgpl3Only;
     makefile = "Makefile";
+    meta = {
+      description = "NeoCD libretro port";
+      license = lib.licenses.lgpl3Only;
+    };
   };
 
-  nestopia = mkLibRetroCore {
+  nestopia = mkLibretroCore {
     core = "nestopia";
-    description = "Nestopia libretro port";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
     preBuild = "cd libretro";
+    meta = {
+      description = "Nestopia libretro port";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  nxengine = mkLibRetroCore {
+  nxengine = mkLibretroCore {
     core = "nxengine";
-    description = "NXEngine libretro port";
-    license = lib.licenses.gpl3Only;
     makefile = "Makefile";
+    meta = {
+      description = "NXEngine libretro port";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  np2kai = mkLibRetroCore rec {
+  np2kai = mkLibretroCore rec {
     core = "np2kai";
     src = getCoreSrc core;
-    description = "Neko Project II kai libretro port";
-    license = lib.licenses.mit;
     makeFlags = [
       # See https://github.com/AZO234/NP2kai/tags
       "NP2KAI_VERSION=rev.22"
       "NP2KAI_HASH=${src.rev}"
     ];
     preBuild = "cd sdl";
+    meta = {
+      description = "Neko Project II kai libretro port";
+      license = lib.licenses.mit;
+    };
   };
 
-  o2em = mkLibRetroCore {
+  o2em = mkLibretroCore {
     core = "o2em";
-    description = "Port of O2EM to libretro";
-    license = lib.licenses.artistic1;
     makefile = "Makefile";
+    meta = {
+      description = "Port of O2EM to libretro";
+      license = lib.licenses.artistic1;
+    };
   };
 
-  opera = mkLibRetroCore {
+  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}" ];
+    meta = {
+      description = "Opera is a port of 4DO/libfreedo to libretro";
+      license = "Non-commercial";
+    };
   };
 
-  parallel-n64 = mkLibRetroCore {
+  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
     '';
+    meta = {
+      description = "Parallel Mupen64plus rewrite for libretro.";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  pcsx2 = mkLibRetroCore {
+  pcsx2 = mkLibretroCore {
     core = "pcsx2";
-    description = "Port of PCSX2 to libretro";
-    license = lib.licenses.gpl3Plus;
     extraNativeBuildInputs = [
       cmake
       gettext
@@ -661,39 +717,47 @@ in
       substituteInPlace CMakeLists.txt --replace "ccache" ""
     '';
     postBuild = "cd /build/source/build/pcsx2";
-    platforms = lib.platforms.x86;
+    meta = {
+      description = "Port of PCSX2 to libretro";
+      license = lib.licenses.gpl3Plus;
+      platforms = lib.platforms.x86;
+    };
   };
 
-  pcsx-rearmed = mkLibRetroCore {
+  pcsx-rearmed = mkLibretroCore {
     core = "pcsx_rearmed";
-    description = "Port of PCSX ReARMed with GNU lightning to libretro";
-    license = lib.licenses.gpl2Only;
     dontConfigure = true;
+    meta = {
+      description = "Port of PCSX ReARMed with GNU lightning to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  picodrive = mkLibRetroCore {
+  picodrive = mkLibretroCore {
     core = "picodrive";
-    description = "Fast MegaDrive/MegaCD/32X emulator";
-    license = "MAME";
     dontConfigure = true;
     makeFlags = lib.optionals stdenv.hostPlatform.isAarch64 [ "platform=aarch64" ];
+    meta = {
+      description = "Fast MegaDrive/MegaCD/32X emulator";
+      license = "MAME";
+    };
   };
 
-  play = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Play! to libretro";
+      license = lib.licenses.bsd2;
+    };
   };
 
-  ppsspp = mkLibRetroCore {
+  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";
@@ -705,149 +769,186 @@ in
       "-DOpenGL_GL_PREFERENCE=GLVND"
     ];
     postBuild = "cd lib";
+    meta = {
+      description = "ppsspp libretro port";
+      license = lib.licenses.gpl2Plus;
+    };
   };
 
-  prboom = mkLibRetroCore {
+  prboom = mkLibretroCore {
     core = "prboom";
-    description = "Prboom libretro port";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
+    meta = {
+      description = "Prboom libretro port";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  prosystem = mkLibRetroCore {
+  prosystem = mkLibretroCore {
     core = "prosystem";
-    description = "Port of ProSystem to libretro";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
+    meta = {
+      description = "Port of ProSystem to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  puae = mkLibRetroCore {
+  puae = mkLibretroCore {
     core = "puae";
-    description = "Amiga emulator based on WinUAE";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
+    # https://github.com/libretro/libretro-uae/pull/529
     patches = fetchpatch {
-        url = "https://github.com/libretro/libretro-uae/commit/90ba4c9bb940e566781c3590553270ad69cf212e.patch";
-        sha256 = "sha256-9xkRravvyFZc0xsIj0OSm2ux5BqYogfQ1TDnH9l6jKw=";
+      url = "https://github.com/libretro/libretro-uae/commit/90ba4c9bb940e566781c3590553270ad69cf212e.patch";
+      sha256 = "sha256-9xkRravvyFZc0xsIj0OSm2ux5BqYogfQ1TDnH9l6jKw=";
+    };
+    meta = {
+      description = "Amiga emulator based on WinUAE";
+      license = lib.licenses.gpl2Only;
     };
   };
 
-  quicknes = mkLibRetroCore {
+  quicknes = mkLibretroCore {
     core = "quicknes";
-    description = "QuickNES libretro port";
-    license = lib.licenses.lgpl21Plus;
     makefile = "Makefile";
+    meta = {
+      description = "QuickNES libretro port";
+      license = lib.licenses.lgpl21Plus;
+    };
   };
 
-  sameboy = mkLibRetroCore {
+  sameboy = mkLibretroCore {
     core = "sameboy";
-    description = "SameBoy libretro port";
-    license = lib.licenses.mit;
     extraNativeBuildInputs = [ which hexdump ];
     preBuild = "cd libretro";
     makefile = "Makefile";
+    meta = {
+      description = "SameBoy libretro port";
+      license = lib.licenses.mit;
+    };
   };
 
-  scummvm = mkLibRetroCore {
+  scummvm = mkLibretroCore {
     core = "scummvm";
-    description = "Libretro port of ScummVM";
-    license = lib.licenses.gpl2Only;
     extraBuildInputs = [ fluidsynth libjpeg libvorbis libGLU libGL ];
     makefile = "Makefile";
     preConfigure = "cd backends/platform/libretro/build";
+    meta = {
+      description = "Libretro port of ScummVM";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  smsplus-gx = mkLibRetroCore {
+  smsplus-gx = mkLibretroCore {
     core = "smsplus";
     src = getCoreSrc "smsplus-gx";
-    description = "SMS Plus GX libretro port";
-    license = lib.licenses.gpl2Plus;
+    meta = {
+      description = "SMS Plus GX libretro port";
+      license = lib.licenses.gpl2Plus;
+    };
   };
 
-  snes9x = mkLibRetroCore {
+  snes9x = mkLibretroCore {
     core = "snes9x";
-    description = "Port of SNES9x git to libretro";
-    license = "Non-commercial";
     makefile = "Makefile";
     preBuild = "cd libretro";
+    meta = {
+      description = "Port of SNES9x git to libretro";
+      license = "Non-commercial";
+    };
   };
 
-  snes9x2002 = mkLibRetroCore {
+  snes9x2002 = mkLibretroCore {
     core = "snes9x2002";
-    description = "Optimized port/rewrite of SNES9x 1.39 to Libretro";
-    license = "Non-commercial";
     makefile = "Makefile";
+    meta = {
+      description = "Optimized port/rewrite of SNES9x 1.39 to Libretro";
+      license = "Non-commercial";
+    };
   };
 
-  snes9x2005 = mkLibRetroCore {
+  snes9x2005 = mkLibretroCore {
     core = "snes9x2005";
-    description = "Optimized port/rewrite of SNES9x 1.43 to Libretro";
-    license = "Non-commercial";
     makefile = "Makefile";
+    meta = {
+      description = "Optimized port/rewrite of SNES9x 1.43 to Libretro";
+      license = "Non-commercial";
+    };
   };
 
-  snes9x2005-plus = mkLibRetroCore {
+  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" ];
+    meta = {
+      description = "Optimized port/rewrite of SNES9x 1.43 to Libretro, with Blargg's APU";
+      license = "Non-commercial";
+    };
   };
 
-  snes9x2010 = mkLibRetroCore {
+  snes9x2010 = mkLibretroCore {
     core = "snes9x2010";
-    description = "Optimized port/rewrite of SNES9x 1.52+ to Libretro";
-    license = "Non-commercial";
+    meta = {
+      description = "Optimized port/rewrite of SNES9x 1.52+ to Libretro";
+      license = "Non-commercial";
+    };
   };
 
-  stella = mkLibRetroCore {
+  stella = mkLibretroCore {
     core = "stella";
-    description = "Port of Stella to libretro";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
     preBuild = "cd src/os/libretro";
     dontConfigure = true;
+    meta = {
+      description = "Port of Stella to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  stella2014 = mkLibRetroCore {
+  stella2014 = mkLibretroCore {
     core = "stella2014";
-    description = "Port of Stella to libretro";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
+    meta = {
+      description = "Port of Stella ~2014 to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  swanstation = mkLibRetroCore {
+  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"
     ];
+    meta = {
+      description = "Port of SwanStation (a fork of DuckStation) to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  tgbdual = mkLibRetroCore {
+  tgbdual = mkLibretroCore {
     core = "tgbdual";
-    description = "Port of TGBDual to libretro";
-    license = lib.licenses.gpl2Only;
     makefile = "Makefile";
+    meta = {
+      description = "Port of TGBDual to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  thepowdertoy = mkLibRetroCore {
+  thepowdertoy = mkLibretroCore {
     core = "thepowdertoy";
-    description = "Port of The Powder Toy to libretro";
-    license = lib.licenses.gpl3Only;
     extraNativeBuildInputs = [ cmake ];
     makefile = "Makefile";
     postBuild = "cd src";
+    meta = {
+      description = "Port of The Powder Toy to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  tic80 = mkLibRetroCore {
+  tic80 = mkLibretroCore {
     core = "tic80";
-    description = "Port of TIC-80 to libretro";
-    license = lib.licenses.mit;
     extraNativeBuildInputs = [ cmake pkg-config ];
     makefile = "Makefile";
     cmakeFlags = [
@@ -860,44 +961,58 @@ in
     ];
     preConfigure = "cd core";
     postBuild = "cd lib";
+    meta = {
+      description = "Port of TIC-80 to libretro";
+      license = lib.licenses.mit;
+    };
   };
 
-  vba-m = mkLibRetroCore {
+  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";
+    meta = {
+      description = "vanilla VBA-M libretro port";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  vba-next = mkLibRetroCore {
+  vba-next = mkLibretroCore {
     core = "vba-next";
-    description = "VBA-M libretro port with modifications for speed";
-    license = lib.licenses.gpl2Only;
+    meta = {
+      description = "VBA-M libretro port with modifications for speed";
+      license = lib.licenses.gpl2Only;
+    };
   };
 
-  vecx = mkLibRetroCore {
+  vecx = mkLibretroCore {
     core = "vecx";
-    description = "Port of Vecx to libretro";
-    license = lib.licenses.gpl3Only;
     extraBuildInputs = [ libGL libGLU ];
+    meta = {
+      description = "Port of Vecx to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  virtualjaguar = mkLibRetroCore {
+  virtualjaguar = mkLibretroCore {
     core = "virtualjaguar";
-    description = "Port of VirtualJaguar to libretro";
-    license = lib.licenses.gpl3Only;
     makefile = "Makefile";
+    meta = {
+      description = "Port of VirtualJaguar to libretro";
+      license = lib.licenses.gpl3Only;
+    };
   };
 
-  yabause = mkLibRetroCore {
+  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";
+    meta = {
+      description = "Port of Yabause to libretro";
+      license = lib.licenses.gpl2Only;
+    };
   };
 }
diff --git a/pkgs/applications/emulators/retroarch/default.nix b/pkgs/applications/emulators/retroarch/default.nix
index a8a20682afd4..d0a281afc373 100644
--- a/pkgs/applications/emulators/retroarch/default.nix
+++ b/pkgs/applications/emulators/retroarch/default.nix
@@ -6,18 +6,17 @@
 , withVulkan ? stdenv.isLinux
 , withWayland ? stdenv.isLinux
 , alsa-lib
-, AppKit
 , dbus
 , fetchFromGitHub
 , ffmpeg_4
-, Foundation
+, flac
 , freetype
 , gamemode
 , libdrm
 , libGL
 , libGLU
-, libobjc
 , libpulseaudio
+, libretro-core-info
 , libv4l
 , libX11
 , libXdmcp
@@ -26,71 +25,61 @@
 , libxml2
 , libXxf86vm
 , makeWrapper
+, mbedtls
 , mesa
 , nvidia_cg_toolkit
 , pkg-config
 , python3
 , SDL2
+, substituteAll
 , udev
 , vulkan-loader
 , wayland
+, zlib
 }:
 
 let
-  version = "1.12.0";
-  libretroCoreInfo = fetchFromGitHub {
-    owner = "libretro";
-    repo = "libretro-core-info";
-    sha256 = "sha256-9Sfp/JkMJIe34YGNRxf93fONOBuQxR2pduoJU+xtuF0=";
-    # Upstream didn't tag a new libretro-core-info in 1.12.0 release
-    rev = "v1.11.1";
-  };
   runtimeLibs =
     lib.optional withVulkan vulkan-loader ++
     lib.optional withGamemode (lib.getLib gamemode);
 in
 stdenv.mkDerivation rec {
   pname = "retroarch-bare";
-  inherit version;
+  version = "1.12.0";
 
   src = fetchFromGitHub {
     owner = "libretro";
     repo = "RetroArch";
-    sha256 = "sha256-doLWNA8aTAllxx3zABtvZaegBQEPIi8276zbytPSdBU=";
+    hash = "sha256-doLWNA8aTAllxx3zABtvZaegBQEPIi8276zbytPSdBU=";
     rev = "v${version}";
   };
 
   patches = [
-    ./use-fixed-paths.patch
+    (substituteAll {
+      src = ./use-fixed-path-for-libretro_core_info.patch;
+      libretro_info_path = libretro-core-info;
+    })
   ];
 
-  postPatch = ''
-    substituteInPlace "frontend/drivers/platform_unix.c" \
-      --subst-var-by libretro_directory "$out/lib" \
-      --subst-var-by libretro_info_path "$out/share/libretro/info" \
-      --subst-var-by out "$out"
-    substituteInPlace "frontend/drivers/platform_darwin.m" \
-      --subst-var-by libretro_directory "$out/lib" \
-      --subst-var-by libretro_info_path "$out/share/libretro/info"
-  '';
-
   nativeBuildInputs = [ pkg-config ] ++
     lib.optional withWayland wayland ++
     lib.optional (runtimeLibs != [ ]) makeWrapper;
 
   buildInputs = [
     ffmpeg_4
+    flac
     freetype
     libGL
     libGLU
     libxml2
+    mbedtls
     python3
     SDL2
+    zlib
   ] ++
   lib.optional enableNvidiaCgToolkit nvidia_cg_toolkit ++
   lib.optional withVulkan vulkan-loader ++
   lib.optional withWayland wayland ++
-  lib.optionals stdenv.isDarwin [ libobjc AppKit Foundation ] ++
   lib.optionals stdenv.isLinux [
     alsa-lib
     dbus
@@ -110,6 +99,9 @@ stdenv.mkDerivation rec {
 
   configureFlags = [
     "--disable-update_cores"
+    "--disable-builtinmbedtls"
+    "--disable-builtinzlib"
+    "--disable-builtinflac"
   ] ++
   lib.optionals stdenv.isLinux [
     "--enable-dbus"
@@ -117,39 +109,13 @@ stdenv.mkDerivation rec {
     "--enable-kms"
   ];
 
-  postInstall = ''
-    # TODO: ideally each core should have its own core information
-    mkdir -p $out/share/libretro/info
-    cp -r ${libretroCoreInfo}/* $out/share/libretro/info
-  '' +
-  lib.optionalString (runtimeLibs != [ ]) ''
+  postInstall = lib.optionalString (runtimeLibs != [ ]) ''
     wrapProgram $out/bin/retroarch \
       --prefix LD_LIBRARY_PATH ':' ${lib.makeLibraryPath runtimeLibs}
-  '' +
-  lib.optionalString stdenv.isDarwin ''
-    # https://github.com/libretro/RetroArch/blob/master/retroarch-apple-packaging.sh
-    app=$out/Applications/RetroArch.app
-    mkdir -p $app/Contents/MacOS
-    cp -r pkg/apple/OSX/* $app/Contents
-    cp $out/bin/retroarch $app/Contents/MacOS
-    # FIXME: using Info_Metal.plist results in input not working
-    # mv $app/Contents/Info_Metal.plist $app/Contents/Info.plist
-
-    substituteInPlace $app/Contents/Info.plist \
-      --replace '${"\${EXECUTABLE_NAME}"}' 'RetroArch' \
-      --replace '$(PRODUCT_BUNDLE_IDENTIFIER)' 'com.libretro.RetroArch' \
-      --replace '${"\${PRODUCT_NAME}"}' 'RetroArch' \
-      --replace '${"\${MACOSX_DEPLOYMENT_TARGET}"}' '10.13'
-
-    cp media/retroarch.icns $app/Contents/Resources/
   '';
 
   preFixup = "rm $out/bin/retroarch-cg2glsl";
 
-  # Workaround for the following error affecting newer versions of Clang:
-  # ./config.def.h:xxx:x: error: 'TARGET_OS_TV' is not defined, evaluates to 0 [-Werror,-Wundef-prefix=TARGET_OS_]
-  NIX_CFLAGS_COMPILE = lib.optionals stdenv.cc.isClang [ "-Wno-undef-prefix" ];
-
   passthru.tests = nixosTests.retroarch;
 
   meta = with lib; {
@@ -159,9 +125,11 @@ stdenv.mkDerivation rec {
     platforms = platforms.unix;
     changelog = "https://github.com/libretro/RetroArch/blob/v${version}/CHANGES.md";
     maintainers = with maintainers; teams.libretro.members ++ [ matthewbauer kolbycrouch ];
-    # FIXME: error while building in macOS:
-    # "Undefined symbols for architecture <arch>"
-    # See also retroarch/wrapper.nix that is also broken in macOS
+    mainProgram = "retroarch";
+    # If you want to (re)-add support for macOS, see:
+    # https://docs.libretro.com/development/retroarch/compilation/osx/
+    # and
+    # https://github.com/libretro/RetroArch/blob/71eb74d256cb4dc5b8b43991aec74980547c5069/.gitlab-ci.yml#L330
     broken = stdenv.isDarwin;
   };
 }
diff --git a/pkgs/applications/emulators/retroarch/libretro-core-info.nix b/pkgs/applications/emulators/retroarch/libretro-core-info.nix
new file mode 100644
index 000000000000..1dd8b0e4b748
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/libretro-core-info.nix
@@ -0,0 +1,28 @@
+{ lib
+, stdenvNoCC
+, fetchFromGitHub
+}:
+
+stdenvNoCC.mkDerivation rec {
+  pname = "libretro-core-info";
+  version = "1.12.0";
+
+  src = fetchFromGitHub {
+    owner = "libretro";
+    repo = "libretro-core-info";
+    hash = "sha256-ByATDM0V40UJxigqVLyTWkHY5tiCC2dvZebksl8GsUI=";
+    rev = "v${version}";
+  };
+
+  makeFlags = [ "PREFIX=$(out)" ];
+
+  dontBuild = true;
+
+  meta = with lib; {
+    description = "Libretro's core info files";
+    homepage = "https://libretro.com";
+    license = licenses.mit;
+    maintainers = with maintainers; teams.libretro.members ++ [ ];
+    platforms = platforms.all;
+  };
+}
diff --git a/pkgs/applications/emulators/retroarch/mkLibretroCore.nix b/pkgs/applications/emulators/retroarch/mkLibretroCore.nix
new file mode 100644
index 000000000000..6ab652127009
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/mkLibretroCore.nix
@@ -0,0 +1,80 @@
+{ lib
+, stdenv
+, core
+, makeWrapper
+, retroarch
+, zlib
+, makefile ? "Makefile.libretro"
+, extraBuildInputs ? [ ]
+, extraNativeBuildInputs ? [ ]
+  # Location of resulting RetroArch core on $out
+, libretroCore ? "/lib/retroarch/cores"
+  # The core filename is derivated from the core name
+  # Setting `normalizeCore` to `true` will convert `-` to `_` on the core filename
+, normalizeCore ? true
+, ...
+}@args:
+
+let
+  d2u = if normalizeCore then (lib.replaceChars [ "-" ] [ "_" ]) else (x: x);
+  coreDir = placeholder "out" + libretroCore;
+  coreFilename = "${d2u core}_libretro${stdenv.hostPlatform.extensions.sharedLibrary}";
+  mainProgram = "retroarch-${core}";
+  extraArgs = builtins.removeAttrs args [
+    "lib"
+    "stdenv"
+    "core"
+    "makeWrapper"
+    "retroarch"
+    "zlib"
+    "makefile"
+    "extraBuildInputs"
+    "extraNativeBuildInputs"
+    "libretroCore"
+    "normalizeCore"
+    "makeFlags"
+    "meta"
+  ];
+in
+stdenv.mkDerivation ({
+  pname = "libretro-${core}";
+
+  buildInputs = [ zlib ] ++ extraBuildInputs;
+  nativeBuildInputs = [ makeWrapper ] ++ extraNativeBuildInputs;
+
+  inherit makefile;
+
+  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 [ ]);
+
+  installPhase = ''
+    runHook preInstall
+
+    install -Dt ${coreDir} ${coreFilename}
+    makeWrapper ${retroarch}/bin/retroarch $out/bin/${mainProgram} \
+      --add-flags "-L ${coreDir}/${coreFilename} $@"
+
+    runHook postInstall
+  '';
+
+  enableParallelBuilding = true;
+
+  passthru = { inherit core libretroCore; };
+
+  meta = with lib; {
+    inherit mainProgram;
+    inherit (retroarch.meta) platforms;
+    homepage = "https://www.libretro.com/";
+    maintainers = with maintainers; teams.libretro.members ++ [ hrdinka ];
+  } // (args.meta or { });
+} // extraArgs)
diff --git a/pkgs/applications/emulators/retroarch/use-fixed-path-for-libretro_core_info.patch b/pkgs/applications/emulators/retroarch/use-fixed-path-for-libretro_core_info.patch
new file mode 100644
index 000000000000..256397fd49ea
--- /dev/null
+++ b/pkgs/applications/emulators/retroarch/use-fixed-path-for-libretro_core_info.patch
@@ -0,0 +1,41 @@
+From 6145cb9ed935621f1974655fe1ab44cf2f0fbcce Mon Sep 17 00:00:00 2001
+From: Thiago Kenji Okada <thiagokokada@gmail.com>
+Date: Sat, 29 Oct 2022 12:27:55 +0100
+Subject: [PATCH] Use fixed path for libretro_core_info
+
+---
+ configuration.c                  | 2 +-
+ frontend/drivers/platform_unix.c | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/configuration.c b/configuration.c
+index af3fc8f43c..c6d56308b3 100644
+--- a/configuration.c
++++ b/configuration.c
+@@ -1468,7 +1468,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 fe5f7341c9..c2a91f8c99 100644
+--- a/frontend/drivers/platform_unix.c
++++ b/frontend/drivers/platform_unix.c
+@@ -1799,8 +1799,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@",
++         "share/libretro/info", 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.38.0
+
diff --git a/pkgs/applications/emulators/retroarch/use-fixed-paths.patch b/pkgs/applications/emulators/retroarch/use-fixed-paths.patch
deleted file mode 100644
index a4837e63af7f..000000000000
--- a/pkgs/applications/emulators/retroarch/use-fixed-paths.patch
+++ /dev/null
@@ -1,154 +0,0 @@
-From 8a1cffebb23f9d2a28228cd8cbf4fd80836157e8 Mon Sep 17 00:00:00 2001
-From: Thiago Kenji Okada <thiagokokada@gmail.com>
-Date: Tue, 18 Oct 2022 17:41:33 +0100
-Subject: [PATCH] Use fixed paths
-
----
- configuration.c                    |  2 +-
- frontend/drivers/platform_darwin.m |  4 +--
- frontend/drivers/platform_unix.c   | 56 +++++++++++++++---------------
- 3 files changed, 31 insertions(+), 31 deletions(-)
-
-diff --git a/configuration.c b/configuration.c
-index ac4779b2d7..d980892dda 100644
---- a/configuration.c
-+++ b/configuration.c
-@@ -1468,7 +1468,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 c771ec0f55..d5e21a1f4d 100644
---- a/frontend/drivers/platform_darwin.m
-+++ b/frontend/drivers/platform_darwin.m
-@@ -400,9 +400,9 @@ static void frontend_darwin_get_env(int *argc, char *argv[],
- 		    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]));
-+		    "@libretro_directory@", "", 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]));
-+   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 29e9a0d633..dba8abe941 100644
---- a/frontend/drivers/platform_unix.c
-+++ b/frontend/drivers/platform_unix.c
-@@ -1792,8 +1792,8 @@ static void frontend_unix_get_env(int *argc,
-       strlcpy(g_defaults.dirs[DEFAULT_DIR_CORE], libretro_directory,
-             sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
-    else
--      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
-@@ -1802,27 +1802,27 @@ 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]));
- 
--   if (path_is_directory("/usr/local/share/retroarch/assets"))
-+   if (path_is_directory("@out@/local/share/retroarch/assets"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
--            "/usr/local/share/retroarch",
-+            "@out@/local/share/retroarch",
-             "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
--   else if (path_is_directory("/usr/share/retroarch/assets"))
-+   else if (path_is_directory("@out@/share/retroarch/assets"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
--            "/usr/share/retroarch",
-+            "@out@/share/retroarch",
-             "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
--   else if (path_is_directory("/usr/local/share/games/retroarch/assets"))
-+   else if (path_is_directory("@out@/local/share/games/retroarch/assets"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
--            "/usr/local/share/games/retroarch",
-+            "@out@/local/share/games/retroarch",
-             "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
--   else if (path_is_directory("/usr/share/games/retroarch/assets"))
-+   else if (path_is_directory("@out@/share/games/retroarch/assets"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
--            "/usr/share/games/retroarch",
-+            "@out@/share/games/retroarch",
-             "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
-    else
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], base_path,
-@@ -1834,41 +1834,41 @@ static void frontend_unix_get_env(int *argc,
-    fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], base_path,
-          "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
- #else
--   if (path_is_directory("/usr/local/share/retroarch/filters/audio"))
-+   if (path_is_directory("@out@/local/share/retroarch/filters/audio"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
--            "/usr/local/share/retroarch",
-+            "@out@/local/share/retroarch",
-             "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
--   else if (path_is_directory("/usr/share/retroarch/filters/audio"))
-+   else if (path_is_directory("@out@/share/retroarch/filters/audio"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
--            "/usr/share/retroarch",
-+            "@out@/share/retroarch",
-             "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
--   else if (path_is_directory("/usr/local/share/games/retroarch/filters/audio"))
-+   else if (path_is_directory("@out@/local/share/games/retroarch/filters/audio"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
--            "/usr/local/share/games/retroarch",
-+            "@out@/local/share/games/retroarch",
-             "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
--   else if (path_is_directory("/usr/share/games/retroarch/filters/audio"))
-+   else if (path_is_directory("@out@/share/games/retroarch/filters/audio"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
--            "/usr/share/games/retroarch",
-+            "@out@/share/games/retroarch",
-             "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
-    else
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER], base_path,
-             "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
- 
--   if (path_is_directory("/usr/local/share/retroarch/filters/video"))
-+   if (path_is_directory("@out@/local/share/retroarch/filters/video"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
--            "/usr/local/share/retroarch",
-+            "@out@/local/share/retroarch",
-             "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
--   else if (path_is_directory("/usr/share/retroarch/filters/video"))
-+   else if (path_is_directory("@out@/share/retroarch/filters/video"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
--            "/usr/share/retroarch",
-+            "@out@/share/retroarch",
-             "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
--   else if (path_is_directory("/usr/local/share/games/retroarch/filters/video"))
-+   else if (path_is_directory("@out@/local/share/games/retroarch/filters/video"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
--            "/usr/local/share/games/retroarch",
-+            "@out@/local/share/games/retroarch",
-             "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
--   else if (path_is_directory("/usr/share/games/retroarch/filters/video"))
-+   else if (path_is_directory("@out@/share/games/retroarch/filters/video"))
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
--            "/usr/share/games/retroarch",
-+            "@out@/share/games/retroarch",
-             "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
-    else
-       fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], base_path,
--- 
-2.37.3
-
diff --git a/pkgs/applications/emulators/retroarch/wrapper.nix b/pkgs/applications/emulators/retroarch/wrapper.nix
index 535cd40db6c3..4136d263eac4 100644
--- a/pkgs/applications/emulators/retroarch/wrapper.nix
+++ b/pkgs/applications/emulators/retroarch/wrapper.nix
@@ -1,41 +1,48 @@
-{ stdenv, lib, makeWrapper, retroarch, cores ? [ ] }:
-
-stdenv.mkDerivation {
-  pname = "retroarch";
-  version = lib.getVersion retroarch;
+{ lib
+, stdenv
+, makeWrapper
+, retroarch
+, symlinkJoin
+, writeTextDir
+, cores ? [ ]
+}:
+
+let
+  # All cores should be located in the same path after symlinkJoin,
+  # but let's be safe here
+  coresPath = lib.lists.unique (map (c: c.libretroCore) cores);
+  wrapperArgs = lib.strings.escapeShellArgs
+    (lib.lists.flatten
+      (map (p: [ "--add-flags" "-L ${placeholder "out" + p}" ]) coresPath));
+in
+symlinkJoin {
+  name = "retroarch-with-cores-${lib.getVersion retroarch}";
+
+  paths = [ retroarch ] ++ cores;
 
   nativeBuildInputs = [ makeWrapper ];
 
-  buildCommand = ''
-    mkdir -p $out/lib
-    for coreDir in $cores; do
-      ln -s $coreDir/* $out/lib/.
-    done
-
-    ln -s -t $out ${retroarch}/share
+  passthru = {
+    inherit cores;
+    unwrapped = retroarch;
+  };
 
-    if [ -d ${retroarch}/Applications ]; then
-      ln -s -t $out ${retroarch}/Applications
-    fi
+  postBuild = ''
+    # remove core specific binaries
+    find $out/bin -name 'retroarch-*' -type l -delete
 
-    makeWrapper ${retroarch}/bin/retroarch $out/bin/retroarch \
-      --suffix-each LD_LIBRARY_PATH ':' "$cores" \
-      --add-flags "-L $out/lib/" \
+    # wrap binary to load cores from the proper location(s)
+    wrapProgram $out/bin/retroarch ${wrapperArgs}
   '';
 
-  cores = map (x: x + x.libretroCore) cores;
-  preferLocalBuild = true;
-
   meta = with retroarch.meta; {
     inherit changelog description homepage license maintainers platforms;
     longDescription = ''
       RetroArch is the reference frontend for the libretro API.
-
-      The following cores are included:
-      ${lib.concatStringsSep "\n" (map (x: "  - ${x.name}") cores)}
+    ''
+    + lib.optionalString (cores != [ ]) ''
+      The following cores are included: ${lib.concatStringsSep ", " (map (c: c.core) cores)}
     '';
-    # FIXME: exit with error on macOS:
-    # No Info.plist file in application bundle or no NSPrincipalClass in the Info.plist file, exiting
-    broken = stdenv.isDarwin;
+    mainProgram = "retroarch";
   };
 }