pdulvp.fr

Cores read from the RetroArch install, and picked per item has been fixed.

See more details at : https://github.com/pdulvp/pmane-sa/issues/4

  • an item run by RetroArch has a Core: row in its properties, listing the

cores of the chosen install that play the file, under the names their .info
files give them: one .iso runs under Dolphin and the next under PPSSPP

  • the cores, their names and the file types they play are read from the

install's retroarch.cfg; a runner is an install, so each RetroArch in
Run with offers its own and nothing is shared between two of them

  • the Cores folder field and the File type / Core table leave

Options > Launchers: there is nothing left to configure there. What an
older version wrote is still read, so an item that names no core keeps the
core its file type gave it

  • a core an item names that the install no longer has is kept in its list and

loads nothing, rather than quietly handing the game to another emulator

A core picked per item, read from the RetroArch install itself

<sub>2026-10-30-0928-a-core-picked-per-item-read-from-the-retroarch-install.md — 2026-10-30 09:28</sub>

An item run by RetroArch gets a Core: row under Run with, listing the
cores of the selected install that claim the item's file type, by the name
their .info gives them. So one .iso runs under Dolphin and another under
PPSSPP, the way two .exe items run under different wine builds.

The cores of an install are discovered rather than configured: retroarch.cfg
names the cores and info folders, and each core's .info says which
extensions it plays.

Decisions

  • Per runner, not per launcher. A runner is an install, with its own

retroarch.cfg, cores and info folders. coresFolderFor(const Launcher &),
which takes the first runner that resolves, becomes an overload of the
per-runner one and is left to the Launchers dialog's folder field, which is
one field for the whole family. The per-runner `coresFolderFor(launcher,
runner) -- the old private coresDirFor()` -- is what a core is loaded from
and what the discovery is keyed on.

  • Discovered at the dialog, stored on the item. ProgramItem::core holds

a core file name, like prefix holds a prefix name. Nothing about the
installed cores is written to the settings.

  • The launcher's extension list is left alone. What RetroArch claims stays

the hand-written extensions field. If claiming depended on discovery, an
install whose retroarch.cfg cannot be found would stop handling .nes
altogether and a drop would go silently nowhere.

  • The extension to core table stays, demoted. It is the default for an item

that names no core, which is every item in an existing desktop. It is no
longer the only way to reach a core, so it can be left empty.

  • A core an item names but the install no longer has is listed as

(no longer installed) and kept, as a removed prefix is: opening the dialog
must not silently change what a game runs under.

  • The scan is cached for the session, per runner id: resolving a runner

given as an alias runs bash -ic, which costs up to three seconds, and the
properties dialog cannot pay that on every keystroke in the command field.

Steps

  • [x] 1. src/LibretroCores.{h,cpp}: the discovery.

`struct LibretroCore { QString file, displayName, systemName; QStringList
extensions; } and QVector<LibretroCore> libretroCores(const Launcher &,
const LauncherRunner &)`.

  • the cfg: the --config= of the runner's command line, else

$XDG_CONFIG_HOME/retroarch/retroarch.cfg, else beside the binary;

  • libretro_directory and libretro_info_path out of it, a leading :

being the cfg's own folder and ~ the home;

  • no cfg found: the launcher's coresDir or the derived cores folder,

with info beside it;

  • each *.so paired with info/<stem>.info, from which

supported_extensions, display_name and systemname;

  • cached per runner id.
  • [x] 2. src/Launchers.{h,cpp}: expose the binary behind a runner

(resolvedRunnerBinary(), the alias probe now in coresDirFor()) so the
discovery and the cores folder agree on what the program is, and cache it.
Remove coresFolderFor(const Launcher &).

  • [x] 3. src/ProgramItem.h: QString core, documented as the core file name,

empty meaning the one the launcher's table gives the file type.

  • [x] 4. src/MainWindow.cpp writeProgramItem()/readProgramItem(): core,

written only when set. src/IconListWidget.cpp writeItem()/
readItem(): appended at the end of the stream, which a drag uses.

  • [x] 5. src/Launchers.{h,cpp}: LauncherChoice::usesCores and cores (the

cores of that runner claiming the file), filled in launchersFor().

  • [x] 6. src/ItemPropertiesDialog.{h,cpp}: m_coreCombo, m_coreName and

updateCoreRow() after the pattern of updatePrefixRow() — row shown
only for a launcher that uses cores, refilled when Run with changes,
first entry Default (by file type) with an empty value. Written back in
item().

  • [x] 7. src/Launchers.cpp coreFor(): the item's core first, resolved in

that runner's cores folder; the table as the fallback. A core the item
names but that is not installed loads nothing rather than falling back,
so a GameCube disc cannot end up in a PSP emulator. chosenCore added
to launcherCommandFor(), which ItemLauncher passes item.core to.

  • [x] 8. Checked on this machine, against the Lutris RetroArch, which is an

alias carrying its own --config: an .iso offers Dolphin, Genesis Plus
GX and PPSSPP and a .gba offers mGBA and VBA Next; the chosen core
reaches the command line as an absolute -L; an item with no core still
gets the table's core for a .nes; a core no longer installed is listed
as such and loads nothing; the row stays hidden for an .exe.

  • [x] 9. README: the core row in the item section, and a line in the launchers

section saying the table is only the default now.

The cores folder and the file type table leave the Launchers dialog

<sub>2026-10-30-0958-cores-folder-and-file-type-table-leave-the-launchers-dialog.md — 2026-10-30 09:58</sub>

Now that an install's cores are discovered and a core is picked per item, the
Cores folder field and the File type / Core table have nothing left to
configure. Both go from the dialog. The RetroArch page is then what every other
launcher's page is: the file types it claims and the programs that can act as
it.

Decisions

  • The settings keep being read. coresDir and cores stay in

Launchers.{h,cpp} and coreFor() still falls back to the table, so every
item of an existing desktop keeps the core its file type gave it. Nothing
writes them any more; what is in the file stays there.

  • No override is lost in practice. The folder is found from the install's

retroarch.cfg, and failing that from cores beside the program, which is
every normal install. An install where neither works would show an empty
core list, and the file's coresDir is still honoured if it was ever set.

  • coresFolderFor(const Launcher &) goes with the folder field that was

its only caller. The per-runner one stays.

Steps

  • [x] 1. src/LaunchersDialog.{h,cpp}: remove m_coresBox, m_coresDir,

m_cores, their Add/Remove/Browse buttons, fillCoreTable(), the
visibility in showPageFor() and the write-back in
commitCurrentPage().

  • [x] 2. src/Launchers.{h,cpp}: remove the launcher-level coresFolderFor()

overload; note on coresDir and cores that they are read-only history
now.

  • [x] 3. Checked: the RetroArch page is File types and Available RetroArch

only, and the eight entries an older version wrote are still in the
launcher after every page has been visited.

  • [x] 4. docs/images/launchers.png retaken with the screenshots target. The

clock in main.png and calendar.png moves with every run, so those two
were put back.

  • [x] 5. README: drop the paragraph about the table being the default and the

folder being an override.

<sub>Filed by prel from the completed plans at commit time.</sub>