Implement Retroarch Importer
I'm quite rusty with Python, so please let me know if there are any (or many) grave mistakes!
This commit is contained in:
@@ -226,6 +226,20 @@ template $PreferencesWindow : Adw.PreferencesWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Adw.ExpanderRow retroarch_expander_row {
|
||||||
|
title: _("Retroarch");
|
||||||
|
show-enable-switch: true;
|
||||||
|
|
||||||
|
Adw.ActionRow retroarch_config_action_row {
|
||||||
|
title: _("Install Location");
|
||||||
|
|
||||||
|
Button retroarch_config_file_chooser_button {
|
||||||
|
icon-name: "folder-symbolic";
|
||||||
|
valign: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Adw.ExpanderRow flatpak_expander_row {
|
Adw.ExpanderRow flatpak_expander_row {
|
||||||
title: _("Flatpak");
|
title: _("Flatpak");
|
||||||
show-enable-switch: true;
|
show-enable-switch: true;
|
||||||
|
|||||||
@@ -64,6 +64,12 @@
|
|||||||
<key name="legendary-location" type="s">
|
<key name="legendary-location" type="s">
|
||||||
<default>"~/.config/legendary/"</default>
|
<default>"~/.config/legendary/"</default>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="retroarch" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
</key>
|
||||||
|
<key name="retroarch-location" type="s">
|
||||||
|
<default>"~/.var/app/org.libretro.RetroArch/config/retroarch/"</default>
|
||||||
|
</key>
|
||||||
<key name="flatpak" type="b">
|
<key name="flatpak" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
</key>
|
</key>
|
||||||
@@ -110,4 +116,4 @@
|
|||||||
<default>"[]"</default>
|
<default>"[]"</default>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"--filesystem=~/.var/app/com.heroicgameslauncher.hgl/config/heroic/:ro",
|
"--filesystem=~/.var/app/com.heroicgameslauncher.hgl/config/heroic/:ro",
|
||||||
"--filesystem=~/.var/app/com.usebottles.bottles/data/bottles/:ro",
|
"--filesystem=~/.var/app/com.usebottles.bottles/data/bottles/:ro",
|
||||||
"--filesystem=~/.var/app/io.itch.itch/config/itch/:ro",
|
"--filesystem=~/.var/app/io.itch.itch/config/itch/:ro",
|
||||||
|
"--filesystem=~/.var/app/org.libretro.RetroArch/config/retroarch/:ro",
|
||||||
"--filesystem=/var/lib/flatpak:ro"
|
"--filesystem=/var/lib/flatpak:ro"
|
||||||
],
|
],
|
||||||
"cleanup" : [
|
"cleanup" : [
|
||||||
|
|||||||
119
src/importer/sources/retroarch_source.py
Normal file
119
src/importer/sources/retroarch_source.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# retroarch_source.py
|
||||||
|
#
|
||||||
|
# Copyright 2023 Rilic
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from json import JSONDecodeError
|
||||||
|
|
||||||
|
from src import shared
|
||||||
|
from src.game import Game
|
||||||
|
from src.importer.sources.location import Location
|
||||||
|
from src.importer.sources.source import (
|
||||||
|
SourceIterationResult,
|
||||||
|
SourceIterator,
|
||||||
|
Source
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RetroarchSourceIterator(SourceIterator):
|
||||||
|
source: "RetroarchSource"
|
||||||
|
|
||||||
|
def generator_builder(self) -> SourceIterationResult:
|
||||||
|
playlist_files = []
|
||||||
|
for file in os.listdir(self.source.config_location["playlists"]):
|
||||||
|
if file.endswith('.lpl'):
|
||||||
|
playlist_files.append(file)
|
||||||
|
|
||||||
|
playlist_items = []
|
||||||
|
for playlist_file in playlist_files:
|
||||||
|
open_file = open(str(self.source.config_location["playlists"]) + "/" + playlist_file)
|
||||||
|
try:
|
||||||
|
playlist_json = json.load(open_file)
|
||||||
|
except (JSONDecodeError, OSError, KeyError):
|
||||||
|
logging.warning("Cannot read playlist file: %s", str(playlist_file))
|
||||||
|
continue
|
||||||
|
|
||||||
|
for item in playlist_json["items"]:
|
||||||
|
# Select the core. Try the content's core first, then the playlist's
|
||||||
|
# default core.
|
||||||
|
core_path = item["core_path"]
|
||||||
|
if core_path == "DETECT":
|
||||||
|
default_core = playlist_json["default_core_path"]
|
||||||
|
if default_core:
|
||||||
|
core_path = default_core
|
||||||
|
else:
|
||||||
|
logging.warning("Cannot find core for: %s", str(item["path"]))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Build game
|
||||||
|
game_title = item["label"].split("(", 1)[0]
|
||||||
|
values = {
|
||||||
|
"source": self.source.id,
|
||||||
|
"added": int(time()),
|
||||||
|
"name": game_title,
|
||||||
|
"game_id": self.source.game_id_format.format(game_id=item["crc32"][:8]),
|
||||||
|
"executable": self.source.executable_format.format(
|
||||||
|
rom_path = item["path"],
|
||||||
|
core_path = core_path,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
game = Game(values)
|
||||||
|
additional_data = {}
|
||||||
|
|
||||||
|
# Get boxart
|
||||||
|
boxart_image_name = item["label"].split(".", 1)[0] + ".png"
|
||||||
|
boxart_folder_name = playlist_file.split(".", 1)[0]
|
||||||
|
image_path = self.source.config_location["thumbnails"] / boxart_folder_name / "Named_Boxarts" / boxart_image_name
|
||||||
|
additional_data = {"local_image_path": image_path}
|
||||||
|
|
||||||
|
yield(game, additional_data)
|
||||||
|
|
||||||
|
|
||||||
|
class RetroarchSource(Source):
|
||||||
|
args = ' -L "{core_path}" "{rom_path}"'
|
||||||
|
|
||||||
|
name = "Retroarch"
|
||||||
|
available_on = {"linux"}
|
||||||
|
iterator_class = RetroarchSourceIterator
|
||||||
|
executable_format = 'retroarch' + args
|
||||||
|
|
||||||
|
config_location = Location(
|
||||||
|
schema_key="retroarch-location",
|
||||||
|
candidates=(
|
||||||
|
shared.flatpak_dir / "org.libretro.RetroArch" / "config" / "retroarch",
|
||||||
|
shared.config_dir / "retroarch",
|
||||||
|
shared.home / ".config" / "retroarch",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"playlists": (True, "playlists"),
|
||||||
|
"thumbnails": (True, "thumbnails"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if installation is flatpak'd.
|
||||||
|
# TODO: There's probably a MUCH better way of doing this.
|
||||||
|
# There's *is* a URI format, but it doesn't seem to work on the flatpak
|
||||||
|
# version of Retroarch. https://github.com/libretro/RetroArch/pull/13563
|
||||||
|
if str(shared.flatpak_dir) in str(config_location["playlists"]):
|
||||||
|
executable_format = 'flatpak run org.libretro.RetroArch' + args
|
||||||
@@ -33,6 +33,7 @@ from src import shared
|
|||||||
from src.details_window import DetailsWindow
|
from src.details_window import DetailsWindow
|
||||||
from src.game import Game
|
from src.game import Game
|
||||||
from src.importer.importer import Importer
|
from src.importer.importer import Importer
|
||||||
|
from src.importer.sources.retroarch_source import RetroarchSource
|
||||||
from src.importer.sources.bottles_source import BottlesSource
|
from src.importer.sources.bottles_source import BottlesSource
|
||||||
from src.importer.sources.flatpak_source import FlatpakSource
|
from src.importer.sources.flatpak_source import FlatpakSource
|
||||||
from src.importer.sources.heroic_source import HeroicSource
|
from src.importer.sources.heroic_source import HeroicSource
|
||||||
@@ -230,6 +231,9 @@ class CartridgesApplication(Adw.Application):
|
|||||||
if shared.schema.get_boolean("legendary"):
|
if shared.schema.get_boolean("legendary"):
|
||||||
importer.add_source(LegendarySource())
|
importer.add_source(LegendarySource())
|
||||||
|
|
||||||
|
if shared.schema.get_boolean("retroarch"):
|
||||||
|
importer.add_source(RetroarchSource())
|
||||||
|
|
||||||
importer.run()
|
importer.run()
|
||||||
|
|
||||||
def on_remove_game_action(self, *_args):
|
def on_remove_game_action(self, *_args):
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from src.importer.sources.flatpak_source import FlatpakSource
|
|||||||
from src.importer.sources.heroic_source import HeroicSource
|
from src.importer.sources.heroic_source import HeroicSource
|
||||||
from src.importer.sources.itch_source import ItchSource
|
from src.importer.sources.itch_source import ItchSource
|
||||||
from src.importer.sources.legendary_source import LegendarySource
|
from src.importer.sources.legendary_source import LegendarySource
|
||||||
|
from src.importer.sources.retroarch_source import RetroarchSource
|
||||||
from src.importer.sources.location import UnresolvableLocationError
|
from src.importer.sources.location import UnresolvableLocationError
|
||||||
from src.importer.sources.lutris_source import LutrisSource
|
from src.importer.sources.lutris_source import LutrisSource
|
||||||
from src.importer.sources.source import Source
|
from src.importer.sources.source import Source
|
||||||
@@ -82,6 +83,9 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
|||||||
legendary_config_action_row = Gtk.Template.Child()
|
legendary_config_action_row = Gtk.Template.Child()
|
||||||
legendary_config_file_chooser_button = Gtk.Template.Child()
|
legendary_config_file_chooser_button = Gtk.Template.Child()
|
||||||
|
|
||||||
|
retroarch_expander_row = Gtk.Template.Child()
|
||||||
|
retroarch_config_file_chooser_button = Gtk.Template.Child()
|
||||||
|
|
||||||
flatpak_expander_row = Gtk.Template.Child()
|
flatpak_expander_row = Gtk.Template.Child()
|
||||||
flatpak_data_action_row = Gtk.Template.Child()
|
flatpak_data_action_row = Gtk.Template.Child()
|
||||||
flatpak_data_file_chooser_button = Gtk.Template.Child()
|
flatpak_data_file_chooser_button = Gtk.Template.Child()
|
||||||
@@ -138,6 +142,7 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
|||||||
ItchSource,
|
ItchSource,
|
||||||
LegendarySource,
|
LegendarySource,
|
||||||
LutrisSource,
|
LutrisSource,
|
||||||
|
RetroarchSource,
|
||||||
SteamSource,
|
SteamSource,
|
||||||
):
|
):
|
||||||
source = source_class()
|
source = source_class()
|
||||||
@@ -385,3 +390,4 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
|||||||
# Set the source row subtitles
|
# Set the source row subtitles
|
||||||
self.resolve_locations(source)
|
self.resolve_locations(source)
|
||||||
self.update_source_action_row_paths(source)
|
self.update_source_action_row_paths(source)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user