From deba4c6eba34dda1f4e3a48f5ee95d5367a4c514 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 15 Feb 2025 09:43:37 +0700 Subject: [PATCH 001/126] Add Vulkan implicit layer path environment for DXVK-NVAPI --- lutris/runners/wine.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index 7a3fb852fd..bc37b144cf 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -1168,6 +1168,12 @@ def get_env(self, os_env=False, disable_runtime=False): if self.runner_config.get("dxvk_nvapi"): env["DXVK_NVAPIHACK"] = "0" env["DXVK_ENABLE_NVAPI"] = "1" + # Add Vulkan implicit layer path for DXVK-NVAPI + nvapi_version = self.runner_config.get("dxvk_nvapi_version") + if nvapi_version: + layer_dir = os.path.join(settings.RUNTIME_DIR, "dxvk-nvapi", nvapi_version, "layer") + if os.path.exists(layer_dir): + env["VK_ADD_LAYER_PATH"] = layer_dir if self.runner_config.get("battleye"): env["PROTON_BATTLEYE_RUNTIME"] = os.path.join(settings.RUNTIME_DIR, "battleye_runtime") From 5e16e999d7e79a0b798eecc03aa46509b047d39d Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 28 Feb 2025 16:00:51 +0700 Subject: [PATCH 002/126] apply nvidia-libs (SveSop) via dxvk-nvapi option --- lutris/util/wine/dxvk_nvapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/util/wine/dxvk_nvapi.py b/lutris/util/wine/dxvk_nvapi.py index a9a729b0d6..7dce6acef0 100644 --- a/lutris/util/wine/dxvk_nvapi.py +++ b/lutris/util/wine/dxvk_nvapi.py @@ -11,7 +11,7 @@ class DXVKNVAPIManager(DLLManager): human_name = "DXVK-NVAPI" # apparently, nvofapi.dll (the 32 bit version) is not being included here - # see https://github.com/jp7677/dxvk-nvapi/pull/213 - managed_dlls = ("nvapi", "nvapi64", "nvml", "nvofapi64") + managed_dlls = ("nvapi", "nvapi64", "nvml", "nvofapi64", "nvoptix", "nvencodeapi", "nvencodeapi64", "nvcuvid", "nvcuda") releases_url = "https://api.github.com/repos/lutris/dxvk-nvapi/releases" dlss_dlls = ("nvngx", "_nvngx") From f58bd9e3eea7f506c4fd0be77227bbb20d41a15e Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 1 Mar 2025 15:21:39 +0700 Subject: [PATCH 003/126] add nvngx_dlssg.dll --- lutris/util/wine/dxvk_nvapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/util/wine/dxvk_nvapi.py b/lutris/util/wine/dxvk_nvapi.py index 7dce6acef0..4a5ec061fc 100644 --- a/lutris/util/wine/dxvk_nvapi.py +++ b/lutris/util/wine/dxvk_nvapi.py @@ -13,7 +13,7 @@ class DXVKNVAPIManager(DLLManager): # see https://github.com/jp7677/dxvk-nvapi/pull/213 managed_dlls = ("nvapi", "nvapi64", "nvml", "nvofapi64", "nvoptix", "nvencodeapi", "nvencodeapi64", "nvcuvid", "nvcuda") releases_url = "https://api.github.com/repos/lutris/dxvk-nvapi/releases" - dlss_dlls = ("nvngx", "_nvngx") + dlss_dlls = ("nvngx", "_nvngx", "nvngx_dlssg") def can_enable(self): return LINUX_SYSTEM.is_vulkan_supported() From 8a2d426761b12a1989e573b04f813a19e367338d Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 2 Mar 2025 09:45:06 +0700 Subject: [PATCH 004/126] Add support for hyprland, wayland display management --- lutris/sysoptions.py | 2 +- lutris/util/display.py | 328 +++++++++++++++++++++++++++++++++++------ 2 files changed, 280 insertions(+), 50 deletions(-) diff --git a/lutris/sysoptions.py b/lutris/sysoptions.py index a42fbb8b5d..6e52137592 100644 --- a/lutris/sysoptions.py +++ b/lutris/sysoptions.py @@ -6,7 +6,7 @@ from lutris import runners from lutris.util import linux, system -from lutris.util.display import DISPLAY_MANAGER, SCREEN_SAVER_INHIBITOR, is_compositing_enabled, is_display_x11 +from lutris.util.display import DISPLAY_MANAGER, SCREEN_SAVER_INHIBITOR, is_compositing_enabled, is_display_x11, is_display_wayland from lutris.util.graphics.gpu import GPUS diff --git a/lutris/util/display.py b/lutris/util/display.py index 11b7d4fcd5..d4c581d2b0 100644 --- a/lutris/util/display.py +++ b/lutris/util/display.py @@ -3,6 +3,7 @@ # isort:skip_file import enum import os +import json import subprocess from typing import Any, Dict @@ -52,65 +53,285 @@ def is_display_x11(): display = Gdk.Display.get_default() return "x11" in type(display).__name__.casefold() +@cache_single +def is_display_wayland(): + """True if the current display is Wayland""" + display = Gdk.Display.get_default() + return "wayland" in type(display).__name__.casefold() class DisplayManager: - """Get display and resolution using GnomeDesktop""" - - def __init__(self, screen: Gdk.Screen): + """Get display and resolution using various backends based on the current environment""" + + def __init__(self, screen=None): + self.display = Gdk.Display.get_default() + self.screen = screen + self.hyprctl_path = None + self.rr_screen = None + self.rr_config = None + + # Determine which backend to use + self.backend = self._determine_backend() + + # Initialize the appropriate backend + if self.backend == "hyprland": + self._init_hyprland() + elif self.backend == "gnome" and screen: + self._init_gnome(screen) + + def _determine_backend(self): + """Determine which backend to use based on the environment""" + # Check for Hyprland + if "HYPRLAND_INSTANCE_SIGNATURE" in os.environ: + for path in os.environ.get("PATH", "").split(os.pathsep): + hyprctl_path = os.path.join(path, "hyprctl") + if os.path.isfile(hyprctl_path) and os.access(hyprctl_path, os.X_OK): + self.hyprctl_path = hyprctl_path + return "hyprland" + + # Check for GNOME Desktop + if LIB_GNOME_DESKTOP_AVAILABLE and self.screen: + return "gnome" + # Generic Wayland + if is_display_wayland(): + return "wayland" + # X11 + if is_display_x11(): + return "x11" + return "fallback" + + def _init_gnome(self, screen): + """Initialize GNOME Desktop backend""" self.rr_screen = GnomeDesktop.RRScreen.new(screen) self.rr_config = GnomeDesktop.RRConfig.new_current(self.rr_screen) self.rr_config.load_current() + def _init_hyprland(self): + """Initialize Hyprland backend""" + # Already have hyprctl_path from _determine_backend + pass + + def _run_hyprctl(self, *args): + """Run hyprctl with given arguments and return the output""" + if not self.hyprctl_path: + return {} + + try: + cmd = [self.hyprctl_path, *args, "-j"] # Use JSON output + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return json.loads(result.stdout) + except (subprocess.SubprocessError, json.JSONDecodeError) as ex: + logger.error(f"Error running hyprctl: {ex}") + return {} + def get_display_names(self): """Return names of connected displays""" - return [output_info.get_display_name() for output_info in self.rr_config.get_outputs()] + if self.backend == "hyprland": + monitors = self._run_hyprctl("monitors") + if not monitors: + return [] + return [monitor.get("name") for monitor in monitors] + + elif self.backend == "gnome" and self.rr_config: + return [output_info.get_display_name() for output_info in self.rr_config.get_outputs()] + + elif self.backend == "wayland": + names = [] + n_monitors = self.display.get_n_monitors() + for i in range(n_monitors): + monitor = self.display.get_monitor(i) + model = monitor.get_model() + names.append(model if model else f"Monitor-{i}") + return names + + else: + # Fallback to XRandR + return LegacyDisplayManager().get_display_names() def get_resolutions(self): """Return available resolutions""" - resolutions = ["%sx%s" % (mode.get_width(), mode.get_height()) for mode in self.rr_screen.list_modes()] - if not resolutions: - logger.error("Failed to generate resolution list from default GdkScreen") - return ["%sx%s" % (DEFAULT_RESOLUTION_WIDTH, DEFAULT_RESOLUTION_HEIGHT)] - return sorted(set(resolutions), key=lambda x: int(x.split("x")[0]), reverse=True) - - def _get_primary_output(self): - """Return the RROutput used as a primary display""" + if self.backend == "hyprland": + modes = [] + monitors = self._run_hyprctl("monitors") + + # If no monitors, return default + if not monitors: + return [f"{DEFAULT_RESOLUTION_WIDTH}x{DEFAULT_RESOLUTION_HEIGHT}"] + + # Get modes from monitors + for monitor in monitors: + if monitor.get("reserved"): # Skip special monitors + continue + + width = monitor.get("width") + height = monitor.get("height") + if width and height: + modes.append(f"{width}x{height}") + + if not modes: + return [f"{DEFAULT_RESOLUTION_WIDTH}x{DEFAULT_RESOLUTION_HEIGHT}"] + + return sorted(set(modes), key=lambda x: int(x.split("x")[0]), reverse=True) + + elif self.backend == "gnome" and self.rr_screen: + resolutions = ["%sx%s" % (mode.get_width(), mode.get_height()) for mode in self.rr_screen.list_modes()] + if not resolutions: + logger.error("Failed to generate resolution list from default GdkScreen") + return ["%sx%s" % (DEFAULT_RESOLUTION_WIDTH, DEFAULT_RESOLUTION_HEIGHT)] + return sorted(set(resolutions), key=lambda x: int(x.split("x")[0]), reverse=True) + + elif self.backend == "wayland": + # For generic Wayland, we can only report current resolutions + resolutions = [] + n_monitors = self.display.get_n_monitors() + for i in range(n_monitors): + monitor = self.display.get_monitor(i) + geometry = monitor.get_geometry() + resolution = f"{geometry.width}x{geometry.height}" + resolutions.append(resolution) + + if not resolutions: + return [f"{DEFAULT_RESOLUTION_WIDTH}x{DEFAULT_RESOLUTION_HEIGHT}"] + + return sorted(set(resolutions), key=lambda x: int(x.split("x")[0]), reverse=True) + + else: + # Fallback to XRandR + return LegacyDisplayManager().get_resolutions() + + def _get_primary_output_gnome(self): + """Return the RROutput used as a primary display (GNOME backend)""" + if not self.rr_screen: + return None + for output in self.rr_screen.list_outputs(): if output.get_is_primary(): return output - return + return None def get_current_resolution(self): """Return the current resolution for the primary display""" - output = self._get_primary_output() - if not output: - logger.error("Failed to get a default output") - return str(DEFAULT_RESOLUTION_WIDTH), str(DEFAULT_RESOLUTION_HEIGHT) - current_mode = output.get_current_mode() - return str(current_mode.get_width()), str(current_mode.get_height()) - - @staticmethod - def set_resolution(resolution): - """Set the resolution of one or more displays. - The resolution can either be a string, which will be applied to the - primary display or a list of configurations as returned by `get_config`. - This method uses XrandR and will not work on Wayland. - """ - return change_resolution(resolution) - - @staticmethod - def get_config(): - """Return the current display resolution - This method uses XrandR and will not work on wayland - The output can be fed in `set_resolution` - """ - return get_outputs() + if self.backend == "hyprland": + monitors = self._run_hyprctl("monitors") + + # Find primary/focused monitor + primary_monitor = None + for monitor in monitors: + if monitor.get("focused", False): + primary_monitor = monitor + break + + if not primary_monitor: + # If no primary found, use the first non-reserved one + for monitor in monitors: + if not monitor.get("reserved"): + primary_monitor = monitor + break + + if not primary_monitor: + logger.error("Failed to get a primary monitor from Hyprland") + return str(DEFAULT_RESOLUTION_WIDTH), str(DEFAULT_RESOLUTION_HEIGHT) + + width = primary_monitor.get("width", DEFAULT_RESOLUTION_WIDTH) + height = primary_monitor.get("height", DEFAULT_RESOLUTION_HEIGHT) + return str(width), str(height) + + elif self.backend == "gnome": + output = self._get_primary_output_gnome() + if not output: + logger.error("Failed to get a default output") + return str(DEFAULT_RESOLUTION_WIDTH), str(DEFAULT_RESOLUTION_HEIGHT) + current_mode = output.get_current_mode() + return str(current_mode.get_width()), str(current_mode.get_height()) + + elif self.backend == "wayland": + monitor = self.display.get_primary_monitor() + if not monitor: + # Fall back to first monitor if no primary + if self.display.get_n_monitors() > 0: + monitor = self.display.get_monitor(0) + + if not monitor: + logger.error("Failed to get a monitor from Wayland display") + return str(DEFAULT_RESOLUTION_WIDTH), str(DEFAULT_RESOLUTION_HEIGHT) + + geometry = monitor.get_geometry() + return str(geometry.width), str(geometry.height) + + else: + # Fallback to XRandR + return LegacyDisplayManager().get_current_resolution() + + def set_resolution(self, resolution): + """Set the resolution of one or more displays""" + if self.backend == "hyprland": + if isinstance(resolution, list): + # We don't support multi-monitor configuration yet + logger.warning("Multi-monitor configuration not supported for Hyprland") + return False + + try: + width, height = resolution.split("x") + monitor = self._get_focused_monitor_name_hyprland() + if not monitor: + logger.error("Failed to get focused monitor") + return False + + # Format: keyword arg=value + cmd = [self.hyprctl_path, "keyword", f"monitor,{monitor},preferred,auto,{float(width)/float(height)}"] + subprocess.run(cmd, check=True) + return True + except (ValueError, subprocess.SubprocessError) as ex: + logger.error(f"Failed to set resolution in Hyprland: {ex}") + return False + + elif self.backend in ("x11", "gnome"): + # Both use XRandR underneath + return change_resolution(resolution) + + elif self.backend == "wayland": + logger.warning("Cannot set resolution directly in generic Wayland mode") + return False + + else: + return LegacyDisplayManager().set_resolution(resolution) + + def _get_focused_monitor_name_hyprland(self): + """Get the name of the focused monitor (Hyprland backend)""" + monitors = self._run_hyprctl("monitors") + for monitor in monitors: + if monitor.get("focused", False): + return monitor.get("name") + return None + + def get_config(self): + """Return the current display configuration""" + if self.backend == "hyprland": + # For Hyprland, we'll just return the current resolutions as strings + monitors = self._run_hyprctl("monitors") + configs = [] + + for monitor in monitors: + if monitor.get("reserved"): + continue + width = monitor.get("width") + height = monitor.get("height") + if width and height: + configs.append(f"{width}x{height}") + + return configs + + elif self.backend == "wayland": + # For generic Wayland, return current resolutions + return self.get_resolutions() + + else: + # Use XRandR for X11 and GNOME + return get_outputs() def get_display_manager(): - """Return the appropriate display manager instance. - Defaults to Mutter if available. This is the only one to support Wayland. - """ + """Return the appropriate display manager instance.""" + # Try Mutter (which supports Wayland) if DBus is available if DBUS_AVAILABLE: try: return MutterDisplayManager() @@ -121,14 +342,13 @@ def get_display_manager(): else: logger.error("DBus is not available, Lutris was not properly installed.") - if LIB_GNOME_DESKTOP_AVAILABLE: - try: - screen = Gdk.Screen.get_default() - if screen: - return DisplayManager(screen) - except GLib.Error: - pass - return LegacyDisplayManager() + # For GNOME Desktop or other backends + try: + screen = Gdk.Screen.get_default() + return DisplayManager(screen) + except Exception as ex: # pylint: disable=broad-except + logger.exception("Failed to instantiate DisplayManager: %s", ex) + return LegacyDisplayManager() DISPLAY_MANAGER = get_display_manager() @@ -141,6 +361,7 @@ class DesktopEnvironment(enum.Enum): MATE = 1 XFCE = 2 DEEPIN = 3 + HYPRLAND = 4 UNKNOWN = 999 @@ -187,6 +408,12 @@ class DesktopEnvironment(enum.Enum): "com.deepin.WMSwitcher.RequestSwitchWM", ], }, + DesktopEnvironment.HYPRLAND: { + "check": ["echo", "$HYPRLAND_CMD"], + "active_result": b"Hyprland\n", + "stop_compositor": ["Hyprland"], + "start_compositor": ["hyprctl", "dispatch", "exit"], + }, } # These additional compositors can be detected by looking for their process, @@ -223,6 +450,8 @@ def get_desktop_environment(): return DesktopEnvironment.DEEPIN if "plasma" in desktop_session: return DesktopEnvironment.PLASMA + if os.environ.get("XDG_SESSION_DESKTOP", "") == "Hyprland": + return DesktopEnvironment.HYPRLAND return DesktopEnvironment.UNKNOWN @@ -304,8 +533,8 @@ def _run_command(*command, run_in_background=False): are you lost little _run_command? """ try: - if run_in_background: - command = " ".join(command) + if not command: + raise ValueError("No command provided") return subprocess.Popen( # pylint: disable=consider-using-with command, stdin=subprocess.DEVNULL, @@ -316,6 +545,7 @@ def _run_command(*command, run_in_background=False): except FileNotFoundError: errorMessage = "FileNotFoundError when running command:", command logger.error(errorMessage) + return None def disable_compositing(): From a496d6c03c7ef616bf5df9813600cef0eaf6e859 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Mon, 3 Mar 2025 16:43:41 -0500 Subject: [PATCH 005/126] Provide a mechanism to redact sensitive query string parameters in URLs. Resolves #5967 --- lutris/services/gog.py | 2 +- lutris/util/http.py | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lutris/services/gog.py b/lutris/services/gog.py index 21acc4b4a9..f3906f9810 100644 --- a/lutris/services/gog.py +++ b/lutris/services/gog.py @@ -180,7 +180,7 @@ def request_token(self, url: str = "", refresh_token: str = "") -> None: } params.update(extra_params) url = "https://auth.gog.com/token?" + urlencode(params) - request = Request(url) + request = Request(url, redacted_query_parameters=("refresh_token", "code")) try: request.get() except HTTPError as http_error: diff --git a/lutris/util/http.py b/lutris/util/http.py index b98fd7c053..cffbb1b9c7 100644 --- a/lutris/util/http.py +++ b/lutris/util/http.py @@ -40,6 +40,7 @@ def __init__( stop_request=None, headers=None, cookies=None, + redacted_query_parameters=None, ): self.url = self._clean_url(url) self.status_code = None @@ -52,6 +53,7 @@ def __init__( self.headers = {"User-Agent": self.user_agent} self.response_headers = None self.info = None + self.redacted_query_parameters = redacted_query_parameters if headers is None: headers = {} if not isinstance(headers, dict): @@ -84,8 +86,31 @@ def _clean_url(url): def user_agent(self): return "{} {}".format(PROJECT, VERSION) + @property + def redacted_url(self): + """A version of the ULR with specified query string parameters + replaced with REDACTED for security. We log these.""" + if self.redacted_query_parameters: + parsed = urllib.parse.urlparse(self.url) + query_items = urllib.parse.parse_qsl(parsed.query) + new_query_items = [] + redacted_any = False + for key, value in query_items: + if key in self.redacted_query_parameters: + redacted_any = True + value = "REDACTED" + new_query_items.append((key, value)) + + if redacted_any: + parsed_parts = list(parsed) + parsed_parts[4] = urllib.parse.urlencode(new_query_items) + parsed = tuple(parsed_parts) + return urllib.parse.urlunparse(parsed) + + return self.url + def _request(self, method, data=None): - logger.debug("%s %s", method, self.url) + logger.debug("%s %s", method, self.redacted_url) try: req = urllib.request.Request(url=self.url, data=data, headers=self.headers, method=method) except ValueError as ex: From 5d6c5555c257fe7c7e6ad1124b8f4beebf35ccc0 Mon Sep 17 00:00:00 2001 From: Eikeno Date: Sat, 1 Mar 2025 18:04:19 +0100 Subject: [PATCH 006/126] fix Frenh locale utf to utf8 to avoid error message In get_locale_choices(): French locale setting is set to "fr_FR.utf" while utf8 is used for other languages. When using the French locale in the GUI error appears, despite /etc/locale.gen is OK. Using utf8 fixe the problem and no errors are reported. --- lutris/sysoptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/sysoptions.py b/lutris/sysoptions.py index 6e52137592..7efd3e68ee 100644 --- a/lutris/sysoptions.py +++ b/lutris/sysoptions.py @@ -31,7 +31,7 @@ def get_locale_choices(): (_("Dutch"), "nl_NL.utf8"), (_("English"), "en_US.utf8"), (_("Finnish"), "fi_FI.utf8"), - (_("French"), "fr_FR.utf"), + (_("French"), "fr_FR.utf8"), (_("Georgian"), "ka_GE.utf8"), (_("German"), "de_DE.utf8"), (_("Greek"), "el_GR.utf8"), From 4399dc7937d95024de99f3accc64e9eb954f6d2f Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 5 Mar 2025 16:25:03 -0500 Subject: [PATCH 007/126] Make 'Uncategorized' a dynamic cateogry. Because it is, and we can't treat it as static. Resolves #5973 --- lutris/gui/widgets/sidebar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/gui/widgets/sidebar.py b/lutris/gui/widgets/sidebar.py index 3056781db9..6ec8f9d22f 100644 --- a/lutris/gui/widgets/sidebar.py +++ b/lutris/gui/widgets/sidebar.py @@ -481,7 +481,7 @@ def initialize_rows(self): self.add( SidebarRow( ".uncategorized", - "category", + "dynamic_category", _("Uncategorized"), self.get_sidebar_icon("tag-symbolic", ["poi-marker", "favorite-symbolic"]), ) From b74617cc79f95bffe0c23702cc77fd8387b6ee63 Mon Sep 17 00:00:00 2001 From: QuoteNat <35079138+QuoteNat@users.noreply.github.com> Date: Thu, 6 Mar 2025 12:16:25 -0600 Subject: [PATCH 008/126] Update Flathub API url --- lutris/services/flathub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/services/flathub.py b/lutris/services/flathub.py index 2f8933a2a4..ed44cf19db 100644 --- a/lutris/services/flathub.py +++ b/lutris/services/flathub.py @@ -57,7 +57,7 @@ class FlathubService(BaseService): icon = "flathub" medias = {"banner": FlathubBanner} default_format = "banner" - api_url = "https://flathub.org/api/v2/category/game" + api_url = "https://flathub.org/api/v2/collection/category/game" cache_path = os.path.join(settings.CACHE_DIR, "flathub-library.json") branch = "stable" From a488bb0812f4fff9bd81a9bacf83d5028e5b9c84 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 8 Mar 2025 22:30:37 +0700 Subject: [PATCH 009/126] Add missing environment variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set __EGL_VENDOR_LIBRARY_FILENAMES environment variable to specify the exact EGL vendor library paths, ensuring proper compatibility between Nvidia and non-Nvidia graphics drivers. This makes vkd3d work correctly. If not set, when choosing NVidia GPU in settings, vkd3d will load 50_mesa.json then fall back to using iGPU in llvmpipe (fall-back software renderer). (fix #4237) - Make default cache path of dxvk, vkd3d same as GLCache dir. RUNNER_DIR ├── GLCache // Nvidia Shader Cache ├── .dxvk-cache // DXVK caches pipeline state ├── vkd3d-proton..exe.cache // vkd3d-proton shader cache ├── vkd3d-proton..exe.cache.write // vkd3d-proton shader cache └── ... Fix vkd3d-proton's inability to create cache file if env cache path is not set, which leads to feed error to the log.(vkd3d-proton internal cache is enabled by default) --- lutris/runners/runner.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lutris/runners/runner.py b/lutris/runners/runner.py index 0b838e44a4..f350a44493 100644 --- a/lutris/runners/runner.py +++ b/lutris/runners/runner.py @@ -19,6 +19,7 @@ from lutris.util.log import logger from lutris.util.process import Process +GLVND_DIR = "/usr/share/glvnd/egl_vendor.d" def kill_processes(sig: int, pids: Iterable[int]) -> None: """Sends a signal to a process list, logging errors without stopping.""" @@ -242,6 +243,11 @@ def get_env(self, os_env=False, disable_runtime=False): env["__GL_SHADER_DISK_CACHE"] = "1" env["__GL_SHADER_DISK_CACHE_PATH"] = self.nvidia_shader_cache_path + # Store vkd3d sharder cache and dxvk cache as a same path as NVidia's shader. + # VKD3D_SHADER_CACHE_PATH not set cause error vkd3d can't not write shader cache + env["VKD3D_SHADER_CACHE_PATH"] = self.nvidia_shader_cache_path + env["DXVK_STATE_CACHE_PATH"] = self.nvidia_shader_cache_path + # Override SDL2 controller configuration sdl_gamecontrollerconfig = self.system_config.get("sdl_gamecontrollerconfig") if sdl_gamecontrollerconfig: @@ -263,8 +269,10 @@ def get_env(self, os_env=False, disable_runtime=False): env["__NV_PRIME_RENDER_OFFLOAD"] = "1" env["__GLX_VENDOR_LIBRARY_NAME"] = "nvidia" env["__VK_LAYER_NV_optimus"] = "NVIDIA_only" + env["__EGL_VENDOR_LIBRARY_FILENAMES"] = os.path.join(GLVND_DIR,"10_nvidia.json") else: env["DRI_PRIME"] = gpu.pci_id + env["__EGL_VENDOR_LIBRARY_FILENAMES"] = os.path.join(GLVND_DIR,"50_mesa.json") env["VK_ICD_FILENAMES"] = gpu.icd_files # Deprecated env["VK_DRIVER_FILES"] = gpu.icd_files # Current form From d0d4bd07aabef1c6e6071b1461e629fe527e364d Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Mon, 10 Mar 2025 11:24:07 +0700 Subject: [PATCH 010/126] fix vulnerabilities xml --- lutris/runners/snes9x.py | 2 +- lutris/services/ea_app.py | 2 +- lutris/util/mame/database.py | 2 +- lutris/util/wine/cabinstall.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lutris/runners/snes9x.py b/lutris/runners/snes9x.py index c8d4c0573f..a87ab85a56 100644 --- a/lutris/runners/snes9x.py +++ b/lutris/runners/snes9x.py @@ -1,7 +1,7 @@ # Standard Library import os import subprocess -import xml.etree.ElementTree as etree +import defusedxml.ElementTree as etree from gettext import gettext as _ # Lutris Modules diff --git a/lutris/services/ea_app.py b/lutris/services/ea_app.py index 9f06e9361a..b6623867b8 100644 --- a/lutris/services/ea_app.py +++ b/lutris/services/ea_app.py @@ -6,7 +6,7 @@ import ssl from gettext import gettext as _ from typing import Any, Dict, Optional -from xml.etree import ElementTree +from defusedxml import ElementTree import requests import urllib3 diff --git a/lutris/util/mame/database.py b/lutris/util/mame/database.py index 8af7b1f234..a1987afb92 100644 --- a/lutris/util/mame/database.py +++ b/lutris/util/mame/database.py @@ -3,7 +3,7 @@ # Standard Library import json import os -from xml.etree import ElementTree +from defusedxml import ElementTree # Lutris Modules from lutris import settings diff --git a/lutris/util/wine/cabinstall.py b/lutris/util/wine/cabinstall.py index b42697ed01..f78408c0c4 100644 --- a/lutris/util/wine/cabinstall.py +++ b/lutris/util/wine/cabinstall.py @@ -3,7 +3,7 @@ import shutil import subprocess import tempfile -import xml.etree.ElementTree +import defusedxml.ElementTree from lutris.util.log import logger from lutris.util.system import execute, read_process_output @@ -117,7 +117,7 @@ def process_value(self, reg_value, arch): def get_registry_from_manifest(self, file_name): out = "" - root = xml.etree.ElementTree.parse(file_name).getroot() + root = defusedxml.ElementTree.parse(file_name).getroot() arch = self.get_arch_from_manifest(root) registry_keys = root.findall("{urn:schemas-microsoft-com:asm.v3}registryKeys") if registry_keys: From ee5f3d4f3abd5bc496a0e0721817e1557a61d18b Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Mon, 10 Mar 2025 12:13:37 +0700 Subject: [PATCH 011/126] dialogs log: Ctrl+mouse wheel zooming Thanks @Merrit for the idea Co-authored-by: Kristen McWilliam <9575627+Merrit@users.noreply.github.com> --- lutris/gui/dialogs/log.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lutris/gui/dialogs/log.py b/lutris/gui/dialogs/log.py index 0a85196188..ddda7bd5f8 100644 --- a/lutris/gui/dialogs/log.py +++ b/lutris/gui/dialogs/log.py @@ -4,7 +4,7 @@ from datetime import datetime from gettext import gettext as _ -from gi.repository import Gdk, GObject, Gtk +from gi.repository import Gdk, GObject, Gtk, Pango from lutris.gui.dialogs import FileDialog from lutris.gui.widgets.log_text_view import LogTextView @@ -37,6 +37,9 @@ def __init__(self, game, buffer, application=None): save_button = builder.get_object("save_button") save_button.connect("clicked", self.on_save_clicked) + self.window.add_events(Gdk.EventMask.SCROLL_MASK) + self.window.connect("scroll-event", self.on_scroll_event) + self.window.connect("key-press-event", self.on_key_press_event) self.window.show_all() @@ -62,3 +65,19 @@ def on_save_clicked(self, _button): text = self.buffer.get_text(self.buffer.get_start_iter(), self.buffer.get_end_iter(), True) with open(log_path, "w", encoding="utf-8") as log_file: log_file.write(text) + def on_scroll_event(self, widget, event): + """Handle Ctrl+scroll to zoom text""" + ctrl_pressed = event.state & Gdk.ModifierType.CONTROL_MASK + + if ctrl_pressed: + change = 0 + font = self.logtextview.get_style_context().get_font(Gtk.StateFlags.NORMAL) + size = font.get_size() / Pango.SCALE + if event.direction == Gdk.ScrollDirection.UP: + if size < 48: # Maximum size + change = 1 + elif event.direction == Gdk.ScrollDirection.DOWN: + if size > 6: + change = -1 + self.logtextview.override_font(Pango.FontDescription(f"monospace {size + change}")) + return From 5aecf9f1a5acaa18fa4c236bc9f593fcee98fa2e Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Mon, 10 Mar 2025 14:02:54 +0700 Subject: [PATCH 012/126] add Chinese (Traditional) locale --- lutris/sysoptions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lutris/sysoptions.py b/lutris/sysoptions.py index 7efd3e68ee..898c5017e0 100644 --- a/lutris/sysoptions.py +++ b/lutris/sysoptions.py @@ -26,7 +26,8 @@ def get_locale_choices(): """ return [ (_("System"), ""), - (_("Chinese"), "zh_CN.utf8"), + (_("Chinese (Simplified)"), "zh_CN.utf8"), + (_("Chinese (Traditional)"), "zh_TW.utf8"), (_("Croatian"), "hr_HR.utf8"), (_("Dutch"), "nl_NL.utf8"), (_("English"), "en_US.utf8"), From f1d09a55e2cd0d35989b2e22fb1b7b5f8c5f4ff6 Mon Sep 17 00:00:00 2001 From: Mathieu Comandon Date: Tue, 11 Mar 2025 08:38:19 -0700 Subject: [PATCH 013/126] Replace 0.5.19 with 0.5.20 in the changelog --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index aafeb11579..8e76464c5c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -lutris (0.5.19) jammy; urgency=medium +lutris (0.5.20) jammy; urgency=medium * Fix Proton integration bugs so Proton-fixes are applied * Do not offer DXVK, VKD3D, D3D Extras or DDXVK-NVAPI on Proton versions; Proton will handle these. From 291c645b729400cc2034f72d071fc462ba75c6df Mon Sep 17 00:00:00 2001 From: Mathieu Comandon Date: Tue, 11 Mar 2025 08:40:01 -0700 Subject: [PATCH 014/126] Remove more traces of 0.5.19 --- share/metainfo/net.lutris.Lutris.metainfo.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/share/metainfo/net.lutris.Lutris.metainfo.xml b/share/metainfo/net.lutris.Lutris.metainfo.xml index d3e5b66dc8..11b5e5ec20 100644 --- a/share/metainfo/net.lutris.Lutris.metainfo.xml +++ b/share/metainfo/net.lutris.Lutris.metainfo.xml @@ -28,7 +28,6 @@ net.lutris.Lutris.desktop -
    From 8e05fe195234b5715a8b870c52213a52dd5f6664 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 11 Mar 2025 18:48:42 -0400 Subject: [PATCH 015/126] Do not crash if the checksum is missing in libretro; that's clearly meant to be optional. Resolves #5982 --- lutris/runners/libretro.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lutris/runners/libretro.py b/lutris/runners/libretro.py index eecab6c45f..e09ac3a3d2 100644 --- a/lutris/runners/libretro.py +++ b/lutris/runners/libretro.py @@ -254,9 +254,9 @@ def prelaunch(self): required_firmware_filename = retro_config["firmware%d_path" % index] required_firmware_path = os.path.join(system_path, required_firmware_filename) required_firmware_name = required_firmware_filename.split("/")[-1] - required_firmware_checksum = checksums[required_firmware_filename] + required_firmware_checksum = checksums.get(required_firmware_filename) if system.path_exists(required_firmware_path): - if required_firmware_filename in checksums: + if required_firmware_checksum: checksum = system.get_md5_hash(required_firmware_path) if checksum == required_firmware_checksum: checksum_status = "Checksum good" @@ -266,8 +266,9 @@ def prelaunch(self): checksum_status = "No checksum info" logger.info("Firmware '%s' found (%s)", required_firmware_filename, checksum_status) else: - get_firmware(required_firmware_name, required_firmware_checksum, system_path) logger.warning("Firmware '%s' not found!", required_firmware_filename) + if required_firmware_checksum: + get_firmware(required_firmware_name, required_firmware_checksum, system_path) def get_runner_parameters(self): parameters = [] From 75ee2b320a8695edca85b83bc54e4d4b0823eb6c Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 11 Mar 2025 18:56:10 -0400 Subject: [PATCH 016/126] Changelog note for Flathub fix (#5976) --- debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/changelog b/debian/changelog index 8e76464c5c..a4e081f221 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,7 @@ lutris (0.5.20) jammy; urgency=medium * Do not offer DXVK, VKD3D, D3D Extras or DDXVK-NVAPI on Proton versions; Proton will handle these. * The "Enable Esync" and "Enable Fsync" settings are now passed on to Proton * DXVK's integrated D8VK will be enabled in Proton + * Fix for updated Flathub API * Emulator BIOS file location (used by libretro) may be set in Preferences * Obtain the release year from GOG and Itch.io. * MAME Machine setting uses a searchable entry for its enourmous list From e598b0630a35bb82fb09903e0196677c7a5229b8 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 11 Mar 2025 18:59:08 -0400 Subject: [PATCH 017/126] F5 to refresh has been removed; we should now update on changes more reliably. --- debian/changelog | 1 - 1 file changed, 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index a4e081f221..859c132e2b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -9,7 +9,6 @@ lutris (0.5.20) jammy; urgency=medium * Obtain the release year from GOG and Itch.io. * MAME Machine setting uses a searchable entry for its enourmous list * Support for importing Commodore 64 ROMs - * F5 will refresh the main Lutris window -- Mathieu Comandon Sun, 23 Feb 2025 10:55:10 -0800 From 454e23af66575a181464199cb2d9cb4b9ec1a794 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 11 Mar 2025 19:13:24 -0400 Subject: [PATCH 018/126] A couple more changelog entries. There's just not much new since 0.5.19. --- debian/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debian/changelog b/debian/changelog index 859c132e2b..63ee2dea17 100644 --- a/debian/changelog +++ b/debian/changelog @@ -9,6 +9,8 @@ lutris (0.5.20) jammy; urgency=medium * Obtain the release year from GOG and Itch.io. * MAME Machine setting uses a searchable entry for its enourmous list * Support for importing Commodore 64 ROMs + * Redundant "Add Games" menu item removed; use the plus button in the corner + * "Manual Script" for the context menu will now see the game's environment variables -- Mathieu Comandon Sun, 23 Feb 2025 10:55:10 -0800 From 43ab1bf25abb6063fdb19befe50d7bcedb8299c4 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Wed, 12 Mar 2025 21:41:41 +0700 Subject: [PATCH 019/126] Update girepository dev for CI Update dependency to libgintrospection2.0-dev for PyGObject 3.51.0 compatibility --- .github/workflows/static.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 1eee53fe3c..9f55966959 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -16,7 +16,7 @@ jobs: - name: Install Ubuntu dependencies run: | sudo apt update - sudo apt-get install libdbus-1-dev pkg-config libgirepository1.0-dev python3-gi-cairo libcairo2-dev + sudo apt-get install libdbus-1-dev pkg-config libgirepository-2.0-dev python3-gi-cairo libcairo2-dev - name: Install Python dependencies run: | python -m pip install --upgrade pip From 077783e2c200382b033f7f5c5e995c72ad69ce8a Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 12 Mar 2025 17:01:05 -0400 Subject: [PATCH 020/126] Do not allow random objects into 'tooltip_default' that should be a str or None. --- lutris/gui/config/widget_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lutris/gui/config/widget_generator.py b/lutris/gui/config/widget_generator.py index c7c6d9fe80..c0ca2a96bd 100644 --- a/lutris/gui/config/widget_generator.py +++ b/lutris/gui/config/widget_generator.py @@ -207,7 +207,7 @@ def configure_wrapper_box(self, wrapper: Gtk.Widget, option: Dict[str, Any], val def get_tooltip(self, option: Dict[str, Any], value: Any, default: Any): tooltip = option.get("help") - if isinstance(self.tooltip_default, str): + if self.tooltip_default: tooltip = tooltip + "\n\n" if tooltip else "" tooltip += _("Default: ") + self.tooltip_default return tooltip @@ -404,7 +404,7 @@ def populate_combobox_choices(): liststore.append(choice) if tooltip_default: - self.tooltip_default = tooltip_default + self.tooltip_default = str(tooltip_default) def expand_combobox_choices(): expanded = [] From 62a07689402e9ac7ee238f77b1ede33ab8ec2dbe Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 13 Mar 2025 09:50:41 +0700 Subject: [PATCH 021/126] add more detail ruff check Improve code formatting --- .github/workflows/static.yml | 2 +- lutris/gui/dialogs/log.py | 30 +++++++++++++++--------------- lutris/runners/runner.py | 5 +++-- lutris/sysoptions.py | 7 ++++++- lutris/util/display.py | 2 ++ lutris/util/wine/dxvk_nvapi.py | 12 +++++++++++- 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 9f55966959..aaf94f8fd2 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -44,4 +44,4 @@ jobs: run: ruff --version ruff check . - name: Check format - run: ruff format . --check + run: ruff format . --check --diff diff --git a/lutris/gui/dialogs/log.py b/lutris/gui/dialogs/log.py index ddda7bd5f8..d3a11c5d20 100644 --- a/lutris/gui/dialogs/log.py +++ b/lutris/gui/dialogs/log.py @@ -65,19 +65,19 @@ def on_save_clicked(self, _button): text = self.buffer.get_text(self.buffer.get_start_iter(), self.buffer.get_end_iter(), True) with open(log_path, "w", encoding="utf-8") as log_file: log_file.write(text) - def on_scroll_event(self, widget, event): + + def on_scroll_event(self, widget: Gtk.Widget, event: Gdk.EventScroll) -> bool: """Handle Ctrl+scroll to zoom text""" - ctrl_pressed = event.state & Gdk.ModifierType.CONTROL_MASK - - if ctrl_pressed: - change = 0 - font = self.logtextview.get_style_context().get_font(Gtk.StateFlags.NORMAL) - size = font.get_size() / Pango.SCALE - if event.direction == Gdk.ScrollDirection.UP: - if size < 48: # Maximum size - change = 1 - elif event.direction == Gdk.ScrollDirection.DOWN: - if size > 6: - change = -1 - self.logtextview.override_font(Pango.FontDescription(f"monospace {size + change}")) - return + if not event.state & Gdk.ModifierType.CONTROL_MASK: + return False + + font = self.logtextview.get_style_context().get_font(Gtk.StateFlags.NORMAL) + size = font.get_size() // Pango.SCALE + if event.direction == Gdk.ScrollDirection.UP and size < 48: + new_size = size + 1 + elif event.direction == Gdk.ScrollDirection.DOWN and size > 6: + new_size = size - 1 + else: + return False + self.logtextview.override_font(Pango.FontDescription(f"monospace {new_size}")) + return True diff --git a/lutris/runners/runner.py b/lutris/runners/runner.py index f350a44493..0e3853e9f6 100644 --- a/lutris/runners/runner.py +++ b/lutris/runners/runner.py @@ -21,6 +21,7 @@ GLVND_DIR = "/usr/share/glvnd/egl_vendor.d" + def kill_processes(sig: int, pids: Iterable[int]) -> None: """Sends a signal to a process list, logging errors without stopping.""" for pid in pids: @@ -269,10 +270,10 @@ def get_env(self, os_env=False, disable_runtime=False): env["__NV_PRIME_RENDER_OFFLOAD"] = "1" env["__GLX_VENDOR_LIBRARY_NAME"] = "nvidia" env["__VK_LAYER_NV_optimus"] = "NVIDIA_only" - env["__EGL_VENDOR_LIBRARY_FILENAMES"] = os.path.join(GLVND_DIR,"10_nvidia.json") + env["__EGL_VENDOR_LIBRARY_FILENAMES"] = os.path.join(GLVND_DIR, "10_nvidia.json") else: env["DRI_PRIME"] = gpu.pci_id - env["__EGL_VENDOR_LIBRARY_FILENAMES"] = os.path.join(GLVND_DIR,"50_mesa.json") + env["__EGL_VENDOR_LIBRARY_FILENAMES"] = os.path.join(GLVND_DIR, "50_mesa.json") env["VK_ICD_FILENAMES"] = gpu.icd_files # Deprecated env["VK_DRIVER_FILES"] = gpu.icd_files # Current form diff --git a/lutris/sysoptions.py b/lutris/sysoptions.py index 898c5017e0..3138f49dab 100644 --- a/lutris/sysoptions.py +++ b/lutris/sysoptions.py @@ -6,7 +6,12 @@ from lutris import runners from lutris.util import linux, system -from lutris.util.display import DISPLAY_MANAGER, SCREEN_SAVER_INHIBITOR, is_compositing_enabled, is_display_x11, is_display_wayland +from lutris.util.display import ( + DISPLAY_MANAGER, + SCREEN_SAVER_INHIBITOR, + is_compositing_enabled, + is_display_x11, +) from lutris.util.graphics.gpu import GPUS diff --git a/lutris/util/display.py b/lutris/util/display.py index d4c581d2b0..e05bc72f63 100644 --- a/lutris/util/display.py +++ b/lutris/util/display.py @@ -53,12 +53,14 @@ def is_display_x11(): display = Gdk.Display.get_default() return "x11" in type(display).__name__.casefold() + @cache_single def is_display_wayland(): """True if the current display is Wayland""" display = Gdk.Display.get_default() return "wayland" in type(display).__name__.casefold() + class DisplayManager: """Get display and resolution using various backends based on the current environment""" diff --git a/lutris/util/wine/dxvk_nvapi.py b/lutris/util/wine/dxvk_nvapi.py index 4a5ec061fc..c3b7f913cd 100644 --- a/lutris/util/wine/dxvk_nvapi.py +++ b/lutris/util/wine/dxvk_nvapi.py @@ -11,7 +11,17 @@ class DXVKNVAPIManager(DLLManager): human_name = "DXVK-NVAPI" # apparently, nvofapi.dll (the 32 bit version) is not being included here - # see https://github.com/jp7677/dxvk-nvapi/pull/213 - managed_dlls = ("nvapi", "nvapi64", "nvml", "nvofapi64", "nvoptix", "nvencodeapi", "nvencodeapi64", "nvcuvid", "nvcuda") + managed_dlls = ( + "nvapi", + "nvapi64", + "nvml", + "nvofapi64", + "nvoptix", + "nvencodeapi", + "nvencodeapi64", + "nvcuvid", + "nvcuda", + ) releases_url = "https://api.github.com/repos/lutris/dxvk-nvapi/releases" dlss_dlls = ("nvngx", "_nvngx", "nvngx_dlssg") From 7904b1ea3833f677954ac7095870b007ef78d0ab Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Thu, 13 Mar 2025 16:54:06 -0400 Subject: [PATCH 022/126] Smarter sort conversions. Always convert any sort value to a common type, or to a default if that can't be managed. Avoid crashing during sort due to clashing types. Convert dates and date-times in the 'year' data to just the year also. Resolves #5985 --- lutris/gui/lutriswindow.py | 74 ++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py index 469dc70712..84892f692a 100644 --- a/lutris/gui/lutriswindow.py +++ b/lutris/gui/lutriswindow.py @@ -4,6 +4,7 @@ # pylint: disable=no-member import os from collections import namedtuple +from datetime import datetime from gettext import gettext as _ from typing import Iterable, List, Set from urllib.parse import unquote, urlparse @@ -465,36 +466,71 @@ def apply_view_sort(self, items, resolver=lambda i: i): "playtime": 0.0, } + def get_sort_default(item): + """Returns the default value to use when the value is missing; we may be able + to extract this from the item..""" + if self.view_sorting == "year" and self.service: + service_year = self.service.get_game_release_date(item) + if service_year: + return service_year + + # Users may have obsolete view_sorting settings, so + # we must tolerate them. We treat them all as blank. + return sort_defaults.get(self.view_sorting, "") + + def convert_value(value): + """Converts 'value' to the type required for the sort that is in use. Returns None if this + can't be managed.""" + try: + if not value: + return None + if self.view_sorting == "name": + return str(value) if value else "" + elif self.view_sorting == "year": + # Years can take many forms! We'll try to convert as best we can. + if isinstance(value, datetime): + value = value.year + else: + try: + return int(value) + except ValueError: + as_date = datetime.strptime(str(value), "%Y-%m-%d") + value = as_date.year + else: + return float(value) + except ValueError: + return None # unable to parse value? + + def extend_value(value): + """Expands the value to sort by to a more complex form, for smarter sorting.""" + if self.view_sorting == "name": + return get_natural_sort_key(value) + elif self.view_sorting == "year": + contains_year = bool(value) + if self.view_reverse_order: + contains_year = not contains_year + return contains_year, value + return value + def get_sort_value(item): db_game = resolver(item) if not db_game: installation_flag = False - value = sort_defaults.get(self.view_sorting, "") + value = None else: installation_flag = bool(db_game.get("installed")) # When sorting by name, check for a valid sortname first, then fall back # on name if valid sortname is not available. - sortname = db_game.get("sortname") - if self.view_sorting == "name" and sortname: - value = sortname + if self.view_sorting == "name": + value = db_game.get("sortname") or db_game.get("name") else: value = db_game.get(self.view_sorting) - if self.view_sorting == "name": - value = get_natural_sort_key(value) - # Users may have obsolete view_sorting settings, so - # we must tolerate them. We treat them all as blank. - value = value or sort_defaults.get(self.view_sorting, "") - if self.view_sorting == "year": - if self.service: - service_value = self.service.get_game_release_date(item) - if service_value: - value = service_value - contains_year = bool(value) - if self.view_reverse_order: - contains_year = not contains_year - value = [contains_year, value] + value = value or get_sort_default(item) + value = convert_value(value) or get_sort_default(item) + value = extend_value(value) + if self.view_sorting_installed_first: # We want installed games to always be first, even in # a descending sort. @@ -502,7 +538,7 @@ def get_sort_value(item): installation_flag = not installation_flag if self.view_sorting == "name": installation_flag = not installation_flag - return [installation_flag, value] + return installation_flag, value return value reverse = self.view_reverse_order if self.view_sorting == "name" else not self.view_reverse_order From 34a277e0aef9e6740b843072e5c541b1935a8229 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Fri, 14 Mar 2025 17:17:41 -0400 Subject: [PATCH 023/126] Try again! Re-arrange order of conversion calls and make sure to actually return converted values. This untyped code is quite challenging! --- lutris/gui/lutriswindow.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py index 84892f692a..65ab8a34e6 100644 --- a/lutris/gui/lutriswindow.py +++ b/lutris/gui/lutriswindow.py @@ -471,6 +471,7 @@ def get_sort_default(item): to extract this from the item..""" if self.view_sorting == "year" and self.service: service_year = self.service.get_game_release_date(item) + service_year = convert_value(service_year) if service_year: return service_year @@ -485,17 +486,17 @@ def convert_value(value): if not value: return None if self.view_sorting == "name": - return str(value) if value else "" - elif self.view_sorting == "year": + return str(value) + if self.view_sorting == "year": # Years can take many forms! We'll try to convert as best we can. if isinstance(value, datetime): - value = value.year + return int(value.year) else: try: return int(value) except ValueError: as_date = datetime.strptime(str(value), "%Y-%m-%d") - value = as_date.year + return int(as_date.year) else: return float(value) except ValueError: @@ -505,7 +506,7 @@ def extend_value(value): """Expands the value to sort by to a more complex form, for smarter sorting.""" if self.view_sorting == "name": return get_natural_sort_key(value) - elif self.view_sorting == "year": + if self.view_sorting == "year": contains_year = bool(value) if self.view_reverse_order: contains_year = not contains_year @@ -527,7 +528,6 @@ def get_sort_value(item): else: value = db_game.get(self.view_sorting) - value = value or get_sort_default(item) value = convert_value(value) or get_sort_default(item) value = extend_value(value) From 94881714c3954cf45aceb5eef79ea481be8008e1 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Fri, 14 Mar 2025 18:03:49 -0400 Subject: [PATCH 024/126] Replace join with just formatting a list- it's just a warning, there's no need to be picky about the test, and this way it won't crash on non-str values in the list. Resolves #5990 --- lutris/runners/commands/wine.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lutris/runners/commands/wine.py b/lutris/runners/commands/wine.py index ab8c3feb6c..97957414fb 100644 --- a/lutris/runners/commands/wine.py +++ b/lutris/runners/commands/wine.py @@ -214,10 +214,7 @@ def winekill(prefix, arch=WINE_DEFAULT_ARCH, wine_path=None, env=None, initial_p if not running_processes: break if num_cycles > 20: - logger.warning( - "Some wine processes are still running: %s", - ", ".join(running_processes), - ) + logger.warning("Some wine processes are still running: %s", running_processes) break time.sleep(0.1) logger.debug("Done waiting.") From 41dce417d9257064fe0bc547ac16e3520ad063c9 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 15 Mar 2025 21:39:15 +0700 Subject: [PATCH 025/126] webconnect_dialog: set fallback locale Co-authored-by: LoKolbasz <86350313+LoKolbasz@users.noreply.github.com> --- lutris/gui/dialogs/webconnect_dialog.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lutris/gui/dialogs/webconnect_dialog.py b/lutris/gui/dialogs/webconnect_dialog.py index c71475b0a5..0061e66e10 100644 --- a/lutris/gui/dialogs/webconnect_dialog.py +++ b/lutris/gui/dialogs/webconnect_dialog.py @@ -15,7 +15,9 @@ gi.require_version("WebKit2", "4.0") from gi.repository import WebKit2 +from lutris.config import LutrisConfig from lutris.gui.dialogs import ModalDialog +from lutris.util.log import logger class WebConnectDialog(ModalDialog): @@ -25,6 +27,11 @@ def __init__(self, service: "OnlineService", parent=None): service.is_login_in_progress = True self.context = WebKit2.WebContext.new() + webview_locales = {"en_US"} # Initialize with fallback locale + for get_locale in [LutrisConfig().system_config.get("locale"), os.getenv("LANG")]: + if get_locale and (_locale := get_locale.split(".")[0]): + webview_locales.add(_locale) + self.context.set_preferred_languages(list(webview_locales)) if "http_proxy" in os.environ: proxy = WebKit2.NetworkProxySettings.new(os.environ["http_proxy"]) From 4b05ffec26ed6477caf6b36a25116c68511cc251 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Mon, 17 Mar 2025 16:40:38 -0400 Subject: [PATCH 026/126] Improved error message if you install a game that needs ac omponentbut it isn't installed yet. See #5993. --- lutris/exceptions.py | 8 ++++++++ lutris/util/wine/dll_manager.py | 10 ++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lutris/exceptions.py b/lutris/exceptions.py index 827e50aa4c..e4759adced 100644 --- a/lutris/exceptions.py +++ b/lutris/exceptions.py @@ -11,6 +11,14 @@ def __init__(self, message, *args, **kwarg): self.message = message +class MissingRuntimeComponentError(LutrisError): + """Raised when a Lutris component isn't found, but should have been installed.""" + + def __init__(self, message, component_name, *args, **kwarg): + super().__init__(message, *args, **kwarg) + self.component_name = component_name + + class MisconfigurationError(LutrisError): """Raised for incorrect configuration or installation, like incorrect or missing settings, missing components, that sort of thing. This has subclasses diff --git a/lutris/util/wine/dll_manager.py b/lutris/util/wine/dll_manager.py index 07238b73d5..3cad5ac7b9 100644 --- a/lutris/util/wine/dll_manager.py +++ b/lutris/util/wine/dll_manager.py @@ -7,6 +7,7 @@ from lutris import settings from lutris.api import get_runtime_versions +from lutris.exceptions import MissingRuntimeComponentError from lutris.util import system from lutris.util.extract import extract_archive from lutris.util.http import download_file @@ -96,8 +97,13 @@ def path(self): """Path to local folder containing DLLs""" version = self.version if not version: - raise RuntimeError( - "No path can be generated for %s because no version information is available." % self.human_name + raise MissingRuntimeComponentError( + _( + "The '%s' runtime component is not installed; " + "visit the Updates tab in the Preferences dialog to install it." + ) + % self.human_name, + component_name=self.name, ) return os.path.join(self.base_dir, version) From 91a999a94318b05751a6eedb9b18095e082c5068 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Mon, 17 Mar 2025 16:53:17 -0400 Subject: [PATCH 027/126] Hide the scary error details for this new exception- it tells the user what to do, and they can use the button to copy to clipboard if that won't do. --- lutris/exceptions.py | 2 ++ lutris/gui/installerwindow.py | 31 ++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lutris/exceptions.py b/lutris/exceptions.py index e4759adced..31ae342ad9 100644 --- a/lutris/exceptions.py +++ b/lutris/exceptions.py @@ -9,6 +9,7 @@ class LutrisError(Exception): def __init__(self, message, *args, **kwarg): super().__init__(message, *args, **kwarg) self.message = message + self.is_expected = False class MissingRuntimeComponentError(LutrisError): @@ -17,6 +18,7 @@ class MissingRuntimeComponentError(LutrisError): def __init__(self, message, component_name, *args, **kwarg): super().__init__(message, *args, **kwarg) self.component_name = component_name + self.is_expected = True class MisconfigurationError(LutrisError): diff --git a/lutris/gui/installerwindow.py b/lutris/gui/installerwindow.py index d8657ba996..f77e838979 100644 --- a/lutris/gui/installerwindow.py +++ b/lutris/gui/installerwindow.py @@ -150,6 +150,7 @@ def __init__(self, installers, service=None, appid=None, installation_kind=Insta self.installer_files_box.connect("files-ready", self.on_files_ready) self.log_buffer = Gtk.TextBuffer() + self.error_details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, no_show_all=True) self.error_details_buffer = Gtk.TextBuffer() self.error_reporter = self.load_error_page @@ -859,14 +860,19 @@ def load_error_page(self, error: BaseException) -> None: def present_error_page(self, error: BaseException) -> None: self.set_status(str(error)) - formatted = traceback.format_exception(type(error), error, error.__traceback__) - formatted = "\n".join(formatted).strip() + is_expected = hasattr(error, "is_expected") and error.is_expected - log = get_log_contents() - if log: - formatted = f"{formatted}\n\nLutris log:\n{log}".strip() + if is_expected: + formatted = traceback.format_exception(type(error), error, error.__traceback__) + formatted = "\n".join(formatted).strip() - self.error_details_buffer.set_text(formatted) + log = get_log_contents() + if log: + formatted = f"{formatted}\n\nLutris log:\n{log}".strip() + + self.error_details_buffer.set_text(formatted) + + self.error_details_box.set_visible(not is_expected) self.stack.present_page("error") self.display_cancel_button() @@ -881,7 +887,8 @@ def on_copy_clicked(_button): clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) clipboard.set_text(text, -1) - box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + error_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + label = Gtk.Label(xalign=0.0, wrap=True) label.set_markup( _( @@ -891,7 +898,8 @@ def on_copy_clicked(_button): "Discord." ) ) - box.pack_start(label, False, False, 0) + self.error_details_box.pack_start(label, False, False, 0) + frame = Gtk.Frame(shadow_type=Gtk.ShadowType.ETCHED_IN) details_textview = Gtk.TextView(editable=False, buffer=self.error_details_buffer) @@ -899,13 +907,14 @@ def on_copy_clicked(_button): scrolledwindow = Gtk.ScrolledWindow() scrolledwindow.add(details_textview) frame.add(scrolledwindow) - box.pack_start(frame, True, True, 0) + self.error_details_box.pack_start(frame, True, True, 0) + error_box.pack_start(self.error_details_box, True, True, 0) copy_button = Gtk.Button(_("Copy Details to Clipboard"), halign=Gtk.Align.START) - box.pack_end(copy_button, False, True, 0) + error_box.pack_end(copy_button, False, True, 0) copy_button.connect("clicked", on_copy_clicked) - return box + return error_box # Finished Page # From 1a8e52542ebb74c0e262be89ed9710b8e0e211ec Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Mon, 17 Mar 2025 17:06:05 -0400 Subject: [PATCH 028/126] Arrange for Lutris to exit if you run an installation with ``-i`` and it closes. --- lutris/gui/application.py | 10 +++++++++- lutris/gui/installerwindow.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lutris/gui/application.py b/lutris/gui/application.py index 70f020e64f..2d607efe0e 100644 --- a/lutris/gui/application.py +++ b/lutris/gui/application.py @@ -44,7 +44,7 @@ from lutris.gui.dialogs import ErrorDialog, InstallOrPlayDialog, NoticeDialog, display_error from lutris.gui.dialogs.delegates import CommandLineUIDelegate, InstallUIDelegate, LaunchUIDelegate from lutris.gui.dialogs.issue import IssueReportWindow -from lutris.gui.installerwindow import InstallationKind, InstallerWindow +from lutris.gui.installerwindow import INSTALLATION_COMPLETED, INSTALLATION_FAILED, InstallationKind, InstallerWindow from lutris.gui.widgets.status_icon import LutrisStatusIcon from lutris.installer import get_installers from lutris.migrations import migrate @@ -80,6 +80,8 @@ def __init__(self): GAME_START.register(self.on_game_start) GAME_STOPPED.register(self.on_game_stopped) settings.SETTINGS_CHANGED.register(self.on_settings_changed) + INSTALLATION_COMPLETED.register(self.on_install_ended) + INSTALLATION_FAILED.register(self.on_install_ended) GLib.set_application_name(_("Lutris")) GLib.set_prgname("net.lutris.Lutris") @@ -805,6 +807,12 @@ def on_game_stopped(self, game: Game) -> None: if self.quit_on_game_exit or not self.has_tray_icon(): self.quit() + def on_install_ended(self): + if not self.window or not self.window.is_visible(): + if not self.has_running_games: + if self.quit_on_game_exit or not self.has_tray_icon(): + self.quit() + def get_launch_ui_delegate(self): return self.launch_ui_delegate diff --git a/lutris/gui/installerwindow.py b/lutris/gui/installerwindow.py index f77e838979..455336c581 100644 --- a/lutris/gui/installerwindow.py +++ b/lutris/gui/installerwindow.py @@ -21,6 +21,7 @@ from lutris.gui.dialogs.delegates import DialogInstallUIDelegate from lutris.gui.installer.files_box import InstallerFilesBox from lutris.gui.installer.script_picker import InstallerPicker +from lutris.gui.widgets import NotificationSource from lutris.gui.widgets.common import FileChooserEntry from lutris.gui.widgets.log_text_view import LogTextView from lutris.gui.widgets.navigation_stack import NavigationStack @@ -35,6 +36,9 @@ from lutris.util.strings import human_size from lutris.util.system import is_removeable +INSTALLATION_FAILED = NotificationSource() +INSTALLATION_COMPLETED = NotificationSource() + class MarkupLabel(Gtk.Label): """Label for installer window""" @@ -253,6 +257,12 @@ def on_cancel_clicked(self, _button=None): if self.interpreter: self.interpreter.cleanup() # still remove temporary downloads in any case + + if self.interpreter and not self.install_in_progress: + INSTALLATION_COMPLETED.fire() + else: + INSTALLATION_FAILED.fire() + self.destroy() def on_source_clicked(self, _button): From 431d415889bed840635ed05aa74ec3b96372b139 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 18 Mar 2025 17:31:30 -0400 Subject: [PATCH 029/126] Support desktop itegration for the Downloads folder. --- lutris/util/wine/prefix.py | 14 +++++++++++--- lutris/util/xdgshortcuts.py | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lutris/util/wine/prefix.py b/lutris/util/wine/prefix.py index 75a8b1832e..fcd79287a0 100644 --- a/lutris/util/wine/prefix.py +++ b/lutris/util/wine/prefix.py @@ -9,9 +9,17 @@ from lutris.util.wine.registry import WineRegistry from lutris.util.xdgshortcuts import get_xdg_entry -DESKTOP_KEYS = ["Desktop", "Personal", "My Music", "My Videos", "My Pictures"] -DEFAULT_DESKTOP_FOLDERS = ["Desktop", "Documents", "Music", "Videos", "Pictures"] -DESKTOP_XDG = ["DESKTOP", "DOCUMENTS", "MUSIC", "VIDEOS", "PICTURES"] +DESKTOP_KEYS = [ + "Desktop", + "Personal", + "My Music", + "My Videos", + "My Pictures", + "{374DE290-123F-4565-9164-39C4925E467B}", # Downloads + "Templates", +] +DEFAULT_DESKTOP_FOLDERS = ["Desktop", "Documents", "Music", "Videos", "Pictures", "Downloads"] +DESKTOP_XDG = ["DESKTOP", "DOCUMENTS", "MUSIC", "VIDEOS", "PICTURES", "DOWNLOADS"] DEFAULT_DLL_OVERRIDES = { "winemenubuilder": "", } diff --git a/lutris/util/xdgshortcuts.py b/lutris/util/xdgshortcuts.py index 217cd32713..b953b86510 100644 --- a/lutris/util/xdgshortcuts.py +++ b/lutris/util/xdgshortcuts.py @@ -29,6 +29,8 @@ def get_xdg_entry(directory): "PICTURES": GLib.UserDirectory.DIRECTORY_PICTURES, "VIDEOS": GLib.UserDirectory.DIRECTORY_VIDEOS, "DOCUMENTS": GLib.UserDirectory.DIRECTORY_DOCUMENTS, + "DOWNLOADS": GLib.UserDirectory.DIRECTORY_DOWNLOAD, + "TEMPLATES": GLib.UserDirectory.DIRECTORY_TEMPLATES, } directory = directory.upper() if directory not in special_dir: From 24f311431c1dfb46ebfc7f7a0775710dd3f9586a Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 18 Mar 2025 17:32:17 -0400 Subject: [PATCH 030/126] Apply desktop integration rules after prefix creation so as to override what Wine does itself. Resolves #6004 --- lutris/util/wine/prefix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lutris/util/wine/prefix.py b/lutris/util/wine/prefix.py index fcd79287a0..8de4431b55 100644 --- a/lutris/util/wine/prefix.py +++ b/lutris/util/wine/prefix.py @@ -164,7 +164,7 @@ def install_desktop_integration(self): folders in your home directory.""" user_dir = self.user_dir home_dir = os.path.expanduser("~") - current_dir = self._get_desktop_integration_assignment() or user_dir + current_dir = self._get_desktop_integration_assignment() if system.path_exists(user_dir, check_symlinks=True) and current_dir != home_dir: desktop_folders = self.get_desktop_folders() @@ -188,7 +188,7 @@ def install_desktop_integration(self): def remove_desktop_integration(self): """Replace the desktop integration links with proper folders.""" user_dir = self.user_dir - current_dir = self._get_desktop_integration_assignment() or user_dir + current_dir = self._get_desktop_integration_assignment() if system.path_exists(user_dir) and current_dir != user_dir: desktop_folders = self.get_desktop_folders() From 9cbe1c94395129f711c52da81a74ee04c734c073 Mon Sep 17 00:00:00 2001 From: Eikeno Date: Tue, 18 Mar 2025 19:17:39 +0100 Subject: [PATCH 031/126] i18n: allow override system locale with global options locale - Get locale for the user, based on global options, else try system locale eikeno: "The interest is for people like me needed en_US (or anything else) system locale, but who still appreciate being able to have GOG download localized installer files in their native (or preferred) language, when available." - Add some prophylactic code against bogus locale values being entered by the user. Co-authored-by: Daniel Johnson --- lutris/util/i18n.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lutris/util/i18n.py b/lutris/util/i18n.py index 1c1c9217ad..343d4bcd6b 100644 --- a/lutris/util/i18n.py +++ b/lutris/util/i18n.py @@ -2,10 +2,20 @@ import locale +from lutris.config import LutrisConfig from lutris.util.log import logger def get_user_locale(): + """Get locale for the user, based on global options, else try system locale""" + config = LutrisConfig(level="system") + if config.system_config.get("locale"): + try: + user_locale, _user_encoding = config.system_config["locale"].split(".") + return user_locale + except ValueError: # If '.' is not found + return config.system_config["locale"] + user_locale, _user_encoding = locale.getlocale() if not user_locale: logger.error("Unable to get locale") From 968f707938d33aef96fdaa6e14f31d89c21fa13b Mon Sep 17 00:00:00 2001 From: Zebra2711 <89755535+Zebra2711@users.noreply.github.com> Date: Wed, 19 Mar 2025 10:39:54 +0700 Subject: [PATCH 032/126] proton: re-enable dxvk, nvapi, vkd3d MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warning if a non-default version of Proton’s dlls is chosen --- lutris/runners/wine.py | 52 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index bc37b144cf..c95ca3ee86 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -4,7 +4,7 @@ import os import shlex from gettext import gettext as _ -from typing import Any, Dict, Iterable, List, Optional, Set, Tuple +from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Callable from lutris import runtime, settings from lutris.api import format_runner_version, normalize_version_architecture @@ -145,6 +145,47 @@ def _get_dxvk_version_warning(_option_key: str, config: LutrisConfig) -> Optiona return None +def _get_dlls_proton_warning(dlls_type: str) -> Callable[[str, LutrisConfig], Optional[str]]: + """Creates a warning for specific DLL types. + + Args: + dlls_type: Type of DLLs to check ("dxvk", "nvapi", or "vkd3d") + + Returns: + A function that checks for warnings based on the provided parameters + """ + version_key = {"dxvk": "dxvk_version", "nvapi": "dxvk_nvapi_version", "vkd3d": "vkd3d_version"}.get(dlls_type) + if not version_key: + return lambda *_: None + + def check_warnings(_option_key: str, config: LutrisConfig) -> Optional[str]: + try: + if not config or not config.runner_config: + return None + + runner_config = config.runner_config + proton_warning = None + + # Only warnings if runner wine version is Proton + if not _is_pre_proton(_option_key, config) and runner_config.get(version_key) != "manual": + proton_warning = _("Warning Not using default %s DLLs of Proton") % dlls_type + + if dlls_type == "dxvk": + vulkan_warning = _get_dxvk_version_warning(_option_key, config) + return ( + f"{proton_warning}\n{vulkan_warning}" + if proton_warning and vulkan_warning + else proton_warning or vulkan_warning + ) + + return proton_warning + + except Exception: + return None + + return check_warnings + + def _get_esync_warning(_option_key: str, config: LutrisConfig) -> Optional[str]: if config.runner_config.get("esync"): limits_set = is_esync_limit_set() @@ -310,19 +351,17 @@ class wine(Runner): "label": _("DXVK version"), "advanced": True, "type": "choice_with_entry", - "visible": _is_pre_proton, "condition": LINUX_SYSTEM.is_vulkan_supported(), "conditional_on": "dxvk", "choices": lambda: DXVKManager().version_choices, "default": lambda: DXVKManager().version, - "warning": _get_dxvk_version_warning, + "warning": _get_dlls_proton_warning("dxvk"), }, { "option": "vkd3d", "section": _("Graphics"), "label": _("Enable VKD3D"), "type": "bool", - "visible": _is_pre_proton, "error": lambda k, c: _get_simple_vulkan_support_error(k, c, _("VKD3D")), "default": True, "active": True, @@ -336,11 +375,11 @@ class wine(Runner): "label": _("VKD3D version"), "advanced": True, "type": "choice_with_entry", - "visible": _is_pre_proton, "condition": LINUX_SYSTEM.is_vulkan_supported(), "conditional_on": "vkd3d", "choices": lambda: VKD3DManager().version_choices, "default": lambda: VKD3DManager().version, + "warning": _get_dlls_proton_warning("vkd3d"), }, { "option": "d3d_extras", @@ -374,7 +413,6 @@ class wine(Runner): "error": lambda k, c: _get_simple_vulkan_support_error(k, c, _("DXVK-NVAPI / DLSS")), "default": True, "advanced": True, - "visible": _is_pre_proton, "help": _("Enable emulation of Nvidia's NVAPI and add DLSS support, if available."), }, { @@ -383,10 +421,10 @@ class wine(Runner): "label": _("DXVK NVAPI version"), "advanced": True, "conditional_on": "dxvk_nvapi", - "visible": _is_pre_proton, "type": "choice_with_entry", "choices": lambda: DXVKNVAPIManager().version_choices, "default": lambda: DXVKNVAPIManager().version, + "warning": _get_dlls_proton_warning("nvapi"), }, { "option": "dgvoodoo2", From dcf8ec49d6c81418f80e47475dda7c8c21bc9b82 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Wed, 19 Mar 2025 16:55:56 +0700 Subject: [PATCH 033/126] gui: add explorer in wine actions --- lutris/runners/wine.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index c95ca3ee86..e08b357b31 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -679,6 +679,7 @@ def context_menu_entries(self): ("wine-regedit", _("Wine registry"), self.run_regedit), ("winecpl", _("Wine Control Panel"), self.run_winecpl), ("winetaskmgr", _("Wine Task Manager"), self.run_taskmgr), + ("wineexplorer", _("Wine Explorer"), self.run_explorer), (None, "-", None), ("winetricks", _("Winetricks"), self.run_winetricks), ] @@ -957,6 +958,11 @@ def run_wineconsole(self, *args): self.prelaunch() self._run_executable("wineconsole") + def run_explorer(self, *args): + """Runs wine explorer inside wine prefix.""" + self.prelaunch() + self._run_executable("explorer") + def run_winecfg(self, *args): """Run winecfg in the current context""" self.prelaunch() From a8dd7718ab5bafca18522acab4b2606adfd82aa8 Mon Sep 17 00:00:00 2001 From: Eikeno Date: Thu, 20 Mar 2025 02:18:08 +0100 Subject: [PATCH 034/126] retroarch: fix bios detection and remove hardcoded RetroArch paths There are references to ~/.config/retroarch but when the runner is installed, it is created at ~/.local/share/lutris/runners/retroarch The references cause correct BIOS files under ~/.local/share/lutris/runners/retroarch/system to be skipped. Due to recent changes, missing firmware won't cause crash, but this still prevent expected behavior (I presume) to avoid interference between Lutris' RA runner setup and a user level setup,and discovery of bios files placed in the runner directory. before change, with ~/.local/share/lutris/runners/retroarch/system populated (files manually added in system/dc/): [libretro.prelaunch:269]:Firmware 'dc/dc_boot.bin' not found! after change, system/ similarly populated: [libretro.prelaunch:268]:Firmware 'dc/awbios.zip' found (No checksum info) Now BIOS files are detected, if present. remove references to ~/.config/retroarch + bios checksum check fix Also modified bios file checksum as it didn't work when an additional intermediary directory is involved (i.e: system/dc/ and a few others). fixes #5977 --- lutris/runners/libretro.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lutris/runners/libretro.py b/lutris/runners/libretro.py index e09ac3a3d2..7014d10e05 100644 --- a/lutris/runners/libretro.py +++ b/lutris/runners/libretro.py @@ -123,14 +123,11 @@ def get_platform(self): return "" def get_core_path(self, core): - """Return the path of a core, prioritizing Retroarch cores""" - lutris_cores_folder = os.path.join(self.directory, "cores") - retroarch_core_folder = os.path.join(os.path.expanduser("~/.config/retroarch/cores")) + """Return the path of a core from libretro's runner only""" + lutris_cores_folder = get_default_config_path("cores") core_filename = "{}_libretro.so".format(core) - retroarch_core = os.path.join(retroarch_core_folder, core_filename) - if system.path_exists(retroarch_core): - return retroarch_core - return os.path.join(lutris_cores_folder, core_filename) + lutris_core = os.path.join(lutris_cores_folder, core_filename) + return lutris_core def get_version(self, use_default=True): return self.game_config["core"] @@ -182,7 +179,7 @@ def get_system_directory(retro_config): """Return the system directory used for storing BIOS and firmwares.""" system_directory = retro_config["system_directory"] if not system_directory or system_directory == "default": - system_directory = "~/.config/retroarch/system" + system_directory = get_default_config_path("system") return os.path.expanduser(system_directory) def prelaunch(self): @@ -212,7 +209,7 @@ def prelaunch(self): retro_config["rgui_config_directory"] = get_default_config_path("config") retro_config["overlay_directory"] = get_default_config_path("overlay") retro_config["assets_directory"] = get_default_config_path("assets") - retro_config["system_directory"] = "~/.config/retroarch/system" + retro_config["system_directory"] = get_default_config_path("system") retro_config.save() else: retro_config = RetroConfig(config_file) @@ -254,7 +251,7 @@ def prelaunch(self): required_firmware_filename = retro_config["firmware%d_path" % index] required_firmware_path = os.path.join(system_path, required_firmware_filename) required_firmware_name = required_firmware_filename.split("/")[-1] - required_firmware_checksum = checksums.get(required_firmware_filename) + required_firmware_checksum = checksums.get(required_firmware_name) if system.path_exists(required_firmware_path): if required_firmware_checksum: checksum = system.get_md5_hash(required_firmware_path) From 124ca9367a5d976ef26dcf9cd41be1a1d5146741 Mon Sep 17 00:00:00 2001 From: Eikeno Date: Sat, 22 Mar 2025 18:30:29 +0100 Subject: [PATCH 035/126] modify refs from ~/.config/lutris to ~/.local/share/lutris Also make utils/clean_config use CONFIG_DIR from settings.py instead of hardcoded ref. 1 typo fix --- README.rst | 13 ++++--------- docs/installers.rst | 2 +- tests/README | 7 +++---- utils/clean_configs.py | 3 +-- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index e1f7d2db11..3a046fd799 100644 --- a/README.rst +++ b/README.rst @@ -51,7 +51,7 @@ This token is stored in ``~/.cache/lutris/auth-token``. Configuration files =================== -* ``~/.config/lutris``: The client, runners, and game configuration files +* ``~/.local/share/lutris``: The client, runners, and game configuration files There is no need to manually edit these files as everything should be done from the client. @@ -63,14 +63,6 @@ Configuration files * ``games/*.yml``: Game-specific configurations -Game-specific configurations overwrite runner-specific configurations, which in -turn overwrite the system configuration. - -Runners and the game database -============================= - -``~/.local/share/lutris``: All data necessary to manage Lutris' library and games, including: - * ``pga.db``: An SQLite database tracking the game library, game installation status, various file locations, and some additional metadata * ``runners/*``: Runners downloaded from `lutris.net `_ @@ -79,6 +71,9 @@ Runners and the game database ``~/.local/share/icons/hicolor/128x128/apps/lutris_*.png``: Game icons +Game-specific configurations overwrite runner-specific configurations, which in +turn overwrite the system configuration. + Command line options ==================== diff --git a/docs/installers.rst b/docs/installers.rst index 4b9aa7fb60..013ef80f27 100644 --- a/docs/installers.rst +++ b/docs/installers.rst @@ -40,7 +40,7 @@ installer has finished. The configuration for a game is constructed from its installer. The `files` and `installer` sections are removed from the script, some variables such as $GAMEDIR are substituted and the results is saved in: -~/.config/lutris/games/-.yml. +~/.local/share/lutris/games/-.yml. Published installers can be accessed from a command line by using the ``lutris:`` URL prefix followed by the installer slug. diff --git a/tests/README b/tests/README index 27cf6f4d24..80fb0be9a1 100644 --- a/tests/README +++ b/tests/README @@ -12,7 +12,7 @@ The tests should be run on any of these systems (32 or 64bit): - Fedora current - Debian testing or sid (stable isn't supported officially) - Archlinux -Any other distrution is good but is not a requirement. +Any other distribution is good but is not a requirement. 0. Take note of your testing config @@ -27,7 +27,6 @@ Write down the following: * Move lutris settings. -mv ~/.config/lutris ~/.config/lutris.bak mv ~/.cache/lutris ~/.cache/lutris.bak mv ~/.local/share/lutris ~/.local/share/lutris.bak @@ -60,8 +59,8 @@ Scenario 1: Launching Lutris - You can find a lutris icon in your menu/launcher (Unity dash, GS launcher, XFCE main menu, ...) in the games category - When clicking the interface loads with no complains -- After the UI has been opened, there should be ~/.config/lutris, - ~/.cache/lutris and ~/.local/share/lutris directories +- After the UI has been opened, there should be ~/.cache/lutris + and ~/.local/share/lutris directories Scenario 2: Installing teeworlds from the website (64bit only ATM) - Using Chrome or Chromium open http://lutris.net/games/teeworlds/ diff --git a/utils/clean_configs.py b/utils/clean_configs.py index 09775589a3..c2bdba18ac 100644 --- a/utils/clean_configs.py +++ b/utils/clean_configs.py @@ -1,8 +1,7 @@ import os from lutris.database.games import get_games - -CONFIG_DIR = os.path.expanduser("~/.config/lutris/games") +from lutris.settings import CONFIG_DIR config_paths = set() for dbgame in get_games(): From a057b011c1c5da05ef12e62a3c69d532a7e8bef8 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 4 Apr 2025 17:36:46 +0700 Subject: [PATCH 036/126] use icoextract intead of extract-icon, which is outdated --- lutris/runners/wine.py | 30 +-- lutris/util/wine/extract_icon.py | 324 ++++++++++++++++++------------- 2 files changed, 194 insertions(+), 160 deletions(-) diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index e08b357b31..bb749c6fd1 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -46,7 +46,7 @@ from lutris.util.wine.dgvoodoo2 import dgvoodoo2Manager from lutris.util.wine.dxvk import REQUIRED_VULKAN_API_VERSION, DXVKManager from lutris.util.wine.dxvk_nvapi import DXVKNVAPIManager -from lutris.util.wine.extract_icon import PEFILE_AVAILABLE, ExtractIcon +from lutris.util.wine.extract_icon import PEFILE_AVAILABLE, IconExtractor from lutris.util.wine.prefix import DEFAULT_DLL_OVERRIDES, WinePrefixManager, find_prefix from lutris.util.wine.vkd3d import VKD3DManager from lutris.util.wine.wine import ( @@ -1374,29 +1374,15 @@ def extract_icon(self, game_slug): if not exe or os.path.exists(pathtoicon) or not PEFILE_AVAILABLE: return False - extractor = ExtractIcon(self.game_exe) - groups = extractor.get_group_icons() + extractor = IconExtractor(exe) - if not groups: - return False + # Get first icon by default + icon = extractor.get_icon() - icons = [] - biggestsize = (0, 0) - biggesticon = -1 - for i in range(len(groups[0])): - icons.append(extractor.export(groups[0], i)) - if icons[i].size > biggestsize: - biggesticon = i - biggestsize = icons[i].size - elif icons[i].size == wantedsize: - icons[i].save(pathtoicon) - return True - - if biggesticon >= 0: - resized = icons[biggesticon].resize(wantedsize) - resized.save(pathtoicon) - return True - return False + if not icon.size == wantedsize: + icon = icon.resize(wantedsize) + icon.save(pathtoicon) + return True except Exception as ex: logger.exception("Unable to extract icon from %s: %s", exe, ex) return False diff --git a/lutris/util/wine/extract_icon.py b/lutris/util/wine/extract_icon.py index c74754912a..5d185108d1 100644 --- a/lutris/util/wine/extract_icon.py +++ b/lutris/util/wine/extract_icon.py @@ -1,6 +1,33 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-2016 Fadhil Mandaga +Copyright (c) 2019 James Lu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + # pylint: disable=no-member import struct from io import BytesIO +import logging +import sys try: import pefile @@ -12,143 +39,164 @@ from PIL import Image -# From https://github.com/firodj/extract-icon-py +GRPICONDIRENTRY_FORMAT = ( + "GRPICONDIRENTRY", + ("B,Width", "B,Height", "B,ColorCount", "B,Reserved", "H,Planes", "H,BitCount", "I,BytesInRes", "H,ID"), +) +GRPICONDIR_FORMAT = ("GRPICONDIR", ("H,Reserved", "H,Type", "H,Count")) + +logger = logging.getLogger("icoextract") +logging.basicConfig() -class ExtractIcon(object): - GRPICONDIRENTRY_format = ( - "GRPICONDIRENTRY", - ("B,Width", "B,Height", "B,ColorCount", "B,Reserved", "H,Planes", "H,BitCount", "I,BytesInRes", "H,ID"), - ) - GRPICONDIR_format = ("GRPICONDIR", ("H,Reserved", "H,Type", "H,Count")) - RES_ICON = 1 - RES_CURSOR = 2 - - def __init__(self, filepath): - self.pe = pefile.PE(filepath) - - def find_resource_base(self, res_type): - if hasattr(self.pe, "DIRECTORY_ENTRY_RESOURCE"): - try: - rt_base_idx = [entry.id for entry in self.pe.DIRECTORY_ENTRY_RESOURCE.entries].index( - pefile.RESOURCE_TYPE[res_type] - ) - - if rt_base_idx is not None: - return self.pe.DIRECTORY_ENTRY_RESOURCE.entries[rt_base_idx] - except (ValueError, IndexError): - pass # if the resource is not found or the index is bogus - - return None - - def find_resource(self, res_type, res_index): - rt_base_dir = self.find_resource_base(res_type) - - if not rt_base_dir: - return None - - if res_index < 0: - try: - idx = [entry.id for entry in rt_base_dir.directory.entries].index(-res_index) - except: - return None - else: - idx = res_index if res_index < len(rt_base_dir.directory.entries) else None - - if idx is None: - return None - - test_res_dir = rt_base_dir.directory.entries[idx] - res_dir = test_res_dir - if test_res_dir.struct.DataIsDirectory: - # another Directory - # probably language take the first one - res_dir = test_res_dir.directory.entries[0] - if res_dir.struct.DataIsDirectory: - # Ooooooooooiconoo no !! another Directory !!! - return None - - return res_dir - - def get_group_icons(self): - rt_base_dir = self.find_resource_base("RT_GROUP_ICON") - - if not rt_base_dir: - return [] - - groups = [] - for res_index in range(0, len(rt_base_dir.directory.entries)): - grp_icon_dir_entry = self.find_resource("RT_GROUP_ICON", res_index) - - if not grp_icon_dir_entry: - continue - - data_rva = grp_icon_dir_entry.data.struct.OffsetToData - size = grp_icon_dir_entry.data.struct.Size - data = self.pe.get_memory_mapped_image()[data_rva : data_rva + size] - file_offset = self.pe.get_offset_from_rva(data_rva) - - grp_icon_dir = pefile.Structure(self.GRPICONDIR_format, file_offset=file_offset) - grp_icon_dir.__unpack__(data) - - if grp_icon_dir.Reserved != 0 or grp_icon_dir.Type != self.RES_ICON: - continue - offset = grp_icon_dir.sizeof() - - entries = [] - for _idx in range(0, grp_icon_dir.Count): - grp_icon = pefile.Structure(self.GRPICONDIRENTRY_format, file_offset=file_offset + offset) - grp_icon.__unpack__(data[offset:]) - offset += grp_icon.sizeof() - entries.append(grp_icon) - - groups.append(entries) - return groups - - def get_icon(self, index): - icon_entry = self.find_resource("RT_ICON", -index) - if not icon_entry: - return None - - data_rva = icon_entry.data.struct.OffsetToData - size = icon_entry.data.struct.Size - data = self.pe.get_memory_mapped_image()[data_rva : data_rva + size] - - return data - - def export_raw(self, entries, index=None): - if index is not None: - entries = entries[index : index + 1] - - ico = struct.pack(" Date: Tue, 1 Apr 2025 13:15:35 +0700 Subject: [PATCH 037/126] gamemode: avoid executing gamemoderun when gamemode is not installed from lutris 0.5.8 * The old versions of gamemode are no longer supported. Make sure you have the one that ships with a `gamemoderun` executable. --- lutris/util/linux.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lutris/util/linux.py b/lutris/util/linux.py index 44490b4748..02f29c743e 100644 --- a/lutris/util/linux.py +++ b/lutris/util/linux.py @@ -208,12 +208,11 @@ def get_kernel_version(): def gamemode_available(self): """Return whether gamemode is available""" - # Current versions of gamemode use gamemoderun + if missing_arch := self.get_missing_lib_arch("GAMEMODE"): + logger.warning("Missing libgamemode arch: %s", missing_arch) + if system.can_find_executable("gamemoderun"): return True - # This is for old versions of gamemode only - if self.is_feature_supported("GAMEMODE"): - return True return False def nvidia_gamescope_support(self): From d595ffa88b55140bababec72d623d5fcd3c97cdf Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 5 Apr 2025 06:38:48 +0700 Subject: [PATCH 038/126] don't save env have empty key fixes #6032 --- lutris/config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lutris/config.py b/lutris/config.py index 138393ef98..d2cffaf522 100644 --- a/lutris/config.py +++ b/lutris/config.py @@ -187,6 +187,9 @@ def merge_to_system_config(self, config): self.system_config["env"] = existing_env self.system_config["env"].update(config["env"]) + # Don't save env have key is empty + self.system_config["env"] = {k: v for k, v in self.system_config["env"].items() if k} + def update_raw_config(self): # Select the right level of config if self.level == "game": From 1d1b465f93f509eb8789a156453a1a1664f7da1d Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Sat, 5 Apr 2025 12:08:07 -0400 Subject: [PATCH 039/126] Add a guard just in case the system_config has no "env' --- lutris/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lutris/config.py b/lutris/config.py index d2cffaf522..f48d660a04 100644 --- a/lutris/config.py +++ b/lutris/config.py @@ -187,8 +187,9 @@ def merge_to_system_config(self, config): self.system_config["env"] = existing_env self.system_config["env"].update(config["env"]) - # Don't save env have key is empty - self.system_config["env"] = {k: v for k, v in self.system_config["env"].items() if k} + # Don't save env items where the key is empty; this would crash when used. + if "env" in self.system_config: + self.system_config["env"] = {k: v for k, v in self.system_config["env"].items() if k} def update_raw_config(self): # Select the right level of config From 13b0f23043f02c6e55704d7d773a600e19d8cfb8 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Sat, 5 Apr 2025 12:10:35 -0400 Subject: [PATCH 040/126] Run the 'env' cleanup code even when an empty config is being merged. --- lutris/config.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lutris/config.py b/lutris/config.py index f48d660a04..d69d94633a 100644 --- a/lutris/config.py +++ b/lutris/config.py @@ -177,15 +177,14 @@ def update_cascaded_config(self): def merge_to_system_config(self, config): """Merge a configuration to the system configuration""" - if not config: - return - existing_env = None - if self.system_config.get("env") and "env" in config: - existing_env = self.system_config["env"] - self.system_config.update(config) - if existing_env: - self.system_config["env"] = existing_env - self.system_config["env"].update(config["env"]) + if config: + existing_env = None + if self.system_config.get("env") and "env" in config: + existing_env = self.system_config["env"] + self.system_config.update(config) + if existing_env: + self.system_config["env"] = existing_env + self.system_config["env"].update(config["env"]) # Don't save env items where the key is empty; this would crash when used. if "env" in self.system_config: From 378227ff4810e93f92e7c31fa9a21ab29b847e13 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 8 Apr 2025 17:08:44 -0400 Subject: [PATCH 041/126] Include explicitly set env-vars from the runner or global config when initializing a prefix. This can be needed to get a Wine (or Proton) build to work, even so far as creating a prefix. Or so I'm told. Resolves #6040 --- lutris/runners/commands/wine.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lutris/runners/commands/wine.py b/lutris/runners/commands/wine.py index 97957414fb..5b66ed587b 100644 --- a/lutris/runners/commands/wine.py +++ b/lutris/runners/commands/wine.py @@ -125,13 +125,20 @@ def create_prefix( logger.info("Winepath: %s", wine_path) - wineenv = { - "WINEARCH": arch, - "WINEPREFIX": prefix, - "WINEDLLOVERRIDES": get_overrides_env(overrides), - "WINE_MONO_CACHE_DIR": os.path.join(os.path.dirname(os.path.dirname(wine_path)), "mono"), - "WINE_GECKO_CACHE_DIR": os.path.join(os.path.dirname(os.path.dirname(wine_path)), "gecko"), - } + if runner: + wineenv = runner.system_config.get("env") or {} + else: + wineenv = {} + + wineenv.update( + { + "WINEARCH": arch, + "WINEPREFIX": prefix, + "WINEDLLOVERRIDES": get_overrides_env(overrides), + "WINE_MONO_CACHE_DIR": os.path.join(os.path.dirname(os.path.dirname(wine_path)), "mono"), + "WINE_GECKO_CACHE_DIR": os.path.join(os.path.dirname(os.path.dirname(wine_path)), "gecko"), + } + ) if install_gecko == "False": wineenv["WINE_SKIP_GECKO_INSTALLATION"] = "1" From 23d527338869e3fc602a81fdf85601293aa9c001 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Wed, 2 Apr 2025 21:52:30 +0700 Subject: [PATCH 042/126] prefix: add more check to reduce hang * when mount via FUSE need check actualy what fs_type of fuseblk * Wine not allow to create prefix that not owned by user --- lutris/runners/commands/wine.py | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/lutris/runners/commands/wine.py b/lutris/runners/commands/wine.py index 5b66ed587b..a1d153e8eb 100644 --- a/lutris/runners/commands/wine.py +++ b/lutris/runners/commands/wine.py @@ -94,6 +94,28 @@ def delete_registry_key(key, wine_path=None, prefix=None, arch=WINE_DEFAULT_ARCH ) +def is_disallowed_fs(prefix): + """ + Check prefix is create in file system that not support to create linux symlink + + Returns: + bool: True if the prefix is on a disallowed filesystem or if the filesystem + type cannot be determined, False otherwise. + """ + + # Need add more if needed + disallowed_fs_types = {"exfat", "fat", "vfat", "msdos", "umsdos", "ncpfs", "iso9660"} + + if not os.path.exists(prefix): + prefix = os.path.dirname(prefix) + + fs_type = linux.LinuxSystem().get_fs_type_for_path(prefix) + if fs_type is None: + return True + logger.info("Creating a prefix in file system type: %s", fs_type) + return fs_type in disallowed_fs_types + + def create_prefix( prefix, wine_path=None, arch=WINE_DEFAULT_ARCH, overrides=None, install_gecko=None, install_mono=None, runner=None ): @@ -118,6 +140,26 @@ def create_prefix( except OSError: logger.error("Failed to delete %s, you may lack permissions on this folder.", prefix) + # Wine does not allow creating a prefix in a parent directory that does not + # exist. For example, if the prefix is /mnt/a/b/c/d but the parent directory + # /mnt/a/b/c does not exist, it is not allowed. + if not os.path.exists(os.path.dirname(prefix)): + raise Exception("Can't create prefix: Not found directory %s" % os.path.dirname(prefix)) + + if not os.path.exists(prefix): + _stat = os.lstat(os.path.dirname(prefix)) + else: + _stat = os.lstat(prefix) + # Wine not allow to create prefix that not owned by user + if _stat.st_uid != os.getuid() or _stat.st_mode & 0o700 != 0o700: + raise Exception( + "%s must be owned by you with full owner permissions, refusing to create a configuration directory there" + % prefix + ) + + if is_disallowed_fs(prefix): + raise Exception("Can't create prefix on file system that not support linux symlink") + if not wine_path: if not runner: runner = import_runner("wine")() From 267d84f5006499e193a53db425d2c6afb3ff57bb Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Tue, 8 Apr 2025 15:32:46 +0700 Subject: [PATCH 043/126] add commit hash and message in log info --- lutris/gui/application.py | 3 ++- meson.build | 2 ++ version.in | 7 +++++++ version.sh | 27 +++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 version.in create mode 100755 version.sh diff --git a/lutris/gui/application.py b/lutris/gui/application.py index 2d607efe0e..ee07aaeb14 100644 --- a/lutris/gui/application.py +++ b/lutris/gui/application.py @@ -35,6 +35,7 @@ from gi.repository import Gio, GLib, Gtk +from lutris.version import VERSION, HASH, MESSAGE from lutris import settings from lutris.api import get_runners, parse_installer_url from lutris.database import games as games_db @@ -477,7 +478,7 @@ def do_command_line(self, command_line): # noqa: C901 # pylint: disable=argume if options.contains("force"): self.force_updates = True - logger.info("Starting Lutris %s", settings.VERSION) + logger.info("Starting Lutris %s, Commit: %s ( %s )", VERSION, HASH, MESSAGE) init_lutris() migrate() diff --git a/meson.build b/meson.build index 42d2124177..bd16e2359a 100644 --- a/meson.build +++ b/meson.build @@ -4,6 +4,8 @@ project( meson_version: '>=0.46.0', ) +run_command('sh', 'version.sh') + # Find Python installation python = import('python').find_installation() diff --git a/version.in b/version.in new file mode 100644 index 0000000000..8abc7db980 --- /dev/null +++ b/version.in @@ -0,0 +1,7 @@ +HASH = "@HASH@" +BRANCH = "@BRANCH@" +MESSAGE = "@MESSAGE@" +DATE = "@DATE@" +TAG = "@TAG@" +COMMITS = "@COMMITS@" +VERSION = "@VERSION@" diff --git a/version.sh b/version.sh new file mode 100755 index 0000000000..8e3ab815ea --- /dev/null +++ b/version.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# if the git directory doesn't exist, don't gather data to avoid overwriting, unless +# the version file is missing altogether (otherwise compiling will fail) +if [ ! -d ./.git ]; then + if [ -f ./lutris/version.py ]; then + exit 0 + fi +fi + +cp -fr ./version.in ./lutris/version.py + +HASH=${HASH-$(git rev-parse HEAD)} +BRANCH=${BRANCH-$(git branch --show-current)} +MESSAGE=${MESSAGE-$(git show -s --format=%s)} +DATE=${DATE-$(git show -s --format=%cd --date=local)} +TAG=${TAG-$(git describe --tags)} +COMMITS=${COMMITS-$(git rev-list --count HEAD)} +VERSION=${VERSION-$(git describe --tags | cut -d'-' -f1)} + +sed -i -e "s#@HASH@#${HASH}#" ./lutris/version.py +sed -i -e "s#@BRANCH@#${BRANCH}#" ./lutris/version.py +sed -i -e "s#@MESSAGE@#${MESSAGE}#" ./lutris/version.py +sed -i -e "s#@DATE@#${DATE}#" ./lutris/version.py +sed -i -e "s#@TAG@#${TAG}#" ./lutris/version.py +sed -i -e "s#@COMMITS@#${COMMITS}#" ./lutris/version.py +sed -i -e "s#@VERSION@#${VERSION}#" ./lutris/version.py From 382140fc9d032d7c9aaceba46a097bd69e237fda Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 8 Apr 2025 18:42:06 -0400 Subject: [PATCH 044/126] We need the runner all the time now; construct it if it's not given, and initialize it more fully as well. --- lutris/runners/commands/wine.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lutris/runners/commands/wine.py b/lutris/runners/commands/wine.py index a1d153e8eb..b511c756a5 100644 --- a/lutris/runners/commands/wine.py +++ b/lutris/runners/commands/wine.py @@ -160,18 +160,15 @@ def create_prefix( if is_disallowed_fs(prefix): raise Exception("Can't create prefix on file system that not support linux symlink") + if not runner: + runner = import_runner("wine")(prefix=prefix, wine_arch=arch) + if not wine_path: - if not runner: - runner = import_runner("wine")() wine_path = runner.get_executable() logger.info("Winepath: %s", wine_path) - if runner: - wineenv = runner.system_config.get("env") or {} - else: - wineenv = {} - + wineenv = runner.system_config.get("env") or {} wineenv.update( { "WINEARCH": arch, From 9bc868cc6fb6cb80ee76aaa1b19088afc3629a50 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Fri, 11 Apr 2025 19:03:38 -0400 Subject: [PATCH 045/126] Add a spinner page if you try to start the installer window when Lutris is downloading components, to suggest you should wait a bit. You can override this with the continue button, but maybe this will give eager gamers pause. This is meant to help with #6034 and others like that. --- lutris/gui/download_queue.py | 7 ++++++- lutris/gui/installerwindow.py | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lutris/gui/download_queue.py b/lutris/gui/download_queue.py index f0e53eea02..da855c4344 100644 --- a/lutris/gui/download_queue.py +++ b/lutris/gui/download_queue.py @@ -2,7 +2,7 @@ import os from typing import Any, Callable, Dict, Iterable, List, Set -from gi.repository import Gtk +from gi.repository import GObject, Gtk from lutris.gui.widgets.gi_composites import GtkTemplate from lutris.gui.widgets.progress_box import ProgressBox @@ -16,6 +16,10 @@ class DownloadQueue(Gtk.ScrolledWindow): """This class is a widget that displays a stack of progress boxes, which you can create and destroy with its methods.""" + __gsignals__ = { + "download-completed": (GObject.SignalFlags.RUN_LAST, None, ()), + } + __gtype_name__ = "DownloadQueue" download_box: Gtk.Box = GtkTemplate.Child() @@ -168,6 +172,7 @@ def completion_callback(result, error): error_function(error) elif completion_function: completion_function(result) + self.emit("download-completed") AsyncCall(operation, completion_callback) return True diff --git a/lutris/gui/installerwindow.py b/lutris/gui/installerwindow.py index 455336c581..5c1583922a 100644 --- a/lutris/gui/installerwindow.py +++ b/lutris/gui/installerwindow.py @@ -158,7 +158,24 @@ def __init__(self, installers, service=None, appid=None, installation_kind=Insta self.error_details_buffer = Gtk.TextBuffer() self.error_reporter = self.load_error_page - self.load_choose_installer_page() + application = Gio.Application.get_default() + if application and application.window and not application.window.download_queue.is_empty: + download_queue = application.window.download_queue + + def on_start_installation(*args): + self.load_choose_installer_page() + download_queue.disconnect(dc_handler) + + self.load_spinner_page("Waiting for Lutris component installation") + self.display_continue_button(on_start_installation) + + def on_download_complete(*args): + if download_queue.is_empty: + on_start_installation() + + dc_handler = download_queue.connect("download-completed", on_download_complete) + else: + self.load_choose_installer_page() # And... go! self.show_all() From 834d2d090279de77a3b8fb80269b8c7ed6559bc3 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Sat, 12 Apr 2025 05:57:23 -0400 Subject: [PATCH 046/126] Add more explanatory text for the wait-for-components spinner. Also support markup for this, so we can make the text smaller, and center justify the text. --- lutris/gui/installerwindow.py | 67 ++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/lutris/gui/installerwindow.py b/lutris/gui/installerwindow.py index 5c1583922a..6217fa6a82 100644 --- a/lutris/gui/installerwindow.py +++ b/lutris/gui/installerwindow.py @@ -4,6 +4,7 @@ import os import traceback from gettext import gettext as _ +from typing import List from gi.repository import Gdk, Gio, GLib, Gtk @@ -44,7 +45,7 @@ class MarkupLabel(Gtk.Label): """Label for installer window""" def __init__(self, markup=None, **kwargs): - super().__init__(label=markup, use_markup=True, wrap=True, max_width_chars=80, **kwargs) + super().__init__(label=markup, use_markup=True, wrap=True, justify=Gtk.Justification.CENTER, **kwargs) self.set_alignment(0.5, 0) @@ -158,26 +159,8 @@ def __init__(self, installers, service=None, appid=None, installation_kind=Insta self.error_details_buffer = Gtk.TextBuffer() self.error_reporter = self.load_error_page - application = Gio.Application.get_default() - if application and application.window and not application.window.download_queue.is_empty: - download_queue = application.window.download_queue - - def on_start_installation(*args): - self.load_choose_installer_page() - download_queue.disconnect(dc_handler) - - self.load_spinner_page("Waiting for Lutris component installation") - self.display_continue_button(on_start_installation) - - def on_download_complete(*args): - if download_queue.is_empty: - on_start_installation() - - dc_handler = download_queue.connect("download-completed", on_download_complete) - else: - self.load_choose_installer_page() - # And... go! + self.load_first_page() self.show_all() self.present() @@ -298,10 +281,10 @@ def _handle_callback_error(self, error): display_error(error, parent=self) self.stack.navigation_reset() - def set_status(self, text): + def set_status(self, markup): """Display a short status text.""" - self.status_label.set_text(text) - self.status_label.set_visible(bool(text)) + self.status_label.set_markup(markup) + self.status_label.set_visible(bool(markup)) def get_status(self): return self.status_label.get_text() if self.status_label.get_visible() else "" @@ -316,6 +299,34 @@ def register_page_creators(self): self.stack.add_named_factory("error", self.create_error_page) self.stack.add_named_factory("nothing", lambda *x: Gtk.Box()) + def load_first_page(self) -> None: + # If we're downloading updates in the background, we'll + # put up a spinner page to wait until that's done. Installations can + # fail if Lutris components are missing, and users sometimes try to install + # a game just after their first Lutris startup. This should help. + application = Gio.Application.get_default() + if application and application.window and not application.window.download_queue.is_empty: + download_queue = application.window.download_queue + + def on_start_installation(*args): + self.load_choose_installer_page() + download_queue.disconnect(dc_handler) + + def on_download_complete(*args): + if download_queue.is_empty: + on_start_installation() + + dc_handler = download_queue.connect("download-completed", on_download_complete) + self.load_spinner_page( + _( + "Waiting for Lutris component installation\n" + "Installations can fail if Lutris components are not installed first." + ) + ) + self.display_continue_button(on_start_installation) + else: + self.load_choose_installer_page() + # Interpreter UI Delegate # # These methods are called from the ScriptInterpreter, and defer work until idle time @@ -332,10 +343,10 @@ def attach_log(self, command): command.set_log_buffer(self.log_buffer) GLib.idle_add(self.load_log_page) - def begin_disc_prompt(self, message, requires, installer, callback): + def begin_disc_prompt(self, message_markup, requires, installer, callback): GLib.idle_add( self.load_ask_for_disc_page, - message, + message_markup, requires, installer, callback, @@ -709,7 +720,7 @@ def launch_installer_commands(self): # Provides a generic progress spinner and displays a status. The back button # is disabled for this page. - def load_spinner_page(self, status, cancellable=True, extra_buttons=None): + def load_spinner_page(self, status: str, cancellable: bool = True, extra_buttons: List[Gtk.Button] = None) -> None: def present_spinner_page(): """Show a spinner in the middle of the view""" @@ -815,7 +826,7 @@ def on_input_menu_changed(self, combobox): # This page asks the user for a disc; it also has a callback used when # the user selects a disc. Again, this is summoned by the installer script. - def load_ask_for_disc_page(self, message, requires, installer, callback): + def load_ask_for_disc_page(self, message_markup, requires, installer, callback): def present_ask_for_disc_page(): """Ask the user to do insert a CD-ROM.""" @@ -829,7 +840,7 @@ def wrapped_callback(*args, **kwargs): self.load_error_page(err) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) - label = MarkupLabel(message) + label = MarkupLabel(message_markup) vbox.pack_start(label, False, False, 0) buttons_box = Gtk.Box() From 57790f369cc5634368297299a9b55cd5b7c0402b Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Sat, 12 Apr 2025 05:59:46 -0400 Subject: [PATCH 047/126] Escape script-provided messages and errors. These haven't been treated as markup before, so we shouldn't just suddenly start doing so. --- lutris/gui/installerwindow.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lutris/gui/installerwindow.py b/lutris/gui/installerwindow.py index 6217fa6a82..6ca72676ae 100644 --- a/lutris/gui/installerwindow.py +++ b/lutris/gui/installerwindow.py @@ -34,7 +34,7 @@ from lutris.util.linux import LINUX_SYSTEM from lutris.util.log import get_log_contents, logger from lutris.util.steam import shortcut as steam_shortcut -from lutris.util.strings import human_size +from lutris.util.strings import gtk_safe, human_size from lutris.util.system import is_removeable INSTALLATION_FAILED = NotificationSource() @@ -336,7 +336,7 @@ def report_error(self, error): GLib.idle_add(self.error_reporter, error) def report_status(self, status): - GLib.idle_add(self.set_status, status) + GLib.idle_add(self.set_status, gtk_safe(status)) def attach_log(self, command): # Hook the log buffer right now, lest we miss updates. @@ -356,7 +356,7 @@ def begin_input_menu(self, alias, options, preselect, callback): GLib.idle_add(self.load_input_menu_page, alias, options, preselect, callback) def report_finished(self, game_id, status): - GLib.idle_add(self.load_finish_install_page, game_id, status) + GLib.idle_add(self.load_finish_install_page, game_id, gtk_safe(status)) # Choose Installer Page # @@ -764,7 +764,7 @@ def on_exit_page(): self.error_reporter = saved_reporter def on_error(error): - self.set_status(str(error)) + self.set_status(gtk_safe(error)) saved_reporter = self.error_reporter self.error_reporter = on_error @@ -896,7 +896,7 @@ def load_error_page(self, error: BaseException) -> None: self.cancel_button.grab_focus() def present_error_page(self, error: BaseException) -> None: - self.set_status(str(error)) + self.set_status(gtk_safe(str(error))) is_expected = hasattr(error, "is_expected") and error.is_expected From 68b6097b5dd929ca726efff3d59d57c68de91032 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Sat, 12 Apr 2025 06:07:43 -0400 Subject: [PATCH 048/126] Also use centering and small text for the 'extras' page prompt. --- lutris/gui/installerwindow.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lutris/gui/installerwindow.py b/lutris/gui/installerwindow.py index 6ca72676ae..0f93b9ca4f 100644 --- a/lutris/gui/installerwindow.py +++ b/lutris/gui/installerwindow.py @@ -576,8 +576,9 @@ def on_continue(_button): self.set_status( _( - "This game has extra content. \nSelect which one you want and " - "they will be available in the 'extras' folder where the game is installed." + "This game has extra content\n" + "Select which one you want and " + "they will be available in the 'extras' folder where the game is installed." ) ) self.stack.present_page("extras") From 4518fb0af316cb428cfba83696685984280ea744 Mon Sep 17 00:00:00 2001 From: xenoxriar <32978806+xenoxriar@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:06:26 +1100 Subject: [PATCH 049/126] Update melonDS to the latest version melonDS has been updated to 1.0RC so it would be good to point to the new linux package. I *guessing???* that this is a permalink that always points to the newest package though I could be wrong. There is a pretty major caveat, that being that melonDS now requires a bunch of Qt6 libraries. I'm currently using the latest stable flatpak release of Lutris which doesn't have those. Not sure how this affects the regular packaged version/s of Lutris. It would be nice if somebody could troubleshoot that for me since I'm not experienced enough to deal with it. Thanks! --- share/lutris/json/melonds.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/lutris/json/melonds.json b/share/lutris/json/melonds.json index f528fc136f..41b57f7193 100644 --- a/share/lutris/json/melonds.json +++ b/share/lutris/json/melonds.json @@ -4,7 +4,7 @@ "platforms": ["Nintendo DS"], "runner_executable": "melonds/melonDS", "flatpak_id": "net.kuribo64.melonDS", - "download_url": "https://melonds.kuribo64.net/downloads/melonDS_0.9.5_linux_x64.zip", + "download_url": "https://melonds.kuribo64.net/downloads/melonDS-ubuntu-x86_64.zip", "game_options": [ { "option": "main_file", From 964bd197f80c1b9e9d053c684128929dfa049539 Mon Sep 17 00:00:00 2001 From: Noxellar <32978806+Noxellar@users.noreply.github.com> Date: Sat, 8 Mar 2025 14:32:21 +1100 Subject: [PATCH 050/126] Update melonds download config to use AppImage Doing it this way means we don't have to bundle in a bunch of Qt libraries as well --- share/lutris/json/melonds.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/lutris/json/melonds.json b/share/lutris/json/melonds.json index 41b57f7193..aefe4e7806 100644 --- a/share/lutris/json/melonds.json +++ b/share/lutris/json/melonds.json @@ -4,7 +4,7 @@ "platforms": ["Nintendo DS"], "runner_executable": "melonds/melonDS", "flatpak_id": "net.kuribo64.melonDS", - "download_url": "https://melonds.kuribo64.net/downloads/melonDS-ubuntu-x86_64.zip", + "download_url": "https://melonds.kuribo64.net/downloads/melonDS-appimage-x86_64.zip", "game_options": [ { "option": "main_file", From c08bbd2ddb83da2fe87b2c043a36fabc0f63d916 Mon Sep 17 00:00:00 2001 From: Ectoplasm Date: Sun, 23 Feb 2025 09:48:46 +0200 Subject: [PATCH 051/126] Add logging ability to the MAME runner Add logging ability to the MAME runner. Makes finding an error easier. --- lutris/runners/mame.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lutris/runners/mame.py b/lutris/runners/mame.py index 7a74a6033e..abbccabecc 100644 --- a/lutris/runners/mame.py +++ b/lutris/runners/mame.py @@ -177,6 +177,30 @@ class mame(Runner): # pylint: disable=invalid-name "help": _("Applies a CRT effect to the screen." "Requires OpenGL renderer."), "default": False, }, + { + "option": "verbose", + "type": "bool", + "section": _("Debugging"), + "label": _("Verbose"), + "help": _("display additional diagnostic information."), + "default": False, + }, + { + "option": "log", + "type": "bool", + "section": _("Debugging"), + "label": _("Log"), + "help": _("generate an error.log file."), + "default": False, + }, + { + "option": "oslog", + "type": "bool", + "section": _("Debugging"), + "label": _("OSLog"), + "help": _("output error.log data to system diagnostic output (debugger or standard error)"), + "default": False, + }, { "option": "video", "type": "choice", @@ -317,6 +341,13 @@ def play(self): command += self.get_shader_params("CRT-geom", ["Gaussx", "Gaussy", "CRT-geom-halation"]) command += ["-nounevenstretch"] + if self.runner_config.get("verbose"): + command += ["-verbose"] + if self.runner_config.get("log"): + command += ["-log"] + if self.runner_config.get("verbose"): + command += ["-oslog"] + if self.game_config.get("machine"): rompath = self.runner_config.get("rompath") if rompath: From ddd27d9a4095f42eaced1c8995aff2ca6fee9308 Mon Sep 17 00:00:00 2001 From: Mathieu Comandon Date: Mon, 14 Apr 2025 02:46:52 -0700 Subject: [PATCH 052/126] Set MAME logging options to advanced --- lutris/runners/mame.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lutris/runners/mame.py b/lutris/runners/mame.py index abbccabecc..c4910fb61f 100644 --- a/lutris/runners/mame.py +++ b/lutris/runners/mame.py @@ -184,6 +184,7 @@ class mame(Runner): # pylint: disable=invalid-name "label": _("Verbose"), "help": _("display additional diagnostic information."), "default": False, + "advanced": True, }, { "option": "log", @@ -192,6 +193,7 @@ class mame(Runner): # pylint: disable=invalid-name "label": _("Log"), "help": _("generate an error.log file."), "default": False, + "advanced": True, }, { "option": "oslog", @@ -200,6 +202,7 @@ class mame(Runner): # pylint: disable=invalid-name "label": _("OSLog"), "help": _("output error.log data to system diagnostic output (debugger or standard error)"), "default": False, + "advanced": True, }, { "option": "video", From 235d0a5bd7b8df99627acd12a0896f2655ea3b05 Mon Sep 17 00:00:00 2001 From: Mathieu Comandon Date: Mon, 14 Apr 2025 02:55:06 -0700 Subject: [PATCH 053/126] Group logging options --- lutris/runners/mame.py | 24 +----------------------- lutris/runners/wine.py | 2 +- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/lutris/runners/mame.py b/lutris/runners/mame.py index c4910fb61f..72dc7c9b78 100644 --- a/lutris/runners/mame.py +++ b/lutris/runners/mame.py @@ -186,24 +186,6 @@ class mame(Runner): # pylint: disable=invalid-name "default": False, "advanced": True, }, - { - "option": "log", - "type": "bool", - "section": _("Debugging"), - "label": _("Log"), - "help": _("generate an error.log file."), - "default": False, - "advanced": True, - }, - { - "option": "oslog", - "type": "bool", - "section": _("Debugging"), - "label": _("OSLog"), - "help": _("output error.log data to system diagnostic output (debugger or standard error)"), - "default": False, - "advanced": True, - }, { "option": "video", "type": "choice", @@ -345,11 +327,7 @@ def play(self): command += ["-nounevenstretch"] if self.runner_config.get("verbose"): - command += ["-verbose"] - if self.runner_config.get("log"): - command += ["-log"] - if self.runner_config.get("verbose"): - command += ["-oslog"] + command += ["-verbose", "-oslog", "-log"] if self.game_config.get("machine"): rompath = self.runner_config.get("rompath") diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index bb749c6fd1..9bf6c9d404 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -1045,7 +1045,7 @@ def set_regedit_keys(self): } for key, path in self.reg_keys.items(): value = self.runner_config.get(key) or "auto" - if not value or value == "auto" and key not in managed_keys: + if not value or (value == "auto" and key not in managed_keys): prefix_manager.clear_registry_subkeys(path, key) elif key in self.runner_config: if key in managed_keys: From 0d9e64f0f88c1471d9d20c891c80e2c1bc0af539 Mon Sep 17 00:00:00 2001 From: Martim Moniz Date: Sun, 2 Mar 2025 22:47:20 +0000 Subject: [PATCH 054/126] feat: add steam family shared games as a source Added a new service source to list the available games from the user's family. Due to time constraints and limited knowledge, the current implementation places the found games in the Steam source, rather than Steam Family source. fix: steam family games appear on the respective source Additionally, new lutris setting "steam_family_include_own" to have all Steam games appear on the Steam Family source. fix: remove unnecessary webconnect url check, properly renew steamfamily access token Removed unnecessary steamfamily webconnect url check by changing the login redirect url. Properly renew steamfamily access token (do not require login if cookies exist and can still login). Remove nix files from git tracking. chore: format steamfamily.py fix: include user's excluded games if steam_family_include_own is set --- lutris/services/__init__.py | 2 + lutris/services/steamfamily.py | 154 +++++++++++++++++++++++++++++++++ lutris/settings.py | 1 + 3 files changed, 157 insertions(+) create mode 100644 lutris/services/steamfamily.py diff --git a/lutris/services/__init__.py b/lutris/services/__init__.py index e998a52ab0..9b1b13b3c7 100644 --- a/lutris/services/__init__.py +++ b/lutris/services/__init__.py @@ -17,6 +17,7 @@ from lutris.services.scummvm import SCUMMVM_CONFIG_FILE, ScummvmService from lutris.services.steam import SteamService from lutris.services.steamwindows import SteamWindowsService +from lutris.services.steamfamily import SteamFamilyService from lutris.services.ubisoft import UbisoftConnectService from lutris.services.xdg import XDGService from lutris.util import system @@ -45,6 +46,7 @@ def get_services(): if LINUX_SYSTEM.has_steam(): _services["steam"] = SteamService _services["steamwindows"] = SteamWindowsService + _services["steamfamily"] = SteamFamilyService if system.path_exists(DOLPHIN_GAME_CACHE_FILE): _services["dolphin"] = DolphinService if system.path_exists(SCUMMVM_CONFIG_FILE): diff --git a/lutris/services/steamfamily.py b/lutris/services/steamfamily.py new file mode 100644 index 0000000000..f476dce735 --- /dev/null +++ b/lutris/services/steamfamily.py @@ -0,0 +1,154 @@ +"""Steam Family service""" + +import json +import os +from gettext import gettext as _ + +import requests + +from lutris import settings +from lutris.services.base import SERVICE_LOGIN, AuthTokenExpiredError, OnlineService +from lutris.util.log import logger +from lutris.services.steam import SteamGame, SteamService +from lutris.util.steam.config import get_active_steamid64, get_steam_library + + +class SteamFamilyGame(SteamGame): + service = "steamfamily" + installer_slug = "steam" + runner = "steam" + + @classmethod + def new_from_steamfamily_game(cls, game): + """Return a Steam Family game instance from an AppManifest""" + game = cls.new_from_steam_game(game) + game.service = cls.service + return game + + +class SteamFamilyService(SteamService, OnlineService): + """Service class for Steam Family sharing""" + + id = "steamfamily" + name = _("Steam Family") + description = _("Use for displaying every game in the Steam family") + login_window_width = 500 + login_window_height = 850 + online = True + requires_login_page = True + game_class = SteamFamilyGame + include_own_games = settings.STEAM_FAMILY_INCLUDE_OWN + cookies_path = os.path.join(settings.CACHE_DIR, ".steam.auth") + token_path = os.path.join(settings.CACHE_DIR, ".steam.token") + cache_path = os.path.join(settings.CACHE_DIR, "steam-library.json") + login_url = "https://store.steampowered.com/login/?redir=/about" + redirect_uri = "https://store.steampowered.com/about" + access_token_url = "https://store.steampowered.com/pointssummary/ajaxgetasyncconfig" + library_url = "https://api.steampowered.com/IFamilyGroupsService/GetSharedLibraryApps/v1/" + family_url = "https://api.steampowered.com/IFamilyGroupsService/GetFamilyGroupForUser/v1/" + + user_agent = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/84.0.4147.38 Safari/537.36" + ) + + def __init__(self): + super().__init__() + self.session = requests.session() + self.session.headers["User-Agent"] = self.user_agent + self.access_token = self.load_access_token() + + def is_connected(self): + return self.is_authenticated() and bool(self.load_access_token()) + + def fetch_access_token(self): + """Fetch the access token from the store, save to disk and set""" + token_data = self.get_access_token() + if not token_data: + raise RuntimeError("Failed to get access token") + with open(self.token_path, "w", encoding="utf-8") as token_file: + token_file.write(json.dumps(token_data, indent=2)) + self.access_token = self.load_access_token() + + def load_access_token(self): + """Load the access token from disk""" + if not os.path.exists(self.token_path): + return "" + with open(self.token_path, encoding="utf-8") as token_file: + token_data = json.load(token_file) + return token_data.get("data").get("webapi_token", "") + + def get_access_token(self): + """Request an access token from steam and return dump""" + logger.debug("Requesting access token") + response = self.session.get( + self.access_token_url, + cookies=self.load_cookies(), + ) + response.raise_for_status() + token_data = response.json() + return token_data + + def login_callback(self, content): + """Once the user logs in in a browser window, they're redirected to a + an arbitrary page (about), then we redirect to a page containing the + store access token which we can use to fetch family games""" + logger.debug("Login to Steam store successful") + self.fetch_access_token() + SERVICE_LOGIN.fire(self) + + def get_family_groupid(self): + """Get the user's family group id""" + response = self.session.get( + self.family_url, params={"access_token": self.load_access_token(), "steamid": get_active_steamid64()} + ) + response.raise_for_status() + resData = response.json() + records = resData["response"] + if not records["is_not_member_of_any_group"]: + return records["family_groupid"] + logger.error("User is not a member of any family group") + return None + + def get_library(self): + steamid = get_active_steamid64() + if not steamid: + logger.error("Unable to find SteamID from Steam config") + return [] + response = self.session.get( + self.library_url, + params={ + "access_token": self.load_access_token(), + "family_groupid": self.get_family_groupid(), + "steamid": get_active_steamid64(), + }, + ) + response.raise_for_status() + resData = response.json() + records = resData["response"]["apps"] + if self.include_own_games: + own_games = get_steam_library(steamid) + ids = {game["appid"] for game in records} + records.extend([game for game in own_games if game["appid"] not in ids]) + return records + + def load(self): + """Load the list of games""" + try: + library = self.get_library() + except Exception as ex: # pylint=disable:broad-except + logger.warning("Access Token expired, will attempt to get a new one") + try: + self.fetch_access_token() + library = self.get_library() + except: + logger.warning("Failed to get a new access token") + raise AuthTokenExpiredError("Access Token expired") from ex + for steam_game in library: + if steam_game["appid"] in self.excluded_appids: + continue + game = self.game_class.new_from_steamfamily_game(steam_game) + game.save() + self.match_games() + return library diff --git a/lutris/settings.py b/lutris/settings.py index bf02dcb4f2..a961a0d6d9 100644 --- a/lutris/settings.py +++ b/lutris/settings.py @@ -58,6 +58,7 @@ RUNTIME_URL = SITE_URL + "/api/runtimes" STEAM_API_KEY = sio.read_setting("steam_api_key") or "34C9698CEB394AB4401D65927C6B3752" +STEAM_FAMILY_INCLUDE_OWN = sio.read_setting("steam_family_include_own", default="False") SHOW_MEDIA = os.environ.get("LUTRIS_HIDE_MEDIA") != "1" and sio.read_setting("hide_media") != "True" From 0d58435236ad4572aa1084d834236f1314892a11 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Sat, 12 Apr 2025 18:14:28 -0400 Subject: [PATCH 055/126] Check for the '(' which is used to find the section name here; we'll just hope it's okay to ignore lines that look like section headers but which aren't parsable. Resolves #6046 --- lutris/util/graphics/glxinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/util/graphics/glxinfo.py b/lutris/util/graphics/glxinfo.py index 6b17b6f3c5..a057f6afbf 100644 --- a/lutris/util/graphics/glxinfo.py +++ b/lutris/util/graphics/glxinfo.py @@ -49,7 +49,7 @@ def parse(self): key = key.replace(" string", "").replace(" ", "_") value = value.strip() - if not value and key.startswith(("Extended_renderer_info", "Memory_info")): + if not value and key.startswith(("Extended_renderer_info", "Memory_info")) and "(" in key: self._section = key[key.index("(") + 1 : -1] setattr(self, self._section, Container()) continue From 75cdc53498a85504230847ecbef56941728d6afc Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Tue, 28 Jan 2025 16:42:50 -0500 Subject: [PATCH 056/126] Add a migration to move banners and cover-art from .cache to .local/share. Also, remove the magic to use either location. --- lutris/migrations/__init__.py | 3 ++- lutris/migrations/migrate_banners_back.py | 33 +++++++++++++++++++++++ lutris/settings.py | 8 ++---- 3 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 lutris/migrations/migrate_banners_back.py diff --git a/lutris/migrations/__init__.py b/lutris/migrations/__init__.py index 68618e472a..5aba83edc2 100644 --- a/lutris/migrations/__init__.py +++ b/lutris/migrations/__init__.py @@ -3,7 +3,7 @@ from lutris import settings from lutris.util.log import logger -MIGRATION_VERSION = 15 # Never decrease this number +MIGRATION_VERSION = 16 # Never decrease this number # Replace deprecated migrations with empty lists MIGRATIONS = [ @@ -22,6 +22,7 @@ ["migrate_sortname"], ["migrate_hidden_category"], ["migrate_ge_proton"], + ["migrate_banners_back"], ] diff --git a/lutris/migrations/migrate_banners_back.py b/lutris/migrations/migrate_banners_back.py new file mode 100644 index 0000000000..3f82fccc3e --- /dev/null +++ b/lutris/migrations/migrate_banners_back.py @@ -0,0 +1,33 @@ +"""Migrate banners and coverart from .cache/lutris to .local/share/lutris""" + +import os + +from lutris import settings +from lutris.util.log import logger + + +def _migrate(dirname): + dest_dir = os.path.join(settings.DATA_DIR, dirname) + src_dir = os.path.join(settings.CACHE_DIR, dirname) + + try: + # init_lutris() creates the new banners directory + if os.path.isdir(src_dir) and os.path.isdir(dest_dir): + for filename in os.listdir(src_dir): + src_file = os.path.join(src_dir, filename) + dest_file = os.path.join(dest_dir, filename) + + if not os.path.exists(dest_file): + os.rename(src_file, dest_file) + else: + os.unlink(src_file) + + if not os.listdir(src_dir): + os.rmdir(src_dir) + except OSError as ex: + logger.exception("Failed to migrate banners: %s", ex) + + +def migrate(): + _migrate("banners") + _migrate("coverart") diff --git a/lutris/settings.py b/lutris/settings.py index a961a0d6d9..735ed7b82e 100644 --- a/lutris/settings.py +++ b/lutris/settings.py @@ -34,12 +34,8 @@ SHADER_CACHE_DIR = os.path.join(CACHE_DIR, "shaders") INSTALLER_CACHE_DIR = os.path.join(CACHE_DIR, "installer") -BANNER_PATH = os.path.join(CACHE_DIR, "banners") -if not os.path.exists(BANNER_PATH): - BANNER_PATH = os.path.join(DATA_DIR, "banners") -COVERART_PATH = os.path.join(CACHE_DIR, "coverart") -if not os.path.exists(COVERART_PATH): - COVERART_PATH = os.path.join(DATA_DIR, "coverart") +BANNER_PATH = os.path.join(DATA_DIR, "banners") +COVERART_PATH = os.path.join(DATA_DIR, "coverart") RUNTIME_VERSIONS_PATH = os.path.join(CACHE_DIR, "versions.json") ICON_PATH = os.path.join(GLib.get_user_data_dir(), "icons", "hicolor", "128x128", "apps") From 8b7a46596f5282be6633aecc6c3094a84eb0c9c0 Mon Sep 17 00:00:00 2001 From: Zybyte85 Date: Sat, 15 Mar 2025 12:16:15 -0600 Subject: [PATCH 057/126] Changed ROM import from adding single file, to adding a folder to bulk import ROMs --- lutris/gui/addgameswindow.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lutris/gui/addgameswindow.py b/lutris/gui/addgameswindow.py index bd8e9c32fc..853520f27c 100644 --- a/lutris/gui/addgameswindow.py +++ b/lutris/gui/addgameswindow.py @@ -42,8 +42,8 @@ class AddGamesWindow(ModelessDialog): # pylint: disable=too-many-public-methods ( "application-x-firmware-symbolic", "go-next-symbolic", - _("Import a ROM"), - _("Import a ROM that is known to Lutris"), + _("Import a ROM folder"), + _("Import a folder with ROMs that are known to Lutris"), "import_rom", ), ( @@ -124,7 +124,7 @@ def __init__(self, **kwargs): self.install_script_file_chooser = FileChooserEntry(title=_("Select script"), action=Gtk.FileChooserAction.OPEN) - self.import_rom_file_chooser = FileChooserEntry(title=_("Select ROM file"), action=Gtk.FileChooserAction.OPEN) + self.import_rom_file_chooser = FileChooserEntry(title=_("Select ROM folder"), action=Gtk.FileChooserAction.SELECT_FOLDER) self.stack.add_named_factory("initial", self.create_initial_page) self.stack.add_named_factory("search_installers", self.create_search_installers_page) @@ -465,10 +465,10 @@ def create_import_rom_page(self): self.import_rom_file_chooser.set_hexpand(True) explanation = _( - "Lutris will identify a ROM via its MD5 hash and download game " + "Lutris will identify ROMs via its MD5 hash and download game " "information from Lutris.net.\n\n" "The ROM data used for this comes from the TOSEC project.\n\n" - "When you click 'Install' below, the process of installing the game will " + "When you click 'Install' below, the process of installing the games will " "begin." ) @@ -476,19 +476,21 @@ def create_import_rom_page(self): return grid def present_import_rom_page(self): - self.set_page_title_markup(_("Select a ROM file")) + self.set_page_title_markup(_("Select a ROM folder")) self.stack.present_page("import_rom") self.display_continue_button(self.on_continue_import_rom_clicked, label=_("_Install")) def on_continue_import_rom_clicked(self, _widget): - path = self.import_rom_file_chooser.get_text() - if not path: - ErrorDialog(_("You must select a ROM file to install."), parent=self) - elif not os.path.isfile(path): - ErrorDialog(_("No file exists at '%s'.") % path, parent=self) + paths = [] + for path, dirs, files in os.walk(self.import_rom_file_chooser.get_text()): + for file in files: + paths.append(os.path.join(path, file)) + + if not paths: + ErrorDialog(_("You must select a ROM folder file to install."), parent=self) else: application = Gio.Application.get_default() - dialog = ImportGameDialog([path], parent=application.window) + dialog = ImportGameDialog(paths, parent=application.window) dialog.show() self.destroy() From 9de122b0026936936eea09dcc0f229a6ddb747ce Mon Sep 17 00:00:00 2001 From: Zybyte85 Date: Sat, 15 Mar 2025 12:19:49 -0600 Subject: [PATCH 058/126] Formatted files --- lutris/gui/addgameswindow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lutris/gui/addgameswindow.py b/lutris/gui/addgameswindow.py index 853520f27c..4f137ee238 100644 --- a/lutris/gui/addgameswindow.py +++ b/lutris/gui/addgameswindow.py @@ -124,7 +124,9 @@ def __init__(self, **kwargs): self.install_script_file_chooser = FileChooserEntry(title=_("Select script"), action=Gtk.FileChooserAction.OPEN) - self.import_rom_file_chooser = FileChooserEntry(title=_("Select ROM folder"), action=Gtk.FileChooserAction.SELECT_FOLDER) + self.import_rom_file_chooser = FileChooserEntry( + title=_("Select ROM folder"), action=Gtk.FileChooserAction.SELECT_FOLDER + ) self.stack.add_named_factory("initial", self.create_initial_page) self.stack.add_named_factory("search_installers", self.create_search_installers_page) From 99e05dedfead3c7069136846d1be041a060e6121 Mon Sep 17 00:00:00 2001 From: Mathieu Comandon Date: Mon, 14 Apr 2025 07:44:30 -0700 Subject: [PATCH 059/126] Change wording --- lutris/gui/addgameswindow.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lutris/gui/addgameswindow.py b/lutris/gui/addgameswindow.py index 4f137ee238..6e0b6b5373 100644 --- a/lutris/gui/addgameswindow.py +++ b/lutris/gui/addgameswindow.py @@ -42,8 +42,8 @@ class AddGamesWindow(ModelessDialog): # pylint: disable=too-many-public-methods ( "application-x-firmware-symbolic", "go-next-symbolic", - _("Import a ROM folder"), - _("Import a folder with ROMs that are known to Lutris"), + _("Import ROMs"), + _("Import ROMs referenced in TOSEC, No-intro or Redump"), "import_rom", ), ( @@ -125,7 +125,7 @@ def __init__(self, **kwargs): self.install_script_file_chooser = FileChooserEntry(title=_("Select script"), action=Gtk.FileChooserAction.OPEN) self.import_rom_file_chooser = FileChooserEntry( - title=_("Select ROM folder"), action=Gtk.FileChooserAction.SELECT_FOLDER + title=_("Select ROMs"), action=Gtk.FileChooserAction.SELECT_FOLDER ) self.stack.add_named_factory("initial", self.create_initial_page) @@ -469,7 +469,7 @@ def create_import_rom_page(self): explanation = _( "Lutris will identify ROMs via its MD5 hash and download game " "information from Lutris.net.\n\n" - "The ROM data used for this comes from the TOSEC project.\n\n" + "The ROM data used for this comes from TOSEC, No-Intro and Redump projects.\n\n" "When you click 'Install' below, the process of installing the games will " "begin." ) @@ -478,18 +478,18 @@ def create_import_rom_page(self): return grid def present_import_rom_page(self): - self.set_page_title_markup(_("Select a ROM folder")) + self.set_page_title_markup(_("Select ROMs")) self.stack.present_page("import_rom") self.display_continue_button(self.on_continue_import_rom_clicked, label=_("_Install")) def on_continue_import_rom_clicked(self, _widget): paths = [] - for path, dirs, files in os.walk(self.import_rom_file_chooser.get_text()): + for path, _dirs, files in os.walk(self.import_rom_file_chooser.get_text()): for file in files: paths.append(os.path.join(path, file)) if not paths: - ErrorDialog(_("You must select a ROM folder file to install."), parent=self) + ErrorDialog(_("You must select ROMs to install."), parent=self) else: application = Gio.Application.get_default() dialog = ImportGameDialog(paths, parent=application.window) From ae54f497010e73f7b06989629b533bd6f924ef30 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Mon, 14 Apr 2025 16:37:46 -0400 Subject: [PATCH 060/126] ruffage - sort imports --- lutris/services/__init__.py | 2 +- lutris/services/steamfamily.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lutris/services/__init__.py b/lutris/services/__init__.py index 9b1b13b3c7..0c3ccba173 100644 --- a/lutris/services/__init__.py +++ b/lutris/services/__init__.py @@ -16,8 +16,8 @@ from lutris.services.mame import MAMEService from lutris.services.scummvm import SCUMMVM_CONFIG_FILE, ScummvmService from lutris.services.steam import SteamService -from lutris.services.steamwindows import SteamWindowsService from lutris.services.steamfamily import SteamFamilyService +from lutris.services.steamwindows import SteamWindowsService from lutris.services.ubisoft import UbisoftConnectService from lutris.services.xdg import XDGService from lutris.util import system diff --git a/lutris/services/steamfamily.py b/lutris/services/steamfamily.py index f476dce735..d00f872531 100644 --- a/lutris/services/steamfamily.py +++ b/lutris/services/steamfamily.py @@ -8,8 +8,8 @@ from lutris import settings from lutris.services.base import SERVICE_LOGIN, AuthTokenExpiredError, OnlineService -from lutris.util.log import logger from lutris.services.steam import SteamGame, SteamService +from lutris.util.log import logger from lutris.util.steam.config import get_active_steamid64, get_steam_library From 4db3b1031e6a97e47ce5031c093bed46cc5efd92 Mon Sep 17 00:00:00 2001 From: Kolja Date: Thu, 23 Jan 2025 00:36:17 +0100 Subject: [PATCH 061/126] Add PackageManager subcategory Similar to what steam does here https://github.com/flathub/com.valvesoftware.Steam/blob/master/com.valvesoftware.Steam.metainfo.xml#L25 --- share/applications/net.lutris.Lutris.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/applications/net.lutris.Lutris.desktop b/share/applications/net.lutris.Lutris.desktop index 1c8ecff316..00d27c2650 100644 --- a/share/applications/net.lutris.Lutris.desktop +++ b/share/applications/net.lutris.Lutris.desktop @@ -2,7 +2,7 @@ Name=Lutris StartupWMClass=Lutris Comment=Video Game Preservation Platform -Categories=Game; +Categories=Game;PackageManager; Keywords=gaming;wine;emulator; Exec=lutris %U Icon=net.lutris.Lutris From bbd3cc891a9ae02d3b46522784a272c28b261184 Mon Sep 17 00:00:00 2001 From: Eikeno Date: Wed, 16 Apr 2025 15:39:58 +0200 Subject: [PATCH 062/126] modify find_windows_game_executable() to be also compatible with newest format Format returned has changed, maybe related to difference between libc 2.40 and 2.41 (not 100% sure, but seems like it) and causes the function to not detect win executable ending in empty 'exe' field in YAML file. Problem identified on Arch current using: GNU C Library (GNU libc) stable release version 2.41 as well as on Fedora Workstation 42, using the same version Not impacting on Ubuntu 24.10 using: GNU C Library (Ubuntu GLIBC 2.40-1ubuntu3.1) stable release version 2.40. Examples of differing outputs: --- Arch, Fedora 42 - win32 exe file: - 'PE32 executable for MS Windows 4.00 (GUI), Intel i386, 5 sections Ubuntu 24.10 exe file: - PE32 executable (GUI) Intel 80386 Arch, Fedora 42 - win64 exe file: - PE32+ executable for MS Windows 5.02 (GUI), x86-64, 7 sections Ubuntu 24.10: - PE32+ executable (GUI) x86-64, for MS Windows, 7 sections The modified code matches substrings, instead of whole string, and should hopefully be more resilient in case of future format changes. Tested on tested the above 3 OS, with the itch.io source and win32 and win64 games. --- lutris/util/game_finder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lutris/util/game_finder.py b/lutris/util/game_finder.py index 1e49c3b692..d720aa6ff1 100644 --- a/lutris/util/game_finder.py +++ b/lutris/util/game_finder.py @@ -89,9 +89,11 @@ def find_windows_game_executable(path): file_type = magic.from_file(abspath) if "MS Windows shortcut" in file_type: candidates["link"] = abspath - elif "PE32+ executable (GUI) x86-64" in file_type: + elif all([_ in file_type for _ in ("PE32+ executable", "(GUI)", "x86-64")]): candidates["64bit"] = abspath - elif "PE32 executable (GUI) Intel 80386" in file_type: + elif all([_ in file_type for _ in ("PE32 executable", "(GUI)")]) and any( + [_ in file_type for _ in ("Intel i386", "Intel 80386")] + ): candidates["32bit"] = abspath if candidates: return candidates.get("link") or candidates.get("64bit") or candidates.get("32bit") From b008049e5cadd8da9547faf640d1971239c83116 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 16 Apr 2025 19:25:15 -0400 Subject: [PATCH 063/126] When multiple versions of Proton are found with the same 'name', we'll prefer our own verison (in the runners directory), not the 'foreign' one. --- lutris/util/wine/proton.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lutris/util/wine/proton.py b/lutris/util/wine/proton.py index cdef329971..5fc8e12d42 100644 --- a/lutris/util/wine/proton.py +++ b/lutris/util/wine/proton.py @@ -129,9 +129,10 @@ def get_proton_versions() -> Dict[str, str]: for proton_path in _iter_proton_locations(): if os.path.isdir(proton_path): for version in os.listdir(proton_path): - wine_path = os.path.join(proton_path, version) - if os.path.isfile(os.path.join(wine_path, "proton")): - versions[version] = wine_path + if version not in versions: + wine_path = os.path.join(proton_path, version) + if os.path.isfile(os.path.join(wine_path, "proton")): + versions[version] = wine_path return versions From c927b048a2bfc003a2fd40f2f6b7d82ebf1e6199 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 16 Apr 2025 19:30:34 -0400 Subject: [PATCH 064/126] Do not allow duplicate wine versions to appear in the version list. Use a dict to preserver the order though- Python dicts are ordered. --- lutris/util/wine/wine.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lutris/util/wine/wine.py b/lutris/util/wine/wine.py index 0e15b4615d..8bef45e672 100644 --- a/lutris/util/wine/wine.py +++ b/lutris/util/wine/wine.py @@ -127,8 +127,22 @@ def list_lutris_wine_versions() -> List[str]: @cache_single def get_installed_wine_versions() -> List[str]: - """Return the list of Wine versions installed""" - return list_system_wine_versions() + proton.list_proton_versions() + list_lutris_wine_versions() + """Return the list of Wine versions installed, with no duplicates and in + the presentation order.""" + versions = {} + for v in list_system_wine_versions(): + if v not in versions: + versions[v] = v + + for v in proton.list_proton_versions(): + if v not in versions: + versions[v] = v + + for v in list_lutris_wine_versions(): + if v not in versions: + versions[v] = v + + return list(versions.keys()) def clear_wine_version_cache() -> None: From 447ba940c1b25ced1f5a5760f8ba03c0816f8bf4 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 16 Apr 2025 19:36:18 -0400 Subject: [PATCH 065/126] Adjust list_lutris_wine_versions() to avoid accepted Proton versions; we are searching the WINE_DIR, we want ordinary Wine only. --- lutris/util/wine/wine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/util/wine/wine.py b/lutris/util/wine/wine.py index 8bef45e672..fe4a61b74b 100644 --- a/lutris/util/wine/wine.py +++ b/lutris/util/wine/wine.py @@ -117,7 +117,7 @@ def list_lutris_wine_versions() -> List[str]: versions = [] for dirname in version_sort(os.listdir(WINE_DIR), reverse=True): try: - wine_path = get_wine_path_for_version(version=dirname) + wine_path = os.path.join(WINE_DIR, dirname, "bin/wine") if wine_path and os.path.isfile(wine_path): versions.append(dirname) except MisconfigurationError: From 55081a54490d6b06d80beda152cf84bfab49af42 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Sat, 19 Apr 2025 15:58:02 -0400 Subject: [PATCH 066/126] Correct incorrect return of 'None' that leads to a crash. --- lutris/services/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lutris/services/base.py b/lutris/services/base.py index 8aa4aebba5..ea6a5af0db 100644 --- a/lutris/services/base.py +++ b/lutris/services/base.py @@ -317,7 +317,6 @@ def get_service_installers(self, db_game, update): service_installers.extend(installers) if not service_installers: logger.error("No installer found for %s", db_game) - return return service_installers, db_game, None def install(self, db_game, update=False): From fe65afb82f595be96fe9299200f9abf67f6d61e5 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 20 Apr 2025 21:59:54 +0700 Subject: [PATCH 067/126] wine-nvml not work when use symlink in prefix --- lutris/util/wine/dxvk_nvapi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lutris/util/wine/dxvk_nvapi.py b/lutris/util/wine/dxvk_nvapi.py index c3b7f913cd..9aebf63838 100644 --- a/lutris/util/wine/dxvk_nvapi.py +++ b/lutris/util/wine/dxvk_nvapi.py @@ -14,7 +14,6 @@ class DXVKNVAPIManager(DLLManager): managed_dlls = ( "nvapi", "nvapi64", - "nvml", "nvofapi64", "nvoptix", "nvencodeapi", From bc56c223aa5414e3004dd11edf45525eebdad61c Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Mon, 21 Apr 2025 12:45:28 +0700 Subject: [PATCH 068/126] add winekill --- lutris/runners/wine.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index 9bf6c9d404..21c3759c33 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -671,6 +671,7 @@ def runner_warning(self): def context_menu_entries(self): """Return the contexual menu entries for wine""" return [ + ("winekill", _("Kill all Wine processes"), self.run_winekill), ("wineexec", _("Run EXE inside Wine prefix"), self.run_wineexec), ("wineshell", _("Open Bash terminal"), self.run_wine_terminal), ("wineconsole", _("Open Wine console"), self.run_wineconsole), From 9f9f75495bb2c98b26b59dcc3b504e6d737bc029 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Wed, 23 Apr 2025 22:44:42 +0700 Subject: [PATCH 069/126] feat: Rename game config files when slug changes * Renames game config files when a game's slug changes * Use the new game name's slug when duplicating game configs --- lutris/config.py | 13 ++++++++++++- lutris/game_actions.py | 2 +- lutris/gui/config/game_common.py | 7 ++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lutris/config.py b/lutris/config.py index d69d94633a..06893ea457 100644 --- a/lutris/config.py +++ b/lutris/config.py @@ -3,7 +3,7 @@ import os import time from shutil import copyfile -from typing import Set +from typing import Set, Optional from lutris import settings, sysoptions from lutris.runners import InvalidRunnerError, import_runner @@ -36,6 +36,17 @@ def duplicate_game_config(game_slug: str, source_config_id: str): return new_config_id +def rename_config(old_config_id: str, new_slug: str) -> Optional[str]: + old_slug, timestamp = old_config_id.rsplit("-", 1) + if old_slug == new_slug: + return None + new_config_id = f"{new_slug}-{timestamp}" + src_path = f"{settings.GAME_CONFIG_DIR}/{old_config_id}.yml" + dest_path = f"{settings.GAME_CONFIG_DIR}/{new_config_id}.yml" + os.rename(src_path, dest_path) + return new_config_id + + class LutrisConfig: """Class where all the configuration handling happens. diff --git a/lutris/game_actions.py b/lutris/game_actions.py index 21172f3d4e..5c5d143ff4 100644 --- a/lutris/game_actions.py +++ b/lutris/game_actions.py @@ -404,7 +404,7 @@ def on_game_duplicate(self, _widget): old_config_id = game.game_config_id if old_config_id: - new_config_id = duplicate_game_config(game.slug, old_config_id) + new_config_id = duplicate_game_config(slugify(new_name), old_config_id) else: new_config_id = None categories = game.get_categories() diff --git a/lutris/gui/config/game_common.py b/lutris/gui/config/game_common.py index 7c49faaf9b..53e3ffda73 100644 --- a/lutris/gui/config/game_common.py +++ b/lutris/gui/config/game_common.py @@ -8,7 +8,7 @@ from gi.repository import GdkPixbuf, Gtk, Pango from lutris import runners, settings -from lutris.config import LutrisConfig, make_game_config_id +from lutris.config import LutrisConfig, make_game_config_id, rename_config from lutris.game import Game from lutris.gui.config import DIALOG_HEIGHT, DIALOG_WIDTH from lutris.gui.config.boxes import GameBox, RunnerBox, SystemConfigBox @@ -696,6 +696,11 @@ def on_save(self, _button): self.game.playtime = playtime self.game.is_installed = True self.game.config = self.lutris_config + + # Rename config file if game slug changed + if new_config_id := rename_config(self.game.config.game_config_id, self.game.slug): + self.game.config.game_config_id = new_config_id + self.game.runner_name = self.runner_name if "icon" not in self.game.custom_images: From dc22f0cc87323baea0569c828298f6b60363332a Mon Sep 17 00:00:00 2001 From: Martin Hansen Date: Mon, 21 Apr 2025 07:38:42 +0200 Subject: [PATCH 070/126] add nb.po and edit LINGUAS --- po/LINGUAS | 1 + po/nb.po | 7842 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 7843 insertions(+) create mode 100644 po/nb.po diff --git a/po/LINGUAS b/po/LINGUAS index e3adfbb3aa..1e089ca8d6 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -9,6 +9,7 @@ hr it ka ko +nb nl pl pt_BR diff --git a/po/nb.po b/po/nb.po new file mode 100644 index 0000000000..eacf8dfe22 --- /dev/null +++ b/po/nb.po @@ -0,0 +1,7842 @@ +msgid "" +msgstr "" +"Project-Id-Version: lutris\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-04-21 07:36+0200\n" +"PO-Revision-Date: 2025-04-21 07:37+0200\n" +"Last-Translator: Martin Hansen \n" +"Language-Team: Norwegian Bokmål\n" +"Language: nb\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 25.04.0\n" + +#: share/applications/net.lutris.Lutris.desktop:2 +#: share/metainfo/net.lutris.Lutris.metainfo.xml:13 +#: share/lutris/ui/lutris-window.ui:25 lutris/gui/application.py:86 +#: lutris/gui/config/accounts_box.py:26 lutris/gui/widgets/status_icon.py:102 +#: lutris/services/lutris.py:49 lutris/sysoptions.py:80 lutris/sysoptions.py:90 +#: lutris/sysoptions.py:102 +msgid "Lutris" +msgstr "Lutris" + +#: share/applications/net.lutris.Lutris.desktop:4 +msgid "Video Game Preservation Platform" +msgstr "Plattform for bevaring av videospill" + +#: share/applications/net.lutris.Lutris.desktop:6 +msgid "gaming;wine;emulator;" +msgstr "spilling;wine;emulator;" + +#: share/metainfo/net.lutris.Lutris.metainfo.xml:8 +msgid "Lutris Team" +msgstr "Lutris-utviklingslaget" + +#: share/metainfo/net.lutris.Lutris.metainfo.xml:14 +#: share/lutris/ui/about-dialog.ui:18 +msgid "Video game preservation platform" +msgstr "Plattform for bevaring av videospill" + +#: share/metainfo/net.lutris.Lutris.metainfo.xml:17 +msgid "Main window" +msgstr "Hovedvindu" + +#: share/metainfo/net.lutris.Lutris.metainfo.xml:21 +msgid "" +"Lutris helps you install and play video games from all eras and from most " +"gaming systems. By leveraging and combining existing emulators, engine re-" +"implementations and compatibility layers, it gives you a central interface " +"to launch all your games." +msgstr "" +"Lutris hjelper deg å installere videospill fra alle generasjoner og fra de " +"fleste spillsystemer. Ved å kombinere og ta i bruk eksisterende emulatorer, " +"spillmotor reimplementeringer og kompatibilitetslag, som gir deg et sentralt " +"brukergrensesnitt for å starte alle spillene dine." + +#: share/lutris/ui/about-dialog.ui:8 share/lutris/ui/lutris-window.ui:795 +msgid "About Lutris" +msgstr "Om Lutris" + +#: share/lutris/ui/about-dialog.ui:21 +msgid "" +"This program is free software: you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License as published by\n" +"the Free Software Foundation, either version 3 of the License, or\n" +"(at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program. If not, see .\n" +msgstr "" +"This program is free software: you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License as published by\n" +"the Free Software Foundation, either version 3 of the License, or\n" +"(at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program. If not, see .\n" + +#: share/lutris/ui/about-dialog.ui:34 lutris/settings.py:17 +msgid "The Lutris team" +msgstr "Lutris-utviklingslaget" + +#: share/lutris/ui/dialog-lutris-login.ui:8 +msgid "Connect to lutris.net" +msgstr "Koble til lutris.net" + +#: share/lutris/ui/dialog-lutris-login.ui:26 +msgid "Forgot password?" +msgstr "Glemt passordet?" + +#: share/lutris/ui/dialog-lutris-login.ui:43 lutris/gui/dialogs/issue.py:51 +msgid "C_ancel" +msgstr "A_vbryt" + +#: share/lutris/ui/dialog-lutris-login.ui:57 +msgid "_Connect" +msgstr "_Koble til" + +#: share/lutris/ui/dialog-lutris-login.ui:96 +msgid "Password" +msgstr "Passord" + +#: share/lutris/ui/dialog-lutris-login.ui:110 +msgid "Username" +msgstr "Brukernavn" + +#: share/lutris/ui/log-window.ui:36 +msgid "Search..." +msgstr "Søk …" + +#: share/lutris/ui/log-window.ui:48 +msgid "Decrease text size" +msgstr "Reduser tekststørrelsen" + +#: share/lutris/ui/log-window.ui:64 +msgid "Increase text size" +msgstr "Øk tekststørrelsen" + +#: share/lutris/ui/lutris-window.ui:113 +msgid "" +"Login to Lutris.net to view your game " +"library" +msgstr "" +"Logg på Lutris.net for å vise ditt spill " +"bibliotek" + +#: share/lutris/ui/lutris-window.ui:126 +msgid "Login" +msgstr "Log inn" + +#: share/lutris/ui/lutris-window.ui:141 +msgid "Turn on Library Sync" +msgstr "Slå på synkronisering av biblioteket" + +#: share/lutris/ui/lutris-window.ui:179 +msgid "" +"Lutris %s is no longer supported. Download %s here!" +msgstr "" +"Lutris %s er ikke lenger støttet. Last ned %s her!" + +#: share/lutris/ui/lutris-window.ui:312 +msgid "" +"Enter the name of a game to search for, or use search terms:\n" +"\n" +"installed:true\t\t\tOnly installed games.\n" +"hidden:true\t\t\tOnly hidden games.\n" +"favorite:true\t\t\tOnly favorite games.\n" +"categorized:true\t\tOnly games in a category.\n" +"category:x\t\t\tOnly games in category x.\n" +"source:steam\t\t\tOnly Steam games\n" +"runner:wine\t\t\tOnly Wine games\n" +"platform:windows\tOnly Windows games\n" +"playtime:>2 hours\tOnly games played for more than 2 " +"hours.\n" +"lastplayed:<2 days\tOnly games played in the last 2 days.\n" +"directory:game/dir\tOnly games at the path." +msgstr "" +"Skriv inn navnet på et spill å søke etter, eller bruk nøkkelord:\n" +"\n" +"installed:true\t\t\tBare installerte spill.\n" +"hidden:true\t\t\tBare skjulte spill.\n" +"favorite:true\t\t\tBare favorittspill.\n" +"categorized:true\t\tBare kategoriserte spill.\n" +"category:x\t\t\tBare spill kategorisert i x.\n" +"source:steam\t\t\tBare Steam-spill\n" +"runner:wine\t\t\tBare Wine-spill\n" +"platform:windows\tBare Windows-spill\n" +"playtime:>2 hours\tBare spill spilt i mer enn 2 timer.\n" +"lastplayed:<2 days\tBare spill spilt de siste 2 dagene.\n" +"directory:game/dir\tBare spill i mappa." + +#: share/lutris/ui/lutris-window.ui:328 lutris/gui/lutriswindow.py:809 +msgid "Search games" +msgstr "Søk etter spill" + +#: share/lutris/ui/lutris-window.ui:376 +msgid "Add Game" +msgstr "Legg til spill" + +#: share/lutris/ui/lutris-window.ui:408 +msgid "Toggle View" +msgstr "Endre visning" + +#: share/lutris/ui/lutris-window.ui:506 +msgid "Zoom " +msgstr "Skalering " + +#: share/lutris/ui/lutris-window.ui:548 +msgid "Sort Installed First" +msgstr "Sorter etter installert først" + +#: share/lutris/ui/lutris-window.ui:562 +msgid "Reverse order" +msgstr "Omvendt rekkefølge" + +#: share/lutris/ui/lutris-window.ui:588 +#: lutris/gui/config/edit_category_games.py:35 +#: lutris/gui/config/edit_saved_search.py:47 +#: lutris/gui/config/game_common.py:181 lutris/gui/views/list.py:65 +#: lutris/gui/views/list.py:215 +msgid "Name" +msgstr "Navn" + +#: share/lutris/ui/lutris-window.ui:603 lutris/gui/views/list.py:67 +msgid "Year" +msgstr "År" + +#: share/lutris/ui/lutris-window.ui:618 lutris/gui/views/list.py:70 +msgid "Last Played" +msgstr "Sist spilt" + +#: share/lutris/ui/lutris-window.ui:633 lutris/gui/views/list.py:72 +msgid "Installed At" +msgstr "Installasjonstidspunkt" + +#: share/lutris/ui/lutris-window.ui:648 lutris/gui/views/list.py:71 +msgid "Play Time" +msgstr "Spilletid" + +#: share/lutris/ui/lutris-window.ui:677 +msgid "_Installed Games Only" +msgstr "_Bare installerte spill" + +#: share/lutris/ui/lutris-window.ui:692 +msgid "Show Side _Panel" +msgstr "Vis side_stolpe" + +#: share/lutris/ui/lutris-window.ui:707 +msgid "Show _Hidden Games" +msgstr "Vis _skjulte spill" + +#: share/lutris/ui/lutris-window.ui:728 +msgid "Preferences" +msgstr "Innstillinger" + +#: share/lutris/ui/lutris-window.ui:753 +msgid "Discord" +msgstr "Discord" + +#: share/lutris/ui/lutris-window.ui:767 +msgid "Lutris forums" +msgstr "Lutris-forum" + +#: share/lutris/ui/lutris-window.ui:781 +msgid "Make a donation" +msgstr "Gi pengegave" + +#: share/lutris/ui/uninstall-dialog.ui:53 +msgid "Uninstalling games" +msgstr "Avinstallerer spill" + +#: share/lutris/ui/uninstall-dialog.ui:92 +msgid "Delete All Files\t" +msgstr "Slett alle filer\t" + +#: share/lutris/ui/uninstall-dialog.ui:108 +msgid "Remove All Games from Library" +msgstr "Fjern alle spill fra biblioteket" + +#: share/lutris/ui/uninstall-dialog.ui:135 +msgid "Remove Games" +msgstr "Fjern spill" + +#: share/lutris/ui/uninstall-dialog.ui:138 lutris/gui/addgameswindow.py:92 +#: lutris/gui/addgameswindow.py:539 lutris/gui/addgameswindow.py:542 +#: lutris/gui/dialogs/__init__.py:168 lutris/gui/dialogs/runner_install.py:275 +#: lutris/gui/installerwindow.py:102 lutris/gui/installerwindow.py:1077 +#: lutris/gui/widgets/download_collection_progress_box.py:153 +#: lutris/gui/widgets/download_progress_box.py:116 +msgid "Cancel" +msgstr "Avbryt" + +#: share/lutris/ui/uninstall-dialog.ui:147 lutris/game_actions.py:198 +#: lutris/game_actions.py:254 +msgid "Remove" +msgstr "Fjern" + +#: lutris/api.py:44 +msgid "never" +msgstr "aldri" + +#: lutris/cache.py:84 +#, python-format +msgid "" +"The cache path '%s' does not exist, but its parent does so it will be " +"created when needed." +msgstr "" +"Hurtiglager mappa «%s» finnes ikke, men dens overmappe finnes, den vil bli " +"opprettet når det trengs" + +#: lutris/cache.py:88 +#, python-format +msgid "" +"The cache path %s does not exist, nor does its parent, so it won't be " +"created." +msgstr "" +"Hurtiglager mappa «%s» og overmappa dens finnes ikke, den blir ikke " +"opprettet." + +#: lutris/exceptions.py:35 +msgid "The directory {} could not be found" +msgstr "Klarte ikke finne mappa {}" + +#: lutris/exceptions.py:49 +msgid "A bios file is required to run this game" +msgstr "Trenger en bios-fil for å kjøre dette spillet" + +#: lutris/exceptions.py:62 +#, python-brace-format +msgid "" +"The following {arch} libraries are required but are not installed on your " +"system:\n" +"{libs}" +msgstr "" +"De følgende {arch}-bibliotekene er nødvendige, men er ikke installert på " +"ditt system:\n" +"{libs}" + +#: lutris/exceptions.py:86 lutris/exceptions.py:98 +msgid "The file {} could not be found" +msgstr "Klarte ikke finne filen {}" + +#: lutris/exceptions.py:100 +msgid "" +"This game has no executable set. The install process didn't finish properly." +msgstr "" +"Dette spillet har ingen programfil satt opp. Installasjonsprosessen kunne " +"ikke fullføre skikkelig" + +#: lutris/exceptions.py:115 +msgid "Your ESYNC limits are not set correctly." +msgstr "ESYNC-grensene dine er ikke satt opp riktig." + +#: lutris/exceptions.py:125 +msgid "" +"Your kernel is not patched for fsync. Please get a patched kernel to use " +"fsync." +msgstr "Kjernen din støtter ikke Fsync. Installer en kjerne som støtter Fsync." + +#: lutris/game_actions.py:190 lutris/game_actions.py:228 +#: lutris/gui/widgets/game_bar.py:217 +msgid "Stop" +msgstr "Stopp" + +#: lutris/game_actions.py:192 lutris/game_actions.py:233 +#: lutris/gui/config/edit_game_categories.py:18 +#: lutris/gui/config/edit_saved_search.py:72 lutris/gui/widgets/sidebar.py:400 +msgid "Categories" +msgstr "Kategorier" + +#: lutris/game_actions.py:193 lutris/game_actions.py:235 +msgid "Add to favorites" +msgstr "Legg til i favoritter" + +#: lutris/game_actions.py:194 lutris/game_actions.py:236 +msgid "Remove from favorites" +msgstr "Fjern fra favoritter" + +#: lutris/game_actions.py:195 lutris/game_actions.py:237 +msgid "Hide game from library" +msgstr "Skjul spillet fra biblioteket" + +#: lutris/game_actions.py:196 lutris/game_actions.py:238 +msgid "Unhide game from library" +msgstr "Synliggjør spillet i biblioteket" + +#. Local services don't show an install dialog, they can be launched directly +#: lutris/game_actions.py:227 lutris/gui/widgets/game_bar.py:210 +#: lutris/gui/widgets/game_bar.py:227 +msgid "Play" +msgstr "Spill" + +#: lutris/game_actions.py:229 +msgid "Execute script" +msgstr "Kjør skript" + +#: lutris/game_actions.py:230 +msgid "Show logs" +msgstr "Vis logg" + +#: lutris/game_actions.py:232 lutris/gui/widgets/sidebar.py:235 +msgid "Configure" +msgstr "Sett opp" + +#: lutris/game_actions.py:234 +msgid "Browse files" +msgstr "Bla gjennom filer" + +#: lutris/game_actions.py:240 lutris/game_actions.py:467 +#: lutris/gui/dialogs/runner_install.py:284 +#: lutris/gui/installer/script_box.py:62 lutris/gui/widgets/game_bar.py:221 +msgid "Install" +msgstr "Installer" + +#: lutris/game_actions.py:241 +msgid "Install another version" +msgstr "Installer en annen versjon" + +#: lutris/game_actions.py:242 +msgid "Install DLCs" +msgstr "Installer DLC-er" + +#: lutris/game_actions.py:243 +msgid "Install updates" +msgstr "Installer oppdateringer" + +#: lutris/game_actions.py:244 lutris/game_actions.py:468 +#: lutris/gui/widgets/game_bar.py:197 +msgid "Locate installed game" +msgstr "Søk etter installert spill" + +#: lutris/game_actions.py:245 lutris/gui/installerwindow.py:451 +msgid "Create desktop shortcut" +msgstr "Lag snarvei til skrivebord" + +#: lutris/game_actions.py:246 +msgid "Delete desktop shortcut" +msgstr "Slett skrivebordssnarvei" + +#: lutris/game_actions.py:247 lutris/gui/installerwindow.py:458 +msgid "Create application menu shortcut" +msgstr "Lag snarvei til programstarter" + +#: lutris/game_actions.py:248 +msgid "Delete application menu shortcut" +msgstr "Slett programstarter-snarvei" + +#: lutris/game_actions.py:249 lutris/gui/installerwindow.py:466 +msgid "Create Steam shortcut" +msgstr "Lag snarvei til Steam" + +#: lutris/game_actions.py:250 +msgid "Delete Steam shortcut" +msgstr "Slett Steam-snarvei" + +#: lutris/game_actions.py:251 lutris/game_actions.py:469 +msgid "View on Lutris.net" +msgstr "Vis på Lutris.net" + +#: lutris/game_actions.py:252 +msgid "Duplicate" +msgstr "Lag kopi" + +#: lutris/game_actions.py:336 +msgid "This game has no installation directory" +msgstr "Spillet har ingen installasjonsmappe" + +#: lutris/game_actions.py:340 +#, python-format +msgid "" +"Can't open %s \n" +"The folder doesn't exist." +msgstr "" +"Klarte ikke finne %s\n" +"Mappa finnes ikke." + +#: lutris/game_actions.py:390 +#, python-format +msgid "" +"Do you wish to duplicate %s?\n" +"The configuration will be duplicated, but the games files will not be " +"duplicated.\n" +"Please enter the new name for the copy:" +msgstr "" +"Ønsker du å lage kopi av %s?\n" +"Oppsettet vil bli kopiert, men spillene dine blir ikke kopiert.\n" +"Skriv inn nytt navn for kopien:" + +#: lutris/game_actions.py:395 +msgid "Duplicate game?" +msgstr "Lag ny kopi av spillet?" + +#. use primary configuration +#: lutris/game_actions.py:446 +msgid "Select shortcut target" +msgstr "Velg målet til snarveien" + +#: lutris/game.py:321 lutris/game.py:508 +msgid "Invalid game configuration: Missing runner" +msgstr "Spilloppsettet er ikke gyldig: Mangler løper" + +#: lutris/game.py:387 +msgid "No updates found" +msgstr "Fant ingen oppdateringer" + +#: lutris/game.py:406 +msgid "No DLC found" +msgstr "Fant ingen DLC-er" + +#: lutris/game.py:440 +msgid "Uninstall the game before deleting" +msgstr "Avinstaller spillet før sletting" + +#: lutris/game.py:506 +msgid "Tried to launch a game that isn't installed." +msgstr "Forsøkte å starte et spill som ikke er installert." + +#: lutris/game.py:540 +msgid "Unable to find Xephyr, install it or disable the Xephyr option" +msgstr "Fant ikke Xephyr, installer eller slå av Xephyr" + +#: lutris/game.py:556 +msgid "Unable to find Antimicrox, install it or disable the Antimicrox option" +msgstr "Fant ikke Antimicrox, installer eller slå av Antimicrox" + +#: lutris/game.py:593 +#, python-format +msgid "" +"The selected terminal application could not be launched:\n" +"%s" +msgstr "" +"Klarte ikke starte det valgte terminalprogrammet:\n" +"%s" + +#: lutris/game.py:896 +msgid "Error lauching the game:\n" +msgstr "Feil ved oppstart av spillet:\n" + +#: lutris/game.py:1002 +#, python-format +msgid "" +"Error: Missing shared library.\n" +"\n" +"%s" +msgstr "" +"Feil: Mangler delt bibliotek.\n" +"\n" +"%s" + +#: lutris/game.py:1008 +msgid "" +"Error: A different Wine version is already using the same Wine prefix." +msgstr "" +"Feil: En annen Wine-versjon bruker allerede den samme Wine-prefiksen." + +#: lutris/game.py:1026 +#, python-format +msgid "Lutris can't move '%s' to a location inside of itself, '%s'." +msgstr "Lutris kan ikke flytte «%s» til en plassering inne i seg selv, «%s»." + +#: lutris/gui/addgameswindow.py:24 +msgid "Search the Lutris website for installers" +msgstr "Søk på Lutris-nettstedet for installatører" + +#: lutris/gui/addgameswindow.py:25 +msgid "Query our website for community installers" +msgstr "Søk på nettstedet våres etter installatører laget av samfunnet" + +#: lutris/gui/addgameswindow.py:31 +msgid "Install a Windows game from an executable" +msgstr "Installer et Windows-spill fra en programfil" + +#: lutris/gui/addgameswindow.py:32 +msgid "Launch a Windows executable (.exe) installer" +msgstr "Start en Windows-programfil (.exe)" + +#: lutris/gui/addgameswindow.py:38 +msgid "Install from a local install script" +msgstr "Installer fra et lokalt installasjonsskript" + +#: lutris/gui/addgameswindow.py:39 +msgid "Run a YAML install script" +msgstr "Kjør et YAML-installasjonsskript" + +#: lutris/gui/addgameswindow.py:45 +msgid "Import ROMs" +msgstr "Importer ROM" + +#: lutris/gui/addgameswindow.py:46 +msgid "Import ROMs referenced in TOSEC, No-intro or Redump" +msgstr "Importer ROM referert i TOSEC, No-intro eller Redump" + +#: lutris/gui/addgameswindow.py:52 +msgid "Add locally installed game" +msgstr "Legg til lokalt installert spill" + +#: lutris/gui/addgameswindow.py:53 +msgid "Manually configure a game available locally" +msgstr "Sett opp et lokalt tilgjengelig spill manuelt" + +#: lutris/gui/addgameswindow.py:59 +msgid "Add games to Lutris" +msgstr "Legg til spill i Lutris" + +#. Header bar buttons +#: lutris/gui/addgameswindow.py:80 lutris/gui/installerwindow.py:95 +msgid "Back" +msgstr "Tilbake" + +#. Continue Button +#: lutris/gui/addgameswindow.py:88 lutris/gui/addgameswindow.py:524 +#: lutris/gui/installerwindow.py:105 lutris/gui/installerwindow.py:1025 +msgid "_Continue" +msgstr "_Fortsett" + +#: lutris/gui/addgameswindow.py:111 lutris/gui/config/storage_box.py:72 +#: lutris/gui/config/widget_generator.py:652 +msgid "Select folder" +msgstr "Velg mappe" + +#: lutris/gui/addgameswindow.py:115 +msgid "Identifier" +msgstr "Identifikator" + +#: lutris/gui/addgameswindow.py:125 +msgid "Select script" +msgstr "Velg skript" + +#: lutris/gui/addgameswindow.py:128 +msgid "Select ROMs" +msgstr "Velg ROM" + +#: lutris/gui/addgameswindow.py:204 +msgid "" +"Lutris will search Lutris.net for games matching the terms you enter, and " +"any that it finds will appear here.\n" +"\n" +"When you click on a game that it found, the installer window will appear to " +"perform the installation." +msgstr "" +"Lutris vil bruke Lutris.net for søk etter spill som samsvarer med nøkkelord " +"du skriver inn, og alle som den finner vil vises her.\n" +"\n" +"Når du trykker på et spill som det fant, installasjonsvinduet vil vises og " +"utføre installasjonen." + +#: lutris/gui/addgameswindow.py:225 +msgid "Search Lutris.net" +msgstr "Søk på Lutris.net" + +#: lutris/gui/addgameswindow.py:256 +msgid "No results" +msgstr "Ingen resultater" + +#: lutris/gui/addgameswindow.py:258 +#, python-format +msgid "Showing %s results" +msgstr "Viser resultater fra %s" + +#: lutris/gui/addgameswindow.py:260 +#, python-format +msgid "%s results, only displaying first %s" +msgstr "%s resultater, viser bare de første %s" + +#: lutris/gui/addgameswindow.py:289 +msgid "Game name" +msgstr "Spillnavn" + +#: lutris/gui/addgameswindow.py:305 +msgid "" +"Enter the name of the game you will install.\n" +"\n" +"When you click 'Install' below, the installer window will appear and guide " +"you through a simple installation.\n" +"\n" +"It will prompt you for a setup executable, and will use Wine to install it.\n" +"\n" +"If you know the Lutris identifier for the game, you can provide it for " +"improved Lutris integration, such as Lutris provided banners." +msgstr "" +"Skriv inn navnet på spillet du vil installere.\n" +"\n" +"Når du trykker «Installer» nedenfor, vil installasjonsvinduet vises og " +"hjelpe deg gjennom en enkel installasjon.\n" +"\n" +"Den vil be deg om en programfil, og vil bruke Wine til å installere den.\n" +"\n" +"Hvis kjenner til Lutris-identifikatoren for spillet, kan du oppgi den for " +"forbedret Lutris-integrasjon, slik som Lutris leverte bannere." + +#: lutris/gui/addgameswindow.py:314 +msgid "Installer preset:" +msgstr "Forvalgt installatør" + +#: lutris/gui/addgameswindow.py:317 +msgid "Windows 10 64-bit (Default)" +msgstr "Windows 10 64-bit (Standard)" + +#: lutris/gui/addgameswindow.py:318 +msgid "Windows 7 64-bit" +msgstr "Windows 7 64-bit" + +#: lutris/gui/addgameswindow.py:319 +msgid "Windows XP 32-bit" +msgstr "Windows XP 32-bit" + +#: lutris/gui/addgameswindow.py:320 +msgid "Windows XP + 3DFX 32-bit" +msgstr "Windows XP + 3DFX 32-bit" + +#: lutris/gui/addgameswindow.py:321 +msgid "Windows 98 32-bit" +msgstr "Windows 98 32-bit" + +#: lutris/gui/addgameswindow.py:322 +msgid "Windows 98 + 3DFX 32-bit" +msgstr "Windows 98 + 3DFX 32-bit" + +#: lutris/gui/addgameswindow.py:333 +msgid "Locale:" +msgstr "Lokale:" + +#: lutris/gui/addgameswindow.py:358 +msgid "Select setup file" +msgstr "Velg oppsettsfil" + +#: lutris/gui/addgameswindow.py:360 lutris/gui/addgameswindow.py:442 +#: lutris/gui/addgameswindow.py:483 lutris/gui/installerwindow.py:1060 +msgid "_Install" +msgstr "_Installer" + +#: lutris/gui/addgameswindow.py:376 +msgid "You must provide a name for the game you are installing." +msgstr "Du må skrive inn navn for spillet du er i ferd med å installere." + +#: lutris/gui/addgameswindow.py:396 +msgid "Setup file" +msgstr "Oppsettsfil" + +#: lutris/gui/addgameswindow.py:402 +msgid "Select the setup file" +msgstr "Velg oppsettsfila" + +#: lutris/gui/addgameswindow.py:423 +msgid "Script file" +msgstr "Skriptfil" + +#: lutris/gui/addgameswindow.py:429 +msgid "" +"Lutris install scripts are YAML files that guide Lutris through the " +"installation process.\n" +"\n" +"They can be obtained on Lutris.net, or written by hand.\n" +"\n" +"When you click 'Install' below, the installer window will appear and load " +"the script, and it will guide the process from there." +msgstr "" +"Lutris-installasjonsskript er YAML-filer som viser Lutris veien gjennom " +"installasjonsprosessen.\n" +"\n" +"De kan hentes fra Lutris.net, eller skrives for hånd.\n" +"\n" +"Når du trykker «Installer» nedenfor, vil installasjonsvinduet vises og laste " +"skriptet, og vil hjelpe deg gjennom prosessen derfra." + +#: lutris/gui/addgameswindow.py:440 +msgid "Select a Lutris installer" +msgstr "Velg Lutris-installatør" + +#: lutris/gui/addgameswindow.py:447 +msgid "You must select a script file to install." +msgstr "Du må velge en skriptfil å installere." + +#: lutris/gui/addgameswindow.py:449 +#, python-format +msgid "No file exists at '%s'." +msgstr "Ingen fil finnes på «%s»." + +#: lutris/gui/addgameswindow.py:464 lutris/runners/atari800.py:37 +#: lutris/runners/duckstation.py:27 lutris/runners/jzintv.py:21 +#: lutris/runners/libretro.py:76 lutris/runners/mame.py:80 +#: lutris/runners/mednafen.py:59 lutris/runners/mupen64plus.py:21 +#: lutris/runners/o2em.py:47 lutris/runners/openmsx.py:20 +#: lutris/runners/osmose.py:20 lutris/runners/snes9x.py:29 +#: lutris/runners/vice.py:37 lutris/runners/yuzu.py:23 +msgid "ROM file" +msgstr "ROM-fil" + +#: lutris/gui/addgameswindow.py:470 +msgid "" +"Lutris will identify ROMs via its MD5 hash and download game information " +"from Lutris.net.\n" +"\n" +"The ROM data used for this comes from TOSEC, No-Intro and Redump projects.\n" +"\n" +"When you click 'Install' below, the process of installing the games will " +"begin." +msgstr "" +"Lutris vil identifisere ROM via dens MD5-sjekksum og laste ned " +"spillinformasjon fra Lutris.net.\n" +"\n" +"ROM-dataen som brukes kommer fra TOSEC, No-intro og Redump-prosjektene.\n" +"\n" +"Når du trykker «Installer» nedenfor, vil prosessen med installering av " +"spillene begynne." + +#: lutris/gui/addgameswindow.py:481 +msgid "Select ROMs" +msgstr "Velg ROM" + +#: lutris/gui/addgameswindow.py:492 +msgid "You must select ROMs to install." +msgstr "Du må velge en ROM å installere." + +#: lutris/gui/application.py:102 +msgid "Do not run Lutris as root." +msgstr "Ikke kjør Lutris som rot." + +#: lutris/gui/application.py:113 +msgid "Your Linux distribution is too old. Lutris won't function properly." +msgstr "Din Linux-distribusjon er for gammel. Lutris vil ikke fungere riktig." + +#: lutris/gui/application.py:119 +msgid "" +"Run a game directly by adding the parameter lutris:rungame/game-identifier.\n" +"If several games share the same identifier you can use the numerical ID " +"(displayed when running lutris --list-games) and add lutris:rungameid/" +"numerical-id.\n" +"To install a game, add lutris:install/game-identifier." +msgstr "" +"Start et spill direkte ved å legge til parameteret «lutris:rungame/" +"spillidentifikator».\n" +"Hvis flere spill bruker samme identifikator, kan du bruke ID-en til spillet " +"(vises ved bruk av «lutris --list-games») og legg til lutris:rungameid/spill-" +"ID.\n" +"For å installere et spill, legg til «lutris:install/spillidentifikator»." + +#: lutris/gui/application.py:133 +msgid "Print the version of Lutris and exit" +msgstr "Vis versjonsnummeret og avslutt" + +#: lutris/gui/application.py:141 +msgid "Show debug messages" +msgstr "Vis feilsøkingsmeldinger" + +#: lutris/gui/application.py:149 +msgid "Install a game from a yml file" +msgstr "Installer et spill fra en yml-fil" + +#: lutris/gui/application.py:157 +msgid "Force updates" +msgstr "Tving gjennom oppdateringer" + +#: lutris/gui/application.py:165 +msgid "Generate a bash script to run a game without the client" +msgstr "Generer et bash-skript for å kjøre et spill uten programmet" + +#: lutris/gui/application.py:173 +msgid "Execute a program with the Lutris Runtime" +msgstr "Kjør et program med Lutris-kjøretiden" + +#: lutris/gui/application.py:181 +msgid "List all games in database" +msgstr "Vis alle spillene fra databasen i en liste" + +#: lutris/gui/application.py:189 +msgid "Only list installed games" +msgstr "Vis bare installerte spill" + +#: lutris/gui/application.py:197 +msgid "List available Steam games" +msgstr "Vis alle tilgjengelige Steam-spill" + +#: lutris/gui/application.py:205 +msgid "List all known Steam library folders" +msgstr "Vis alle kjente Steam-bibliotekmappene" + +#: lutris/gui/application.py:213 +msgid "List all known runners" +msgstr "Vis alle kjente løpere" + +#: lutris/gui/application.py:221 +msgid "List all known Wine versions" +msgstr "Vis alle kjente Wine-versjoner" + +#: lutris/gui/application.py:229 +msgid "List all games for all services in database" +msgstr "Vis alle spillene fra alle tjenestene i databasen" + +#: lutris/gui/application.py:237 +msgid "List all games for provided service in database" +msgstr "Vis alle spillene gitte tjenester i databasen" + +#: lutris/gui/application.py:245 +msgid "Install a Runner" +msgstr "Installer en løper" + +#: lutris/gui/application.py:253 +msgid "Uninstall a Runner" +msgstr "Avinstaller en løper" + +#: lutris/gui/application.py:261 +msgid "Export a game" +msgstr "Eksporter et spill" + +#: lutris/gui/application.py:269 lutris/gui/dialogs/game_import.py:23 +msgid "Import a game" +msgstr "Importer et spill" + +#: lutris/gui/application.py:278 +msgid "Show statistics about a game saves" +msgstr "Vis statistikk om lagrede spill" + +#: lutris/gui/application.py:286 +msgid "Upload saves" +msgstr "Last opp lagrede spill" + +#: lutris/gui/application.py:294 +msgid "Verify status of save syncing" +msgstr "Bekreft status for synkronisering av lagrede spill" + +#: lutris/gui/application.py:303 +msgid "Destination path for export" +msgstr "Målmappe for eksport" + +#: lutris/gui/application.py:311 +msgid "Display the list of games in JSON format" +msgstr "Vis liste over spill i JSON-format" + +#: lutris/gui/application.py:319 +msgid "Reinstall game" +msgstr "Installer spillet på nytt" + +#: lutris/gui/application.py:322 +msgid "Submit an issue" +msgstr "Send inn en sak" + +#: lutris/gui/application.py:328 +msgid "URI to open" +msgstr "URI som skal åpnes" + +#: lutris/gui/application.py:413 +msgid "No installer available." +msgstr "Ingen installatør tilgjengelig." + +#: lutris/gui/application.py:629 +#, python-format +msgid "%s is not a valid URI" +msgstr "%s er ikke en gyldig URI" + +#: lutris/gui/application.py:652 +#, python-format +msgid "Failed to download %s" +msgstr "Klarte ikke laste ned %s" + +#: lutris/gui/application.py:661 +#, python-brace-format +msgid "download {url} to {file} started" +msgstr "nedlasting fra {url} til {file} begynt" + +#: lutris/gui/application.py:672 +#, python-format +msgid "No such file: %s" +msgstr "Manglende fil: %s" + +#: lutris/gui/config/accounts_box.py:39 +msgid "Sync Again" +msgstr "Synkroniser igjen" + +#: lutris/gui/config/accounts_box.py:49 +msgid "Steam accounts" +msgstr "Steam-kontoer" + +#: lutris/gui/config/accounts_box.py:52 +msgid "" +"Select which Steam account is used for Lutris integration and creating Steam " +"shortcuts." +msgstr "" +"Velg hvilken Steam-konto som skal brukes for Lutris-integrasjon, og " +"opprettelse av Steam-snarveier." + +#: lutris/gui/config/accounts_box.py:89 +#, python-format +msgid "Connected as %s" +msgstr "Tilkoblet som %s" + +#: lutris/gui/config/accounts_box.py:91 +msgid "Not connected" +msgstr "Ikke koblet til" + +#: lutris/gui/config/accounts_box.py:96 +msgid "Logout" +msgstr "Logg ut" + +#: lutris/gui/config/accounts_box.py:99 +msgid "Login" +msgstr "Logg inn" + +#: lutris/gui/config/accounts_box.py:112 +msgid "Keep your game library synced with Lutris.net" +msgstr "Hold ditt spillbibliotek synkronisert med Lutris.net" + +#: lutris/gui/config/accounts_box.py:125 +msgid "" +"This will send play time, last played, runner, platform \n" +"and store information to the lutris website so you can \n" +"sync this data on multiple devices" +msgstr "" +"Dette vil sende spilletid, sist spilt, løper, plattform \n" +"og lagre informasjon til Lutris-nettsiden så du kan \n" +"synkronisere denne dataen på flere enheter" + +#: lutris/gui/config/accounts_box.py:153 +msgid "No Steam account found" +msgstr "Fant ingen Steam-konto" + +#: lutris/gui/config/accounts_box.py:180 +msgid "Syncing library..." +msgstr "Synkroniserer bibliotek …" + +#: lutris/gui/config/accounts_box.py:189 +#, python-format +msgid "Last synced %s." +msgstr "Sist synkronisert %s." + +#: lutris/gui/config/accounts_box.py:201 +msgid "Synchronize library?" +msgstr "Synkroniser biblioteket?" + +#: lutris/gui/config/accounts_box.py:202 +msgid "Enable library sync and run a full sync with lutris.net?" +msgstr "" +"Slå på synkronisering av biblioteket og begynn en full synkronisering med " +"lutris.net?" + +#: lutris/gui/config/add_game_dialog.py:11 +msgid "Add a new game" +msgstr "Legg til et nytt spill" + +#: lutris/gui/config/boxes.py:211 +msgid "" +"If modified, these options supersede the same options from the base runner " +"configuration." +msgstr "" +"Hvis endret, erstatter disse innstillingene de samme fra " +"grunnkjøreroppsettet." + +#: lutris/gui/config/boxes.py:237 +msgid "" +"If modified, these options supersede the same options from the base runner " +"configuration, which themselves supersede the global preferences." +msgstr "" +"Hvis endret, erstatter disse innstillingene de samme fra " +"grunnkjøreroppsettet, som også overstyrer det globale oppsettet." + +#: lutris/gui/config/boxes.py:244 +msgid "" +"If modified, these options supersede the same options from the global " +"preferences." +msgstr "" +"Hvis endret, erstatter disse innstillingene de samme fra det globale " +"oppsettet." + +#: lutris/gui/config/boxes.py:293 +msgid "Reset option to global or default config" +msgstr "Tilbakestiller til globale- eller standardinnstillinger" + +#: lutris/gui/config/boxes.py:323 +msgid "" +"(Italic indicates that this option is modified in a lower configuration " +"level.)" +msgstr "" +"(Kursiv indikerer at dette alternativet er endret i et lavere " +"oppsettsnivå.)" + +#: lutris/gui/config/boxes.py:336 +#, python-format +msgid "No options match '%s'" +msgstr "Ingen innstillinger samsvarer med «%s»" + +#: lutris/gui/config/boxes.py:338 +msgid "Only advanced options available" +msgstr "Bare avanserte innstillinger tilgjengelige" + +#: lutris/gui/config/boxes.py:340 +msgid "No options available" +msgstr "Ingen innstillinger tilgjengelige" + +#: lutris/gui/config/edit_category_games.py:18 +#: lutris/gui/config/edit_game.py:10 lutris/gui/config/edit_saved_search.py:315 +#: lutris/gui/config/runner.py:18 +#, python-format +msgid "Configure %s" +msgstr "Sett opp %s" + +#: lutris/gui/config/edit_category_games.py:69 +#, python-format +msgid "Do you want to delete the category '%s'?" +msgstr "Vil du slette kategorien «%s»?" + +#: lutris/gui/config/edit_category_games.py:71 +msgid "" +"This will permanently destroy the category, but the games themselves will " +"not be deleted." +msgstr "" +"Dette vil ødelegge kategorien for alltid, men spillene dem selv vil ikke " +"slettes." + +#: lutris/gui/config/edit_category_games.py:113 +#, python-format +msgid "'%s' is a reserved category name." +msgstr "«%s» er et reservert kategorinavn." + +#: lutris/gui/config/edit_category_games.py:118 +#, python-format +msgid "Merge the category '%s' into '%s'?" +msgstr "Flett kategorien «%s» til «%s»?" + +#: lutris/gui/config/edit_category_games.py:120 +#, python-format +msgid "" +"If you rename this category, it will be combined with '%s'. Do you want to " +"merge them?" +msgstr "" +"Hvis du gir nytt navn til denne kategorien, vil den bli kombinert med «%s». " +"Vil du flette dem sammen?" + +#: lutris/gui/config/edit_game_categories.py:78 +#, python-format +msgid "%d games" +msgstr "%d spill" + +#: lutris/gui/config/edit_game_categories.py:117 +msgid "Add Category" +msgstr "Legg til kategori" + +#: lutris/gui/config/edit_game_categories.py:119 +msgid "Adds the category to the list." +msgstr "Legger kategorien til lista." + +#: lutris/gui/config/edit_game_categories.py:154 +msgid "" +"You are updating the categories on 1 game. Are you sure you want to change " +"it?" +msgstr "" +"Du oppdaterer kategorien til 1 spill. Er du sikker på at du vil endre den?" + +#: lutris/gui/config/edit_game_categories.py:157 +#, python-format +msgid "" +"You are updating the categories on %d games. Are you sure you want to change " +"them?" +msgstr "" +"Du oppdaterer kategoriene til %d spill. Er du sikker på at du vil endre dem?" + +#: lutris/gui/config/edit_game_categories.py:163 +msgid "Changing Categories" +msgstr "Endrer kategoriene" + +#: lutris/gui/config/edit_saved_search.py:19 +msgid "New Saved Search" +msgstr "Nytt lagret søk" + +#: lutris/gui/config/edit_saved_search.py:53 +msgid "Search" +msgstr "Søk" + +#: lutris/gui/config/edit_saved_search.py:130 +msgid "Installed" +msgstr "Installert" + +#: lutris/gui/config/edit_saved_search.py:131 +msgid "Favorite" +msgstr "Favoritt" + +#: lutris/gui/config/edit_saved_search.py:132 lutris/gui/widgets/sidebar.py:493 +msgid "Hidden" +msgstr "Skjult" + +#: lutris/gui/config/edit_saved_search.py:133 +msgid "Categorized" +msgstr "Kategorisert" + +#: lutris/gui/config/edit_saved_search.py:170 +#: lutris/gui/config/edit_saved_search.py:223 +msgid "-" +msgstr "–" + +#: lutris/gui/config/edit_saved_search.py:171 +msgid "Yes" +msgstr "Ja" + +#: lutris/gui/config/edit_saved_search.py:172 +msgid "No" +msgstr "Nei" + +#: lutris/gui/config/edit_saved_search.py:184 +msgid "Source" +msgstr "Kilde" + +#: lutris/gui/config/edit_saved_search.py:191 +#: lutris/gui/config/game_common.py:280 lutris/gui/views/list.py:68 +msgid "Runner" +msgstr "Løper" + +#: lutris/gui/config/edit_saved_search.py:198 lutris/gui/views/list.py:69 +#: lutris/runners/dolphin.py:34 lutris/runners/scummvm.py:259 +msgid "Platform" +msgstr "Plattform" + +#: lutris/gui/config/edit_saved_search.py:292 +#, python-format +msgid "'%s' is already a saved search." +msgstr "«%s» er allerede et lagret søk." + +#: lutris/gui/config/edit_saved_search.py:335 +#, python-format +msgid "Do you want to delete the saved search '%s'?" +msgstr "Vil du slette det lagrede søket «%s»?" + +#: lutris/gui/config/edit_saved_search.py:337 +msgid "" +"This will permanently destroy the saved search, but the games themselves " +"will not be deleted." +msgstr "" +"Dette vil ødelegge det lagrede søket for alltid, men spillene dem selv vil " +"ikke slettes." + +#: lutris/gui/config/game_common.py:35 +msgid "Select a runner in the Game Info tab" +msgstr "Velg en løper i spillinfo-siden" + +#: lutris/gui/config/game_common.py:144 lutris/gui/config/runner.py:27 +#, python-format +msgid "Search %s options" +msgstr "Søk etter %s innstillinger" + +#: lutris/gui/config/game_common.py:146 lutris/gui/config/game_common.py:509 +msgid "Search options" +msgstr "Søkeinnstillinger" + +#: lutris/gui/config/game_common.py:177 +msgid "Game info" +msgstr "Spillinfo" + +#: lutris/gui/config/game_common.py:192 +msgid "Sort name" +msgstr "Sorter etter navn" + +#: lutris/gui/config/game_common.py:208 +#, python-format +msgid "" +"Identifier\n" +"(Internal ID: %s)" +msgstr "" +"Identifikator\n" +"(Intern-ID: %s)" + +#: lutris/gui/config/game_common.py:217 lutris/gui/config/game_common.py:408 +msgid "Change" +msgstr "Endre" + +#: lutris/gui/config/game_common.py:228 +msgid "Directory" +msgstr "Mappe" + +#: lutris/gui/config/game_common.py:234 +msgid "Move" +msgstr "Flytt" + +#: lutris/gui/config/game_common.py:254 +msgid "The default launch option will be used for this game" +msgstr "Standard startinnstillingen vil bli brukt for dette spillet" + +#: lutris/gui/config/game_common.py:256 +#, python-format +msgid "The '%s' launch option will be used for this game" +msgstr "Startinnstillingen «%s» vil bli brukt for dette spillet" + +#: lutris/gui/config/game_common.py:263 +msgid "Reset" +msgstr "Tilbakestill" + +#: lutris/gui/config/game_common.py:294 +msgid "Set custom cover art" +msgstr "Velg tilpasset omslag" + +#: lutris/gui/config/game_common.py:294 +msgid "Remove custom cover art" +msgstr "Fjern tilpasset omslag" + +#: lutris/gui/config/game_common.py:295 +msgid "Set custom banner" +msgstr "Velg tilpasset banner" + +#: lutris/gui/config/game_common.py:295 +msgid "Remove custom banner" +msgstr "Fjern tilpasset banner" + +#: lutris/gui/config/game_common.py:296 +msgid "Set custom icon" +msgstr "Velg tilpasset ikon" + +#: lutris/gui/config/game_common.py:296 +msgid "Remove custom icon" +msgstr "Fjern tilpasset ikon" + +#: lutris/gui/config/game_common.py:329 +msgid "Release year" +msgstr "Utgivelsesår" + +#: lutris/gui/config/game_common.py:342 +msgid "Playtime" +msgstr "Spilletid" + +#: lutris/gui/config/game_common.py:388 +msgid "Select a runner from the list" +msgstr "Velg en løper fra lista" + +#: lutris/gui/config/game_common.py:396 +msgid "Apply" +msgstr "Bruk" + +#: lutris/gui/config/game_common.py:451 lutris/gui/config/game_common.py:460 +#: lutris/gui/config/game_common.py:466 +msgid "Game options" +msgstr "Spillinnstillinger" + +#: lutris/gui/config/game_common.py:471 lutris/gui/config/game_common.py:474 +msgid "Runner options" +msgstr "Løperinnstillinger" + +#: lutris/gui/config/game_common.py:478 +msgid "System options" +msgstr "Systeminnstillinger" + +#: lutris/gui/config/game_common.py:515 +msgid "Show advanced options" +msgstr "Vis avanserte innstillinger" + +#: lutris/gui/config/game_common.py:517 +msgid "Advanced" +msgstr "Avansert" + +#: lutris/gui/config/game_common.py:571 +msgid "" +"Are you sure you want to change the runner for this game ? This will reset " +"the full configuration for this game and is not reversible." +msgstr "" +"Er du sikker på at du vil endre løper for dette spillet? Dette vil " +"tilbakestille hele oppsettet for dette spillet og kan ikke reverseres." + +#: lutris/gui/config/game_common.py:575 +msgid "Confirm runner change" +msgstr "Bekreft endring av løper" + +#: lutris/gui/config/game_common.py:628 +msgid "Runner not provided" +msgstr "Løper ikke oppgitt" + +#: lutris/gui/config/game_common.py:631 +msgid "Please fill in the name" +msgstr "Skriv inn navnet" + +#: lutris/gui/config/game_common.py:634 +msgid "Steam AppID not provided" +msgstr "Steam AppID ikke oppgitt" + +#: lutris/gui/config/game_common.py:660 +msgid "The following fields have invalid values: " +msgstr "Følgende felt har ugyldige verdier: " + +#: lutris/gui/config/game_common.py:667 +msgid "Current configuration is not valid, ignoring save request" +msgstr "Gjeldende oppsett er ikke gyldig, ignorerer lagringsforespørselen" + +#: lutris/gui/config/game_common.py:711 +msgid "Please choose a custom image" +msgstr "Velg et tilpasset bilde" + +#: lutris/gui/config/game_common.py:719 +msgid "Images" +msgstr "Bilder" + +#: lutris/gui/config/preferences_box.py:22 +msgid "Minimize client when a game is launched" +msgstr "Minimer programmet når et spill er i ferd med å starte" + +#: lutris/gui/config/preferences_box.py:24 +msgid "" +"Minimize the Lutris window while playing a game; it will return when the " +"game exits." +msgstr "" +"Minimer vinduet mens du spiller; den vises igjen når spillet avsluttes." + +#: lutris/gui/config/preferences_box.py:28 +msgid "Hide text under icons" +msgstr "Skjul tekst under ikoner" + +#: lutris/gui/config/preferences_box.py:30 +msgid "" +"Removes the names from the Lutris window when in grid view, but not list " +"view." +msgstr "Fjerner spillnavnene i rutenettvisning men ikke i listevisning." + +#: lutris/gui/config/preferences_box.py:34 +msgid "Hide badges on icons (Ctrl+p to toggle)" +msgstr "Skjul merker på ikoner («Ctrl + P» for å slå av/på)" + +#: lutris/gui/config/preferences_box.py:37 +msgid "" +"Removes the platform and missing-game badges from icons in the Lutris window." +msgstr "Fjerner plattform- og manglende spillmerker fra ikoner i vinduet." + +#: lutris/gui/config/preferences_box.py:41 +msgid "Show Tray Icon" +msgstr "Vis systemkurveikon" + +#: lutris/gui/config/preferences_box.py:45 +msgid "" +"Adds a Lutris icon to the tray, and prevents Lutris from exiting when the " +"Lutris window is closed. You can still exit using the menu of the tray icon." +msgstr "" +"Legger til et Lutris-ikon til systemkurven, og hindrer Lutris fra å avslutte " +"når vinduet lukkes. Du kan fortsatt avslutte ved å bruke menyen til ikonet " +"på systemkurven." + +#: lutris/gui/config/preferences_box.py:51 +msgid "Enable Discord Rich Presence for Available Games" +msgstr "Bruk Discord Rich Presence for tilgjengelige spill" + +#: lutris/gui/config/preferences_box.py:57 +msgid "Theme" +msgstr "Tema" + +#: lutris/gui/config/preferences_box.py:59 +msgid "System Default" +msgstr "Systemstandard" + +#: lutris/gui/config/preferences_box.py:60 +msgid "Light" +msgstr "Lys" + +#: lutris/gui/config/preferences_box.py:61 +msgid "Dark" +msgstr "Mørk" + +#: lutris/gui/config/preferences_box.py:64 +msgid "Overrides Lutris's appearance to be light or dark." +msgstr "Overstyrer Lutris sitt utseende til å være lyst eller mørkt." + +#: lutris/gui/config/preferences_box.py:72 +msgid "Interface options" +msgstr "Grensesnittinstillinger" + +#: lutris/gui/config/preferences_dialog.py:23 +msgid "Lutris settings" +msgstr "Lutris-innstillinger" + +#: lutris/gui/config/preferences_dialog.py:35 +msgid "Interface" +msgstr "Grensesnitt" + +#: lutris/gui/config/preferences_dialog.py:36 lutris/gui/widgets/sidebar.py:403 +msgid "Runners" +msgstr "Løpere" + +#: lutris/gui/config/preferences_dialog.py:37 lutris/gui/widgets/sidebar.py:402 +msgid "Sources" +msgstr "Kilder" + +#: lutris/gui/config/preferences_dialog.py:38 +msgid "Accounts" +msgstr "Kontoer" + +#: lutris/gui/config/preferences_dialog.py:39 +msgid "Updates" +msgstr "Oppdateringer" + +#: lutris/gui/config/preferences_dialog.py:40 lutris/sysoptions.py:28 +msgid "System" +msgstr "System" + +#: lutris/gui/config/preferences_dialog.py:41 +msgid "Storage" +msgstr "Lagring" + +#: lutris/gui/config/preferences_dialog.py:42 +msgid "Global options" +msgstr "Globale innstillinger" + +#: lutris/gui/config/preferences_dialog.py:111 +msgid "Search global options" +msgstr "Søk etter globale innstillinger" + +#: lutris/gui/config/runner_box.py:89 lutris/gui/widgets/sidebar.py:254 +#, python-format +msgid "Manage %s versions" +msgstr "Håndter %s versjoner" + +#: lutris/gui/config/runner_box.py:109 +#, python-format +msgid "Do you want to uninstall %s?" +msgstr "Vil du avinstallere %s?" + +#: lutris/gui/config/runner_box.py:110 +#, python-format +msgid "This will remove %s and all associated data." +msgstr "Dette vil fjerne %s og alle tilknyttede data." + +#: lutris/gui/config/runners_box.py:22 +msgid "Add, remove or configure runners" +msgstr "Legg til, fjern eller sett opp løpere" + +#: lutris/gui/config/runners_box.py:25 +msgid "" +"Runners are programs such as emulators, engines or translation layers " +"capable of running games." +msgstr "" +"Løpere er programmer eks. emulatorer, spillmotorer eller oversettelseslag i " +"stand til å kjøre spill." + +#: lutris/gui/config/runners_box.py:28 +msgid "No runners matched the search" +msgstr "Ingen løpere samsvarer med søket" + +#. pretty sure there will always be many runners, so assume plural +#: lutris/gui/config/runners_box.py:47 +#, python-format +msgid "Search %s runners" +msgstr "Søk etter %s løpere" + +#: lutris/gui/config/services_box.py:18 +msgid "Enable integrations with game sources" +msgstr "Bruk integrasjoner med spillkilder" + +#: lutris/gui/config/services_box.py:21 +msgid "" +"Access your game libraries from various sources. Changes require a restart " +"to take effect." +msgstr "" +"Få tilgang til spillbibliotekene dine fra forskjellige kilder. Endringer " +"krever omstart for å tre i kraft." + +#: lutris/gui/config/storage_box.py:24 +msgid "Paths" +msgstr "Mapper" + +#: lutris/gui/config/storage_box.py:36 +msgid "Game library" +msgstr "Spillbibliotek" + +#: lutris/gui/config/storage_box.py:40 lutris/sysoptions.py:87 +msgid "The default folder where you install your games." +msgstr "Standardmappa der du installerer spillene dine." + +#: lutris/gui/config/storage_box.py:43 +msgid "Installer cache" +msgstr "Installasjonshurtiglager" + +#: lutris/gui/config/storage_box.py:48 +msgid "" +"If provided, files downloaded during game installs will be kept there\n" +"\n" +"Otherwise, all downloaded files are discarded." +msgstr "" +"Hvis oppgitt, vil filer som lastes ned under spillinstallasjoner, bli " +"oppbevart der\n" +"\n" +"Ellers blir alle nedlastede filer forkastet." + +#: lutris/gui/config/storage_box.py:53 +msgid "Emulator BIOS files location" +msgstr "Plassering for emulator BIOS-filer" + +#: lutris/gui/config/storage_box.py:57 +msgid "The folder Lutris will search in for emulator BIOS files if needed" +msgstr "Mappa Lutris vil søke gjennom for emulator BIOS-filer, hvis det trengs" + +#: lutris/gui/config/storage_box.py:119 +#, python-format +msgid "Folder is too large (%s)" +msgstr "Mappa er for stor (%s)" + +#: lutris/gui/config/storage_box.py:121 +msgid "Too many files in folder" +msgstr "For mange filer i mappa" + +#: lutris/gui/config/storage_box.py:123 +msgid "Folder is too deep" +msgstr "Mappa er for dyp" + +#: lutris/gui/config/sysinfo_box.py:18 +msgid "Vulkan support" +msgstr "Vulkan støtte" + +#: lutris/gui/config/sysinfo_box.py:22 +msgid "Esync support" +msgstr "Esync støtte" + +#: lutris/gui/config/sysinfo_box.py:26 +msgid "Fsync support" +msgstr "Fsync støtte" + +#: lutris/gui/config/sysinfo_box.py:30 +msgid "Wine installed" +msgstr "Wine installert" + +#: lutris/gui/config/sysinfo_box.py:33 lutris/sysoptions.py:210 +#: lutris/sysoptions.py:219 lutris/sysoptions.py:230 lutris/sysoptions.py:245 +#: lutris/sysoptions.py:261 lutris/sysoptions.py:271 lutris/sysoptions.py:286 +#: lutris/sysoptions.py:298 lutris/sysoptions.py:308 +msgid "Gamescope" +msgstr "Gamescope" + +#: lutris/gui/config/sysinfo_box.py:34 +msgid "Mangohud" +msgstr "Mangohud" + +#: lutris/gui/config/sysinfo_box.py:35 +msgid "Gamemode" +msgstr "GameMode" + +#: lutris/gui/config/sysinfo_box.py:36 lutris/gui/installer/file_box.py:100 +#: lutris/runners/steam.py:30 lutris/services/steam.py:74 +msgid "Steam" +msgstr "Steam" + +#: lutris/gui/config/sysinfo_box.py:37 +msgid "In Flatpak" +msgstr "I Flatpak" + +#: lutris/gui/config/sysinfo_box.py:42 +msgid "System information" +msgstr "Systeminformasjon" + +#: lutris/gui/config/sysinfo_box.py:51 +msgid "Copy system info to Clipboard" +msgstr "Kopier systeminformasjon til utklippstavla" + +#: lutris/gui/config/sysinfo_box.py:56 +msgid "Lutris logs" +msgstr "Lutris-logg" + +#: lutris/gui/config/sysinfo_box.py:65 +msgid "Copy logs to Clipboard" +msgstr "Kopier loggene til utklippstavla" + +#: lutris/gui/config/sysinfo_box.py:133 +msgid "YES" +msgstr "JA" + +#: lutris/gui/config/sysinfo_box.py:134 +msgid "NO" +msgstr "NEI" + +#: lutris/gui/config/updates_box.py:29 +msgid "Wine update channel" +msgstr "Wine oppdateringskanal" + +#: lutris/gui/config/updates_box.py:41 +msgid "Runtime updates" +msgstr "Kjøretid oppdateringer" + +#: lutris/gui/config/updates_box.py:42 +msgid "Runtime components include DXVK, VKD3D and Winetricks." +msgstr "Kjøretid komponenter inkluderer DXVK, VKD3D og Winetricks." + +#: lutris/gui/config/updates_box.py:43 +msgid "Check for Updates" +msgstr "Se etter oppdateringer" + +#: lutris/gui/config/updates_box.py:47 +msgid "Automatically Update the Lutris runtime" +msgstr "Oppdater Lutris-kjøretiden automatisk" + +#: lutris/gui/config/updates_box.py:52 +msgid "Media updates" +msgstr "Medieoppdateringer" + +#: lutris/gui/config/updates_box.py:53 +msgid "Download Missing Media" +msgstr "Last ned manglende media" + +#: lutris/gui/config/updates_box.py:59 +msgid "" +"Stable:\n" +"Wine-GE updates are downloaded automatically and the latest version is " +"always used unless overridden in the settings.\n" +"\n" +"This allows us to keep track of regressions more efficiently and provide " +"fixes more reliably." +msgstr "" +"Stabilt:\n" +"Wine-GE oppdateringer lastes ned automatisk, og den nyeste versjonen brukes " +"alltid med mindre det overstyres i innstillingene\n" +"\n" +"Dette gjør det enklere å holde styr på diverse feil og gi rettelser mer " +"pålitelig." + +#: lutris/gui/config/updates_box.py:71 +msgid "" +"Self-maintained:\n" +"Wine updates are no longer delivered automatically and you have full " +"responsibility of your Wine versions.\n" +"\n" +"Please note that this mode is fully unsupported. In order to submit " +"issues on Github or ask for help on Discord, switch back to the Stable " +"channel." +msgstr "" +"Vedlikeholdes av deg selv:\n" +"Wine-oppdateringer mottas ikke lenger automatisk og du har fullt ansvar for " +"dine Wine-versjoner.\n" +"\n" +"Merk denne modusen mangler støtte. For å sende inn en sak til Github " +"eller spør om hjelp på Discord, bytt tilbake til innstillingen Stabilt." + +#: lutris/gui/config/updates_box.py:90 +msgid "" +"No compatible Wine version could be identified. No updates are available." +msgstr "" +"Klarte ikke finne kompatible Wine-versjoner. Ingen oppdateringer er " +"tilgjengelige." + +#: lutris/gui/config/updates_box.py:91 lutris/gui/config/updates_box.py:100 +msgid "Check Again" +msgstr "Sjekk igjen" + +#: lutris/gui/config/updates_box.py:96 +#, python-format +msgid "" +"Your Wine version is up to date. Using: %s\n" +"Last checked %s." +msgstr "" +"Din Wine-versjon er oppdatert. Bruker: %s\n" +"Sist sjekket %s." + +#: lutris/gui/config/updates_box.py:103 +#, python-format +msgid "" +"You don't have any Wine version installed.\n" +"We recommend %s" +msgstr "" +"Du har ingen Wine-versjoner installert.\n" +"Vi anbefaler %s" + +#: lutris/gui/config/updates_box.py:106 lutris/gui/config/updates_box.py:111 +#, python-format +msgid "Download %s" +msgstr "Last ned %s" + +#: lutris/gui/config/updates_box.py:109 +#, python-format +msgid "You don't have the recommended Wine version: %s" +msgstr "Du har ikke den anbefalte Wine-versjonen: %s" + +#: lutris/gui/config/updates_box.py:135 +msgid "Checking for missing media..." +msgstr "Ser etter manglende medium …" + +#: lutris/gui/config/updates_box.py:142 +msgid "Nothing to update" +msgstr "Ingenting å oppdatere" + +#: lutris/gui/config/updates_box.py:144 +msgid "Updated: " +msgstr "Oppdatert: " + +#: lutris/gui/config/updates_box.py:146 +msgid "banner" +msgstr "banner" + +#: lutris/gui/config/updates_box.py:147 +msgid "icon" +msgstr "ikon" + +#: lutris/gui/config/updates_box.py:148 +msgid "cover" +msgstr "omslag" + +#: lutris/gui/config/updates_box.py:149 +msgid "banners" +msgstr "bannere" + +#: lutris/gui/config/updates_box.py:150 +msgid "icons" +msgstr "ikoner" + +#: lutris/gui/config/updates_box.py:151 +msgid "covers" +msgstr "omslag" + +#: lutris/gui/config/updates_box.py:160 +msgid "No new media found." +msgstr "Fant ingen nye media." + +#: lutris/gui/config/updates_box.py:194 +msgid "Downloading..." +msgstr "Laster ned …" + +#: lutris/gui/config/updates_box.py:196 lutris/gui/config/updates_box.py:231 +msgid "Updates are already being downloaded and installed." +msgstr "Oppdateringer blir allerede lastet ned og installert." + +#: lutris/gui/config/updates_box.py:198 lutris/gui/config/updates_box.py:233 +msgid "No updates are required at this time." +msgstr "Ingen oppdateringer er nødvendige for øyeblikket." + +#: lutris/gui/config/updates_box.py:220 +#, python-format +msgid "%s has been updated." +msgstr "%s har blitt oppdatert." + +#: lutris/gui/config/updates_box.py:222 +#, python-format +msgid "%s have been updated." +msgstr "%s har blitt oppdatert." + +#: lutris/gui/config/updates_box.py:229 +msgid "Checking for updates..." +msgstr "Ser etter oppdateringer …" + +#: lutris/gui/config/updates_box.py:242 +msgid "" +"Without the Wine-GE updates enabled, we can no longer provide support on " +"Github and Discord." +msgstr "" +"Uten Wine-GE oppdateringer i bruk, kan vi ikke gi støtte på Github eller " +"Discord." + +#: lutris/gui/config/widget_generator.py:212 +msgid "Default: " +msgstr "Standard: " + +#: lutris/gui/config/widget_generator.py:374 lutris/runners/wine.py:569 +msgid "Enabled" +msgstr "Slått på" + +#: lutris/gui/config/widget_generator.py:374 lutris/runners/easyrpg.py:372 +#: lutris/runners/mednafen.py:80 lutris/runners/wine.py:568 +msgid "Disabled" +msgstr "Slått av" + +#: lutris/gui/config/widget_generator.py:421 +#, python-format +msgid "%s (default)" +msgstr "%s (standard)" + +#: lutris/gui/config/widget_generator.py:485 +#, python-format +msgid "" +"The setting '%s' is no longer available. You should select another choice." +msgstr "" +"Innstillingen «%s» er ikke lenger tilgjengelig. Du bør velge en annen " +"innstilling." + +#: lutris/gui/config/widget_generator.py:531 lutris/gui/widgets/common.py:49 +msgid "Select file" +msgstr "Velg fil" + +#: lutris/gui/config/widget_generator.py:564 +msgid "Select files" +msgstr "Velg filer" + +#: lutris/gui/config/widget_generator.py:567 +msgid "_Add" +msgstr "_Legg til" + +#: lutris/gui/config/widget_generator.py:568 lutris/gui/dialogs/__init__.py:434 +#: lutris/gui/dialogs/__init__.py:460 lutris/gui/dialogs/issue.py:74 +#: lutris/gui/widgets/common.py:167 +#: lutris/gui/widgets/download_collection_progress_box.py:56 +#: lutris/gui/widgets/download_progress_box.py:64 +msgid "_Cancel" +msgstr "_Avbryt" + +#: lutris/gui/config/widget_generator.py:603 +msgid "Add Files" +msgstr "Legg til filer" + +#: lutris/gui/config/widget_generator.py:623 +#: lutris/installer/installer_file_collection.py:88 +msgid "Files" +msgstr "Filer" + +#: lutris/gui/dialogs/cache.py:12 +msgid "Download cache configuration" +msgstr "Last ned hurtiglageroppsett" + +#: lutris/gui/dialogs/cache.py:35 +msgid "Cache path" +msgstr "Mappe for hurtiglager" + +#: lutris/gui/dialogs/cache.py:38 +msgid "Set the folder for the cache path" +msgstr "Velg mappa for hurtiglageret" + +#: lutris/gui/dialogs/cache.py:52 +msgid "" +"If provided, this location will be used by installers to cache downloaded " +"files locally for future re-use. \n" +"If left empty, the installer files are discarded after the install " +"completion." +msgstr "" +"Hvis oppgitt, denne mappa vil bli brukt av installatører for lagring av " +"nedlastede filer lokalt for fremtidig bruk.\n" +"Hvis tom, forkastes filene etter fullført installasjon." + +#: lutris/gui/dialogs/delegates.py:41 +#, python-format +msgid "The required runner '%s' is not installed." +msgstr "Den nødvendige løperen «%s» er ikke installert." + +#: lutris/gui/dialogs/delegates.py:207 +msgid "Select game to launch" +msgstr "Velg spill å starte" + +#: lutris/gui/dialogs/download.py:14 +msgid "Downloading file" +msgstr "Laster ned fil" + +#: lutris/gui/dialogs/download.py:17 lutris/runtime.py:126 +#, python-format +msgid "Downloading %s" +msgstr "Laster ned %s" + +#: lutris/gui/dialogs/game_import.py:96 +msgid "Launch" +msgstr "Start" + +#: lutris/gui/dialogs/game_import.py:129 +msgid "Calculating checksum..." +msgstr "Kalkulerer sjekksum …" + +#: lutris/gui/dialogs/game_import.py:138 +msgid "Looking up checksum on Lutris.net..." +msgstr "Ser etter sjekksum på Lutris.net …" + +#: lutris/gui/dialogs/game_import.py:141 +msgid "This ROM could not be identified." +msgstr "Klarte ikke identifisere ROM." + +#: lutris/gui/dialogs/game_import.py:150 +msgid "Looking for installed game..." +msgstr "Ser etter installert spill …" + +#: lutris/gui/dialogs/game_import.py:199 +#, python-format +msgid "Failed to import a ROM: %s" +msgstr "Klarte ikke importere ROM: %s" + +#: lutris/gui/dialogs/game_import.py:220 +msgid "Game already installed in Lutris" +msgstr "Spillet er allerede installert" + +#: lutris/gui/dialogs/game_import.py:242 +#, python-format +msgid "The platform '%s' is unknown to Lutris." +msgstr "Plattformen «%s» er ukjent for Lutris." + +#: lutris/gui/dialogs/game_import.py:252 +#, python-format +msgid "Lutris does not have a default installer for the '%s' platform." +msgstr "Lutris har ikke en standard installatør for «%s»-plattformen." + +#: lutris/gui/dialogs/__init__.py:171 +msgid "Save" +msgstr "Lagre" + +#: lutris/gui/dialogs/__init__.py:292 +msgid "Lutris has encountered an error" +msgstr "Lutris har støtt på en feil" + +#: lutris/gui/dialogs/__init__.py:319 lutris/gui/installerwindow.py:952 +msgid "Copy Details to Clipboard" +msgstr "Kopier detaljer til utklippstavla" + +#: lutris/gui/dialogs/__init__.py:339 +msgid "" +"You can get support from GitHub or Discord. Make sure " +"to provide the error details;\n" +"use the 'Copy Details to Clipboard' button to get them." +msgstr "" +"Du kan få støtte fra GitHub " +"eller Discord. Sørg for " +"å oppgi feildetaljene;\n" +"bruk «Kopier detaljer til utklippstavla»-knappen for å hente dem." + +#: lutris/gui/dialogs/__init__.py:348 +msgid "Error details" +msgstr "Feildetaljer" + +#: lutris/gui/dialogs/__init__.py:433 lutris/gui/dialogs/__init__.py:459 +#: lutris/gui/dialogs/issue.py:73 lutris/gui/widgets/common.py:167 +msgid "_OK" +msgstr "_OK" + +#: lutris/gui/dialogs/__init__.py:450 +msgid "Please choose a file" +msgstr "Velg en fil" + +#: lutris/gui/dialogs/__init__.py:474 +#, python-format +msgid "%s is already installed" +msgstr "%s er allerede installert" + +#: lutris/gui/dialogs/__init__.py:484 +msgid "Launch game" +msgstr "Start spill" + +#: lutris/gui/dialogs/__init__.py:488 +msgid "Install the game again" +msgstr "Installer spillet på nytt" + +#: lutris/gui/dialogs/__init__.py:527 +msgid "Do not ask again for this game." +msgstr "Ikke spør igjen for dette spillet." + +#: lutris/gui/dialogs/__init__.py:582 +msgid "Login failed" +msgstr "Innlogging mislyktes" + +#: lutris/gui/dialogs/__init__.py:592 +msgid "Install script for {}" +msgstr "Installasjonsskript for {}" + +#: lutris/gui/dialogs/__init__.py:616 +msgid "Humble Bundle Cookie Authentication" +msgstr "Humble Bundle infokapsel autentisering" + +#: lutris/gui/dialogs/__init__.py:628 +msgid "" +"Humble Bundle Authentication via cookie import\n" +"\n" +"In Firefox\n" +"- Install the following extension: https://addons.mozilla.org/en-US/firefox/" +"addon/export-cookies-txt/\n" +"- Open a tab to humblebundle.com and make sure you are logged in.\n" +"- Click the cookie icon in the top right corner, next to the settings menu\n" +"- Check 'Prefix HttpOnly cookies' and click 'humblebundle.com'\n" +"- Open the generated file and paste the contents below. Click OK to finish.\n" +"- You can delete the cookies file generated by Firefox\n" +"- Optionally, open a support ticket to ask Humble Bundle to fix their " +"configuration." +msgstr "" +"Humble Bundle autentisering via infokapsel import\n" +"\n" +"I Firefox\n" +"- Installer følgende utvidelse: https://addons.mozilla.org/en-US/firefox/" +"addon/export-cookies-txt/\n" +"- Åpne en fane til humblebundle.com og sørg for at du er pålogget.\n" +"- Trykk på infokapsel-ikonet øverst til høyre, ved siden av " +"innstillingsmenyen\n" +"- Sjekk «Prefix HttpOnly cookies» og trykk «humblebundle.com»\n" +"- Åpne den genererte fila og lim inn innholdet nedenfor. Trykk OK for å " +"fullføre.\n" +"- Du kan slette infokapslene generert av Firefox\n" +"- Valgfritt, åpne en forespørsel om støtte for å be Humble Bundle om å fikse " +"deres oppsett." + +#: lutris/gui/dialogs/__init__.py:707 +#, python-format +msgid "The key '%s' could not be found." +msgstr "Fant ikke nøkkelen «%s»." + +#: lutris/gui/dialogs/issue.py:24 +msgid "Submit an issue" +msgstr "Send inn en sak" + +#: lutris/gui/dialogs/issue.py:30 +msgid "" +"Describe the problem you're having in the text box below. This information " +"will be sent the Lutris team along with your system information. You can " +"also save this information locally if you are offline." +msgstr "" +"Beskriv problemet du opplever i tekstboksen nedenfor. Denne informasjonen " +"sendes til Lutris sammen med din systeminformasjon. Du kan også lagre denne " +"informasjonen lokalt hvis du er frakoblet." + +#: lutris/gui/dialogs/issue.py:54 +msgid "_Save" +msgstr "_Lagre" + +#: lutris/gui/dialogs/issue.py:70 +msgid "Select a location to save the issue" +msgstr "Velg mappe for lagring av saken" + +#: lutris/gui/dialogs/issue.py:90 +#, python-format +msgid "Issue saved in %s" +msgstr "Saken lagret i %s" + +#: lutris/gui/dialogs/log.py:23 +msgid "Log for {}" +msgstr "Logg for {}" + +#: lutris/gui/dialogs/move_game.py:28 +#, python-format +msgid "Moving %s to %s..." +msgstr "Flytter %s til %s …" + +#: lutris/gui/dialogs/move_game.py:57 +msgid "" +"Do you want to change the game location anyway? No files can be moved, and " +"the game configuration may need to be adjusted." +msgstr "" +"Vil du endre spillplasseringen likevel? Ingen filer kan flyttes, og " +"spilloppsettet må kanskje justerses." + +#: lutris/gui/dialogs/runner_install.py:59 +#, python-format +msgid "Showing games using %s" +msgstr "Viser spill som bruker %s" + +#: lutris/gui/dialogs/runner_install.py:115 +#, python-format +msgid "Waiting for response from %s" +msgstr "Venter på svar fra %s" + +#: lutris/gui/dialogs/runner_install.py:176 +#, python-format +msgid "Unable to get runner versions: %s" +msgstr "Klarte ikke hente løperversjoner: %s" + +#: lutris/gui/dialogs/runner_install.py:182 +msgid "Unable to get runner versions from lutris.net" +msgstr "Klarte ikke hente løperversjoner fra lutris.net" + +#: lutris/gui/dialogs/runner_install.py:189 +#, python-format +msgid "%s version management" +msgstr "%s versjonsbehandling" + +#: lutris/gui/dialogs/runner_install.py:241 +#, python-format +msgid "View %d game" +msgid_plural "View %d games" +msgstr[0] "Se %d spill" +msgstr[1] "Se %d spillene" + +#: lutris/gui/dialogs/runner_install.py:280 +#: lutris/gui/dialogs/uninstall_dialog.py:219 +msgid "Uninstall" +msgstr "Avinstaller" + +#: lutris/gui/dialogs/runner_install.py:294 +msgid "Wine version usage" +msgstr "Bruk av Wine-versjon" + +#: lutris/gui/dialogs/runner_install.py:365 +#, python-format +msgid "Version %s is not longer available" +msgstr "Versjonen %s er ikke tilgjengelig lengre" + +#: lutris/gui/dialogs/runner_install.py:390 +msgid "Downloading…" +msgstr "Laster ned …" + +#: lutris/gui/dialogs/runner_install.py:393 +msgid "Extracting…" +msgstr "Pakker ut …" + +#: lutris/gui/dialogs/runner_install.py:423 +msgid "Failed to retrieve the runner archive" +msgstr "Klarte ikke hente løperarkiv" + +#: lutris/gui/dialogs/uninstall_dialog.py:124 +#, python-format +msgid "Uninstall %s" +msgstr "Avinstaller %s" + +#: lutris/gui/dialogs/uninstall_dialog.py:126 +#, python-format +msgid "Remove %s" +msgstr "Fjerner %s" + +#: lutris/gui/dialogs/uninstall_dialog.py:128 +#, python-format +msgid "Uninstall %d games" +msgstr "Avinstaller spillene %d" + +#: lutris/gui/dialogs/uninstall_dialog.py:130 +#, python-format +msgid "Remove %d games" +msgstr "Fjern spillene %d" + +#: lutris/gui/dialogs/uninstall_dialog.py:132 +#, python-format +msgid "Uninstall %d games and remove %d games" +msgstr "Avinstaller spillene %d og fjern spillene %d" + +#: lutris/gui/dialogs/uninstall_dialog.py:145 +msgid "After you uninstall these games, you won't be able play them in Lutris." +msgstr "" +"Etter at du har avinstallert disse spillene, vil du ikke kunne spille dem i " +"Lutris." + +#: lutris/gui/dialogs/uninstall_dialog.py:148 +msgid "" +"Uninstalled games that you remove from the library will no longer appear in " +"the 'Games' view, but those that remain will retain their playtime data." +msgstr "" +"Avinstallerte spill som du fjerner fra biblioteket vil ikke vises i «Spill»-" +"visningen, men de som består vil beholde spilltidsdataene." + +#: lutris/gui/dialogs/uninstall_dialog.py:153 +msgid "" +"After you remove these games, they will no longer appear in the 'Games' view." +msgstr "" +"Etter at du har fjernet disse spillene, vil de ikke vises i «Spill»-" +"visningen." + +#: lutris/gui/dialogs/uninstall_dialog.py:158 +msgid "" +"Some of the game directories cannot be removed because they are shared with " +"other games that you are not removing." +msgstr "" +"Noen av spillmappene kan ikke fjernes fordi de deles med andre spill som " +"ikke skal fjernes." + +#: lutris/gui/dialogs/uninstall_dialog.py:164 +msgid "" +"Some of the game directories cannot be removed because they are protected." +msgstr "Noen av spillmappene kan ikke fjernes fordi de er beskyttet." + +#: lutris/gui/dialogs/uninstall_dialog.py:261 +#, python-format +msgid "" +"Please confirm.\n" +"Everything under %s\n" +"will be moved to the trash." +msgstr "" +"Bekreft.\n" +"Alt under %s\n" +"vil flyttes til papirkurven." + +#: lutris/gui/dialogs/uninstall_dialog.py:265 +#, python-format +msgid "" +"Please confirm.\n" +"All the files for %d games will be moved to the trash." +msgstr "" +"Bekreft.\n" +"Alle filene for spillet %d vil flyttes til papirkurven." + +#: lutris/gui/dialogs/uninstall_dialog.py:273 +msgid "Permanently delete files?" +msgstr "Slett filer for alltid?" + +#: lutris/gui/dialogs/uninstall_dialog.py:353 +msgid "Remove from Library" +msgstr "Fjern fra biblioteket" + +#: lutris/gui/dialogs/uninstall_dialog.py:359 +msgid "Delete Files" +msgstr "Slett filer" + +#: lutris/gui/dialogs/webconnect_dialog.py:140 +msgid "Loading..." +msgstr "Laster …" + +#: lutris/gui/installer/file_box.py:84 +#, python-brace-format +msgid "Steam game {appid}" +msgstr "Steam-spill {appid}" + +#: lutris/gui/installer/file_box.py:96 +msgid "Download" +msgstr "Last ned" + +#: lutris/gui/installer/file_box.py:98 +msgid "Use Cache" +msgstr "Bruk hurtiglager" + +#: lutris/gui/installer/file_box.py:102 +msgid "Select File" +msgstr "Velg fil" + +#: lutris/gui/installer/file_box.py:159 +msgid "Cache file for future installations" +msgstr "Hurtiglagringsfil for fremtidige installasjoner" + +#: lutris/gui/installer/file_box.py:178 +msgid "Source:" +msgstr "Kilde:" + +#: lutris/gui/installerwindow.py:127 +msgid "Configure download cache" +msgstr "Sett opp hurtiglager for nedlastinger" + +#: lutris/gui/installerwindow.py:129 +msgid "Change where Lutris downloads game installer files." +msgstr "Endrer hvor Lutris laster ned spillinstallasjonsfiler." + +#: lutris/gui/installerwindow.py:132 +msgid "View installer source" +msgstr "Vis installasjonskilde" + +#: lutris/gui/installerwindow.py:228 +msgid "Remove game files" +msgstr "Fjern spillfiler" + +#: lutris/gui/installerwindow.py:243 +msgid "Are you sure you want to cancel the installation?" +msgstr "Er du sikker på at du vil avbryte installasjonen?" + +#: lutris/gui/installerwindow.py:244 +msgid "Cancel installation?" +msgstr "Avbryte installasjonen?" + +#: lutris/gui/installerwindow.py:322 +msgid "" +"Waiting for Lutris component installation\n" +"Installations can fail if Lutris components are not installed first." +msgstr "" +"Venter på installering av Lutris-komponent\n" +"Feil kan oppstå hvis Lutris-komponentene ikke installeres først." + +#: lutris/gui/installerwindow.py:379 +#, python-format +msgid "Install %s" +msgstr "Installer %s" + +#: lutris/gui/installerwindow.py:401 +#, python-format +msgid "This game requires %s. Do you want to install it?" +msgstr "Spillet trenger %s. Vil du installere den?" + +#: lutris/gui/installerwindow.py:402 +msgid "Missing dependency" +msgstr "Mangler avhengighet" + +#: lutris/gui/installerwindow.py:410 +msgid "Installing {}" +msgstr "Installerer {}" + +#: lutris/gui/installerwindow.py:416 +msgid "No installer available" +msgstr "Ingen installatør tilgjengelig" + +#: lutris/gui/installerwindow.py:422 +#, python-format +msgid "Missing field \"%s\" in install script" +msgstr "Mangler «%s» i installasjonsskriptet" + +#: lutris/gui/installerwindow.py:425 +#, python-format +msgid "Improperly formatted file \"%s\"" +msgstr "Fila «%s» er formatert feil" + +#: lutris/gui/installerwindow.py:475 +msgid "Select installation directory" +msgstr "Velg installasjonsmappe" + +#: lutris/gui/installerwindow.py:485 +msgid "Preparing Lutris for installation" +msgstr "Forbereder Lutris for installasjon" + +#: lutris/gui/installerwindow.py:579 +msgid "" +"This game has extra content\n" +"Select which one you want and they will be available in the 'extras' " +"folder where the game is installed." +msgstr "" +"Dette spillet har ekstra innhold\n" +"Velg hvilken du vil ha og de vil tilgjengeliggjøres i «extras»-mappa " +"hvor spillet er installert." + +#: lutris/gui/installerwindow.py:675 +msgid "" +"Please review the files needed for the installation then click 'Install'" +msgstr "Les gjennom filene som trengs for installasjonen og trykk «Installer»" + +#: lutris/gui/installerwindow.py:683 +msgid "Downloading game data" +msgstr "Laster ned spilldata" + +#: lutris/gui/installerwindow.py:700 +#, python-format +msgid "Unable to get files: %s" +msgstr "Klarte ikke hente filene: %s" + +#: lutris/gui/installerwindow.py:714 +msgid "Installing game data" +msgstr "Installerer spilldata" + +#. Lutris flatplak doesn't autodetect files on CD-ROM properly +#. and selecting this option doesn't let the user click "Back" +#. so the only option is to cancel the install. +#: lutris/gui/installerwindow.py:856 +msgid "Autodetect" +msgstr "Oppdag automatisk" + +#: lutris/gui/installerwindow.py:861 +msgid "Browse…" +msgstr "Bla gjennom …" + +#: lutris/gui/installerwindow.py:868 +msgid "Eject" +msgstr "Løs ut" + +#: lutris/gui/installerwindow.py:880 +msgid "Select the folder where the disc is mounted" +msgstr "Velg mappa der plata er montert" + +#: lutris/gui/installerwindow.py:934 +msgid "" +"An unexpected error has occurred while installing this game. Please share " +"the details below with the Lutris team on GitHub or Discord." +msgstr "" +"Det har oppstått en uventet feil under installasjonen av dette spillet. Del " +"detaljene nedenfor hvis du ønsker støtte fra Lutris på GitHub eller Discord." + +#: lutris/gui/installerwindow.py:993 +msgid "_Launch" +msgstr "_Start" + +#: lutris/gui/installerwindow.py:1073 +msgid "_Abort" +msgstr "_Avbryt" + +#: lutris/gui/installerwindow.py:1074 +msgid "Abort and revert the installation" +msgstr "Avbryt og reverser installasjonen" + +#: lutris/gui/installerwindow.py:1077 +msgid "_Close" +msgstr "_Lukk" + +#: lutris/gui/lutriswindow.py:655 +#, python-format +msgid "Connect your %s account to access your games" +msgstr "koble til %s-kontoen din for å få tilgang til spillene dine" + +#: lutris/gui/lutriswindow.py:736 +#, python-format +msgid "Add a game matching '%s' to your favorites to see it here." +msgstr "" +"Legg til et spill som samsvarer med «%s» til favorittene dine for å se det " +"her." + +#: lutris/gui/lutriswindow.py:738 +#, python-format +msgid "No hidden games matching '%s' found." +msgstr "Fant ingen skjulte spill som samsvarer med «%s»." + +#: lutris/gui/lutriswindow.py:741 +#, python-format +msgid "" +"No installed games matching '%s' found. Press Ctrl+I to show uninstalled " +"games." +msgstr "" +"Fant ingen installerte spill som samsvarer med «%s». Trykk «Ctrl + I» for å " +"vise avinstallerte spill." + +#: lutris/gui/lutriswindow.py:744 +#, python-format +msgid "No games matching '%s' found " +msgstr "Fant ingen spill som samsvarer med «%s»" + +#: lutris/gui/lutriswindow.py:747 +msgid "Add games to your favorites to see them here." +msgstr "Legg til spill i favorittene dine for å se dem her." + +#: lutris/gui/lutriswindow.py:749 +msgid "No games are hidden." +msgstr "Ingen spill er skjult." + +#: lutris/gui/lutriswindow.py:751 +msgid "No installed games found. Press Ctrl+I to show uninstalled games." +msgstr "" +"Fant ingen installerte spill. Trykk «Ctrl + I» for å vise avinstallerte " +"spill." + +#: lutris/gui/lutriswindow.py:760 +msgid "No games found" +msgstr "Fant ingen spill" + +#: lutris/gui/lutriswindow.py:805 +#, python-format +msgid "Search %s games" +msgstr "Søk etter %s spill" + +#: lutris/gui/lutriswindow.py:807 +msgid "Search 1 game" +msgstr "Søk etter 1 spill" + +#: lutris/gui/lutriswindow.py:1070 +msgid "Unsupported Lutris Version" +msgstr "Lutris-versjonen er ikke støttet" + +#: lutris/gui/lutriswindow.py:1072 +msgid "" +"This version of Lutris will no longer receive support on Github and Discord, " +"and may not interoperate properly with Lutris.net. Do you want to use it " +"anyway?" +msgstr "" +"Denne versjonen av Lutris vil ikke motta støtte lengre på Github og Discord, " +"og kan samkjøre dårlig med Lutris.net. Vil du bruke den likevel?" + +#: lutris/gui/lutriswindow.py:1283 +msgid "Show Hidden Games" +msgstr "Vis skjulte spill" + +#: lutris/gui/lutriswindow.py:1285 +msgid "Rehide Hidden Games" +msgstr "Skjul de skjulte spillen på nytt" + +#: lutris/gui/lutriswindow.py:1483 +msgid "" +"Your limits are not set correctly. Please increase them as described here: " +"How-to:-" +"Esync (https://github.com/lutris/docs/blob/master/HowToEsync.md)" +msgstr "" +"Grensene dine er ikke satt opp riktig. Øk dem som beskrevet her: How-to:-" +"Esync (https://github.com/lutris/docs/blob/master/HowToEsync.md)" + +#: lutris/gui/widgets/cellrenderers.py:385 lutris/gui/widgets/sidebar.py:501 +msgid "Missing" +msgstr "Mangler" + +#: lutris/gui/widgets/common.py:84 +msgid "Select a folder" +msgstr "Velg en mappe" + +#: lutris/gui/widgets/common.py:86 +msgid "Select a file" +msgstr "Velg en fil" + +#: lutris/gui/widgets/common.py:92 +msgid "Open in file browser" +msgstr "Åpne i filbehandleren" + +#: lutris/gui/widgets/common.py:243 +msgid "" +"Warning! The selected path is located on a drive formatted by " +"Windows.\n" +"Games and programs installed on Windows drives don't work." +msgstr "" +"Advarsel! Den valgte mappa er plassert på en lagringsenhet formatert " +"av Windows.\n" +"Spill og programmer installert på en Windows-enhet fungerer ikke." + +#: lutris/gui/widgets/common.py:252 +msgid "" +"Warning! The selected path contains files. Installation will not work " +"properly." +msgstr "" +"Advarsel! Den valgte mappa inneholder filer. Installasjonen vil ikke " +"fungere ordentlig." + +#: lutris/gui/widgets/common.py:260 +msgid "" +"Warning The destination folder is not writable by the current user." +msgstr "Advarsel! Målmappa er ikke skrivbar for den gjeldende brukeren." + +#: lutris/gui/widgets/common.py:378 +msgid "Add" +msgstr "Legg til" + +#: lutris/gui/widgets/common.py:382 +msgid "Delete" +msgstr "Slett" + +#: lutris/gui/widgets/download_collection_progress_box.py:145 +#: lutris/gui/widgets/download_progress_box.py:109 +msgid "Retry" +msgstr "Prøv igjen" + +#: lutris/gui/widgets/download_collection_progress_box.py:172 +#: lutris/gui/widgets/download_progress_box.py:136 +msgid "Download interrupted" +msgstr "Nedlasting avbrutt" + +#: lutris/gui/widgets/download_collection_progress_box.py:191 +#: lutris/gui/widgets/download_progress_box.py:144 +#, python-brace-format +msgid "{downloaded} / {size} ({speed:0.2f}MB/s), {time} remaining" +msgstr "{downloaded} / {size} ({speed:0.2f} MB/s), {time} gjenstår" + +#: lutris/gui/widgets/game_bar.py:174 +#, python-format +msgid "" +"Platform:\n" +"%s" +msgstr "" +"Plattform:\n" +"%s" + +#: lutris/gui/widgets/game_bar.py:183 +#, python-format +msgid "" +"Time played:\n" +"%s" +msgstr "" +"Tid spilt:\n" +"%s" + +#: lutris/gui/widgets/game_bar.py:192 +#, python-format +msgid "" +"Last played:\n" +"%s" +msgstr "" +"Sist spilt:\n" +"%s" + +#: lutris/gui/widgets/game_bar.py:214 +msgid "Launching" +msgstr "Starter" + +#: lutris/gui/widgets/sidebar.py:156 lutris/gui/widgets/sidebar.py:189 +#: lutris/gui/widgets/sidebar.py:234 +msgid "Run" +msgstr "Kjør" + +#: lutris/gui/widgets/sidebar.py:157 lutris/gui/widgets/sidebar.py:190 +msgid "Reload" +msgstr "Last på nytt" + +#: lutris/gui/widgets/sidebar.py:191 +msgid "Disconnect" +msgstr "Koble fra" + +#: lutris/gui/widgets/sidebar.py:192 +msgid "Connect" +msgstr "Koble til" + +#: lutris/gui/widgets/sidebar.py:231 +msgid "Manage Versions" +msgstr "Håndter versjoner" + +#: lutris/gui/widgets/sidebar.py:277 lutris/gui/widgets/sidebar.py:317 +msgid "Edit Games" +msgstr "Rediger spill" + +#: lutris/gui/widgets/sidebar.py:399 +msgid "Library" +msgstr "Bibliotek" + +#: lutris/gui/widgets/sidebar.py:401 +msgid "Saved Searches" +msgstr "Lagret søk" + +#: lutris/gui/widgets/sidebar.py:404 +msgid "Platforms" +msgstr "Plattformer" + +#: lutris/gui/widgets/sidebar.py:458 lutris/util/system.py:32 +msgid "Games" +msgstr "Spill" + +#: lutris/gui/widgets/sidebar.py:467 +msgid "Recent" +msgstr "Nylige" + +#: lutris/gui/widgets/sidebar.py:476 +msgid "Favorites" +msgstr "Favoritter" + +#: lutris/gui/widgets/sidebar.py:485 +msgid "Uncategorized" +msgstr "Ukategorisert" + +#: lutris/gui/widgets/sidebar.py:509 +msgid "Running" +msgstr "Kjører" + +#: lutris/gui/widgets/status_icon.py:89 lutris/gui/widgets/status_icon.py:113 +msgid "Show Lutris" +msgstr "Vis Lutris" + +#: lutris/gui/widgets/status_icon.py:94 +msgid "Quit" +msgstr "Avslutt" + +#: lutris/gui/widgets/status_icon.py:111 +msgid "Hide Lutris" +msgstr "Skjul Lutris" + +#: lutris/installer/commands.py:61 +#, python-format +msgid "Invalid runner provided %s" +msgstr "Ugyldig løper oppgitt %s" + +#: lutris/installer/commands.py:77 +#, python-brace-format +msgid "One of {params} parameter is mandatory for the {cmd} command" +msgstr "En av {params}-parameterne er nødvendig for {cmd}-kommandoen" + +#: lutris/installer/commands.py:78 lutris/installer/interpreter.py:165 +#: lutris/installer/interpreter.py:188 +msgid " or " +msgstr " eller " + +#: lutris/installer/commands.py:85 +#, python-brace-format +msgid "The {param} parameter is mandatory for the {cmd} command" +msgstr "Parameteren {param} er nødvendig for {cmd}-kommandoen" + +#: lutris/installer/commands.py:95 +#, python-format +msgid "Invalid file '%s'. Can't make it executable" +msgstr "Ugyldig fil «%s». Klarte ikke gjøre den kjørbar" + +#: lutris/installer/commands.py:108 +msgid "" +"Parameters file and command can't be used at the same time for the execute " +"command" +msgstr "" +"Parameterfil og kommando kan ikke brukes samtidig for utføringen av kommando" + +#: lutris/installer/commands.py:142 +msgid "No parameters supplied to execute command." +msgstr "Ingen parametere oppgitt for utføring av kommando." + +#: lutris/installer/commands.py:158 +#, python-format +msgid "Unable to find executable %s" +msgstr "Fant ikke programfila %s" + +#: lutris/installer/commands.py:192 +#, python-format +msgid "%s does not exist" +msgstr "%s finnes ikke" + +#: lutris/installer/commands.py:198 lutris/runtime.py:127 +#, python-format +msgid "Extracting %s" +msgstr "Pakker ut %s" + +#: lutris/installer/commands.py:232 +msgid "" +"Insert or mount game disc and click Autodetect or\n" +"use Browse if the disc is mounted on a non standard location." +msgstr "" +"Sett inn eller monter spillplate og trykk «Oppdag automatisk» eller\n" +"«Bla gjennom» hvis enheten er montert på en ikke-standarisert mappe." + +#: lutris/installer/commands.py:238 +#, python-format +msgid "" +"\n" +"\n" +"Lutris is looking for a mounted disk drive or image \n" +"containing the following file or folder:\n" +"%s" +msgstr "" +"\n" +"\n" +"Lutris leter etter en montert lagringsenhet eller bilde \n" +"som inneholder følgende fil eller mappe:\n" +"%s" + +#: lutris/installer/commands.py:261 +#, python-format +msgid "The required file '%s' could not be located." +msgstr "Fant ikke den nødvendige fila «%s»." + +#: lutris/installer/commands.py:282 +#, python-format +msgid "Source does not exist: %s" +msgstr "Kilden finnes ikke: %s" + +#: lutris/installer/commands.py:308 +#, python-format +msgid "Invalid source for 'move' operation: %s" +msgstr "Ugyldig kilde for «flytt»-operasjonen: %s" + +#: lutris/installer/commands.py:327 +#, python-brace-format +msgid "" +"Can't move {src} \n" +"to destination {dst}" +msgstr "" +"Klarte ikke flytte {src} \n" +"til målet {dst}" + +#: lutris/installer/commands.py:334 +#, python-format +msgid "Rename error, source path does not exist: %s" +msgstr "Feil ved endring av navn, kildemappa finnes ikke: %s" + +#: lutris/installer/commands.py:341 +#, python-format +msgid "Rename error, destination already exists: %s" +msgstr "Feil ved endring av navn, målet finnes allerede: %s" + +#: lutris/installer/commands.py:357 +msgid "Missing parameter src" +msgstr "Mangler parameter «src»" + +#: lutris/installer/commands.py:360 +msgid "Wrong value for 'src' param" +msgstr "Feil verdi for «src»-parameter" + +#: lutris/installer/commands.py:364 +msgid "Wrong value for 'dst' param" +msgstr "Feil verdi for «dst»-parameter" + +#: lutris/installer/commands.py:439 +#, python-format +msgid "Command exited with code %s" +msgstr "Kommando avsluttet med koden %s" + +#: lutris/installer/commands.py:458 +#, python-format +msgid "Wrong value for write_file mode: '%s'" +msgstr "Feil verdi for «write_file»-modusen: «%s»" + +#: lutris/installer/commands.py:651 +msgid "install_or_extract only works with wine!" +msgstr "«install_or_extract» fungerer bare med Wine!" + +#: lutris/installer/errors.py:42 +#, python-format +msgid "This game requires %s." +msgstr "Dette spillet krever %s." + +#: lutris/installer/installer_file_collection.py:86 +msgid "File" +msgstr "Fil" + +#: lutris/installer/installer_file.py:48 +#, python-format +msgid "missing field `url` for file `%s`" +msgstr "mangler felt «url» for fila «%s»" + +#: lutris/installer/installer_file.py:67 +#, python-format +msgid "missing field `filename` in file `%s`" +msgstr "mangler feil «filename» i fila «%s»" + +#: lutris/installer/installer_file.py:162 +#, python-brace-format +msgid "{file} on {host}" +msgstr "{file} på {host}" + +#: lutris/installer/installer_file.py:253 +msgid "Invalid checksum, expected format (type:hash) " +msgstr "Ugyldig sjekksum, forventet format (type:hash) " + +#: lutris/installer/installer_file.py:259 +msgid " checksum mismatch " +msgstr " sjekksum samsvarer ikke " + +#: lutris/installer/installer.py:38 +#, python-format +msgid "The script was missing the '%s' key, which is required." +msgstr "Skriptet manglet «%s» nøkkelen, som trengs." + +#: lutris/installer/installer.py:218 +msgid "Game config key must be a string" +msgstr "Spilloppsettsnøkkel må være en tekst" + +#: lutris/installer/installer.py:266 +msgid "Invalid 'game' section" +msgstr "Ugyldig «spill»-seksjon" + +#: lutris/installer/interpreter.py:85 +msgid "This installer doesn't have a 'script' section" +msgstr "Installatøren har ikke en «skript»-seksjon" + +#: lutris/installer/interpreter.py:90 +msgid "" +"Invalid script: \n" +"{}" +msgstr "" +"Ugyldig skript: \n" +"{}" + +#: lutris/installer/interpreter.py:165 lutris/installer/interpreter.py:168 +#, python-format +msgid "This installer requires %s on your system" +msgstr "Installatøren trenger %s på ditt system" + +#: lutris/installer/interpreter.py:181 +msgid "You need to install {} before" +msgstr "Du må installere {} før" + +#: lutris/installer/interpreter.py:230 +msgid "Lutris does not have the necessary permissions to install to path:" +msgstr "Lutris har ikke de nødvendige tillatelsene for å installere til mappa:" + +#: lutris/installer/interpreter.py:235 +#, python-format +msgid "Path %s not found, unable to create game folder. Is the disk mounted?" +msgstr "" +"Fant ikke mappa %s, klarte ikke opprette spillmappa. Er lagringsenheten " +"montert?" + +#: lutris/installer/interpreter.py:310 +msgid "Installer commands are not formatted correctly" +msgstr "Installatør kommandoene er ikke formatert riktig" + +#: lutris/installer/interpreter.py:362 +#, python-format +msgid "The command \"%s\" does not exist." +msgstr "Kommandoen «%s» finnes ikke." + +#: lutris/installer/interpreter.py:372 +#, python-format +msgid "" +"The executable at path %s can't be found, please check the destination " +"folder.\n" +"Some parts of the installation process may have not completed successfully." +msgstr "" +"Klarte ikke finne programfila på %s, sjekk målmappa.\n" +"Deler av installasjonsprosessen er kanskje ikke fullført." + +#: lutris/installer/interpreter.py:379 +msgid "Installation completed!" +msgstr "Installasjon fullført!" + +#: lutris/installer/steam_installer.py:43 +#, python-format +msgid "Malformed steam path: %s" +msgstr "Feilskrevet Steam-mappe: %s" + +#: lutris/runners/atari800.py:16 +msgid "Desktop resolution" +msgstr "Skrivebordsoppløsning" + +#: lutris/runners/atari800.py:21 +msgid "Atari800" +msgstr "Atari 800" + +#: lutris/runners/atari800.py:22 +msgid "Atari 8bit computers" +msgstr "Atari 8 bit datamaskiner" + +#: lutris/runners/atari800.py:25 +msgid "Atari 400, 800 and XL emulator" +msgstr "Atari 400, 800 og XL emulator" + +#: lutris/runners/atari800.py:39 +msgid "" +"The game data, commonly called a ROM image. \n" +"Supported formats: ATR, XFD, DCM, ATR.GZ, XFD.GZ and PRO." +msgstr "" +"Spilldataen, kjent som ROM-bilde. \n" +"Støttede formater: ATR, XFD, DCM, ATR.GZ, XFD.GZ og PRO." + +#: lutris/runners/atari800.py:50 +msgid "BIOS location" +msgstr "Plassering for BIOS" + +#: lutris/runners/atari800.py:52 +msgid "" +"A folder containing the Atari 800 BIOS files.\n" +"They are provided by Lutris so you shouldn't have to change this." +msgstr "" +"En mappe som inneholder Atari 800 BIOS-filer.\n" +"De er levert av Lutris så du bør ikke måtte endre dette." + +#: lutris/runners/atari800.py:61 +msgid "Emulate Atari 800" +msgstr "Emuler Atari 800" + +#: lutris/runners/atari800.py:62 +msgid "Emulate Atari 800 XL" +msgstr "Emuler Atari 800 XL" + +#: lutris/runners/atari800.py:63 +msgid "Emulate Atari 320 XE (Compy Shop)" +msgstr "Emuler Atari 320 XE (Compy Shop)" + +#: lutris/runners/atari800.py:64 +msgid "Emulate Atari 320 XE (Rambo)" +msgstr "Emuler Atari 320 XE (Rambo)" + +#: lutris/runners/atari800.py:65 +msgid "Emulate Atari 5200" +msgstr "Emuler Atari 5200" + +#: lutris/runners/atari800.py:68 lutris/runners/mame.py:85 +#: lutris/runners/vice.py:86 +msgid "Machine" +msgstr "Maskin" + +#: lutris/runners/atari800.py:74 lutris/runners/atari800.py:82 +#: lutris/runners/dosbox.py:67 lutris/runners/duckstation.py:36 +#: lutris/runners/duckstation.py:44 lutris/runners/easyrpg.py:296 +#: lutris/runners/easyrpg.py:304 lutris/runners/easyrpg.py:320 +#: lutris/runners/easyrpg.py:338 lutris/runners/easyrpg.py:346 +#: lutris/runners/easyrpg.py:354 lutris/runners/easyrpg.py:368 +#: lutris/runners/fsuae.py:249 lutris/runners/fsuae.py:256 +#: lutris/runners/fsuae.py:264 lutris/runners/fsuae.py:277 +#: lutris/runners/hatari.py:64 lutris/runners/hatari.py:71 +#: lutris/runners/hatari.py:79 lutris/runners/hatari.py:94 +#: lutris/runners/jzintv.py:42 lutris/runners/jzintv.py:46 +#: lutris/runners/mame.py:168 lutris/runners/mame.py:175 +#: lutris/runners/mame.py:192 lutris/runners/mame.py:206 +#: lutris/runners/mednafen.py:73 lutris/runners/mednafen.py:77 +#: lutris/runners/mednafen.py:91 lutris/runners/o2em.py:79 +#: lutris/runners/o2em.py:86 lutris/runners/pico8.py:38 +#: lutris/runners/pico8.py:45 lutris/runners/redream.py:24 +#: lutris/runners/redream.py:28 lutris/runners/scummvm.py:111 +#: lutris/runners/scummvm.py:118 lutris/runners/scummvm.py:126 +#: lutris/runners/scummvm.py:139 lutris/runners/scummvm.py:162 +#: lutris/runners/scummvm.py:182 lutris/runners/scummvm.py:197 +#: lutris/runners/scummvm.py:221 lutris/runners/scummvm.py:239 +#: lutris/runners/snes9x.py:35 lutris/runners/snes9x.py:39 +#: lutris/runners/vice.py:51 lutris/runners/vice.py:58 +#: lutris/runners/vice.py:65 lutris/runners/vice.py:72 +#: lutris/runners/wine.py:294 lutris/runners/wine.py:309 +#: lutris/runners/wine.py:322 lutris/runners/wine.py:335 +#: lutris/runners/wine.py:347 lutris/runners/wine.py:360 +#: lutris/runners/wine.py:371 lutris/runners/wine.py:382 +#: lutris/runners/wine.py:393 lutris/runners/wine.py:406 +msgid "Graphics" +msgstr "Bilde" + +#: lutris/runners/atari800.py:75 lutris/runners/cemu.py:38 +#: lutris/runners/duckstation.py:35 lutris/runners/easyrpg.py:297 +#: lutris/runners/hatari.py:65 lutris/runners/jzintv.py:42 +#: lutris/runners/libretro.py:95 lutris/runners/mame.py:169 +#: lutris/runners/mednafen.py:73 lutris/runners/mupen64plus.py:29 +#: lutris/runners/o2em.py:80 lutris/runners/osmose.py:33 +#: lutris/runners/pcsx2.py:31 lutris/runners/pico8.py:39 +#: lutris/runners/redream.py:24 lutris/runners/reicast.py:40 +#: lutris/runners/scummvm.py:112 lutris/runners/snes9x.py:35 +#: lutris/runners/vice.py:52 lutris/runners/vita3k.py:41 +#: lutris/runners/xemu.py:26 lutris/runners/yuzu.py:42 lutris/sysoptions.py:276 +msgid "Fullscreen" +msgstr "Fullskjerm" + +#: lutris/runners/atari800.py:83 +msgid "Fullscreen resolution" +msgstr "Fullskjermsoppløsning" + +#: lutris/runners/atari800.py:93 +msgid "Could not download Atari 800 BIOS archive" +msgstr "Klarte ikke laste ned Atari 800 BIOS-arkiv" + +#: lutris/runners/cemu.py:12 +msgid "Cemu" +msgstr "Cemu" + +#: lutris/runners/cemu.py:13 +msgid "Wii U" +msgstr "Wii U" + +#: lutris/runners/cemu.py:14 +msgid "Wii U emulator" +msgstr "Wii U emulator" + +#: lutris/runners/cemu.py:22 lutris/runners/easyrpg.py:24 +msgid "Game directory" +msgstr "Spillmappe" + +#: lutris/runners/cemu.py:24 +msgid "" +"The directory in which the game lives. If installed into Cemu, this will be " +"in the mlc directory, such as mlc/usr/title/00050000/101c9500." +msgstr "" +"Mappa der spillet ligger. Hvis det er installert i Cemu, vil det være i " +"«mlc»-mappa, eks. mlc/usr/title/00050000/101c9500." + +#: lutris/runners/cemu.py:31 +msgid "Compressed ROM" +msgstr "Komprimert ROM" + +#: lutris/runners/cemu.py:32 +msgid "" +"A game compressed into a single file (WUA format), only use if not using " +"game directory" +msgstr "" +"Et spill komprimert til en enkelt fil (WUA-format), bare bruk hvis du ikke " +"bruker spillmappa" + +#: lutris/runners/cemu.py:44 +msgid "Custom mlc folder location" +msgstr "Tilpasset «mlc»-mappe plassering" + +#: lutris/runners/cemu.py:49 +msgid "Render in upside down mode" +msgstr "Gjengi i opp ned-modus" + +#: lutris/runners/cemu.py:56 +msgid "NSight debugging options" +msgstr "NSight-feilsøkingsinnstillinger" + +#: lutris/runners/cemu.py:63 +msgid "Intel legacy graphics mode" +msgstr "Intel foreldet bildemodus" + +#: lutris/runners/dolphin.py:11 lutris/runners/dolphin.py:35 +msgid "Nintendo GameCube" +msgstr "Nintendo GameCube" + +#: lutris/runners/dolphin.py:11 lutris/runners/dolphin.py:35 +msgid "Nintendo Wii" +msgstr "Nintendo Wii" + +#: lutris/runners/dolphin.py:15 +msgid "GameCube and Wii emulator" +msgstr "GameCube og Wii emulator" + +#: lutris/runners/dolphin.py:16 lutris/services/dolphin.py:29 +msgid "Dolphin" +msgstr "Dolphin" + +#: lutris/runners/dolphin.py:29 lutris/runners/pcsx2.py:22 +#: lutris/runners/xemu.py:19 +msgid "ISO file" +msgstr "ISO-fil" + +#: lutris/runners/dolphin.py:42 +msgid "Batch" +msgstr "Batch" + +#: lutris/runners/dolphin.py:45 +msgid "Exit Dolphin with emulator." +msgstr "Avslutt Dolphin med emulator." + +#: lutris/runners/dolphin.py:52 +msgid "Custom Global User Directory" +msgstr "Tilpasset global brukermappe" + +#: lutris/runners/dosbox.py:16 +msgid "DOSBox" +msgstr "DOSBox" + +#: lutris/runners/dosbox.py:17 +msgid "MS-DOS emulator" +msgstr "MS-DOS emulator" + +#: lutris/runners/dosbox.py:18 +msgid "MS-DOS" +msgstr "MS-DOS" + +#: lutris/runners/dosbox.py:26 +msgid "Main file" +msgstr "Hovedfil" + +#: lutris/runners/dosbox.py:28 +msgid "" +"The CONF, EXE, COM or BAT file to launch.\n" +"If the executable is managed in the config file, this should be the config " +"file, instead specifying it in 'Configuration file'." +msgstr "" +"CONF- EXE- COM- eller BAT-fila som skal kjøres.\n" +"Hvis den kjørbare fila behandles i oppsettsfila, burde dette være " +"oppsettsfila, i stedet for å velge den i «Oppsettsfil»." + +#: lutris/runners/dosbox.py:36 +msgid "Configuration file" +msgstr "Oppsettsfil" + +#: lutris/runners/dosbox.py:38 +msgid "" +"Start DOSBox with the options specified in this file. \n" +"It can have a section in which you can put commands to execute on startup. " +"Read DOSBox's documentation for more information." +msgstr "" +"Start DOSBox med innstillingene i denne fila. \n" +"Den kan ha en seksjon hvor du kan skrive inn kommandoer som skal kjøres ved " +"oppstart. Les DOSBox-dokumentasjonen for mer informasjon." + +#: lutris/runners/dosbox.py:47 +msgid "Command line arguments" +msgstr "Kommandolinje argumenter" + +#: lutris/runners/dosbox.py:48 +msgid "Command line arguments used when launching DOSBox" +msgstr "Kommandolinje argumenter brukt ved oppstart av DOSBox" + +#: lutris/runners/dosbox.py:54 lutris/runners/flatpak.py:69 +#: lutris/runners/linux.py:39 lutris/runners/wine.py:224 +msgid "Working directory" +msgstr "Arbeidsmappe" + +#: lutris/runners/dosbox.py:57 lutris/runners/linux.py:41 +#: lutris/runners/wine.py:226 +msgid "" +"The location where the game is run from.\n" +"By default, Lutris uses the directory of the executable." +msgstr "" +"Plasseringen hvor spillet kjøres fra.\n" +"Som standard bruker Lutris mappa til programfila." + +#: lutris/runners/dosbox.py:68 +msgid "Open game in fullscreen" +msgstr "Åpne spillet i fullskjerm" + +#: lutris/runners/dosbox.py:71 +msgid "Tells DOSBox to launch the game in fullscreen." +msgstr "Ber DOSBox starte spillet i fullskjerm." + +#: lutris/runners/dosbox.py:75 +msgid "Exit DOSBox with the game" +msgstr "Avslutt DOSBox med spillet" + +#: lutris/runners/dosbox.py:78 +msgid "Shut down DOSBox when the game is quit." +msgstr "Avslutter DOSBox når spillet avsluttes." + +#: lutris/runners/duckstation.py:13 +msgid "DuckStation" +msgstr "DuckStation" + +#: lutris/runners/duckstation.py:14 +msgid "PlayStation 1 Emulator" +msgstr "PlayStation 1 emulator" + +#: lutris/runners/duckstation.py:15 lutris/runners/mednafen.py:31 +msgid "Sony PlayStation" +msgstr "Sony PlayStation" + +#: lutris/runners/duckstation.py:37 +msgid "Enters fullscreen mode immediately after starting." +msgstr "Går inn i fullskjermmodus med en gang etter oppstart." + +#: lutris/runners/duckstation.py:43 +msgid "No Fullscreen" +msgstr "Ingen fullskjerm" + +#: lutris/runners/duckstation.py:45 +msgid "Prevents fullscreen mode from triggering if enabled." +msgstr "Hindrer fullskjermmodus hvis slått på." + +#: lutris/runners/duckstation.py:51 +msgid "Batch Mode" +msgstr "Batch-modus" + +#: lutris/runners/duckstation.py:52 lutris/runners/duckstation.py:61 +#: lutris/runners/duckstation.py:69 +msgid "Boot" +msgstr "Oppstart" + +#: lutris/runners/duckstation.py:53 +msgid "Enables batch mode (exits after powering off)." +msgstr "Tar i bruk batch-modus (avslutter etter avslåing)." + +#: lutris/runners/duckstation.py:60 +msgid "Force Fastboot" +msgstr "Tving raskoppstart" + +#: lutris/runners/duckstation.py:62 +msgid "Force fast boot." +msgstr "Tvinger raskoppstart." + +#: lutris/runners/duckstation.py:68 +msgid "Force Slowboot" +msgstr "Tving sakteoppstart" + +#: lutris/runners/duckstation.py:70 +msgid "Force slow boot." +msgstr "Tvinger sakteoppstart." + +#: lutris/runners/duckstation.py:76 +msgid "No Controllers" +msgstr "Ingen kontrollere" + +#: lutris/runners/duckstation.py:77 lutris/runners/o2em.py:64 +#: lutris/runners/o2em.py:72 +msgid "Controllers" +msgstr "Kontrollere" + +#: lutris/runners/duckstation.py:79 +msgid "" +"Prevents the emulator from polling for controllers. Try this option if " +"you're having difficulties starting the emulator." +msgstr "" +"Hindrer emulatoren fra å søke etter kontrollere. Prøv dette hvis du har " +"problemer med oppstart av emulatoren." + +#: lutris/runners/duckstation.py:87 +msgid "Custom configuration file" +msgstr "Tilpasset oppsettsfil" + +#: lutris/runners/duckstation.py:89 +msgid "" +"Loads a custom settings configuration from the specified filename. Default " +"settings applied if file not found." +msgstr "" +"Laster en tilpasset oppsettsfil fra den oppgitte fila. Standard " +"innstillinger brukes hvis fila ikke blir funnet." + +#: lutris/runners/easyrpg.py:12 +msgid "EasyRPG Player" +msgstr "EasyRPG Player" + +#: lutris/runners/easyrpg.py:13 +msgid "Runs RPG Maker 2000/2003 games" +msgstr "Starter RPG Maker 2000/2003 spill" + +#: lutris/runners/easyrpg.py:14 lutris/runners/flatpak.py:22 +#: lutris/runners/linux.py:17 lutris/runners/linux.py:19 +#: lutris/runners/scummvm.py:56 lutris/runners/steam.py:31 +#: lutris/runners/zdoom.py:15 +msgid "Linux" +msgstr "Linux" + +#: lutris/runners/easyrpg.py:25 +msgid "Select the directory of the game. (required)" +msgstr "Velg mappa til spillet. (nødvendig)" + +#: lutris/runners/easyrpg.py:31 +msgid "Encoding" +msgstr "Tegnkoding" + +#: lutris/runners/easyrpg.py:33 +msgid "" +"Instead of auto detecting the encoding or using the one in RPG_RT.ini, the " +"specified encoding is used." +msgstr "" +"I stedet for å oppdage tegnkodingen automatisk eller bruke den i RPG_RT.ini, " +"brukes den valgte tegnkodingen." + +#: lutris/runners/easyrpg.py:37 lutris/runners/easyrpg.py:62 +#: lutris/runners/easyrpg.py:221 lutris/runners/easyrpg.py:240 +#: lutris/runners/fsuae.py:123 lutris/runners/mame.py:195 +#: lutris/runners/scummvm.py:186 lutris/runners/scummvm.py:201 +#: lutris/runners/scummvm.py:225 lutris/runners/wine.py:246 +#: lutris/runners/wine.py:547 lutris/sysoptions.py:50 +msgid "Auto" +msgstr "Auto" + +#: lutris/runners/easyrpg.py:38 +msgid "Auto (ignore RPG_RT.ini)" +msgstr "Auto (ignorer RPG_RT.ini)" + +#: lutris/runners/easyrpg.py:39 +msgid "Western European" +msgstr "Vesteuropeisk" + +#: lutris/runners/easyrpg.py:40 +msgid "Central/Eastern European" +msgstr "Mellom- Øst-Europa" + +#: lutris/runners/easyrpg.py:41 lutris/runners/redream.py:50 +#: lutris/sysoptions.py:39 +msgid "Japanese" +msgstr "Japansk" + +#: lutris/runners/easyrpg.py:42 +msgid "Cyrillic" +msgstr "Kyrillisk" + +#: lutris/runners/easyrpg.py:43 lutris/sysoptions.py:40 +msgid "Korean" +msgstr "Koreansk" + +#: lutris/runners/easyrpg.py:44 +msgid "Chinese (Simplified)" +msgstr "Kinesisk (forenklet)" + +#: lutris/runners/easyrpg.py:45 +msgid "Chinese (Traditional)" +msgstr "Kinesisk (tradisjonell)" + +#: lutris/runners/easyrpg.py:46 lutris/sysoptions.py:37 +msgid "Greek" +msgstr "Gresk" + +#: lutris/runners/easyrpg.py:47 lutris/sysoptions.py:45 +msgid "Turkish" +msgstr "Tyrkisk" + +#: lutris/runners/easyrpg.py:48 +msgid "Hebrew" +msgstr "Hebraisk" + +#: lutris/runners/easyrpg.py:49 +msgid "Arabic" +msgstr "Arabisk" + +#: lutris/runners/easyrpg.py:50 +msgid "Baltic" +msgstr "Baltikum" + +#: lutris/runners/easyrpg.py:51 +msgid "Thai" +msgstr "Thai" + +#: lutris/runners/easyrpg.py:59 lutris/runners/easyrpg.py:212 +#: lutris/runners/easyrpg.py:232 lutris/runners/easyrpg.py:250 +msgid "Engine" +msgstr "Spillmotor" + +#: lutris/runners/easyrpg.py:60 +msgid "Disable auto detection of the simulated engine." +msgstr "Slår av automatisk oppdaging av den simulerte spillmotoren." + +#: lutris/runners/easyrpg.py:63 +msgid "RPG Maker 2000 engine (v1.00 - v1.10)" +msgstr "RPG Maker 2000 spillmotor (v1.00 - v1.10)" + +#: lutris/runners/easyrpg.py:64 +msgid "RPG Maker 2000 engine (v1.50 - v1.51)" +msgstr "RPG Maker 2000 spillmotor (v1.50 - v1.51)" + +#: lutris/runners/easyrpg.py:65 +msgid "RPG Maker 2000 (English release) engine" +msgstr "RPG Maker 2000 (Engelsk utgave) spillmotor" + +#: lutris/runners/easyrpg.py:66 +msgid "RPG Maker 2003 engine (v1.00 - v1.04)" +msgstr "RPG Maker 2003 spillmotor (v1.00 - v1.04)" + +#: lutris/runners/easyrpg.py:67 +msgid "RPG Maker 2003 engine (v1.05 - v1.09a)" +msgstr "RPG Maker 2003 spillmotor (v1.05 - v1.09a)" + +#: lutris/runners/easyrpg.py:68 +msgid "RPG Maker 2003 (English release) engine" +msgstr "RPG Maker 2003 (Engelsk utgave) spillmotor" + +#: lutris/runners/easyrpg.py:76 +msgid "Patches" +msgstr "Programrettelser" + +#: lutris/runners/easyrpg.py:78 +msgid "" +"Instead of autodetecting patches used by this game, force emulation of " +"certain patches.\n" +"\n" +"Available patches:\n" +"common-this: \"This Event\" in common eventsdynrpg: DynRPG " +"patch by Cherrykey-patch: Key Patch by Inelukimaniac: Maniac " +"Patch by BingShanpic-unlock: Pictures are not blocked by " +"messagesrpg2k3-cmds: Support all RPG Maker 2003 event commands in any " +"version of the engine\n" +"\n" +"You can provide multiple patches or use 'none' to disable all engine patches." +msgstr "" +"I stedet for å automatisk oppdage programrettelser som brukes av dette " +"spillet, tving gjennom enkelte programrettelser.\n" +"\n" +"Tilgjengelige programrettelser:\n" +"common-this: «Denne hendelsen» i vanlige hendelserdynrpg: " +"DynRPG Patch fra Cherrykey-patch: Key Patch fra Inelukimaniac: " +"Maniac Patch fra BingShanpic-unlock: Bilder blokkeres ikke av " +"meldingerrpg2k3-cmds: Støtt alle RPG Maker 2003 hendelseskommandoer i " +"alle versjoner av spillmotoren\n" +"\n" +"Du kan velge flere programrettelser eller bruke «Ingen» for å slå av " +"spillmotor-programrettelser." + +#: lutris/runners/easyrpg.py:93 lutris/runners/scummvm.py:276 +msgid "Language" +msgstr "Språk" + +#: lutris/runners/easyrpg.py:94 +msgid "Load the game translation in the language/LANG directory." +msgstr "Laster inn spilloversettelser i mappa «language/LANG»." + +#: lutris/runners/easyrpg.py:99 lutris/runners/zdoom.py:46 +msgid "Save path" +msgstr "Lagre mappe" + +#: lutris/runners/easyrpg.py:102 +msgid "" +"Instead of storing save files in the game directory they are stored in the " +"specified path. The directory must exist." +msgstr "" +"I stedet for å lagre brukerfiler i spillmappa, lagres de i den valgte mappa. " +"Mappa må finnes." + +#: lutris/runners/easyrpg.py:109 +msgid "New game" +msgstr "Nytt spill" + +#: lutris/runners/easyrpg.py:110 +msgid "Skip the title scene and start a new game directly." +msgstr "Hopp over tittel og start nytt spill direkte." + +#: lutris/runners/easyrpg.py:116 +msgid "Load game ID" +msgstr "Last spill-ID" + +#: lutris/runners/easyrpg.py:117 +msgid "" +"Skip the title scene and load SaveXX.lsd.\n" +"Set to 0 to disable." +msgstr "" +"Hopp over tittel og last «SaveXX.lsd».\n" +"Sett til 0 for å slå av." + +#: lutris/runners/easyrpg.py:126 +msgid "Record input" +msgstr "Ta opp inndata" + +#: lutris/runners/easyrpg.py:127 +msgid "Records all button input to the specified log file." +msgstr "Tar opp alle knappeinndata til en valgt loggfil." + +#: lutris/runners/easyrpg.py:133 +msgid "Replay input" +msgstr "Gjenta inndata" + +#: lutris/runners/easyrpg.py:135 +msgid "" +"Replays button input from the specified log file, as generated by 'Record " +"input'.\n" +"If the RNG seed and the state of the save file directory is also the same as " +"it was when the log was recorded, this should reproduce an identical run to " +"the one recorded." +msgstr "" +"Gjentar knappeinndata fra den valgte loggfila, generert av «Ta opp " +"inndata».\n" +"Hvis det tilfeldige frøet og tilstanden av brukerspillfilmappa også er det " +"samme som det var når loggen ble loggført, burde dette gjengi identiske " +"forhold til den som er tatt opp." + +#: lutris/runners/easyrpg.py:144 lutris/runners/easyrpg.py:153 +#: lutris/runners/easyrpg.py:162 lutris/runners/easyrpg.py:177 +#: lutris/runners/easyrpg.py:189 lutris/runners/easyrpg.py:201 +msgid "Debug" +msgstr "Feilsøk" + +#: lutris/runners/easyrpg.py:145 +msgid "Test play" +msgstr "Testspill" + +#: lutris/runners/easyrpg.py:146 +msgid "Enable TestPlay (debug) mode." +msgstr "Bruk TestPlay (feilsøk)-modus." + +#: lutris/runners/easyrpg.py:154 +msgid "Hide title" +msgstr "Skjul tittel" + +#: lutris/runners/easyrpg.py:155 +msgid "Hide the title background image and center the command menu." +msgstr "Skjuler tittel bakgrunnsbilde og fremviser kommandomenyen." + +#: lutris/runners/easyrpg.py:163 +msgid "Start map ID" +msgstr "Start kart-ID" + +#: lutris/runners/easyrpg.py:165 +msgid "" +"Overwrite the map used for new games and use MapXXXX.lmu instead.\n" +"Set to 0 to disable.\n" +"\n" +"Incompatible with 'Load game ID'." +msgstr "" +"Overstyr kartet brukt for nye spill og bruk «MapXXXX.lmu» i stedet.\n" +"Sett til 0 for å slå av.\n" +"\n" +"Støttes ikke med «Last spill-ID»." + +#: lutris/runners/easyrpg.py:178 +msgid "Start position" +msgstr "Start posisjon" + +#: lutris/runners/easyrpg.py:180 +msgid "" +"Overwrite the party start position and move the party to the specified " +"position.\n" +"Provide two numbers separated by a space.\n" +"\n" +"Incompatible with 'Load game ID'." +msgstr "" +"Overstyr festens startposisjon og flytt festen til den valgte posisjonen.\n" +"Oppgi to tall atskilt med et mellomrom.\n" +"\n" +"Støttes ikke med «Last spill-ID»." + +#: lutris/runners/easyrpg.py:190 +msgid "Start party" +msgstr "Start festen" + +#: lutris/runners/easyrpg.py:192 +msgid "" +"Overwrite the starting party members with the actors with the specified " +"IDs.\n" +"Provide one to four numbers separated by spaces.\n" +"\n" +"Incompatible with 'Load game ID'." +msgstr "" +"Overstyr de opprinnelige festmedlemmene med de valgte aktør-ID-ene.\n" +"Oppgi en til fire tall atskilt med et mellomrom.\n" +"\n" +"Støttes ikke med «Last spill-ID»." + +#: lutris/runners/easyrpg.py:202 +msgid "Battle test" +msgstr "Kamptest" + +#: lutris/runners/easyrpg.py:203 +msgid "Start a battle test with the specified monster party." +msgstr "Start en kamptest med den valgte monster festen." + +#: lutris/runners/easyrpg.py:213 +msgid "AutoBattle algorithm" +msgstr "Autokamp algoritme" + +#: lutris/runners/easyrpg.py:215 +msgid "" +"Which AutoBattle algorithm to use.\n" +"\n" +"RPG_RT: The default RPG_RT compatible algorithm, including RPG_RT " +"bugs.\n" +"RPG_RT+: The default RPG_RT compatible algorithm, with bug-fixes.\n" +"ATTACK: Like RPG_RT+ but only physical attacks, no skills." +msgstr "" +"Hvilken autokamp algoritme som skal brukes.\n" +"\n" +"RPG_RT: Den standardiserte RPG_RT kompatible algoritmen, med RPG_RT " +"feil.\n" +"RPG_RT+: Den standardiserte RPG_RT kompatible algoritmen, med " +"feilrettelser.\n" +"ATTACK: Som RPG_RT+ men bare fysiske angrep, ingen ferdigheter." + +#: lutris/runners/easyrpg.py:222 lutris/runners/easyrpg.py:241 +msgid "RPG_RT" +msgstr "RPG_RT" + +#: lutris/runners/easyrpg.py:223 lutris/runners/easyrpg.py:242 +msgid "RPG_RT+" +msgstr "RPG_RT+" + +#: lutris/runners/easyrpg.py:224 +msgid "ATTACK" +msgstr "ATTACK" + +#: lutris/runners/easyrpg.py:233 +msgid "EnemyAI algorithm" +msgstr "FiendeKI algoritme" + +#: lutris/runners/easyrpg.py:235 +msgid "" +"Which EnemyAI algorithm to use.\n" +"\n" +"RPG_RT: The default RPG_RT compatible algorithm, including RPG_RT " +"bugs.\n" +"RPG_RT+: The default RPG_RT compatible algorithm, with bug-fixes.\n" +msgstr "" +"Hvilken FiendeKI algoritme som skal brukes.\n" +"\n" +"RPG_RT: Den standardiserte RPG_RT kompatible algoritmen, med RPG_RT " +"feil.\n" +"RPG_RT+: Den standardiserte RPG_RT+ kompatible algoritmen, med " +"feilrettelser.\n" + +#: lutris/runners/easyrpg.py:251 +msgid "RNG seed" +msgstr "Tilfeldig frø" + +#: lutris/runners/easyrpg.py:252 +msgid "" +"Seeds the random number generator.\n" +"Use -1 to disable." +msgstr "" +"Frø til tilfeldig tallgenerator.\n" +"Bruk -1 for å slå av." + +#: lutris/runners/easyrpg.py:260 lutris/runners/easyrpg.py:268 +#: lutris/runners/easyrpg.py:278 lutris/runners/easyrpg.py:289 +#: lutris/runners/redream.py:72 lutris/runners/scummvm.py:300 +#: lutris/runners/scummvm.py:308 lutris/runners/scummvm.py:315 +#: lutris/runners/scummvm.py:337 lutris/runners/scummvm.py:350 +#: lutris/runners/scummvm.py:372 lutris/runners/scummvm.py:380 +#: lutris/runners/scummvm.py:388 lutris/runners/scummvm.py:396 +#: lutris/runners/scummvm.py:403 lutris/runners/scummvm.py:411 +#: lutris/runners/scummvm.py:420 lutris/runners/scummvm.py:432 +#: lutris/sysoptions.py:346 +msgid "Audio" +msgstr "Lyd" + +#: lutris/runners/easyrpg.py:261 +msgid "Enable audio" +msgstr "Slå på lyd" + +#: lutris/runners/easyrpg.py:262 +msgid "Switch off to disable audio." +msgstr "Slå av for å ikke ha lyd." + +#: lutris/runners/easyrpg.py:269 +msgid "BGM volume" +msgstr "Lydstyrke for BGM" + +#: lutris/runners/easyrpg.py:270 +msgid "Volume of the background music." +msgstr "Lydstyrke for bakgrunnsmusikken." + +#: lutris/runners/easyrpg.py:279 lutris/runners/scummvm.py:381 +msgid "SFX volume" +msgstr "Lydstyrke for filmtriks" + +#: lutris/runners/easyrpg.py:280 +msgid "Volume of the sound effects." +msgstr "Lydstyrke for lydeffekter." + +#: lutris/runners/easyrpg.py:290 lutris/runners/scummvm.py:405 +msgid "Soundfont" +msgstr "Soundfont" + +#: lutris/runners/easyrpg.py:291 +msgid "Soundfont in sf2 format to use when playing MIDI files." +msgstr "Soundfont i sf2-format for bruk ved avspilling av MIDI-filer." + +#: lutris/runners/easyrpg.py:298 +msgid "Start in fullscreen mode." +msgstr "Start i fullskjermmodus." + +#: lutris/runners/easyrpg.py:306 +msgid "Game resolution" +msgstr "Spilloppløsning" + +#: lutris/runners/easyrpg.py:308 +msgid "" +"Force a different game resolution.\n" +"\n" +"This is experimental and can cause glitches or break games!" +msgstr "" +"Tving gjennom en annen spilloppløsning.\n" +"\n" +"Dette er eksperimentell og kan forårsake diverse feil og ødelegge spill!" + +#: lutris/runners/easyrpg.py:311 +msgid "320×240 (4:3, Original)" +msgstr "320 × 240 (4:3, original)" + +#: lutris/runners/easyrpg.py:312 +msgid "416×240 (16:9, Widescreen)" +msgstr "416 × 240 (16:9, bredformat)" + +#: lutris/runners/easyrpg.py:313 +msgid "560×240 (21:9, Ultrawide)" +msgstr "560 × 240 (19:9, ultrabredformat)" + +#: lutris/runners/easyrpg.py:321 +msgid "Scaling" +msgstr "Skalering" + +#: lutris/runners/easyrpg.py:323 +msgid "" +"How the video output is scaled.\n" +"\n" +"Nearest: Scale to screen size (causes scaling artifacts)\n" +"Integer: Scale to multiple of the game resolution\n" +"Bilinear: Like Nearest, but output is blurred to avoid artifacts\n" +msgstr "" +"Hvordan videoutgangen er skalert.\n" +"\n" +"Nærmeste: Skalerer til skjermstørrelsen (forårsaker skaleringsfeil)\n" +"Heltall: Skalerer til multiplikatorer av spilloppløsningen\n" +"Bilineær: Som «Nærmeste», men utdataen er sløret for å unngå feil\n" + +#: lutris/runners/easyrpg.py:329 +msgid "Nearest" +msgstr "Nærmeste" + +#: lutris/runners/easyrpg.py:330 +msgid "Integer" +msgstr "Heltall" + +#: lutris/runners/easyrpg.py:331 +msgid "Bilinear" +msgstr "Bilineær" + +#: lutris/runners/easyrpg.py:339 lutris/runners/redream.py:30 +#: lutris/runners/scummvm.py:229 +msgid "Stretch" +msgstr "Strekk" + +#: lutris/runners/easyrpg.py:340 +msgid "" +"Ignore the aspect ratio and stretch video output to the entire width of the " +"screen." +msgstr "" +"Ignorer størrelsesforholdene og strekk videoutganger til hele bredden av " +"skjermen." + +#: lutris/runners/easyrpg.py:347 +msgid "Enable VSync" +msgstr "Bruk VSync" + +#: lutris/runners/easyrpg.py:348 +msgid "Switch off to disable VSync and use the FPS limit." +msgstr "Slå av for å ikke bruke VSync og bruken av dens FPS-grense." + +#: lutris/runners/easyrpg.py:355 +msgid "FPS limit" +msgstr "FPS-grense" + +#: lutris/runners/easyrpg.py:357 +msgid "" +"Set a custom frames per second limit.\n" +"If unspecified, the default is 60 FPS.\n" +"Set to 0 to disable the frame limiter." +msgstr "" +"Velg en tilpasset grense for bilder per sekund.\n" +"Hvis ikke satt opp, velges standarden 60 FPS.\n" +"Velg 0 for å slå av fps-grensen." + +#: lutris/runners/easyrpg.py:369 lutris/runners/wine.py:571 +msgid "Show FPS" +msgstr "Vis FPS" + +#: lutris/runners/easyrpg.py:370 +msgid "Enable frames per second counter." +msgstr "Bruk teller for bilder per sekund" + +#: lutris/runners/easyrpg.py:373 +msgid "Fullscreen & title bar" +msgstr "Fullskjerm og tittellinje" + +#: lutris/runners/easyrpg.py:374 +msgid "Fullscreen, title bar & window" +msgstr "Fullskjerm, tittellinje og vindu" + +#: lutris/runners/easyrpg.py:381 lutris/runners/easyrpg.py:389 +#: lutris/runners/easyrpg.py:396 lutris/runners/easyrpg.py:403 +msgid "Runtime Package" +msgstr "Kjøretid-pakke" + +#: lutris/runners/easyrpg.py:382 +msgid "Enable RTP" +msgstr "Bruk RTP" + +#: lutris/runners/easyrpg.py:383 +msgid "Switch off to disable support for the Runtime Package (RTP)." +msgstr "Slå av for å ikke ha støtte for kjøretid-pakke (RTP)." + +#: lutris/runners/easyrpg.py:390 +msgid "RPG2000 RTP location" +msgstr "RPG2000 RTP plassering" + +#: lutris/runners/easyrpg.py:391 +msgid "" +"Full path to a directory containing an extracted RPG Maker 2000 Run-Time-" +"Package (RTP)." +msgstr "" +"Adresse til en mappe som inneholder en utpakket RPG Maker 2000 kjøretid-" +"pakke (RTP)." + +#: lutris/runners/easyrpg.py:397 +msgid "RPG2003 RTP location" +msgstr "RPG2003 RTP plassering" + +#: lutris/runners/easyrpg.py:398 +msgid "" +"Full path to a directory containing an extracted RPG Maker 2003 Run-Time-" +"Package (RTP)." +msgstr "" +"Adresse til en mappe som inneholder en utpakket RPG Maker 2003 kjøretid-" +"pakke (RTP)." + +#: lutris/runners/easyrpg.py:404 +msgid "Fallback RTP location" +msgstr "Reserve RPT plassering" + +#: lutris/runners/easyrpg.py:405 +msgid "Full path to a directory containing a combined RTP." +msgstr "Adresse til en mappe som inneholder en kombinert RTP." + +#: lutris/runners/easyrpg.py:518 +msgid "No game directory provided" +msgstr "Ingen spillmappe oppgitt" + +#: lutris/runners/flatpak.py:21 +msgid "Runs Flatpak applications" +msgstr "Kjører Flatpak-programmer" + +#: lutris/runners/flatpak.py:24 +msgid "Flatpak" +msgstr "Flatpak" + +#: lutris/runners/flatpak.py:33 lutris/runners/steam.py:37 +msgid "Application ID" +msgstr "Program-ID" + +#: lutris/runners/flatpak.py:34 +msgid "The application's unique three-part identifier (tld.domain.app)." +msgstr "Programmets unike tredelte identifikator (tld.domain.app)." + +#: lutris/runners/flatpak.py:39 +msgid "Architecture" +msgstr "Arkitektur" + +#: lutris/runners/flatpak.py:41 +msgid "" +"The architecture to run. See flatpak --supported-arches for architectures " +"supported by the host." +msgstr "" +"Arkitekturen som skal kjøres. Se kommandoen «flatpak --supported-arches» for " +"arkitekturer som støttes av verten." + +#: lutris/runners/flatpak.py:45 +msgid "Branch" +msgstr "Gren" + +#: lutris/runners/flatpak.py:45 +msgid "The branch to use." +msgstr "Grenen som skal brukes." + +#: lutris/runners/flatpak.py:49 +msgid "Install type" +msgstr "Installasjonstype" + +#: lutris/runners/flatpak.py:50 +msgid "Can be system or user." +msgstr "Kan være systemet eller bruker." + +#: lutris/runners/flatpak.py:56 +msgid "Args" +msgstr "Args" + +#: lutris/runners/flatpak.py:57 +msgid "Arguments to be passed to the application." +msgstr "Argumenter som skal sendes til programmet." + +#: lutris/runners/flatpak.py:62 +msgid "Command" +msgstr "Kommando" + +#: lutris/runners/flatpak.py:63 +msgid "" +"The command to run instead of the one listed in the application metadata." +msgstr "" +"Kommandoen som skal kjøres i stedet for den oppført i program-metadataen." + +#: lutris/runners/flatpak.py:71 +msgid "" +"The directory to run the command in. Note that this must be a directory " +"inside the sandbox." +msgstr "" +"Mappa som komandoen skal kjøres i. Merk at dette må være en mappe inne i " +"sandkassen." + +#: lutris/runners/flatpak.py:77 lutris/sysoptions.py:414 +msgid "Environment variables" +msgstr "Miljøvariabler" + +#: lutris/runners/flatpak.py:79 +msgid "" +"Set an environment variable in the application. This overrides to the " +"Context section from the application metadata." +msgstr "" +"Velg en miljøvariabel i programmet. Dette overstyrer til «Context»-seksjonen " +"fra program-metadataen." + +#: lutris/runners/flatpak.py:92 +msgid "The Flatpak executable could not be found." +msgstr "Klarte ikke finne Flatpak-programfila." + +#: lutris/runners/flatpak.py:98 +msgid "" +"Flatpak installation is not handled by Lutris.\n" +"Install Flatpak with the package provided by your distribution." +msgstr "" +"Flatpak-installasjon håndteres ikke av Lutris.\n" +"Installer Flatpak med pakken levert av din distribusjon." + +#: lutris/runners/flatpak.py:140 +msgid "No application specified." +msgstr "Ingen program valgt" + +#: lutris/runners/flatpak.py:144 +msgid "" +"Application ID is not specified in correct format.Must be something like: " +"tld.domain.app" +msgstr "" +"Program-ID-en er ikke satt opp i riktig format. Må se ut som: tld.domain.app" + +#: lutris/runners/flatpak.py:148 +msgid "Application ID field must not contain options or arguments." +msgstr "Program-ID feltet må ikke inneholde instillinger eller argumenter." + +#: lutris/runners/fsuae.py:12 +msgid "Amiga 500" +msgstr "Amiga 500" + +#: lutris/runners/fsuae.py:19 +msgid "Amiga 500+" +msgstr "Amiga 500+" + +#: lutris/runners/fsuae.py:20 +msgid "Amiga 600" +msgstr "Amiga 600" + +#: lutris/runners/fsuae.py:21 +msgid "Amiga 1200" +msgstr "Amiga 1200" + +#: lutris/runners/fsuae.py:22 +msgid "Amiga 3000" +msgstr "Amiga 3000" + +#: lutris/runners/fsuae.py:24 +msgid "Amiga 4000" +msgstr "Amiga 4000" + +#: lutris/runners/fsuae.py:27 +msgid "Amiga 1000" +msgstr "Amiga 1000" + +#: lutris/runners/fsuae.py:29 +msgid "Amiga CD32" +msgstr "Amiga CD32" + +#: lutris/runners/fsuae.py:34 +msgid "Commodore CDTV" +msgstr "Commodore CDTV" + +#: lutris/runners/fsuae.py:91 +msgid "FS-UAE" +msgstr "FS-UAE" + +#: lutris/runners/fsuae.py:92 +msgid "Amiga emulator" +msgstr "Amiga emulator" + +#: lutris/runners/fsuae.py:109 +msgid "68000" +msgstr "68000" + +#: lutris/runners/fsuae.py:110 +msgid "68010" +msgstr "68010" + +#: lutris/runners/fsuae.py:111 +msgid "68020 with 24-bit addressing" +msgstr "68020 med 24 bit adressering" + +#: lutris/runners/fsuae.py:112 +msgid "68020" +msgstr "68020" + +#: lutris/runners/fsuae.py:113 +msgid "68030 without internal MMU" +msgstr "68030 uten intern MMU" + +#: lutris/runners/fsuae.py:114 +msgid "68030" +msgstr "68030" + +#: lutris/runners/fsuae.py:115 +msgid "68040 without internal FPU and MMU" +msgstr "68040 uten intern FPU og MMU" + +#: lutris/runners/fsuae.py:116 +msgid "68040 without internal FPU" +msgstr "68040 uten intern FPU" + +#: lutris/runners/fsuae.py:117 +msgid "68040 without internal MMU" +msgstr "68040 uten intern MMU" + +#: lutris/runners/fsuae.py:118 +msgid "68040" +msgstr "68040" + +#: lutris/runners/fsuae.py:119 +msgid "68060 without internal FPU and MMU" +msgstr "68060 uten intern FPU og MMU" + +#: lutris/runners/fsuae.py:120 +msgid "68060 without internal FPU" +msgstr "68060 uten intern FPU" + +#: lutris/runners/fsuae.py:121 +msgid "68060 without internal MMU" +msgstr "68060 uten intern MMU" + +#: lutris/runners/fsuae.py:122 +msgid "68060" +msgstr "68060" + +#: lutris/runners/fsuae.py:126 lutris/runners/fsuae.py:133 +#: lutris/runners/fsuae.py:167 +msgid "0" +msgstr "0" + +#: lutris/runners/fsuae.py:127 lutris/runners/fsuae.py:134 +#: lutris/runners/fsuae.py:168 +msgid "1 MB" +msgstr "1 MB" + +#: lutris/runners/fsuae.py:128 lutris/runners/fsuae.py:135 +#: lutris/runners/fsuae.py:169 +msgid "2 MB" +msgstr "2 MB" + +#: lutris/runners/fsuae.py:129 lutris/runners/fsuae.py:136 +#: lutris/runners/fsuae.py:170 +msgid "4 MB" +msgstr "4 MB" + +#: lutris/runners/fsuae.py:130 lutris/runners/fsuae.py:137 +#: lutris/runners/fsuae.py:171 +msgid "8 MB" +msgstr "8 MB" + +#: lutris/runners/fsuae.py:138 lutris/runners/fsuae.py:172 +msgid "16 MB" +msgstr "16 MB" + +#: lutris/runners/fsuae.py:139 lutris/runners/fsuae.py:173 +msgid "32 MB" +msgstr "32 MB" + +#: lutris/runners/fsuae.py:140 lutris/runners/fsuae.py:174 +msgid "64 MB" +msgstr "64 MB" + +#: lutris/runners/fsuae.py:141 lutris/runners/fsuae.py:175 +msgid "128 MB" +msgstr "128 MB" + +#: lutris/runners/fsuae.py:142 lutris/runners/fsuae.py:176 +msgid "256 MB" +msgstr "256 MB" + +#: lutris/runners/fsuae.py:143 +msgid "384 MB" +msgstr "384 MB" + +#: lutris/runners/fsuae.py:144 +msgid "512 MB" +msgstr "512 MB" + +#: lutris/runners/fsuae.py:145 +msgid "768 MB" +msgstr "768 MB" + +#: lutris/runners/fsuae.py:146 +msgid "1 GB" +msgstr "1 GB" + +#: lutris/runners/fsuae.py:179 +msgid "Turbo" +msgstr "Turbo" + +#: lutris/runners/fsuae.py:190 +msgid "Boot disk" +msgstr "Oppstartsenhet" + +#: lutris/runners/fsuae.py:193 +msgid "" +"The main floppy disk file with the game data. \n" +"FS-UAE supports floppy images in multiple file formats: ADF, IPF, DMS are " +"the most common. ADZ (compressed ADF) and ADFs in zip files are a also " +"supported.\n" +"Files ending in .hdf will be mounted as hard drives and ISOs can be used for " +"Amiga CD32 and CDTV models." +msgstr "" +"Hoved diskettfila med spilldataen. \n" +"FS-UAE støtter disketter i flere filformater: ADF, IPF, DMS er de vanligste. " +"ADZ (komprimert ADF) and ADF-er i zip-filer er også støttet.\n" +"Filer som ender i .hdf vil monteres som en harddisk og ISO-er kan brukes for " +"Amiga CD32 og CDTV-modeller." + +#: lutris/runners/fsuae.py:203 lutris/runners/fsuae.py:211 +#: lutris/runners/fsuae.py:325 lutris/runners/fsuae.py:335 +msgid "Media" +msgstr "Media" + +#: lutris/runners/fsuae.py:205 +msgid "Additional floppies" +msgstr "Ytterligere disketter" + +#: lutris/runners/fsuae.py:207 +msgid "The additional floppy disk image(s)." +msgstr "De ytterligere diskett bildene." + +#: lutris/runners/fsuae.py:212 +msgid "CD-ROM image" +msgstr "CD-ROM bilde" + +#: lutris/runners/fsuae.py:214 +msgid "CD-ROM image to use on non CD32/CDTV models" +msgstr "CD-ROM bilde for bruk av ikke-CD32/CDTV-modeller" + +#: lutris/runners/fsuae.py:221 +msgid "Amiga model" +msgstr "Amiga modell" + +#: lutris/runners/fsuae.py:225 +msgid "Specify the Amiga model you want to emulate." +msgstr "Velg Amiga-versjonen du vil emulere." + +#: lutris/runners/fsuae.py:229 lutris/runners/fsuae.py:242 +msgid "Kickstart" +msgstr "Kickstart" + +#: lutris/runners/fsuae.py:230 +msgid "Kickstart ROMs location" +msgstr "Kickstart ROM plassering" + +#: lutris/runners/fsuae.py:233 +msgid "" +"Choose the folder containing original Amiga Kickstart ROMs. Refer to FS-UAE " +"documentation to find how to acquire them. Without these, FS-UAE uses a " +"bundled replacement ROM which is less compatible with Amiga software." +msgstr "" +"Velg mappa som inneholder de originale Amiga Kickstart ROM. Les FS-UAE-" +"dokumentasjonen for å finne ut av hvordan man får tak i dem. Uten disse, " +"bruker FS-UAE en erstattet ROM som støttes noe dårligere av Amiga-" +"programvare." + +#: lutris/runners/fsuae.py:243 +msgid "Extended Kickstart location" +msgstr "Utvidet Kickstart plassering" + +#: lutris/runners/fsuae.py:245 +msgid "Location of extended Kickstart used for CD32" +msgstr "Plassering av utvidet Kickstart brukt for CD32" + +#: lutris/runners/fsuae.py:250 +msgid "Fullscreen (F12 + F to switch)" +msgstr "Fullskjerm («F12 + F» for å bytte)" + +#: lutris/runners/fsuae.py:257 lutris/runners/o2em.py:87 +msgid "Scanlines display style" +msgstr "Skannelinje skjermstil" + +#: lutris/runners/fsuae.py:260 lutris/runners/o2em.py:89 +msgid "" +"Activates a display filter adding scanlines to imitate the displays of " +"yesteryear." +msgstr "" +"Tar i bruk et skjermfilter som legger til skannelinjer for å emulere " +"katodeskjermer." + +#: lutris/runners/fsuae.py:265 +msgid "Graphics Card" +msgstr "Skjermkort" + +#: lutris/runners/fsuae.py:271 +msgid "" +"Use this option to enable a graphics card. This option is none by default, " +"in which case only chipset graphics (OCS/ECS/AGA) support is available." +msgstr "" +"Bruk denne innstillingen for å bruke et skjermkort. Denne innstillingen er " +"satt til «Ingen» som standard, hvor bare brikkesettbilde (OCS/ECS/AGA) " +"støtte er tilgjengelig." + +#: lutris/runners/fsuae.py:278 +msgid "Graphics Card RAM" +msgstr "Skjermkort-minne" + +#: lutris/runners/fsuae.py:284 +msgid "" +"Override the amount of graphics memory on the graphics card. The 0 MB option " +"is not really valid, but exists for user interface reasons." +msgstr "" +"Overstyrer mengen skjermkort-minne tilgjengelig. 0 MB støttes egentlig ikke, " +"men finnes av grensesnitt begrunnelser." + +#: lutris/runners/fsuae.py:290 lutris/sysoptions.py:320 +#: lutris/sysoptions.py:328 lutris/sysoptions.py:337 +msgid "CPU" +msgstr "Prosessor" + +#: lutris/runners/fsuae.py:296 +msgid "" +"Use this option to override the CPU model in the emulated Amiga. All Amiga " +"models imply a default CPU model, so you only need to use this option if you " +"want to use another CPU." +msgstr "" +"Bruk denne innstillingen for å overstyre prosessormodellen i den emulerte " +"Amiga-systemet. Alle Amiga-modeller antyder til en standard prosessormodell, " +"så du trenger bare å bruke denne innstillingen hvis du vil bruke en annen " +"prosessor." + +#: lutris/runners/fsuae.py:303 +msgid "Fast Memory" +msgstr "Rask minne" + +#: lutris/runners/fsuae.py:308 +msgid "Specify how much Fast Memory the Amiga model should have." +msgstr "Velg hvor mye rask minne Amiga-modellen burde ha." + +#: lutris/runners/fsuae.py:312 +msgid "Zorro III RAM" +msgstr "Zorro III minne" + +#: lutris/runners/fsuae.py:318 +msgid "" +"Override the amount of Zorro III Fast memory, specified in KB. Must be a " +"multiple of 1024. The default value depends on [amiga_model]. Requires a " +"processor with 32-bit address bus, (use for example the A1200/020 model)." +msgstr "" +"Overstyrer mengden rask minne for Zorro III, formatert i KB. Må være en " +"multiplum av 1024. Standard verdien avhenger av [amiga_model]. Krever en " +"prosessor med 32 bit adressebuss, (for eksempel A1200/020-modellen)." + +#: lutris/runners/fsuae.py:326 +msgid "Floppy Drive Volume" +msgstr "Lydstyrke for diskett" + +#: lutris/runners/fsuae.py:331 +msgid "" +"Set volume to 0 to disable floppy drive clicks when the drive is empty. Max " +"volume is 100." +msgstr "" +"Velg lydstyrken 0 for å slå av diskett klikkelyd når disketten er tom. Maks " +"lydstyrke er 100." + +#: lutris/runners/fsuae.py:336 +msgid "Floppy Drive Speed" +msgstr "Disketthastighet" + +#: lutris/runners/fsuae.py:342 +msgid "" +"Set the speed of the emulated floppy drives, in percent. For example, you " +"can specify 800 to get an 8x increase in speed. Use 0 to specify turbo mode. " +"Turbo mode means that all floppy operations complete immediately. The " +"default is 100 for most models." +msgstr "" +"Velger hastigheten til den emulerte disketten i prosent. For eksempel, kan " +"du velge 800 for å få en 8x økning i hastighet. Bruk 0 for å velge " +"turbomodus. Turbomodus betyr at alle diskettoperasjoner fullføres med en " +"gang. Standarden er 100 for de fleste modeller." + +#: lutris/runners/fsuae.py:350 +msgid "JIT Compiler" +msgstr "JIT-kompilator" + +#: lutris/runners/fsuae.py:357 +msgid "Feral GameMode" +msgstr "Feral GameMode" + +#: lutris/runners/fsuae.py:361 +msgid "" +"Automatically uses Feral GameMode daemon if available. Set to true to " +"disable the feature." +msgstr "" +"Automatisk bruk av Feral GameMode-tjenesten hvis tilgjengelig. Sett til " +"«true» for å slå av funksjonen." + +#: lutris/runners/fsuae.py:365 +msgid "CPU governor warning" +msgstr "Prosessor-regulator advarsel" + +#: lutris/runners/fsuae.py:370 +msgid "" +"Warn if running with a CPU governor other than performance. Set to true to " +"disable the warning." +msgstr "" +"Varsler fra hvis du kjører med en prosessor-regulator satt til noe annet enn " +"ytelse. Velg «true» for å slå av advarselen" + +#: lutris/runners/fsuae.py:375 +msgid "UAE bsdsocket.library" +msgstr "UAE bsdsocket.library" + +#: lutris/runners/hatari.py:14 +msgid "Hatari" +msgstr "Hatari" + +#: lutris/runners/hatari.py:15 +msgid "Atari ST computers emulator" +msgstr "Atari ST datamaskin-emulator" + +#: lutris/runners/hatari.py:16 lutris/runners/scummvm.py:212 +msgid "Atari ST" +msgstr "Atari ST" + +#: lutris/runners/hatari.py:26 +msgid "Floppy Disk A" +msgstr "Diskett A" + +#: lutris/runners/hatari.py:28 lutris/runners/hatari.py:39 +msgid "" +"Hatari supports floppy disk images in the following formats: ST, DIM, MSA, " +"STX, IPF, RAW and CRT. The last three require the caps library (capslib). " +"ZIP is supported, you don't need to uncompress the file." +msgstr "" +"Hatari støtter diskett bilder i følgende formater: ST, DIM, MSA, STX, IPF, " +"RAW og CRT. De tre siste trenger (capslib)-biblioteket. ZIP støttes, du " +"trenger ikke å pakke ut fila." + +#: lutris/runners/hatari.py:37 +msgid "Floppy Disk B" +msgstr "Diskett B" + +#: lutris/runners/hatari.py:47 lutris/runners/redream.py:74 +#: lutris/runners/zdoom.py:66 +msgid "None" +msgstr "Ingen" + +#: lutris/runners/hatari.py:47 +msgid "Keyboard" +msgstr "Tastatur" + +#: lutris/runners/hatari.py:47 lutris/runners/o2em.py:41 +#: lutris/runners/scummvm.py:269 +msgid "Joystick" +msgstr "Styrespak" + +#: lutris/runners/hatari.py:53 +msgid "Bios file (TOS)" +msgstr "BIOS-fil (TOS)" + +#: lutris/runners/hatari.py:55 +msgid "" +"TOS is the operating system of the Atari ST and is necessary to run " +"applications with the best fidelity, minimizing risks of issues.\n" +"TOS 1.02 is recommended for games." +msgstr "" +"TOS er operativsystemet til Atari ST og er nødvendig for å kjøre programmer " +"med høyeste kvalitet, reduserer risiko for feil.\n" +"TOS 1.02 er anbefalt for spill." + +#: lutris/runners/hatari.py:72 +msgid "Scale up display by 2 (Atari ST/STE)" +msgstr "Utvid skjermen med to (Atari ST/STE)" + +#: lutris/runners/hatari.py:74 +msgid "Double the screen size in windowed mode." +msgstr "Dobbel skjermstørrelse i vindusmodus." + +#: lutris/runners/hatari.py:80 +msgid "Add borders to display" +msgstr "Legg kantlinjer til skjermen" + +#: lutris/runners/hatari.py:83 +msgid "" +"Useful for some games and demos using the overscan technique. The Atari ST " +"displayed borders around the screen because it was not powerful enough to " +"display graphics in fullscreen. But people from the demo scene were able to " +"remove them and some games made use of this technique." +msgstr "" +"Hjelpsom for noen spill og demoer som bruker overskannings-teknikken. Atari " +"ST bruker kantlinjer rundt skjermen fordi den ikke var kraftfull nok for å " +"vise bilde i fullskjerm. Men noen personer fra demoscenen fikk til å fjerne " +"dem og noen spill brukte denne teknikken." + +#: lutris/runners/hatari.py:95 +msgid "Display status bar" +msgstr "Vis statuslinje" + +#: lutris/runners/hatari.py:98 +msgid "" +"Displays a status bar with some useful information, like green leds lighting " +"up when the floppy disks are read." +msgstr "" +"Viser en statuslinje med hjelpsom informasjon, for eksempel grønne lysdioder " +"som lyser opp når diskettene leses fra." + +#: lutris/runners/hatari.py:106 lutris/runners/hatari.py:114 +msgid "Joysticks" +msgstr "Styrespaker" + +#: lutris/runners/hatari.py:107 +msgid "Joystick 0" +msgstr "Styrespak 0" + +#: lutris/runners/hatari.py:115 +msgid "Joystick 1" +msgstr "Styrespak 1" + +#: lutris/runners/hatari.py:126 +msgid "Do you want to select an Atari ST BIOS file?" +msgstr "Vil du velge en Atari ST BIOS-fil?" + +#: lutris/runners/hatari.py:127 +msgid "Use BIOS file?" +msgstr "Bruk BIOS-fil?" + +#: lutris/runners/hatari.py:128 +msgid "Select a BIOS file" +msgstr "Velg en BIOS-fil" + +#: lutris/runners/jzintv.py:13 +msgid "jzIntv" +msgstr "jzIntv" + +#: lutris/runners/jzintv.py:14 +msgid "Intellivision Emulator" +msgstr "Intellivision emulator" + +#: lutris/runners/jzintv.py:15 +msgid "Intellivision" +msgstr "Intellivision" + +#: lutris/runners/jzintv.py:24 +msgid "" +"The game data, commonly called a ROM image. \n" +"Supported formats: ROM, BIN+CFG, INT, ITV \n" +"The file extension must be lower-case." +msgstr "" +"Spilldataen, kjent som ROM bilde. \n" +"Støtter formatene: ROM, BIN + CFG, INT, ITV \n" +"Filtypen må være små bokstaver." + +#: lutris/runners/jzintv.py:34 +msgid "Bios location" +msgstr "Plassering for BIOS" + +#: lutris/runners/jzintv.py:36 +msgid "" +"Choose the folder containing the Intellivision BIOS files (exec.bin and " +"grom.bin).\n" +"These files contain code from the original hardware necessary to the " +"emulation." +msgstr "" +"Velg mappa som inneholder Intellivision BIOS-filene (exec.bin og grom.bin).\n" +"Disse filene inneholder kode fra den originale maskinvaren som trengs for " +"emulering." + +#: lutris/runners/jzintv.py:47 +msgid "Resolution" +msgstr "Oppløsning" + +#: lutris/runners/libretro.py:69 +msgid "Libretro" +msgstr "Libretro" + +#: lutris/runners/libretro.py:70 +msgid "Multi-system emulator" +msgstr "Multi-system emulator" + +#: lutris/runners/libretro.py:80 +msgid "Core" +msgstr "Kjerne" + +#: lutris/runners/libretro.py:89 lutris/runners/zdoom.py:76 +msgid "Config file" +msgstr "Oppsettsfil" + +#: lutris/runners/libretro.py:101 +msgid "Verbose logging" +msgstr "Detaljert loggføring" + +#: lutris/runners/libretro.py:150 +msgid "The installer does not specify the libretro 'core' version." +msgstr "Installatøren oppgir ikke libretro «kjerne»-versjonen." + +#: lutris/runners/libretro.py:246 +msgid "" +"The emulator files BIOS location must be configured in the Preferences " +"dialog." +msgstr "" +"Plasseringen av emulator-BIOS-filene må settes opp i innstillingsvinduet." + +#: lutris/runners/libretro.py:292 +msgid "No core has been selected for this game" +msgstr "Ingen kjerne valgt for dette spillet" + +#: lutris/runners/libretro.py:298 +msgid "No game file specified" +msgstr "Ingen spillfil valgt" + +#: lutris/runners/linux.py:18 +msgid "Runs native games" +msgstr "Kjører lokale spill" + +#: lutris/runners/linux.py:27 lutris/runners/wine.py:211 +msgid "Executable" +msgstr "Programfil" + +#: lutris/runners/linux.py:28 +msgid "The game's main executable file" +msgstr "Spillets hoved-programfil" + +#: lutris/runners/linux.py:33 lutris/runners/mame.py:126 +#: lutris/runners/scummvm.py:66 lutris/runners/steam.py:49 +#: lutris/runners/steam.py:99 lutris/runners/wine.py:217 +#: lutris/runners/zdoom.py:28 +msgid "Arguments" +msgstr "Argumenter" + +#: lutris/runners/linux.py:34 lutris/runners/mame.py:127 +#: lutris/runners/scummvm.py:67 +msgid "Command line arguments used when launching the game" +msgstr "Kommandolinje argumenter som brukes ved oppstart av spillet" + +#: lutris/runners/linux.py:49 +msgid "Preload library" +msgstr "Forhåndslast bibliotekfil" + +#: lutris/runners/linux.py:51 +msgid "A library to load before running the game's executable." +msgstr "Bibliotekfil å laste før oppstart av spillets programfil." + +#: lutris/runners/linux.py:56 +msgid "Add directory to LD_LIBRARY_PATH" +msgstr "Legg mappa til LD_LIBRARY_PATH" + +#: lutris/runners/linux.py:59 +msgid "" +"A directory where libraries should be searched for first, before the " +"standard set of directories; this is useful when debugging a new library or " +"using a nonstandard library for special purposes." +msgstr "" +"En mappe der bibliotekfiler burde letes etter først, før standard sett med " +"mapper; hjelpsomt ved feilsøking av en ny bibliotekfil eller ved bruk av " +"ikke-standard bibliotekfil for spesielle grunner." + +#: lutris/runners/linux.py:141 +msgid "" +"The runner could not find a command or exe to use for this configuration." +msgstr "Løperen klarte ikke finne en kommando eller exe for dette oppsettet." + +#: lutris/runners/linux.py:164 +#, python-format +msgid "The file %s is not executable" +msgstr "Filen %s er ikke kjørbar" + +#: lutris/runners/mame.py:66 lutris/services/mame.py:13 +msgid "MAME" +msgstr "MAME" + +#: lutris/runners/mame.py:67 +msgid "Arcade game emulator" +msgstr "Arkadespill emulator" + +#: lutris/runners/mame.py:87 lutris/runners/mednafen.py:69 +msgid "The emulated machine." +msgstr "Den emulerte maskinen." + +#: lutris/runners/mame.py:92 +msgid "Storage type" +msgstr "Lagringstype" + +#: lutris/runners/mame.py:94 +msgid "Floppy disk" +msgstr "Diskett" + +#: lutris/runners/mame.py:95 +msgid "Floppy drive 1" +msgstr "Diskett 1" + +#: lutris/runners/mame.py:96 +msgid "Floppy drive 2" +msgstr "Diskett 2" + +#: lutris/runners/mame.py:97 +msgid "Floppy drive 3" +msgstr "Diskett 3" + +#: lutris/runners/mame.py:98 +msgid "Floppy drive 4" +msgstr "Diskett 4" + +#: lutris/runners/mame.py:99 +msgid "Cassette (tape)" +msgstr "Kassett (magnetbånd)" + +#: lutris/runners/mame.py:100 +msgid "Cassette 1 (tape)" +msgstr "Kassett 1 (magnetbånd)" + +#: lutris/runners/mame.py:101 +msgid "Cassette 2 (tape)" +msgstr "Kassett 2 (magnetbånd)" + +#: lutris/runners/mame.py:102 +msgid "Cartridge" +msgstr "Spillmodul" + +#: lutris/runners/mame.py:103 +msgid "Cartridge 1" +msgstr "Spillmodul 1" + +#: lutris/runners/mame.py:104 +msgid "Cartridge 2" +msgstr "Spillmodul 2" + +#: lutris/runners/mame.py:105 +msgid "Cartridge 3" +msgstr "Spillmodul 3" + +#: lutris/runners/mame.py:106 +msgid "Cartridge 4" +msgstr "Spillmodul 4" + +#: lutris/runners/mame.py:107 +msgid "Snapshot" +msgstr "Øyeblikksbilde" + +#: lutris/runners/mame.py:108 +msgid "Hard Disk" +msgstr "Harddisk" + +#: lutris/runners/mame.py:109 +msgid "Hard Disk 1" +msgstr "Harddisk 1" + +#: lutris/runners/mame.py:110 +msgid "Hard Disk 2" +msgstr "Harddisk 2" + +#: lutris/runners/mame.py:111 +msgid "CD-ROM" +msgstr "CD-ROM" + +#: lutris/runners/mame.py:112 +msgid "CD-ROM 1" +msgstr "CD-ROM 1" + +#: lutris/runners/mame.py:113 +msgid "CD-ROM 2" +msgstr "CD-ROM 2" + +#: lutris/runners/mame.py:114 +msgid "Snapshot (dump)" +msgstr "Øyeblikksbilde (dump)" + +#: lutris/runners/mame.py:115 +msgid "Quickload" +msgstr "Hurtiglast" + +#: lutris/runners/mame.py:116 +msgid "Memory Card" +msgstr "Minnekort" + +#: lutris/runners/mame.py:117 +msgid "Cylinder" +msgstr "Sylinder" + +#: lutris/runners/mame.py:118 +msgid "Punch Tape 1" +msgstr "Hullbånd 1" + +#: lutris/runners/mame.py:119 +msgid "Punch Tape 2" +msgstr "Hullbånd 2" + +#: lutris/runners/mame.py:120 +msgid "Print Out" +msgstr "Skriv ut" + +#: lutris/runners/mame.py:138 lutris/runners/mame.py:147 +msgid "Autoboot" +msgstr "Automatisk oppstart" + +#: lutris/runners/mame.py:139 +msgid "Autoboot command" +msgstr "Automatisk oppstartskommando" + +#: lutris/runners/mame.py:141 +msgid "" +"Autotype this command when the system has started, an enter keypress is " +"automatically added." +msgstr "" +"Skriver denne kommandoen automatisk ved oppstart av systemet, et «Enter»-" +"tastetrykk skjer automatisk." + +#: lutris/runners/mame.py:148 +msgid "Delay before entering autoboot command" +msgstr "Forsink før begynnelse av automatisk oppstartskommando" + +#: lutris/runners/mame.py:158 +msgid "ROM/BIOS path" +msgstr "ROM/BIOS mappe" + +#: lutris/runners/mame.py:160 +msgid "" +"Choose the folder containing ROMs and BIOS files.\n" +"These files contain code from the original hardware necessary to the " +"emulation." +msgstr "" +"Velg mappa som inneholder ROM- og BIOS-filer.\n" +"Disse filene inneholder kode fra den originale maskinvaren som trengs for " +"emulering." + +#: lutris/runners/mame.py:176 +msgid "CRT effect ()" +msgstr "CRT-effekt ()" + +#: lutris/runners/mame.py:177 +msgid "Applies a CRT effect to the screen.Requires OpenGL renderer." +msgstr "Bruker en CRT-effekt på skjermen. Krever OpenGL-opptegneren." + +#: lutris/runners/mame.py:183 lutris/runners/scummvm.py:466 +#: lutris/runners/scummvm.py:474 +msgid "Debugging" +msgstr "Feilsøking" + +#: lutris/runners/mame.py:184 +msgid "Verbose" +msgstr "Detaljert" + +#: lutris/runners/mame.py:185 +msgid "display additional diagnostic information." +msgstr "vis ytterligere diagnostikk informasjon" + +#: lutris/runners/mame.py:193 +msgid "Video backend" +msgstr "Videomotor" + +#: lutris/runners/mame.py:199 lutris/runners/scummvm.py:187 +#: lutris/runners/vice.py:74 +msgid "Software" +msgstr "Programvare" + +#: lutris/runners/mame.py:207 +msgid "Wait for VSync" +msgstr "Vent for VSync" + +#: lutris/runners/mame.py:209 +msgid "" +"Enable waiting for the start of vblank before flipping screens; " +"reduces tearing effects." +msgstr "" +"Venter på starten av VBLANK før rotering av skjermene; reduserer " +"skjermriving." + +#: lutris/runners/mame.py:217 +msgid "Menu mode key" +msgstr "Menymodustast" + +#: lutris/runners/mame.py:219 +msgid "Scroll Lock" +msgstr "Scroll Lock" + +#: lutris/runners/mame.py:220 +msgid "Num Lock" +msgstr "Num Lock" + +#: lutris/runners/mame.py:221 +msgid "Caps Lock" +msgstr "Caps Lock" + +#: lutris/runners/mame.py:222 +msgid "Menu" +msgstr "Meny" + +#: lutris/runners/mame.py:223 +msgid "Right Control" +msgstr "Høyre Ctrl" + +#: lutris/runners/mame.py:224 +msgid "Left Control" +msgstr "Venstre Ctrl" + +#: lutris/runners/mame.py:225 +msgid "Right Alt" +msgstr "Høyre Alt" + +#: lutris/runners/mame.py:226 +msgid "Left Alt" +msgstr "Venstre Alt" + +#: lutris/runners/mame.py:227 +msgid "Right Super" +msgstr "Høyre Super" + +#: lutris/runners/mame.py:228 +msgid "Left Super" +msgstr "Venstre Super" + +#: lutris/runners/mame.py:232 +msgid "" +"Key to switch between Full Keyboard Mode and Partial Keyboard Mode (default: " +"Scroll Lock)" +msgstr "" +"Tast for å bytte mellom fullstendig-tastaturmodus og delvis-tastaturmodus " +"(standard: «Scroll Lock»)" + +#: lutris/runners/mame.py:245 lutris/runners/mame.py:292 +msgid "Arcade" +msgstr "Arkade" + +#: lutris/runners/mame.py:245 lutris/runners/mame.py:291 +msgid "Nintendo Game & Watch" +msgstr "Nintendo Game & Watch" + +#: lutris/runners/mame.py:341 +#, python-format +msgid "No device is set for machine %s" +msgstr "Ingen enhet er satt for maskinen %s" + +#: lutris/runners/mame.py:351 +#, python-format +msgid "The path '%s' is not set. please set it in the options." +msgstr "Mappa «%s» er ikke satt opp. Sett opp mappa i innstillingene." + +#: lutris/runners/mednafen.py:18 +msgid "Mednafen" +msgstr "Mednafen" + +#: lutris/runners/mednafen.py:19 +msgid "Multi-system emulator: NES, PC Engine, PSX…" +msgstr "Multi-system emulator: NES, PC Engine, PSX …" + +#: lutris/runners/mednafen.py:21 +msgid "Nintendo Game Boy (Color)" +msgstr "Nintendo Game Boy (Color)" + +#: lutris/runners/mednafen.py:22 +msgid "Nintendo Game Boy Advance" +msgstr "Nintendo Game Boy Advance" + +#: lutris/runners/mednafen.py:23 +msgid "Sega Game Gear" +msgstr "Sega Game Gear" + +#: lutris/runners/mednafen.py:24 +msgid "Sega Genesis/Mega Drive" +msgstr "Sega Genesis/Mega Drive" + +#: lutris/runners/mednafen.py:25 +msgid "Atari Lynx" +msgstr "Atari Lynx" + +#: lutris/runners/mednafen.py:26 lutris/runners/osmose.py:14 +msgid "Sega Master System" +msgstr "Sega Master System" + +#: lutris/runners/mednafen.py:27 +msgid "SNK Neo Geo Pocket (Color)" +msgstr "SNK Neo Geo Pocket (Color)" + +#: lutris/runners/mednafen.py:28 +msgid "Nintendo NES" +msgstr "Nintendo NES" + +#: lutris/runners/mednafen.py:29 +msgid "NEC PC Engine TurboGrafx-16" +msgstr "NEC PC Engine TurboGrafx-16" + +#: lutris/runners/mednafen.py:30 +msgid "NEC PC-FX" +msgstr "NEC PC-FX" + +#: lutris/runners/mednafen.py:32 +msgid "Sega Saturn" +msgstr "Sega Saturn" + +#: lutris/runners/mednafen.py:33 lutris/runners/snes9x.py:20 +msgid "Nintendo SNES" +msgstr "Nintendo SNES" + +#: lutris/runners/mednafen.py:34 +msgid "Bandai WonderSwan" +msgstr "Bandai WonderSwan" + +#: lutris/runners/mednafen.py:35 +msgid "Nintendo Virtual Boy" +msgstr "Nintendo Virtual Boy" + +#: lutris/runners/mednafen.py:38 +msgid "Game Boy (Color)" +msgstr "Game Boy (Color)" + +#: lutris/runners/mednafen.py:39 +msgid "Game Boy Advance" +msgstr "Game Boy Advance" + +#: lutris/runners/mednafen.py:40 +msgid "Game Gear" +msgstr "Game Gear" + +#: lutris/runners/mednafen.py:41 +msgid "Genesis/Mega Drive" +msgstr "Genesis/Mega Drive" + +#: lutris/runners/mednafen.py:42 +msgid "Lynx" +msgstr "Lynx" + +#: lutris/runners/mednafen.py:43 +msgid "Master System" +msgstr "Master System" + +#: lutris/runners/mednafen.py:44 +msgid "Neo Geo Pocket (Color)" +msgstr "Neo Geo Pocket (Color)" + +#: lutris/runners/mednafen.py:45 +msgid "NES" +msgstr "NES" + +#: lutris/runners/mednafen.py:46 +msgid "PC Engine" +msgstr "PC Engine" + +#: lutris/runners/mednafen.py:47 +msgid "PC-FX" +msgstr "PC-FX" + +#: lutris/runners/mednafen.py:48 +msgid "PlayStation" +msgstr "PlayStation" + +#: lutris/runners/mednafen.py:49 +msgid "Saturn" +msgstr "Saturn" + +#: lutris/runners/mednafen.py:50 +msgid "SNES" +msgstr "SNES" + +#: lutris/runners/mednafen.py:51 +msgid "WonderSwan" +msgstr "WonderSwan" + +#: lutris/runners/mednafen.py:52 +msgid "Virtual Boy" +msgstr "Virtual Boy" + +#: lutris/runners/mednafen.py:61 +msgid "" +"The game data, commonly called a ROM image. \n" +"Mednafen supports GZIP and ZIP compressed ROMs." +msgstr "" +"Spilldataen, kjent som ROM bilde. \n" +"Mednafen støtter GZIP og ZIP komprimerte ROM." + +#: lutris/runners/mednafen.py:67 +msgid "Machine type" +msgstr "Maskintype" + +#: lutris/runners/mednafen.py:78 +msgid "Aspect ratio" +msgstr "Størrelsesforhold" + +#: lutris/runners/mednafen.py:81 +msgid "Stretched" +msgstr "Strukket" + +#: lutris/runners/mednafen.py:82 lutris/runners/vice.py:66 +msgid "Preserve aspect ratio" +msgstr "Behold størrelsesforhold" + +#: lutris/runners/mednafen.py:83 +msgid "Integer scale" +msgstr "Heltall skala" + +#: lutris/runners/mednafen.py:84 +msgid "Multiple of 2 scale" +msgstr "Multippel av 2 skala" + +#: lutris/runners/mednafen.py:92 +msgid "Video scaler" +msgstr "Videoskala" + +#: lutris/runners/mednafen.py:116 +msgid "Sound device" +msgstr "Lydenhet" + +#: lutris/runners/mednafen.py:118 +msgid "Mednafen default" +msgstr "Mednafen-standard" + +#: lutris/runners/mednafen.py:119 +msgid "ALSA default" +msgstr "ALSA-standard" + +#: lutris/runners/mednafen.py:129 +msgid "Use default Mednafen controller configuration" +msgstr "Bruk standard Mednafen kontroller-oppsett" + +#: lutris/runners/mupen64plus.py:13 +msgid "Mupen64Plus" +msgstr "Mupen64Plus" + +#: lutris/runners/mupen64plus.py:14 +msgid "Nintendo 64 emulator" +msgstr "Nintendo 64 emulator" + +#: lutris/runners/mupen64plus.py:15 +msgid "Nintendo 64" +msgstr "Nintendo 64" + +#: lutris/runners/mupen64plus.py:22 lutris/runners/o2em.py:49 +#: lutris/runners/openmsx.py:21 lutris/runners/ryujinx.py:26 +#: lutris/runners/snes9x.py:30 lutris/runners/yuzu.py:24 +msgid "The game data, commonly called a ROM image." +msgstr "Spilldataen, kjent som ROM bilde." + +#: lutris/runners/mupen64plus.py:32 +msgid "Hide OSD" +msgstr "Skjul OSD" + +#: lutris/runners/o2em.py:13 +msgid "O2EM" +msgstr "O2EM" + +#: lutris/runners/o2em.py:14 +msgid "Magnavox Odyssey² Emulator" +msgstr "Magnavox Odyssey² emulator" + +#: lutris/runners/o2em.py:16 lutris/runners/o2em.py:32 +msgid "Magnavox Odyssey²" +msgstr "Magnavox Odyssey²" + +#: lutris/runners/o2em.py:17 lutris/runners/o2em.py:33 +msgid "Phillips C52" +msgstr "Phillips C52" + +#: lutris/runners/o2em.py:18 lutris/runners/o2em.py:34 +msgid "Phillips Videopac+" +msgstr "Phillips Videopac+" + +#: lutris/runners/o2em.py:19 lutris/runners/o2em.py:35 +msgid "Brandt Jopac" +msgstr "Brandt Jopac" + +#: lutris/runners/o2em.py:38 lutris/runners/wine.py:528 +msgid "Disable" +msgstr "Slå av" + +#: lutris/runners/o2em.py:39 +msgid "Arrow Keys and Right Shift" +msgstr "Piltaster og Høyre Shift" + +#: lutris/runners/o2em.py:40 +msgid "W,S,A,D,SPACE" +msgstr "W,S,A,D,MELLOMROM" + +#: lutris/runners/o2em.py:57 +msgid "BIOS" +msgstr "BIOS" + +#: lutris/runners/o2em.py:65 +msgid "First controller" +msgstr "Første kontroller" + +#: lutris/runners/o2em.py:73 +msgid "Second controller" +msgstr "Andre kontroller" + +#: lutris/runners/openmsx.py:12 +msgid "openMSX" +msgstr "openMSX" + +#: lutris/runners/openmsx.py:13 +msgid "MSX computer emulator" +msgstr "MSX datamaskin-emulator" + +#: lutris/runners/openmsx.py:14 +msgid "MSX, MSX2, MSX2+, MSX turboR" +msgstr "MSX, MSX2, MSX2+, MSX turboR" + +#: lutris/runners/osmose.py:12 +msgid "Osmose" +msgstr "Osmose" + +#: lutris/runners/osmose.py:13 +msgid "Sega Master System Emulator" +msgstr "Sega Master System emulator" + +#: lutris/runners/osmose.py:23 +msgid "" +"The game data, commonly called a ROM image.\n" +"Supported formats: SMS and GG files. ZIP compressed ROMs are supported." +msgstr "" +"Spilldataen, kjent som ROM bilde.\n" +"Støtter formatene: SMS- og GG-filer. ZIP komprimerte ROM støttes." + +#: lutris/runners/pcsx2.py:12 +msgid "PCSX2" +msgstr "PCSX2" + +#: lutris/runners/pcsx2.py:13 +msgid "PlayStation 2 emulator" +msgstr "PlayStation 2 emulator" + +#: lutris/runners/pcsx2.py:14 +msgid "Sony PlayStation 2" +msgstr "Sony PlayStation 2" + +#: lutris/runners/pcsx2.py:34 +msgid "Fullboot" +msgstr "Full oppstart" + +#: lutris/runners/pcsx2.py:35 lutris/runners/rpcs3.py:26 +msgid "No GUI" +msgstr "Ingen grensesnitt" + +#: lutris/runners/pico8.py:21 +msgid "Runs PICO-8 fantasy console cartridges" +msgstr "Kjører spillmoduler for PICO-8 fantasikonsoll" + +#: lutris/runners/pico8.py:23 lutris/runners/pico8.py:24 +msgid "PICO-8" +msgstr "PICO-8" + +#: lutris/runners/pico8.py:29 +msgid "Cartridge file/URL/ID" +msgstr "Spillmodul fil/URL/ID" + +#: lutris/runners/pico8.py:30 +msgid "You can put a .p8.png file path, URL, or BBS cartridge ID here." +msgstr "" +"Du kan velge en .p8.png-fil plassering, URL eller BBS-spillmodul-ID her." + +#: lutris/runners/pico8.py:41 +msgid "Launch in fullscreen." +msgstr "Start i fullskjerm." + +#: lutris/runners/pico8.py:46 lutris/runners/web.py:47 +msgid "Window size" +msgstr "Vindusstørrelse" + +#: lutris/runners/pico8.py:49 +msgid "The initial size of the game window." +msgstr "Den opprinnelige størrelsen på spillvinduet." + +#: lutris/runners/pico8.py:54 +msgid "Start in splore mode" +msgstr "Start i splore-modus" + +#: lutris/runners/pico8.py:60 +msgid "Extra arguments" +msgstr "Ekstra argumenter" + +#: lutris/runners/pico8.py:62 +msgid "Extra arguments to the executable" +msgstr "Ekstra argumenter for programfila" + +#: lutris/runners/pico8.py:68 +msgid "Engine (web only)" +msgstr "Spillmotor (bare på nett)" + +#: lutris/runners/pico8.py:70 +msgid "Name of engine (will be downloaded) or local file path" +msgstr "Navnet på spillmotor (vil lastes ned) eller en lokal filplassering" + +#: lutris/runners/redream.py:10 +msgid "Redream" +msgstr "Redream" + +#: lutris/runners/redream.py:11 lutris/runners/reicast.py:17 +msgid "Sega Dreamcast emulator" +msgstr "Sega Dreamcast emulator" + +#: lutris/runners/redream.py:12 lutris/runners/reicast.py:18 +msgid "Sega Dreamcast" +msgstr "Sega Dreamcast" + +#: lutris/runners/redream.py:19 lutris/runners/reicast.py:28 +msgid "Disc image file" +msgstr "Platebildefil" + +#: lutris/runners/redream.py:20 +msgid "" +"Game data file\n" +"Supported formats: GDI, CDI, CHD" +msgstr "" +"Spilldatafil\n" +"Støtter formatene: GDI, CDI og CHD" + +#: lutris/runners/redream.py:29 +msgid "Aspect Ratio" +msgstr "Størrelsesforhold" + +#: lutris/runners/redream.py:30 +msgid "4:3" +msgstr "4:3" + +#: lutris/runners/redream.py:36 +msgid "Region" +msgstr "Region" + +#: lutris/runners/redream.py:37 +msgid "USA" +msgstr "USA" + +#: lutris/runners/redream.py:37 +msgid "Europe" +msgstr "Europa" + +#: lutris/runners/redream.py:37 +msgid "Japan" +msgstr "Japan" + +#: lutris/runners/redream.py:43 +msgid "System Language" +msgstr "Systemspråk" + +#: lutris/runners/redream.py:45 lutris/sysoptions.py:32 +msgid "English" +msgstr "Engelsk" + +#: lutris/runners/redream.py:46 lutris/sysoptions.py:36 +msgid "German" +msgstr "Tysk" + +#: lutris/runners/redream.py:47 lutris/sysoptions.py:34 +msgid "French" +msgstr "Fransk" + +#: lutris/runners/redream.py:48 lutris/sysoptions.py:44 +msgid "Spanish" +msgstr "Spansk" + +#: lutris/runners/redream.py:49 lutris/sysoptions.py:38 +msgid "Italian" +msgstr "Italiensk" + +#: lutris/runners/redream.py:59 +msgid "NTSC" +msgstr "NTSC" + +#: lutris/runners/redream.py:60 +msgid "PAL" +msgstr "PAL" + +#: lutris/runners/redream.py:61 +msgid "PAL-M (Brazil)" +msgstr "PAL-M (Brasil)" + +#: lutris/runners/redream.py:62 +msgid "PAL-N (Argentina, Paraguay, Uruguay)" +msgstr "PAL-N (Argentina, Paraguay og Uruguay)" + +#: lutris/runners/redream.py:69 +msgid "Time Sync" +msgstr "Tidssynkronisering" + +#: lutris/runners/redream.py:71 +msgid "Audio and video" +msgstr "Lyd og video" + +#: lutris/runners/redream.py:73 +msgid "Video" +msgstr "Video" + +#: lutris/runners/redream.py:82 +msgid "Internal Video Resolution Scale" +msgstr "Intern videooppløsningsskala" + +#: lutris/runners/redream.py:95 +msgid "Only available in premium version." +msgstr "Bare tilgjengelig i premium-versjonen." + +#: lutris/runners/redream.py:102 +msgid "Do you want to select a premium license file?" +msgstr "Vil du velge en premium-lisensfil?" + +#: lutris/runners/redream.py:103 lutris/runners/redream.py:104 +msgid "Use premium version?" +msgstr "Bruk premium-versjonen?" + +#: lutris/runners/reicast.py:16 +msgid "Reicast" +msgstr "Reicast" + +#: lutris/runners/reicast.py:29 +msgid "" +"The game data.\n" +"Supported formats: ISO, CDI" +msgstr "" +"Spilldataen.\n" +"Støtter formatene: ISO og CDI" + +#: lutris/runners/reicast.py:46 lutris/runners/reicast.py:54 +#: lutris/runners/reicast.py:62 lutris/runners/reicast.py:70 +msgid "Gamepads" +msgstr "Spillkontroller" + +#: lutris/runners/reicast.py:47 +msgid "Gamepad 1" +msgstr "Spillkontroller 1" + +#: lutris/runners/reicast.py:55 +msgid "Gamepad 2" +msgstr "Spillkontroller 2" + +#: lutris/runners/reicast.py:63 +msgid "Gamepad 3" +msgstr "Spillkontroller 3" + +#: lutris/runners/reicast.py:71 +msgid "Gamepad 4" +msgstr "Spillkontroller 4" + +#: lutris/runners/rpcs3.py:12 +msgid "RPCS3" +msgstr "RPCS3" + +#: lutris/runners/rpcs3.py:13 +msgid "PlayStation 3 emulator" +msgstr "PlayStation 3 emulator" + +#: lutris/runners/rpcs3.py:14 +msgid "Sony PlayStation 3" +msgstr "Sony PlayStation 3" + +#: lutris/runners/rpcs3.py:23 +msgid "Path to EBOOT.BIN" +msgstr "Plassering til EBOOT.BIN" + +#: lutris/runners/runner.py:178 +msgid "Custom executable for the runner" +msgstr "Tilpasset programfil for løperen" + +#: lutris/runners/runner.py:185 +msgid "Side Panel" +msgstr "Sidepanel" + +#: lutris/runners/runner.py:188 +msgid "Visible in Side Panel" +msgstr "Synlig i sidepanelet" + +#: lutris/runners/runner.py:192 +msgid "" +"Show this runner in the side panel if it is installed or available through " +"Flatpak." +msgstr "" +"Vis denne løperen i sidepanelet hvis den er installert eller tilgjengelig " +"med Flatpak." + +#: lutris/runners/runner.py:207 lutris/runners/runner.py:216 +#: lutris/runners/vice.py:115 lutris/util/system.py:261 +#, python-format +msgid "The executable '%s' could not be found." +msgstr "Fant ikke programfila «%s»." + +#: lutris/runners/runner.py:465 +msgid "" +"The required runner is not installed.\n" +"Do you wish to install it now?" +msgstr "" +"Den nødvendige løperen er ikke installert.\n" +"Vil du installere den nå?" + +#: lutris/runners/runner.py:466 +msgid "Required runner unavailable" +msgstr "Den nødvendige løperen er ikke tilgjengelig" + +#: lutris/runners/runner.py:521 +msgid "Failed to retrieve {} ({}) information" +msgstr "Klarte ikke hente {} ({}) informasjon" + +#: lutris/runners/runner.py:526 +#, python-format +msgid "The '%s' version of the '%s' runner can't be downloaded." +msgstr "Klarte ikke laste ned «%s»-versjonen av «%s»-løperen." + +#: lutris/runners/runner.py:529 +#, python-format +msgid "The the '%s' runner can't be downloaded." +msgstr "Klarte ikke laste ned «%s»-løperen" + +#: lutris/runners/runner.py:558 +msgid "Failed to extract {}" +msgstr "Klarte ikke pakke ut {}" + +#: lutris/runners/runner.py:563 +msgid "Failed to extract {}: {}" +msgstr "Klarte ikke pakke ut {}: {}" + +#: lutris/runners/ryujinx.py:13 +msgid "Ryujinx" +msgstr "Ryujinx" + +#: lutris/runners/ryujinx.py:14 lutris/runners/yuzu.py:14 +msgid "Nintendo Switch" +msgstr "Nintendo Switch" + +#: lutris/runners/ryujinx.py:15 lutris/runners/yuzu.py:15 +msgid "Nintendo Switch emulator" +msgstr "Nintendo Switch emulator" + +#: lutris/runners/ryujinx.py:25 +msgid "NSP file" +msgstr "NSP-fil" + +#: lutris/runners/ryujinx.py:32 lutris/runners/yuzu.py:30 +msgid "Encryption keys" +msgstr "Krypteringsnøkler" + +#: lutris/runners/ryujinx.py:34 lutris/runners/yuzu.py:32 +msgid "File containing the encryption keys." +msgstr "Fil som inneholder krypteringsnøkler." + +#: lutris/runners/ryujinx.py:38 lutris/runners/yuzu.py:36 +msgid "Title keys" +msgstr "Tittelnøkler" + +#: lutris/runners/ryujinx.py:40 lutris/runners/yuzu.py:38 +msgid "File containing the title keys." +msgstr "Fil som inneholder tittelnøkler." + +#: lutris/runners/scummvm.py:32 +msgid "Warning Scalers may not work with OpenGL rendering." +msgstr "" +"Advarsel Videoskalaer fungerer kanskje ikke med OpenGL-opptegneren." + +#: lutris/runners/scummvm.py:45 +#, python-format +msgid "Warning The '%s' scaler does not work with a scale factor of %s." +msgstr "" +"Advarsel «%s»-skaleringsverktøyet fungerer ikke med en " +"skaleringsfaktor av %s." + +#: lutris/runners/scummvm.py:54 +msgid "Engine for point-and-click games." +msgstr "Spillmotor for pek-og-klikk-spill." + +#: lutris/runners/scummvm.py:55 lutris/services/scummvm.py:29 +msgid "ScummVM" +msgstr "ScummVM" + +#: lutris/runners/scummvm.py:61 +msgid "Game identifier" +msgstr "Spillidentifikator" + +#: lutris/runners/scummvm.py:62 +msgid "Game files location" +msgstr "Plassering for spillfiler" + +#: lutris/runners/scummvm.py:119 +msgid "Enable subtitles" +msgstr "Bruk undertekster" + +#: lutris/runners/scummvm.py:127 +msgid "Aspect ratio correction" +msgstr "Korrigering av størrelsesforhold" + +#: lutris/runners/scummvm.py:131 +msgid "" +"Most games supported by ScummVM were made for VGA display modes using " +"rectangular pixels. Activating this option for these games will preserve the " +"4:3 aspect ratio they were made for." +msgstr "" +"De fleste spill som støttes av ScummVM ble laget for VGA-skjermer med bruk " +"av rektangulære piksler. Bruk av denne innstillingen for disse spillene vil " +"bevare 4:3 størrelsesforholdet de var ment for." + +#: lutris/runners/scummvm.py:140 +msgid "Graphic scaler" +msgstr "Bildeskala" + +#: lutris/runners/scummvm.py:157 +msgid "" +"The algorithm used to scale up the game's base resolution, resulting in " +"different visual styles. " +msgstr "" +"Algoritmen brukt for oppskalering av spilletsoppløsning, noe som resulterer " +"i forskjellige visuelle stiler." + +#: lutris/runners/scummvm.py:163 +msgid "Scale factor" +msgstr "Skaleringsfaktor" + +#: lutris/runners/scummvm.py:174 +msgid "" +"Changes the resolution of the game. For example, a 2x scale will take a " +"320x200 resolution game and scale it up to 640x400. " +msgstr "" +"Endrer spilloppløsningen. For eksempel en 2x-skala vil gjøre en 320 × 200-" +"spilloppløsning om til 640 × 400. " + +#: lutris/runners/scummvm.py:183 +msgid "Renderer" +msgstr "Opptegner" + +#: lutris/runners/scummvm.py:188 +msgid "OpenGL" +msgstr "OpenGL" + +#: lutris/runners/scummvm.py:189 +msgid "OpenGL (with shaders)" +msgstr "OpenGL (med skyggeleggere)" + +#: lutris/runners/scummvm.py:193 +msgid "Changes the rendering method used for 3D games." +msgstr "Endrer opptegningsmetode brukt av 3D-spill." + +#: lutris/runners/scummvm.py:198 +msgid "Render mode" +msgstr "Opptegningsmodus" + +#: lutris/runners/scummvm.py:202 +msgid "Hercules (Green)" +msgstr "Hercules (grønn)" + +#: lutris/runners/scummvm.py:203 +msgid "Hercules (Amber)" +msgstr "Hercules (gyllen)" + +#: lutris/runners/scummvm.py:204 +msgid "CGA" +msgstr "CGA" + +#: lutris/runners/scummvm.py:205 +msgid "EGA" +msgstr "EGA" + +#: lutris/runners/scummvm.py:206 +msgid "VGA" +msgstr "VGA" + +#: lutris/runners/scummvm.py:207 +msgid "Amiga" +msgstr "Amiga" + +#: lutris/runners/scummvm.py:208 +msgid "FM Towns" +msgstr "FM Towns" + +#: lutris/runners/scummvm.py:209 +msgid "PC-9821" +msgstr "PC-9821" + +#: lutris/runners/scummvm.py:210 +msgid "PC-9801" +msgstr "PC-9801" + +#: lutris/runners/scummvm.py:211 +msgid "Apple IIgs" +msgstr "Apple IIgs" + +#: lutris/runners/scummvm.py:213 +msgid "Macintosh" +msgstr "Macintosh" + +#: lutris/runners/scummvm.py:217 +msgid "" +"Changes the graphics hardware the game will target, if the game supports " +"this." +msgstr "" +"Endrer maskinvaren som driver skjermbildet som spillet vil målrette, hvis " +"spillet støtter dette." + +#: lutris/runners/scummvm.py:222 +msgid "Stretch mode" +msgstr "Strekkmodus" + +#: lutris/runners/scummvm.py:226 +msgid "Center" +msgstr "Midten" + +#: lutris/runners/scummvm.py:227 +msgid "Pixel Perfect" +msgstr "Perfekte piksler" + +#: lutris/runners/scummvm.py:228 +msgid "Even Pixels" +msgstr "Gjevne piksler" + +#: lutris/runners/scummvm.py:230 +msgid "Fit" +msgstr "Fasong" + +#: lutris/runners/scummvm.py:231 +msgid "Fit (force aspect ratio)" +msgstr "Fasong (tving størrelsesforhold)" + +#: lutris/runners/scummvm.py:235 +msgid "Changes how the game is placed when the window is resized." +msgstr "Endrer hvordan spillet plasseres og vindustørrelsen." + +#: lutris/runners/scummvm.py:240 +msgid "Filtering" +msgstr "Filtrering" + +#: lutris/runners/scummvm.py:243 +msgid "" +"Uses bilinear interpolation instead of nearest neighbor resampling for the " +"aspect ratio correction and stretch mode." +msgstr "" +"Tar i bruk bilineær interpolasjon i stedet for nærmeste nabo-datapunkter for " +"korrigering av størrelsesforhold og strekkmodus." + +#: lutris/runners/scummvm.py:251 +msgid "Data directory" +msgstr "Datamappe" + +#: lutris/runners/scummvm.py:253 +msgid "Defaults to share/scummvm if unspecified." +msgstr "Bruker standarden «share/scummvm» hvis ikke valgt." + +#: lutris/runners/scummvm.py:261 +msgid "" +"Specifes platform of game. Allowed values: 2gs, 3do, acorn, amiga, atari, " +"c64, fmtowns, nes, mac, pc pc98, pce, segacd, wii, windows" +msgstr "" +"Velger plattform for spillene. Tillatte verdier: 2gs, 3do, acorn, amiga, " +"atari, c64, fmtowns, nes, mac, pc pc98, pce, segacd, wii og windows" + +#: lutris/runners/scummvm.py:270 +msgid "Enables joystick input (default: 0 = first joystick)" +msgstr "Tar i bruk styrespakinndata (standard: 0 = første styrespak)" + +#: lutris/runners/scummvm.py:277 +msgid "" +"Selects language (en, de, fr, it, pt, es, jp, zh, kr, se, gb, hb, ru, cz)" +msgstr "" +"Velger språk (en, de, fr, it, pt, es, jp, zh, kr, se, gb, hb, ru og cz)" + +#: lutris/runners/scummvm.py:283 +msgid "Engine speed" +msgstr "Spillmotor hastighet" + +#: lutris/runners/scummvm.py:285 +msgid "" +"Sets frames per second limit (0 - 100) for Grim Fandango or Escape from " +"Monkey Island (default: 60)." +msgstr "" +"Velger grense på bilde per sekund (0 – 100) for Grim Fandango eller Escape " +"from Money Island (standard: 60)." + +#: lutris/runners/scummvm.py:293 +msgid "Talk speed" +msgstr "Dialog hastighet" + +#: lutris/runners/scummvm.py:294 +msgid "Sets talk speed for games (default: 60)" +msgstr "Velger hastighet for dialogene i spill (standard: 60)s" + +#: lutris/runners/scummvm.py:301 +msgid "Music tempo" +msgstr "Musikk tempo" + +#: lutris/runners/scummvm.py:302 +msgid "Sets music tempo (in percent, 50-200) for SCUMM games (default: 100)" +msgstr "" +"Velger musikk tempoen (i prosent, 50 – 200) for SCUMM-spill (standard: 100)" + +#: lutris/runners/scummvm.py:309 +msgid "Digital iMuse tempo" +msgstr "Digital iMuse tempo" + +#: lutris/runners/scummvm.py:310 +msgid "Sets internal Digital iMuse tempo (10 - 100) per second (default: 10)" +msgstr "Velger intern digital iMuse tempo (10 – 100) per sekund (standard: 10)" + +#: lutris/runners/scummvm.py:316 +msgid "Music driver" +msgstr "Musikkdriver" + +#: lutris/runners/scummvm.py:332 +msgid "Specifies the device ScummVM uses to output audio." +msgstr "Velger enheten ScummVM skal bruke for lydutdata." + +#: lutris/runners/scummvm.py:338 +msgid "Output rate" +msgstr "Utdatafrekvens" + +#: lutris/runners/scummvm.py:345 +msgid "Selects output sample rate in Hz." +msgstr "Velger utdata samplingsrate i Hz." + +#: lutris/runners/scummvm.py:351 +msgid "OPL driver" +msgstr "OPL-driver" + +#: lutris/runners/scummvm.py:364 +msgid "" +"Chooses which emulator is used by ScummVM when the AdLib emulator is chosen " +"as the Preferred device." +msgstr "" +"Velger hvilken emulator som brukes av ScummVM når AdLib-emulator velges som " +"foretrukkne enhet." + +#: lutris/runners/scummvm.py:373 +msgid "Music volume" +msgstr "Lydstyrke for musikk" + +#: lutris/runners/scummvm.py:374 +msgid "Sets the music volume, 0-255 (default: 192)" +msgstr "Velger lydstyrke for musikk, 0 – 225 (standard: 192)" + +#: lutris/runners/scummvm.py:382 +msgid "Sets the sfx volume, 0-255 (default: 192)" +msgstr "Velger lydstyrke for filmtriks, 0 – 255 (standard: 192)" + +#: lutris/runners/scummvm.py:389 +msgid "Speech volume" +msgstr "Lydstyrke for tale" + +#: lutris/runners/scummvm.py:390 +msgid "Sets the speech volume, 0-255 (default: 192)" +msgstr "Velger lydstyrke for tale, 0 – 255 (standard: 192)" + +#: lutris/runners/scummvm.py:397 +msgid "MIDI gain" +msgstr "MIDI-sporvolum" + +#: lutris/runners/scummvm.py:398 +msgid "Sets the gain for MIDI playback. 0-1000 (default: 100)" +msgstr "Velger sporvolum for MIDI-avspilling. 0 – 1000 (standard: 100)" + +#: lutris/runners/scummvm.py:406 +msgid "Specifies the path to a soundfont file." +msgstr "Velger mappa til en «soundfont»-fil." + +#: lutris/runners/scummvm.py:412 +msgid "Mixed AdLib/MIDI mode" +msgstr "Blandet AdLib/MIDI-modus" + +#: lutris/runners/scummvm.py:415 +msgid "Combines MIDI music with AdLib sound effects." +msgstr "Kombinerer Midi-musikk med AdLib-lydeffekter." + +#: lutris/runners/scummvm.py:421 +msgid "True Roland MT-32" +msgstr "Ekte Roland MT-32" + +#: lutris/runners/scummvm.py:425 +msgid "" +"Tells ScummVM that the MIDI device is an actual Roland MT-32, LAPC-I, CM-64, " +"CM-32L, CM-500 or other MT-32 device." +msgstr "" +"Forteller ScummVM at MIDI-enheten er en ekte Roland MT-32-, LAPC-I-, CM-64-, " +"CM-32L-, CM-500- eller andre MT-32-enheter." + +#: lutris/runners/scummvm.py:433 +msgid "Enable Roland GS" +msgstr "Bruk Roland GS" + +#: lutris/runners/scummvm.py:437 +msgid "" +"Tells ScummVM that the MIDI device is a GS device that has an MT-32 map, " +"such as an SC-55, SC-88 or SC-8820." +msgstr "" +"Forteller ScummVM at MIDI-enheten er en GS-enhet som har en MT-32-utforming, " +"for eksempel en SC-55, SC-88 eller SC-8820." + +#: lutris/runners/scummvm.py:445 +msgid "Use alternate intro" +msgstr "Bruk alternativ intro" + +#: lutris/runners/scummvm.py:446 +msgid "Uses alternative intro for CD versions" +msgstr "Tar i bruk alternative introer for CD-versjoner" + +#: lutris/runners/scummvm.py:452 +msgid "Copy protection" +msgstr "Kopibeskyttelse" + +#: lutris/runners/scummvm.py:453 +msgid "Enables copy protection" +msgstr "Tar i bruk Kopibeskyttelse" + +#: lutris/runners/scummvm.py:459 +msgid "Demo mode" +msgstr "Demomodus" + +#: lutris/runners/scummvm.py:460 +msgid "Starts demo mode of Maniac Mansion or The 7th Guest" +msgstr "Starter en demomodus av Maniac Mansion eller The 7th Guest" + +#: lutris/runners/scummvm.py:467 +msgid "Debug level" +msgstr "Feilsøkingsnivå" + +#: lutris/runners/scummvm.py:468 +msgid "Sets debug verbosity level" +msgstr "Velger nivået på detaljert feilsøking" + +#: lutris/runners/scummvm.py:475 +msgid "Debug flags" +msgstr "Feilsøkingsflagg" + +#: lutris/runners/scummvm.py:476 +msgid "Enables engine specific debug flags" +msgstr "Tar i bruk spillmotor spesifikke feilsøkingsflagg" + +#: lutris/runners/snes9x.py:18 +msgid "Super Nintendo emulator" +msgstr "Super Nintendo emulator" + +#: lutris/runners/snes9x.py:19 +msgid "Snes9x" +msgstr "Snes9x" + +#: lutris/runners/snes9x.py:40 +msgid "Maintain aspect ratio (4:3)" +msgstr "Behold størrelsesforholdet (4:3)" + +#: lutris/runners/snes9x.py:43 +msgid "" +"Super Nintendo games were made for 4:3 screens with rectangular pixels, but " +"modern screens have square pixels, which results in a vertically squeezed " +"image. This option corrects this by displaying rectangular pixels." +msgstr "" +"Super Nintendo-spill ble laget for 4:3-skjermer med rektangulære piksler, " +"men moderne skjermer har firkantede piksler, som resulterer i vertikalt " +"skviste bilder. Denne innstillingen korrigerer dette ved visning av " +"firkantede piksler." + +#: lutris/runners/snes9x.py:53 +msgid "Sound driver" +msgstr "Lyddriver" + +#: lutris/runners/steam.py:29 +msgid "Runs Steam for Linux games" +msgstr "Starter Steam for Linux-spill" + +#: lutris/runners/steam.py:40 +msgid "" +"The application ID can be retrieved from the game's page at " +"steampowered.com. Example: 235320 is the app ID for Original War " +"in: \n" +"http://store.steampowered.com/app/235320/" +msgstr "" +"Program-ID-en kan hentes fra spillsiden på steampowered.com. Eks. 235320 er " +"ID-en for Original War på: \n" +"http://store.steampowered.com/app/235320/" + +#: lutris/runners/steam.py:51 +msgid "" +"Command line arguments used when launching the game.\n" +"Ignored when Steam Big Picture mode is enabled." +msgstr "" +"Kommandolinje argumenter som brukes ved oppstart av spillet.\n" +"Ignoreres når Steam Big Picture-modus er i bruk." + +#: lutris/runners/steam.py:57 +msgid "DRM free mode (Do not launch Steam)" +msgstr "DRA-frimodus (Ikke start Steam)" + +#: lutris/runners/steam.py:61 +msgid "" +"Run the game directly without Steam, requires the game binary path to be set" +msgstr "" +"Start spillene direkte uten Steam, krever at mappa til spillets programfil " +"er satt" + +#: lutris/runners/steam.py:66 +msgid "Game binary path" +msgstr "Mappe for spill-programfil" + +#: lutris/runners/steam.py:68 +msgid "Path to the game executable (Required by DRM free mode)" +msgstr "Mappa til spillets programfil (Kreves av DRA-frimodus)" + +#: lutris/runners/steam.py:74 +msgid "Start Steam in Big Picture mode" +msgstr "Start Steam i Big Picture-modus" + +#: lutris/runners/steam.py:78 +msgid "" +"Launches Steam in Big Picture mode.\n" +"Only works if Steam is not running or already running in Big Picture mode.\n" +"Useful when playing with a Steam Controller." +msgstr "" +"Starter Steam i Big Picture-modus.\n" +"Fungerer bare hvis Steam ikke kjører eller er allerede i Big Picture-modus.\n" +"Hjelpsom når du spiller med en Steam-kontroller." + +#: lutris/runners/steam.py:86 +msgid "Start Steam with LSI" +msgstr "Start Steam med en LSI" + +#: lutris/runners/steam.py:90 +msgid "" +"Launches steam with LSI patches enabled. Make sure Lutris Runtime is " +"disabled and you have LSI installed. https://github.com/solus-project/linux-" +"steam-integration" +msgstr "" +"Starter Steam med LSI-programrettelser. Kontroller at Lutris-kjøretid er " +"slått av og du har LSI installert. https://github.com/solus-project/linux-" +"steam-integration" + +#: lutris/runners/steam.py:101 +msgid "Extra command line arguments used when launching Steam" +msgstr "Ekstra kommandolinje argumenter som brukes ved oppstart av Steam" + +#: lutris/runners/steam.py:188 +msgid "" +"Steam for Linux installation is not handled by Lutris.\n" +"Please go to http://steampowered.com " +"or install Steam with the package provided by your distribution." +msgstr "" +"Steam-installasjon for Linux håndteres ikke av Lutris.\n" +"Besøk http://steampowered.com eller " +"installer Steam med pakken levert av distribusjonen din." + +#: lutris/runners/steam.py:202 +msgid "Could not find Steam path, is Steam installed?" +msgstr "Klarte ikke finne Steam-mappa, er Steam installert?" + +#: lutris/runners/vice.py:14 +msgid "Commodore Emulator" +msgstr "Commodore emulator" + +#: lutris/runners/vice.py:15 +msgid "Vice" +msgstr "Vice" + +#: lutris/runners/vice.py:18 +msgid "Commodore 64" +msgstr "Commodore 64" + +#: lutris/runners/vice.py:19 +msgid "Commodore 128" +msgstr "Commodore 128" + +#: lutris/runners/vice.py:20 +msgid "Commodore VIC20" +msgstr "Commodore VIC20" + +#: lutris/runners/vice.py:21 +msgid "Commodore PET" +msgstr "Commodore PET" + +#: lutris/runners/vice.py:22 +msgid "Commodore Plus/4" +msgstr "Commodore Plus/4" + +#: lutris/runners/vice.py:23 +msgid "Commodore CBM II" +msgstr "Commodore CBM II" + +#: lutris/runners/vice.py:39 +msgid "" +"The game data, commonly called a ROM image.\n" +"Supported formats: X64, D64, G64, P64, D67, D71, D81, D80, D82, D1M, D2M, " +"D4M, T46, P00 and CRT." +msgstr "" +"Spilldataen, kjent som ROM bilde.\n" +"Støtter formatene: X64, D64, G64, P64, D67, D71, D81, D80, D82, D1M, D2M, " +"D4M, T46, P00 og CRT." + +#: lutris/runners/vice.py:47 +msgid "Use joysticks" +msgstr "Bruk styrespaker" + +#: lutris/runners/vice.py:59 +msgid "Scale up display by 2" +msgstr "Skaler skjermen opp med 2" + +#: lutris/runners/vice.py:73 +msgid "Graphics renderer" +msgstr "Bildeopptegner" + +#: lutris/runners/vice.py:80 +msgid "Enable sound emulation of disk drives" +msgstr "Bruk lydemulering av lagringsenheter" + +#: lutris/runners/vice.py:190 +msgid "No rom provided" +msgstr "Ingen ROM oppgitt" + +#: lutris/runners/vita3k.py:12 lutris/runners/vita3k.py:98 +msgid "The Vita App has no Title ID set" +msgstr "Vita-programmet har ingen tittel-ID satt opp" + +#: lutris/runners/vita3k.py:18 +msgid "Vita3K" +msgstr "Vita3K" + +#: lutris/runners/vita3k.py:19 +msgid "Sony PlayStation Vita" +msgstr "Sony PlayStation Vita" + +#: lutris/runners/vita3k.py:20 +msgid "Sony PlayStation Vita emulator" +msgstr "Sony PlayStation Vita emulator" + +#: lutris/runners/vita3k.py:29 +msgid "Title ID of Installed Application" +msgstr "Tittel-ID for installerte program" + +#: lutris/runners/vita3k.py:32 +msgid "" +"Title ID of installed application. Eg.\"PCSG00042\". User installed apps are " +"located in ux0:/app/<title-id>." +msgstr "" +"Tittel-ID for installerte program. Eks. «PCSG00042». Bruker installerte " +"programmer er plassert i ux0:/app/<title-id>." + +#: lutris/runners/vita3k.py:44 +msgid "Start the emulator in fullscreen mode." +msgstr "Start emulatoren i fullskjermmodus." + +#: lutris/runners/vita3k.py:49 +msgid "Config location" +msgstr "Plassering for oppsett" + +#: lutris/runners/vita3k.py:52 +msgid "" +"Get a configuration file from a given location. If a filename is given, it " +"must end with \".yml\", otherwise it will be assumed to be a directory." +msgstr "" +"Hent en oppsettsfil fra en gitt plassering. Hvis en filnavn er oppgitt, må " +"den ende med «.yml», ellers vil det antas å være en mappe." + +#: lutris/runners/vita3k.py:58 +msgid "Load configuration file" +msgstr "Last oppsettsfil" + +#: lutris/runners/vita3k.py:61 +msgid "" +"If trues, informs the emualtor to load the config file from the \"Config " +"location\" option." +msgstr "" +"Hvis sant, ber emulatoren om å laste inn oppsettsfila fra «Plassering for " +"oppsett»-innstillingen." + +#: lutris/runners/web.py:19 lutris/runners/web.py:21 +msgid "Web" +msgstr "Nett" + +#: lutris/runners/web.py:20 +msgid "Runs web based games" +msgstr "Kjører nettbaserte spill" + +#: lutris/runners/web.py:26 +msgid "Full URL or HTML file path" +msgstr "Full URL eller HTML filplassering" + +#: lutris/runners/web.py:27 +msgid "The full address of the game's web page or path to a HTML file." +msgstr "Den fulle adressen til spillets nettside eller mappa til en HTML-fil." + +#: lutris/runners/web.py:33 +msgid "Open in fullscreen" +msgstr "Åpne i fullskjerm" + +#: lutris/runners/web.py:36 +msgid "Launch the game in fullscreen." +msgstr "Starter spillet i fullskjerm." + +#: lutris/runners/web.py:40 +msgid "Open window maximized" +msgstr "Åpne vinduet maksimert" + +#: lutris/runners/web.py:43 +msgid "Maximizes the window when game starts." +msgstr "Maksimerer vinduet ved oppstart av spillet." + +#: lutris/runners/web.py:58 +msgid "The initial size of the game window when not opened." +msgstr "Den opprinnelige størrelsen på spillvinduet når det ikke er åpent." + +#: lutris/runners/web.py:62 +msgid "Disable window resizing (disables fullscreen and maximize)" +msgstr "Slå endring av vindusstørrelse (slår av fullskjerm og maksimere)" + +#: lutris/runners/web.py:65 +msgid "You can't resize this window." +msgstr "Du kan ikke endre størrelsen på dette vinduet." + +#: lutris/runners/web.py:69 +msgid "Borderless window" +msgstr "Fullskjerm i vindu" + +#: lutris/runners/web.py:72 +msgid "The window has no borders/frame." +msgstr "Dette vinduet har ingen kant/ramme." + +#: lutris/runners/web.py:76 +msgid "Disable menu bar and default shortcuts" +msgstr "Slå av menylinje og standard hurtigtaster" + +#: lutris/runners/web.py:79 +msgid "" +"This also disables default keyboard shortcuts, like copy/paste and " +"fullscreen toggling." +msgstr "" +"Dette slår også av standard hurtigtaster, for eksempel kopier, lim inn og " +"endring av fullskjerm." + +#: lutris/runners/web.py:83 +msgid "Disable page scrolling and hide scrollbars" +msgstr "Slå av siderulling og skjul rullelinja" + +#: lutris/runners/web.py:86 +msgid "Disables scrolling on the page." +msgstr "Slår av rulling på denne siden." + +#: lutris/runners/web.py:90 +msgid "Hide mouse cursor" +msgstr "Skjul musepekeren" + +#: lutris/runners/web.py:93 +msgid "Prevents the mouse cursor from showing when hovering above the window." +msgstr "Hindrer musepekeren fra å vise ved sveving over vinduet." + +#: lutris/runners/web.py:97 +msgid "Open links in game window" +msgstr "Åpne lenker i spillvindu" + +#: lutris/runners/web.py:101 +msgid "" +"Enable this option if you want clicked links to open inside the game window. " +"By default all links open in your default web browser." +msgstr "" +"Bruk denne innstillingen hvid du vil at trykkte lenker skal åpne inne i " +"spillvinduet. Som standard vil alle lenker åpne i nettleseren." + +#: lutris/runners/web.py:107 +msgid "Remove default margin & padding" +msgstr "Fjern standard marg og luft" + +#: lutris/runners/web.py:110 +msgid "" +"Sets margin and padding to zero on <html> and <body> elements." +msgstr "" +"Velger marg og luft til null på <html> and <body>-elementer." + +#: lutris/runners/web.py:114 +msgid "Enable Adobe Flash Player" +msgstr "Bruk Adobe Flash Player" + +#: lutris/runners/web.py:117 +msgid "Enable Adobe Flash Player." +msgstr "Bruk Adobe Flash Player." + +#: lutris/runners/web.py:121 +msgid "Custom User-Agent" +msgstr "Tilpasset brukeragent." + +#: lutris/runners/web.py:124 +msgid "Overrides the default User-Agent header used by the runner." +msgstr "Overstyrer standard brukeragent-topptekst, brukt av løperen." + +#: lutris/runners/web.py:129 +msgid "Debug with Developer Tools" +msgstr "Feilsøk med utviklingsverktøy" + +#: lutris/runners/web.py:132 +msgid "Let's you debug the page." +msgstr "Lar deg feilsøke siden." + +#: lutris/runners/web.py:137 +msgid "Open in web browser (old behavior)" +msgstr "Åpne i nettleser (gammel atferd)" + +#: lutris/runners/web.py:140 +msgid "Launch the game in a web browser." +msgstr "Start spillet i en nettleser." + +#: lutris/runners/web.py:144 +msgid "Custom web browser executable" +msgstr "Tilpasset nettleser-programfil" + +#: lutris/runners/web.py:147 +msgid "" +"Select the executable of a browser on your system.\n" +"If left blank, Lutris will launch your default browser (xdg-open)." +msgstr "" +"Velg programfila til en nettleser på ditt system.\n" +"Hvis tom, Lutris vil bruke din standard nettleser (xdg-open).v" + +#: lutris/runners/web.py:153 +msgid "Web browser arguments" +msgstr "Nettleser argumenter" + +#: lutris/runners/web.py:157 +msgid "" +"Command line arguments to pass to the executable.\n" +"$GAME or $URL inserts the game url.\n" +"\n" +"For Chrome/Chromium app mode use: --app=\"$GAME\"" +msgstr "" +"Kommandolinje argumenter som skal sendes til programfila.\n" +"$GAME eller $URL skriver inn spill-URL.\n" +"For Chrome/Chromium app-modus bruk: --app=\"$GAME\"" + +#: lutris/runners/web.py:176 +msgid "" +"The web address is empty, \n" +"verify the game's configuration." +msgstr "" +"Nettadressen er tom, \n" +"kontroller spilletsoppsett." + +#: lutris/runners/web.py:183 +#, python-format +msgid "" +"The file %s does not exist, \n" +"verify the game's configuration." +msgstr "" +"Fila %s finnes ikke, \n" +"Kontroller spilletsoppsett." + +#: lutris/runners/wine.py:79 lutris/runners/wine.py:800 +msgid "Proton is not compatible with 32-bit prefixes." +msgstr "Proton støtter ikke 32-bit-prefikser." + +#: lutris/runners/wine.py:93 +msgid "" +"Warning Some Wine configuration options cannot be applied, if no " +"prefix can be found." +msgstr "" +"Advarsel Noen Wine-oppsett innstillinger kan ikke tas i bruk, hvis " +"man ikke finner noen prefiks." + +#: lutris/runners/wine.py:100 +#, python-format +msgid "" +"Warning Your NVIDIA driver is outdated.\n" +"You are currently running driver %s which does not fully support all " +"features for Vulkan and DXVK games." +msgstr "" +"Advarsel Din NVIDIA-driver er foreldet.\n" +"Din nåværende driver er %s som ikke helt støtter alle funksjonene for " +"Vulkan- og DXVK-spill." + +#: lutris/runners/wine.py:113 +#, python-format +msgid "" +"Error Vulkan is not installed or is not supported by your system, %s " +"is not available." +msgstr "" +"Feil Vulkan er ikke installert eller støttes ikke av ditt system, %s " +"er ikke tilgjengelig." + +#: lutris/runners/wine.py:129 +#, python-format +msgid "" +"Warning Lutris has detected that Vulkan API version %s is installed, " +"but to use the latest DXVK version, %s is required." +msgstr "" +"Advarsel Lutris har oppdaget at Vulkan-API-versjon %s er installert, " +"men for å bruke den nyeste DXVK-versjonen, trengs %s." + +#: lutris/runners/wine.py:137 +#, python-format +msgid "" +"Warning Lutris has detected that the best device available ('%s') " +"supports Vulkan API %s, but to use the latest DXVK version, %s is required." +msgstr "" +"Advarsel Lutris har oppdaget at den beste enheten tilgjengelig («%s») " +"støtter Vulkan-API %s, men for å bruke den nyeste DXVK-versjonen, trengs %s." + +#: lutris/runners/wine.py:153 +msgid "" +"Warning Your limits are not set correctly. Please increase them as " +"described here:\n" +"How-to-" +"Esync (https://github.com/lutris/docs/blob/master/HowToEsync.md)" +msgstr "" +"Advarsel Grensene dine er ikke satt opp riktig. Øk dem som beskrevet " +"her:\n" +"How-to-" +"Esync (https://github.com/lutris/docs/blob/master/HowToEsync.md)" + +#: lutris/runners/wine.py:164 +msgid "Warning Your kernel is not patched for fsync." +msgstr "Advarsel Kjernen din støtter ikke Fsync." + +#: lutris/runners/wine.py:169 +msgid "Wine virtual desktop is no longer supported" +msgstr "Wine-virtuelt skrivebord støttes ikke lenger" + +#: lutris/runners/wine.py:175 +msgid "Virtual desktops cannot be enabled in Proton or GE Wine versions." +msgstr "" +"Virtuelle skrivebord kan ikke tas i bruk med Proton- eller GE Wine-versjoner." + +#: lutris/runners/wine.py:180 +msgid "Custom (select executable below)" +msgstr "Tilpasset (velg programfil nedenfor)" + +#: lutris/runners/wine.py:182 +msgid "WineHQ Devel ({})" +msgstr "WineHQ-utvikling ({})" + +#: lutris/runners/wine.py:183 +msgid "WineHQ Staging ({})" +msgstr "WineHQ-eksperimentell ({})" + +#: lutris/runners/wine.py:184 +msgid "Wine Development ({})" +msgstr "Wine-utvikling ({})" + +#: lutris/runners/wine.py:185 +msgid "System ({})" +msgstr "System ({})" + +#: lutris/runners/wine.py:193 +msgid "GE-Proton (Latest)" +msgstr "GE-Proton (Nyeste)" + +#: lutris/runners/wine.py:201 +msgid "Runs Windows games" +msgstr "Kjører Windows-spill" + +#: lutris/runners/wine.py:202 +msgid "Wine" +msgstr "Wine" + +#: lutris/runners/wine.py:203 +msgid "Windows" +msgstr "Windows" + +#: lutris/runners/wine.py:212 +msgid "The game's main EXE file" +msgstr "Spillets hoved EXE-fil" + +#: lutris/runners/wine.py:218 +msgid "Windows command line arguments used when launching the game" +msgstr "Windows-kommandolinje argumenter som brukes ved oppstart av spillet" + +#: lutris/runners/wine.py:234 +msgid "Wine prefix" +msgstr "Wine-prefiks" + +#: lutris/runners/wine.py:237 +msgid "" +"The prefix used by Wine.\n" +"It's a directory containing a set of files and folders making up a confined " +"Windows environment." +msgstr "" +"Prefiksen brukt av Wine.\n" +"Er en mappe som inneholder filer og mapper som bygger opp under Windows-" +"miljøet." + +#: lutris/runners/wine.py:245 +msgid "Prefix architecture" +msgstr "Prefiks-arkitektur" + +#: lutris/runners/wine.py:246 +msgid "32-bit" +msgstr "32-bit" + +#: lutris/runners/wine.py:246 +msgid "64-bit" +msgstr "64-bit" + +#: lutris/runners/wine.py:248 +msgid "The architecture of the Windows environment" +msgstr "Arkitekturen av Windows-miljøet" + +#: lutris/runners/wine.py:253 +msgid "Integrate system files in the prefix" +msgstr "Integer systemfiler i prefiksen" + +#: lutris/runners/wine.py:257 +msgid "" +"Place 'Documents', 'Pictures', and similar files in your home folder, " +"instead of keeping them in the game's prefix. This includes some saved games." +msgstr "" +"Plasser «Dokumenter», «Bilder» og lignenede filer i hjemmemappa, i stedet " +"for å holde dem i spillets-prefiks. Dette inkluderer noen brukerfiler." + +#: lutris/runners/wine.py:266 +msgid "Wine version" +msgstr "Wine-versjon" + +#: lutris/runners/wine.py:272 +msgid "" +"The version of Wine used to launch the game.\n" +"Using the last version is generally recommended, but some games work better " +"on older versions." +msgstr "" +"Wine-versjonen som brukes for oppstart av spillet.\n" +"Bruk av den nyeste versjonen støttes generelt, men noen spill fungerer bedre " +"med eldre versjoner." + +#: lutris/runners/wine.py:279 +msgid "Custom Wine executable" +msgstr "Tilpasset Wine-programfil" + +#: lutris/runners/wine.py:282 +msgid "" +"The Wine executable to be used if you have selected \"Custom\" as the Wine " +"version." +msgstr "" +"Wine-programfila som skal brukes hvis du valgte «Tilpasset» som Wine-" +"versjonen." + +#: lutris/runners/wine.py:286 +msgid "Use system winetricks" +msgstr "Bruk systemets-Winetricks" + +#: lutris/runners/wine.py:290 +msgid "Switch on to use /usr/bin/winetricks for winetricks." +msgstr "Endre til /usr/bin/winetricks for Winetricks." + +#: lutris/runners/wine.py:295 +msgid "Enable DXVK" +msgstr "Bruk DXVK" + +#: lutris/runners/wine.py:299 +msgid "DXVK" +msgstr "DXVK" + +#: lutris/runners/wine.py:302 +msgid "" +"Use DXVK to increase compatibility and performance in Direct3D 11, 10 and 9 " +"applications by translating their calls to Vulkan." +msgstr "" +"Bruk DXVK for å øke kompaibilitet og ytelse i Direct3D 11-, 10- og 9-" +"programmer ved å oversette kallene til Vulkan." + +#: lutris/runners/wine.py:310 +msgid "DXVK version" +msgstr "DXVK-versjon" + +#: lutris/runners/wine.py:323 +msgid "Enable VKD3D" +msgstr "Bruk VKD3D" + +#: lutris/runners/wine.py:326 +msgid "VKD3D" +msgstr "VKD3D" + +#: lutris/runners/wine.py:330 +msgid "" +"Use VKD3D to enable support for Direct3D 12 applications by translating " +"their calls to Vulkan." +msgstr "" +"Bruk VKD3D for å ta i bruk støtte for Direct3D 12-programmer ved å oversette " +"kallene til Vulkan." + +#: lutris/runners/wine.py:336 +msgid "VKD3D version" +msgstr "VKD3D-versjon" + +#: lutris/runners/wine.py:348 +msgid "Enable D3D Extras" +msgstr "Bruk D3D-Extras" + +#: lutris/runners/wine.py:354 +msgid "" +"Replace Wine's D3DX and D3DCOMPILER libraries with alternative ones. Needed " +"for proper functionality of DXVK with some games." +msgstr "" +"Erstatt Wine-D3DX- og D3DCOMPILER-biblioteker med alternativer. Trengs for " +"ordentlig funksjonalitet av DXVK i noen spill." + +#: lutris/runners/wine.py:361 +msgid "D3D Extras version" +msgstr "D3D-Extras-versjon" + +#: lutris/runners/wine.py:372 +msgid "Enable DXVK-NVAPI / DLSS" +msgstr "Bruk DXVK-NVAPI / DLSS" + +#: lutris/runners/wine.py:374 +msgid "DXVK-NVAPI / DLSS" +msgstr "DXVK-NVAPI / DLSS" + +#: lutris/runners/wine.py:378 +msgid "Enable emulation of Nvidia's NVAPI and add DLSS support, if available." +msgstr "" +"Bruk emulering av Nvidia's NVAPI og legg til DLSS-støtte, hvis tilgjengelig." + +#: lutris/runners/wine.py:383 +msgid "DXVK NVAPI version" +msgstr "DXVK-NVAPI-versjon" + +#: lutris/runners/wine.py:394 +msgid "Enable dgvoodoo2" +msgstr "Bruk dgvoodoo2" + +#: lutris/runners/wine.py:399 +msgid "" +"dgvoodoo2 is an alternative translation layer for rendering old games that " +"utilize D3D1-7 and Glide APIs. As it translates to D3D11, it's recommended " +"to use it in combination with DXVK. Only 32-bit apps are supported." +msgstr "" +"dgvoodoo2 er et alternativt oversettelseslag for opptegning av eldre spill " +"som bruker D3D1-7 og Glide-API-er. Ettersom den oversetter til D3D11, " +"anbefales det å bruke den i kombinasjon med DXVK. Bare 32-bit programmer " +"støttes." + +#: lutris/runners/wine.py:407 +msgid "dgvoodoo2 version" +msgstr "dgvoodoo2-versjon" + +#: lutris/runners/wine.py:416 +msgid "Enable Esync" +msgstr "Bruk Esync" + +#: lutris/runners/wine.py:422 +msgid "" +"Enable eventfd-based synchronization (esync). This will increase performance " +"in applications that take advantage of multi-core processors." +msgstr "" +"Bruk eventfs-basert synkronisering (Esync). Dette vil øke ytelse i " +"programmer som tar nytte av flerkjerne prosesser." + +#: lutris/runners/wine.py:429 +msgid "Enable Fsync" +msgstr "Bruk Fsync" + +#: lutris/runners/wine.py:435 +msgid "" +"Enable futex-based synchronization (fsync). This will increase performance " +"in applications that take advantage of multi-core processors. Requires " +"kernel 5.16 or above." +msgstr "" +"Bruk futex-basert synkronisering (Fsync). Dette vil øke ytelse i programmer " +"som tar nytte av flerkjerne prosesser. Krever kjernen 5.16 eller høyere." + +#: lutris/runners/wine.py:443 +msgid "Enable AMD FidelityFX Super Resolution (FSR)" +msgstr "Bruk AMD FidelityFX Super Resolution (FSR)" + +#: lutris/runners/wine.py:447 +msgid "" +"Use FSR to upscale the game window to native resolution.\n" +"Requires Lutris Wine FShack >= 6.13 and setting the game to a lower " +"resolution.\n" +"Does not work with games running in borderless window mode or that perform " +"their own upscaling." +msgstr "" +"Bruk FSR for å oppskalere et spillvindu til den opprinnelige oppløsningen.\n" +"Krever «Lutris Wine FShack» >= 6.13 og sette spilloppløsningen til en lavere " +"oppløsning.\n" +"Fungerer ikke med spill som kjører med fullskjerm i vindumodus eller som " +"styrer sin egen oppskalering." + +#: lutris/runners/wine.py:454 +msgid "Enable BattlEye Anti-Cheat" +msgstr "Bruk BattlEye Anti-Cheat" + +#: lutris/runners/wine.py:458 +msgid "" +"Enable support for BattlEye Anti-Cheat in supported games\n" +"Requires Lutris Wine 6.21-2 and newer or any other compatible Wine build.\n" +msgstr "" +"Bruk BattlEye Anti-Cheat i spill som støtter dette.\n" +"Krever «Lutris Wine» 6.21-2 og nyere eller andre kompatible Wine-versjoner.\n" + +#: lutris/runners/wine.py:464 +msgid "Enable Easy Anti-Cheat" +msgstr "Bruk Easy Anti-Cheat" + +#: lutris/runners/wine.py:468 +msgid "" +"Enable support for Easy Anti-Cheat in supported games\n" +"Requires Lutris Wine 7.2 and newer or any other compatible Wine build.\n" +msgstr "" +"Bruk Easy Anti-Cheat i spill som støtter dette.\n" +"Krever «Lutris Wine» 7.2 og nyere eller andre kompatible Wine-versjoner.\n" + +#: lutris/runners/wine.py:474 lutris/runners/wine.py:489 +msgid "Virtual Desktop" +msgstr "Virtuelle skrivebord" + +#: lutris/runners/wine.py:475 +msgid "Windowed (virtual desktop)" +msgstr "I vindu (virtuelt skrivebord)" + +#: lutris/runners/wine.py:482 +msgid "" +"Run the whole Windows desktop in a window.\n" +"Otherwise, run it fullscreen.\n" +"This corresponds to Wine's Virtual Desktop option." +msgstr "" +"Start hele Windows-skrivebordet i et vindu.\n" +"Ellers, kjør i fullskjerm.\n" +"Dette samsvarer med virtuelt skrivebord-innstillingen i Wine." + +#: lutris/runners/wine.py:490 +msgid "Virtual desktop resolution" +msgstr "Oppløsning for virtuelt skrivebord" + +#: lutris/runners/wine.py:496 +msgid "The size of the virtual desktop in pixels." +msgstr "Størrelsen på virtuelt skrivebord i piksler." + +#: lutris/runners/wine.py:500 lutris/runners/wine.py:512 +#: lutris/runners/wine.py:513 +msgid "DPI" +msgstr "DPI" + +#: lutris/runners/wine.py:501 +msgid "Enable DPI Scaling" +msgstr "Bruk DPI-skalering" + +#: lutris/runners/wine.py:506 +msgid "" +"Enables the Windows application's DPI scaling.\n" +"Otherwise, the Screen Resolution option in 'Wine configuration' controls " +"this." +msgstr "" +"Bruker DPI-skalering i Windows program.\n" +"Ellers, styres dette av skjermoppløsning-innstillingen i «Wine-oppsett»." + +#: lutris/runners/wine.py:518 +msgid "" +"The DPI to be used if 'Enable DPI Scaling' is turned on.\n" +"If blank or 'auto', Lutris will auto-detect this." +msgstr "" +"DPI-en som skal brukes hvis «Bruk DPI-skalering» er slått på.\n" +"Hvis tom eller «auto», oppdager Lutris dette automatisk." + +#: lutris/runners/wine.py:524 +msgid "Mouse Warp Override" +msgstr "Overstyr museforvrengning" + +#: lutris/runners/wine.py:527 +msgid "Enable" +msgstr "Bruk" + +#: lutris/runners/wine.py:529 +msgid "Force" +msgstr "Tving" + +#: lutris/runners/wine.py:534 +msgid "" +"Override the default mouse pointer warping behavior\n" +"Enable: (Wine default) warp the pointer when the mouse is exclusively " +"acquired \n" +"Disable: never warp the mouse pointer \n" +"Force: always warp the pointer" +msgstr "" +"Overstyrer den standardiserte museforvrengning atferden\n" +"Bruk: (Wine standard) forvreng pekeren når musa utelukkende " +"anskaffet \n" +"Slå av: aldri forvreng musepekeren \n" +"Tving: alltid forvreng pekeren" + +#: lutris/runners/wine.py:543 +msgid "Audio driver" +msgstr "Lyddriver" + +#: lutris/runners/wine.py:554 +msgid "" +"Which audio backend to use.\n" +"By default, Wine automatically picks the right one for your system." +msgstr "" +"Hvilken lydmotor som skal brukes.\n" +"Som standard, velger Wine automatisk den riktige for ditt system." + +#: lutris/runners/wine.py:560 +msgid "DLL overrides" +msgstr "DLL-overstyring" + +#: lutris/runners/wine.py:561 +msgid "Sets WINEDLLOVERRIDES when launching the game." +msgstr "Velger WINEDLLOVERRIDES ved oppstart av spillet." + +#: lutris/runners/wine.py:565 +msgid "Output debugging info" +msgstr "Skriv ut diagnostikk" + +#: lutris/runners/wine.py:570 +msgid "Inherit from environment" +msgstr "Hent fra miljøet" + +#: lutris/runners/wine.py:572 +msgid "Full (CAUTION: Will cause MASSIVE slowdown)" +msgstr "Full (ADVARSEL: Vil føre til MASSIV nedbremsing)" + +#: lutris/runners/wine.py:575 +msgid "Output debugging information in the game log (might affect performance)" +msgstr "Skriv ut feilsøkingsinformasjon i spilletslogg (kan påvirke ytelsen)" + +#: lutris/runners/wine.py:579 +msgid "Show crash dialogs" +msgstr "Vis krasjdialoger" + +#: lutris/runners/wine.py:587 +msgid "Autoconfigure joypads" +msgstr "Sett opp kontrollere automatisk" + +#: lutris/runners/wine.py:590 +msgid "" +"Automatically disables one of Wine's detected joypad to avoid having 2 " +"controllers detected" +msgstr "" +"Slår av en Wine oppdaget kontroller automatisk for å unngå å ha 2 " +"kontrollere oppdaget" + +#: lutris/runners/wine.py:624 +msgid "" +"Warning Wine is not installed on your system\n" +"\n" +"Having Wine installed on your system guarantees that Wine builds from Lutris " +"will have all required dependencies.\n" +"Please follow the instructions given in the Lutris Wiki to install Wine." +msgstr "" +"Advarsel Wine er ikke installert på ditt system\n" +"\n" +"Det å ha Wine installert på ditt system garanterer at Wine-versjoner fra " +"Lutris vil ha alle de nødvendige avhengighetene\n" +"Følg de gitte instruksjonene på Lutris Wiki for å installere Wine." + +#: lutris/runners/wine.py:636 +msgid "Run EXE inside Wine prefix" +msgstr "Start EXE inne i en Wine-prefiks" + +#: lutris/runners/wine.py:637 +msgid "Open Bash terminal" +msgstr "Åpne Bash-konsoll" + +#: lutris/runners/wine.py:638 +msgid "Open Wine console" +msgstr "Åpne Wine-konsoll" + +#: lutris/runners/wine.py:640 +msgid "Wine configuration" +msgstr "Wine-oppsett" + +#: lutris/runners/wine.py:641 +msgid "Wine registry" +msgstr "Wine-registeret" + +#: lutris/runners/wine.py:642 +msgid "Wine Control Panel" +msgstr "Wine-kontrollpanel" + +#: lutris/runners/wine.py:643 +msgid "Wine Task Manager" +msgstr "Wine-oppgavebehandler" + +#: lutris/runners/wine.py:645 +msgid "Winetricks" +msgstr "Winetricks" + +#: lutris/runners/wine.py:775 lutris/runners/wine.py:781 +#, python-format +msgid "The Wine executable at '%s' is missing." +msgstr "Fant ikke Wine-programfila «%s»." + +#: lutris/runners/wine.py:839 +#, python-format +msgid "The required game '%s' could not be found." +msgstr "Fant ikke det nødvendige spillet «%s»" + +#: lutris/runners/wine.py:872 +msgid "The runner configuration does not specify a Wine version." +msgstr "Løperoppsettet oppgir ikke en Wine-versjon." + +#: lutris/runners/wine.py:910 +msgid "Select an EXE or MSI file" +msgstr "Velg en EXE- eller MSI-fil" + +#: lutris/runners/xemu.py:9 +msgid "xemu" +msgstr "xemu" + +#: lutris/runners/xemu.py:10 +msgid "Xbox" +msgstr "Xbox" + +#: lutris/runners/xemu.py:11 +msgid "Xbox emulator" +msgstr "Xbox emulator" + +#: lutris/runners/xemu.py:20 +msgid "DVD image in iso format" +msgstr "DVD-bilde i iso-format" + +#: lutris/runners/yuzu.py:13 +msgid "Yuzu" +msgstr "Yuzu" + +#. http://zdoom.org/wiki/Command_line_parameters +#: lutris/runners/zdoom.py:13 +msgid "GZDoom Game Engine" +msgstr "GZDoom spillmotor" + +#: lutris/runners/zdoom.py:14 +msgid "GZDoom" +msgstr "GZDoom" + +#: lutris/runners/zdoom.py:22 +msgid "WAD file" +msgstr "WAD-fil" + +#: lutris/runners/zdoom.py:23 +msgid "The game data, commonly called a WAD file." +msgstr "Spilldataen, kjent som WAD-fil." + +#: lutris/runners/zdoom.py:29 +msgid "Command line arguments used when launching the game." +msgstr "Kommandolinje argumenter som brukes ved oppstart av spillet." + +#: lutris/runners/zdoom.py:34 +msgid "PWAD files" +msgstr "PWAD-filer" + +#: lutris/runners/zdoom.py:35 +msgid "" +"Used to load one or more PWAD files which generally contain user-created " +"levels." +msgstr "" +"Brukes for å laste en eller flere PWAD-filer som generelt inneholder bruker-" +"genererte-nivåer." + +#: lutris/runners/zdoom.py:40 +msgid "Warp to map" +msgstr "Teleporter til kart" + +#: lutris/runners/zdoom.py:41 +msgid "Starts the game on the given map." +msgstr "Starter spillet på det gitte kartet.v" + +#: lutris/runners/zdoom.py:48 +msgid "User-specified path where save files should be located." +msgstr "Brukervalgt mappe der brukerfilene skal plasseres." + +#: lutris/runners/zdoom.py:52 +msgid "Pixel Doubling" +msgstr "Dobling av piksler" + +#: lutris/runners/zdoom.py:53 +msgid "Pixel Quadrupling" +msgstr "Firedobling av piksler" + +#: lutris/runners/zdoom.py:56 +msgid "Disable Startup Screens" +msgstr "Slå av oppstartsbildet" + +#: lutris/runners/zdoom.py:62 +msgid "Skill" +msgstr "Ferdighet" + +#: lutris/runners/zdoom.py:67 +msgid "I'm Too Young To Die (1)" +msgstr "Jeg er for ung til å dø (1)" + +#: lutris/runners/zdoom.py:68 +msgid "Hey, Not Too Rough (2)" +msgstr "Hei, alltid forsiktig (2)" + +#: lutris/runners/zdoom.py:69 +msgid "Hurt Me Plenty (3)" +msgstr "Gjør meg veldig vondt (3)" + +#: lutris/runners/zdoom.py:70 +msgid "Ultra-Violence (4)" +msgstr "Ultra-vold (4)" + +#: lutris/runners/zdoom.py:71 +msgid "Nightmare! (5)" +msgstr "Mareritt! (5)" + +#: lutris/runners/zdoom.py:79 +msgid "" +"Used to load a user-created configuration file. If specified, the file must " +"contain the wad directory list or launch will fail." +msgstr "" +"Brukes for å laste bruker opprettet oppsettsfil. Hvis valgt, må fila " +"inneholde wad-mappeoppføringen ellers vil oppstarten mislykkes." + +#: lutris/runtime.py:125 +#, python-format +msgid "Updating %s" +msgstr "Oppdaterer %s" + +#: lutris/runtime.py:128 +#, python-format +msgid "Updated %s" +msgstr "Oppdaterte %s" + +#: lutris/services/amazon.py:69 +msgid "Amazon" +msgstr "Amazon" + +#: lutris/services/amazon.py:179 +msgid "No Amazon user data available, please log in again" +msgstr "Ingen Amazon brukerdata tilgjengelig, logg inn på nytt" + +#: lutris/services/amazon.py:244 +msgid "Unable to register device, please log in again" +msgstr "Klarte ikke registrere enheten. logg inn på nytt" + +#: lutris/services/amazon.py:259 +msgid "Invalid token info found, please log in again" +msgstr "Ugyldig kjennemerkeinfo funnet, logg inn på nytt" + +#: lutris/services/amazon.py:293 +msgid "Unable to refresh token, please log in again" +msgstr "Klarte ikke oppdatere kjennemerke, logg inn på nyttt" + +#: lutris/services/amazon.py:465 +msgid "Unable to get game manifest info" +msgstr "Klarte ikke hente spillmanifest-informasjon" + +#: lutris/services/amazon.py:486 +msgid "Unable to get game manifest" +msgstr "Klarte ikke hente spillmanifest" + +#: lutris/services/amazon.py:501 +msgid "Unknown compression algorithm found in manifest" +msgstr "Fant ukjent komprimeringsalgoritme i manifest" + +#: lutris/services/amazon.py:526 +#, python-format +msgid "Unable to get the patches of game '%s'" +msgstr "Klarte ikke hente programrettelsene til spillet «%s»" + +#: lutris/services/amazon.py:603 +msgid "Unable to get fuel.json file." +msgstr "Klarte ikke hente fuel.json-fila." + +#: lutris/services/amazon.py:614 +msgid "Invalid response from Amazon APIs" +msgstr "Ugyldig svar fra Amazon-API-ene" + +#: lutris/services/amazon.py:648 lutris/services/gog.py:548 +msgid "Couldn't load the downloads for this game" +msgstr "Klarte ikke laste nedlastingene for dette spillet" + +#: lutris/services/amazon.py:696 +msgid "Amazon Prime Gaming" +msgstr "Amazon Prime Gaming" + +#: lutris/services/base.py:446 +#, python-format +msgid "" +"This service requires a game launcher. The following steps will install it.\n" +"Once the client is installed, you can login to %s." +msgstr "" +"Denne tjenesten krever en spillstarter. De følgende trinnene vil installere " +"den.\n" +"Når programmet er installert, kan du logge på %s." + +#: lutris/services/battlenet.py:105 +msgid "Battle.net" +msgstr "Battle.net" + +#: lutris/services/ea_app.py:148 +msgid "EA App" +msgstr "EA App" + +#: lutris/services/egs.py:142 +msgid "Epic Games Store" +msgstr "Epic Games Store" + +#: lutris/services/flathub.py:56 +msgid "Flathub" +msgstr "Flathub" + +#: lutris/services/flathub.py:84 +msgid "No flatpak or flatpak-spawn found" +msgstr "Fant ingen flatpak eller flatpak-spawn" + +#: lutris/services/flathub.py:106 +msgid "" +"Flathub is not configured on the system. Visit https://flatpak.org/setup/ " +"for instructions." +msgstr "" +"Flathub er ikke satt opp på systemet. Besøk https://flatpak.org/setup/ for " +"instruksjoner." + +#: lutris/services/gog.py:79 +msgid "GOG" +msgstr "GOG" + +#: lutris/services/gog.py:482 +msgid "Couldn't load the download links for this game" +msgstr "Klarte ikke laste nedlastingslenka til dette spillet" + +#: lutris/services/gog.py:539 +msgid "Unable to determine correct file to launch installer" +msgstr "Klarte ikke bestemme den riktige fila for å starte installatøren" + +#: lutris/services/humblebundle.py:62 +msgid "Humble Bundle" +msgstr "Humble Bundle" + +#: lutris/services/humblebundle.py:82 +msgid "Workaround for Humble Bundle authentication" +msgstr "Løsning for Humble Bundle-autentisering" + +#: lutris/services/humblebundle.py:84 +msgid "" +"Humble Bundle is restricting API calls from software like Lutris and " +"GameHub.\n" +"Authentication to the service will likely fail.\n" +"There is a workaround involving copying cookies from Firefox, do you want to " +"do this instead?" +msgstr "" +"Humble Bundle skrenker inn API-kaller fra programvare som Lutris og " +"GameHub.\n" +"Autentiseringen til tjenesten vil mest sannsynlig mislykkes.\n" +"Det finnes en løsning som innvolverer kopiering av infokapsler fra Firefox, " +"vil du gjøre dette i stedet?" + +#: lutris/services/humblebundle.py:235 +msgid "The download URL for the game could not be determined." +msgstr "Klarte ikke bestemme nedlastings-URL-en til spillet." + +#: lutris/services/humblebundle.py:237 +msgid "No game found on Humble Bundle" +msgstr "Fant ingen spill på Humble Bundle" + +#. According to their branding, "itch.io" is supposed to be all lowercase +#: lutris/services/itchio.py:95 +msgid "itch.io" +msgstr "itch.io" + +#: lutris/services/lutris.py:132 +#, python-format +msgid "Lutris has no installers for %s. Try using a different service instead." +msgstr "" +"Lutris har ingen installatører for %s. Prøv å bruke en annen tjeneste i " +"stedet." + +#: lutris/services/steamfamily.py:33 +msgid "Steam Family" +msgstr "Steam-familie" + +#: lutris/services/steamfamily.py:34 +msgid "Use for displaying every game in the Steam family" +msgstr "Bruk for visning av hvert eneste spill i Steam-familie" + +#: lutris/services/steam.py:98 +msgid "" +"Failed to load games. Check that your profile is set to public during the " +"sync." +msgstr "" +"Klarte ikke laste spill. Kontroller at din profil er satt til offentlig " +"under synkronisering." + +#: lutris/services/steamwindows.py:24 +msgid "Steam for Windows" +msgstr "Steam for Windows" + +#: lutris/services/steamwindows.py:25 +msgid "" +"Use only for the rare games or mods requiring the Windows version of Steam" +msgstr "" +"Bruk for de få spillene eller tilleggene som krever Windows-versjonen av " +"Steam" + +#: lutris/services/ubisoft.py:85 +msgid "Ubisoft Connect" +msgstr "Ubisoft Connect" + +#: lutris/services/xdg.py:44 +msgid "Local" +msgstr "Lokal" + +#: lutris/settings.py:16 +msgid "(c) 2009 Lutris Team" +msgstr "© 2009 Lutris-utviklingslaget" + +#: lutris/startup.py:138 +#, python-format +msgid "" +"Failed to open database file in %s. Try renaming this file and relaunch " +"Lutris" +msgstr "" +"Klarte ikke åpne databasefila i %s. Prøv å gi nytt navn til fila og start " +"Lutris på nytt" + +#: lutris/sysoptions.py:19 +msgid "Keep current" +msgstr "Behold gjeldende" + +#: lutris/sysoptions.py:29 +msgid "Chinese" +msgstr "Kinesisk" + +#: lutris/sysoptions.py:30 +msgid "Croatian" +msgstr "Kroatisk" + +#: lutris/sysoptions.py:31 +msgid "Dutch" +msgstr "Nederlandsk" + +#: lutris/sysoptions.py:33 +msgid "Finnish" +msgstr "Finsk" + +#: lutris/sysoptions.py:35 +msgid "Georgian" +msgstr "Georgisk" + +#: lutris/sysoptions.py:41 +msgid "Portuguese (Brazilian)" +msgstr "Portugisisk (Brasil)" + +#: lutris/sysoptions.py:42 +msgid "Polish" +msgstr "Polsk" + +#: lutris/sysoptions.py:43 +msgid "Russian" +msgstr "Russisk" + +#: lutris/sysoptions.py:60 lutris/sysoptions.py:69 lutris/sysoptions.py:514 +msgid "Off" +msgstr "Av" + +#: lutris/sysoptions.py:61 +msgid "Primary" +msgstr "Hoved" + +#: lutris/sysoptions.py:83 +msgid "Default installation folder" +msgstr "Standard installasjonsmappe" + +#: lutris/sysoptions.py:93 +msgid "Disable Lutris Runtime" +msgstr "Slå av Lutris-kjøretid" + +#: lutris/sysoptions.py:96 +msgid "" +"The Lutris Runtime loads some libraries before running the game, which can " +"cause some incompatibilities in some cases. Check this option to disable it." +msgstr "" +"Lutris-kjøretiden laster noen biblioteker før oppstart av spillet. som kan " +"gi kompatibilitets problemer i noen tilfeller. Bruk denne innstillingen for " +"å slå den av." + +#: lutris/sysoptions.py:105 +msgid "Prefer system libraries" +msgstr "Foretrekk systembiblioteker" + +#: lutris/sysoptions.py:107 +msgid "" +"When the runtime is enabled, prioritize the system libraries over the " +"provided ones." +msgstr "Når kjøretid er i bruk, prioriter systembiblioteker over de leverte." + +#: lutris/sysoptions.py:110 lutris/sysoptions.py:120 lutris/sysoptions.py:129 +#: lutris/sysoptions.py:143 lutris/sysoptions.py:154 lutris/sysoptions.py:168 +#: lutris/sysoptions.py:183 lutris/sysoptions.py:199 +msgid "Display" +msgstr "Skjerm" + +#: lutris/sysoptions.py:113 +msgid "GPU" +msgstr "Skjermkort" + +#: lutris/sysoptions.py:117 +msgid "GPU to use to run games" +msgstr "Skjermkort som skal brukes for å kjøre spill" + +#: lutris/sysoptions.py:123 +msgid "FPS counter (MangoHud)" +msgstr "FPS-viser (MangoHud)" + +#: lutris/sysoptions.py:126 +msgid "" +"Display the game's FPS + other information. Requires MangoHud to be " +"installed." +msgstr "" +"Viser spillets FPS og annen informasjon. Krever at MangoHud er installert." + +#: lutris/sysoptions.py:132 +msgid "Restore resolution on game exit" +msgstr "Gjenopprett oppløsning ved avsltutting av spill" + +#: lutris/sysoptions.py:137 +msgid "" +"Some games don't restore your screen resolution when \n" +"closed or when they crash. This is when this option comes \n" +"into play to save your bacon." +msgstr "" +"Noen spill gjenoppretter ikke skjermensoppløsning ved\n" +"lukking eller når de krasjer. Dette er når innstillingen kommer \n" +"til redning." + +#: lutris/sysoptions.py:145 +msgid "Disable desktop effects" +msgstr "Slå av skrivebordseffekter" + +#: lutris/sysoptions.py:151 +msgid "" +"Disable desktop effects while game is running, reducing stuttering and " +"increasing performance" +msgstr "" +"Slå av skrivebordseffekter når spillet kjører, reduserer hakking og øker " +"ytelsen" + +#: lutris/sysoptions.py:156 +msgid "Disable screen saver" +msgstr "Slå av pauseskjerm" + +#: lutris/sysoptions.py:162 +msgid "" +"Disable the screen saver while a game is running. Requires the screen " +"saver's functionality to be exposed over DBus." +msgstr "" +"Slå av pauseskjerm mens spillet kjører. Krever at pauseskjermens " +"funksjonalitet skjer over DBus." + +#: lutris/sysoptions.py:171 +msgid "SDL 1.2 Fullscreen Monitor" +msgstr "SDL 1.2 Fullskjermsmonitor" + +#: lutris/sysoptions.py:177 +msgid "" +"Hint SDL 1.2 games to use a specific monitor when going fullscreen by " +"setting the SDL_VIDEO_FULLSCREEN environment variable" +msgstr "" +"Tipser SDL 1.2 spill å bruke en spesifikk monitor ved bruk av fullskjerm ved " +"å skrive inn SDL_VIDEO_FULLSCREEN miljøvariablen" + +#: lutris/sysoptions.py:186 +msgid "Turn off monitors except" +msgstr "Slå av monitor unntatt" + +#: lutris/sysoptions.py:192 +msgid "" +"Only keep the selected screen active while the game is running. \n" +"This is useful if you have a dual-screen setup, and are \n" +"having display issues when running a game in fullscreen." +msgstr "" +"Hold kun den valgte skjermen i bruk mens spillet kjører. \n" +"Dette er hjelpsomt hvis du har flere skjermer, og har \n" +"problemer med skjermene ved kjøring av spill i fullskjerm." + +#: lutris/sysoptions.py:202 +msgid "Switch resolution to" +msgstr "Endre oppløsning til" + +#: lutris/sysoptions.py:207 +msgid "Switch to this screen resolution while the game is running." +msgstr "Bytt til denne skjermoppløsningen mens spillet kjører." + +#: lutris/sysoptions.py:213 +msgid "Enable Gamescope" +msgstr "Bruk Gamescope" + +#: lutris/sysoptions.py:216 +msgid "" +"Use gamescope to draw the game window isolated from your desktop.\n" +"Toggle fullscreen: Super + F" +msgstr "" +"Bruk Gamescope for å gjengi spillvinduet på en isolert måte fra " +"skrivebordet.\n" +"Slå av/på fullskjerm: «Super + F»" + +#: lutris/sysoptions.py:222 +msgid "Enable HDR (Experimental)" +msgstr "Bruk HDR (eksperimentell)" + +#: lutris/sysoptions.py:227 +msgid "" +"Enable HDR for games that support it.\n" +"Requires Plasma 6 and VK_hdr_layer." +msgstr "" +"Bruk HDR for spill som støtter dette.\n" +"Krever Plasma 6 og VK_hdr_layer." + +#: lutris/sysoptions.py:233 +msgid "Relative Mouse Mode" +msgstr "Relativ musemodus" + +#: lutris/sysoptions.py:239 +msgid "" +"Always use relative mouse mode instead of flipping\n" +"dependent on cursor visibility\n" +"Can help with games where the player's camera faces the floor" +msgstr "" +"Bruk alltid relativ musemodus i stedet for omsnuing\n" +"avhenger av pekersynliget\n" +"Kan hjelpe med spill der spillerenskamera er rettet mot gulvet" + +#: lutris/sysoptions.py:248 +msgid "Output Resolution" +msgstr "Utdataoppløsning" + +#: lutris/sysoptions.py:254 +msgid "" +"Set the resolution used by gamescope.\n" +"Resizing the gamescope window will update these settings.\n" +"\n" +"Custom Resolutions: (width)x(height)" +msgstr "" +"Velg oppløsningen brukt av Gamescope.\n" +"Endring av Gamescope-vinduet vil oppdatere disse innstillingene.\n" +"\n" +"Tilpasset oppløsning: (bredde) × (høyde)" + +#: lutris/sysoptions.py:264 +msgid "Game Resolution" +msgstr "Spilloppløsning" + +#: lutris/sysoptions.py:268 +msgid "" +"Set the maximum resolution used by the game.\n" +"\n" +"Custom Resolutions: (width)x(height)" +msgstr "" +"Velg maks oppløsning brukt av spillet.\n" +"\n" +"Tilpasset oppløsning: (bredde) × (høyde)" + +#: lutris/sysoptions.py:273 +msgid "Window Mode" +msgstr "Vindumodus" + +#: lutris/sysoptions.py:277 +msgid "Windowed" +msgstr "I vindu" + +#: lutris/sysoptions.py:278 +msgid "Borderless" +msgstr "Fullskjerm i vindu" + +#: lutris/sysoptions.py:283 +msgid "" +"Run gamescope in fullscreen, windowed or borderless mode\n" +"Toggle fullscreen : Super + F" +msgstr "" +"Kjør Gamescope i fullskjerm-, i vindu- eller fullskjerm i vindumodus\n" +"Slå av/på fullskjerm: «Super + F»" + +#: lutris/sysoptions.py:288 +msgid "FSR Level" +msgstr "FSR-nivå" + +#: lutris/sysoptions.py:294 +msgid "" +"Use AMD FidelityFX™ Super Resolution 1.0 for upscaling.\n" +"Upscaler sharpness from 0 (max) to 20 (min)." +msgstr "" +"Bruk AMD FidelityFX™ Super Resolution 1.0 for oppskalering.\n" +"Oppskalering-skarphet fra 0 (maks) til 20 (min)." + +#: lutris/sysoptions.py:300 +msgid "Framerate Limiter" +msgstr "Bildefrekvens begrenser" + +#: lutris/sysoptions.py:305 +msgid "Set a frame-rate limit for gamescope specified in frames per second." +msgstr "" +"Velg en bildefrekvens begrensning for Gamescope valgt i bilde per sekund." + +#: lutris/sysoptions.py:310 +msgid "Custom Settings" +msgstr "Tilpassede innstillinger" + +#: lutris/sysoptions.py:316 +msgid "" +"Set additional flags for gamescope (if available).\n" +"See 'gamescope --help' for a full list of options." +msgstr "" +"Velg ytterligere flagg for Gamescope (hvis tilgjengelig).\n" +"Les «gamescope --help» for en full liste av muligheter." + +#: lutris/sysoptions.py:323 +msgid "Restrict number of cores used" +msgstr "Begrens antall kjerner brukt" + +#: lutris/sysoptions.py:325 +msgid "Restrict the game to a maximum number of CPU cores." +msgstr "Begrens spillet til et maks antall prosessorkjerner." + +#: lutris/sysoptions.py:331 +msgid "Restrict number of cores to" +msgstr "Begrens antall kjerner til" + +#: lutris/sysoptions.py:334 +msgid "" +"Maximum number of CPU cores to be used, if 'Restrict number of cores used' " +"is turned on." +msgstr "" +"Maks antall prosessorkjerner som skal brukes, hvis «Begrens antall kjerner " +"brukt» er slått på." + +#: lutris/sysoptions.py:342 +msgid "Enable Feral GameMode" +msgstr "Bruk Feral GameMode" + +#: lutris/sysoptions.py:343 +msgid "Request a set of optimisations be temporarily applied to the host OS" +msgstr "" +"Be om et sett med optimaliseringer som skal midlertidig brukes på " +"vertsystemet" + +#: lutris/sysoptions.py:349 +msgid "Reduce PulseAudio latency" +msgstr "Reduser PulseAudio forsinkelse" + +#: lutris/sysoptions.py:353 +msgid "" +"Set the environment variable PULSE_LATENCY_MSEC=60 to improve audio quality " +"on some games" +msgstr "" +"Velg miljøvariablen «PULSE_LATENCY_MSEC=60» for å bedre lydkvaliteten til " +"noen spill" + +#: lutris/sysoptions.py:356 lutris/sysoptions.py:366 lutris/sysoptions.py:374 +msgid "Input" +msgstr "Inndata" + +#: lutris/sysoptions.py:359 +msgid "Switch to US keyboard layout" +msgstr "Bytt til amerikansk tastaturoppsett" + +#: lutris/sysoptions.py:363 +msgid "Switch to US keyboard QWERTY layout while game is running" +msgstr "Bytt til amerikansk QWERTY tastaturoppsett mens spillet kjører" + +#: lutris/sysoptions.py:369 +msgid "AntiMicroX Profile" +msgstr "AntiMicroX-profil" + +#: lutris/sysoptions.py:371 +msgid "Path to an AntiMicroX profile file" +msgstr "Mappe til en AntiMicroX-profil" + +#: lutris/sysoptions.py:377 +msgid "SDL2 gamepad mapping" +msgstr "SDL2-kontrolleroppsett" + +#: lutris/sysoptions.py:380 +msgid "" +"SDL_GAMECONTROLLERCONFIG mapping string or path to a custom " +"gamecontrollerdb.txt file containing mappings." +msgstr "" +"PULSE_LATENCY_MSEC=60 oppsettstekst eller en tilpasset mappe til " +"gamecontrollerdb.txt som inneholder oppsett." + +#: lutris/sysoptions.py:385 lutris/sysoptions.py:397 +msgid "Text based games" +msgstr "Tekstbaserte spill" + +#: lutris/sysoptions.py:387 +msgid "CLI mode" +msgstr "Kommandolinje-modus" + +#: lutris/sysoptions.py:392 +msgid "" +"Enable a terminal for text-based games. Only useful for ASCII based games. " +"May cause issues with graphical games." +msgstr "" +"Bruk en terminal for tekst-baserte-spill. Bare hjelpsomt for ASCII baserte " +"spill. Kan føre til problemer med moderne spill." + +#: lutris/sysoptions.py:399 +msgid "Text based games emulator" +msgstr "Tekst-baserte-spill emulator" + +#: lutris/sysoptions.py:405 +msgid "" +"The terminal emulator used with the CLI mode. Choose from the list of " +"detected terminal apps or enter the terminal's command or path." +msgstr "" +"Terminalemulatoren som brukes med Kommandolinje-modusen. Velg fra lista med " +"oppdagede terminalprogram eller skriv inn terminalens kommando eller mappe." + +#: lutris/sysoptions.py:411 lutris/sysoptions.py:418 lutris/sysoptions.py:428 +#: lutris/sysoptions.py:436 lutris/sysoptions.py:444 lutris/sysoptions.py:452 +#: lutris/sysoptions.py:462 lutris/sysoptions.py:470 lutris/sysoptions.py:483 +#: lutris/sysoptions.py:497 +msgid "Game execution" +msgstr "Spillutførelse" + +#: lutris/sysoptions.py:415 +msgid "Environment variables loaded at run time" +msgstr "Miljøvariabler som lastes ved kjøretid" + +#: lutris/sysoptions.py:421 +msgid "Locale" +msgstr "Lokale" + +#: lutris/sysoptions.py:425 +msgid "" +"Can be used to force certain locale for an app. Fixes encoding issues in " +"legacy software." +msgstr "" +"Kan brukes til å tvinge gjennom et gitt lokale for et program. Retter " +"tegnkoding problemer for eldre programvare." + +#: lutris/sysoptions.py:431 +msgid "Command prefix" +msgstr "Kommandoprefiks" + +#: lutris/sysoptions.py:433 +msgid "" +"Command line instructions to add in front of the game's execution command." +msgstr "Kommandolinje instruksjoner å legge foran spillets-oppstartskommando." + +#: lutris/sysoptions.py:439 +msgid "Manual script" +msgstr "Manuelt skript" + +#: lutris/sysoptions.py:441 +msgid "Script to execute from the game's contextual menu" +msgstr "Skript som skal kjøres fra spillets-sprettopp" + +#: lutris/sysoptions.py:447 +msgid "Pre-launch script" +msgstr "Skript før oppstart" + +#: lutris/sysoptions.py:449 +msgid "Script to execute before the game starts" +msgstr "Skript som skal kjøres før spillet starter" + +#: lutris/sysoptions.py:455 +msgid "Wait for pre-launch script completion" +msgstr "Vent på ferdigstillingen av skript før oppstart" + +#: lutris/sysoptions.py:459 +msgid "Run the game only once the pre-launch script has exited" +msgstr "Start spillet bare når skriptet før oppstart har avsluttet" + +#: lutris/sysoptions.py:465 +msgid "Post-exit script" +msgstr "Skript etter avslutting" + +#: lutris/sysoptions.py:467 +msgid "Script to execute when the game exits" +msgstr "Skript som skal kjøres etter spillavslutning" + +#: lutris/sysoptions.py:473 +msgid "Include processes" +msgstr "Inkluder prosesser" + +#: lutris/sysoptions.py:476 +msgid "" +"What processes to include in process monitoring. This is to override the " +"built-in exclude list.\n" +"Space-separated list, processes including spaces can be wrapped in quotation " +"marks." +msgstr "" +"Hvilke prosesser å inkludere i prosessovervåkningen. Dette er for å " +"overstyre den innebygde ekskluderingslista.\n" +"Mellomrom atskilt liste, prosesser som inneholder mellomrom kan pakkes inn i " +"anførselstegn." + +#: lutris/sysoptions.py:486 +msgid "Exclude processes" +msgstr "Ekskluder prosesser" + +#: lutris/sysoptions.py:489 +msgid "" +"What processes to exclude in process monitoring. For example background " +"processes that stick around after the game has been closed.\n" +"Space-separated list, processes including spaces can be wrapped in quotation " +"marks." +msgstr "" +"Hvilke prosesser å ekskludere i prosessovervåkningen. For eksempel " +"bakgrunnsprosesser som henger etter at spillet er avsluttet.\n" +"Mellomrom atskilt liste, prosesser som inneholder mellomrom kan pakkes inn i " +"anførselstegn." + +#: lutris/sysoptions.py:500 +msgid "Killswitch file" +msgstr "Avslåingsfil" + +#: lutris/sysoptions.py:503 +msgid "" +"Path to a file which will stop the game when deleted \n" +"(usually /dev/input/js0 to stop the game on joystick unplugging)" +msgstr "" +"Mappe til en fil som stopper spillet når det slettes \n" +"(vanligvis /dev/input/js0 for å avslutte spillet når du kobler fra " +"kontrolleren)" + +#: lutris/sysoptions.py:509 lutris/sysoptions.py:525 lutris/sysoptions.py:534 +msgid "Xephyr (Deprecated, use Gamescope)" +msgstr "Xephyr (foreldet, bruk Gamescope)" + +#: lutris/sysoptions.py:511 +msgid "Use Xephyr" +msgstr "Bruk Xephyr" + +#: lutris/sysoptions.py:515 +msgid "8BPP (256 colors)" +msgstr "8BPP (256 farger)" + +#: lutris/sysoptions.py:516 +msgid "16BPP (65536 colors)" +msgstr "16BPP (65536 farger)" + +#: lutris/sysoptions.py:517 +msgid "24BPP (16M colors)" +msgstr "24BPP (16M farger)" + +#: lutris/sysoptions.py:522 +msgid "Run program in Xephyr to support 8BPP and 16BPP color modes" +msgstr "Start program med Xephyr for å støtte 8BPP og 16BPP fargemoduser" + +#: lutris/sysoptions.py:528 +msgid "Xephyr resolution" +msgstr "Xephyr-oppløsning" + +#: lutris/sysoptions.py:531 +msgid "Screen resolution of the Xephyr server" +msgstr "Skjermoppløsning for Xephyr-tjeneren" + +#: lutris/sysoptions.py:537 +msgid "Xephyr Fullscreen" +msgstr "Xephyr-fullskjerm" + +#: lutris/sysoptions.py:541 +msgid "Open Xephyr in fullscreen (at the desktop resolution)" +msgstr "Åpne Xephyr i fullskjerm (med skrivebordsoppløsning)" + +#: lutris/util/flatpak.py:28 +msgid "Flatpak is not installed" +msgstr "Flatpak er ikke installert" + +#: lutris/util/portals.py:83 +#, python-format +msgid "" +"'%s' could not be moved to the trash. You will need to delete it yourself." +msgstr "Klarte ikke flytte «%s» til papirkurven. Du må slette den selv." + +#: lutris/util/portals.py:88 +#, python-format +msgid "" +"The items could not be moved to the trash. You will need to delete them " +"yourself:\n" +"%s" +msgstr "" +"Klarte ikke flytte elementene til papirkurven. Du må slette dem selv:\n" +"%s" + +#: lutris/util/strings.py:17 +msgid "Never played" +msgstr "Aldri spilt" + +#. This function works out how many hours are meant by some +#. number of some unit. +#: lutris/util/strings.py:207 lutris/util/strings.py:272 +msgid "hour" +msgstr "time" + +#: lutris/util/strings.py:207 lutris/util/strings.py:272 +msgid "hours" +msgstr "timer" + +#: lutris/util/strings.py:209 lutris/util/strings.py:273 +msgid "minute" +msgstr "minutt" + +#: lutris/util/strings.py:209 lutris/util/strings.py:273 +msgid "minutes" +msgstr "minutter" + +#: lutris/util/strings.py:216 lutris/util/strings.py:301 +msgid "Less than a minute" +msgstr "Mindre enn et minutt siden" + +#: lutris/util/strings.py:274 +msgid "day" +msgstr "dag" + +#: lutris/util/strings.py:274 +msgid "days" +msgstr "dager" + +#: lutris/util/strings.py:275 +msgid "week" +msgstr "uke" + +#: lutris/util/strings.py:275 +msgid "weeks" +msgstr "uker" + +#: lutris/util/strings.py:276 +msgid "month" +msgstr "måned" + +#: lutris/util/strings.py:276 +msgid "months" +msgstr "måneder" + +#: lutris/util/strings.py:277 +msgid "year" +msgstr "år" + +#: lutris/util/strings.py:277 +msgid "years" +msgstr "år" + +#: lutris/util/strings.py:307 +#, python-format +msgid "'%s' is not a valid playtime." +msgstr "«%s» er ikke en gyldig spilletid." + +#: lutris/util/strings.py:392 +msgid "in the future" +msgstr "I fremtiden" + +#: lutris/util/strings.py:394 +msgid "just now" +msgstr "Akkurat nå" + +#: lutris/util/strings.py:403 +#, python-format +msgid "%d days" +msgstr "%d dager" + +#: lutris/util/strings.py:407 +#, python-format +msgid "%d hours" +msgstr "%d timer" + +#: lutris/util/strings.py:412 +#, python-format +msgid "%d minutes" +msgstr "%d minutter" + +#: lutris/util/strings.py:414 +msgid "1 minute" +msgstr "1 minutt" + +#: lutris/util/strings.py:418 +#, python-format +msgid "%d seconds" +msgstr "%d sekunder" + +#: lutris/util/strings.py:420 +msgid "1 second" +msgstr "1 sekund" + +#: lutris/util/strings.py:422 +msgid "ago" +msgstr "siden" + +#: lutris/util/system.py:25 +msgid "Documents" +msgstr "Dokumenter" + +#: lutris/util/system.py:26 +msgid "Downloads" +msgstr "Nedlastinger" + +#: lutris/util/system.py:27 +msgid "Desktop" +msgstr "Skrivebord" + +#: lutris/util/system.py:28 lutris/util/system.py:30 +msgid "Pictures" +msgstr "Bilder" + +#: lutris/util/system.py:29 +msgid "Videos" +msgstr "Videoer" + +#: lutris/util/system.py:31 +msgid "Projects" +msgstr "Prosjekter" + +#: lutris/util/ubisoft/client.py:95 +#, python-format +msgid "Ubisoft authentication has been lost: %s" +msgstr "Ubisoft autentisering har gått tapt: %s" + +#: lutris/util/wine/dll_manager.py:102 +#, python-format +msgid "" +"The '%s' runtime component is not installed; visit the Updates tab in the " +"Preferences dialog to install it." +msgstr "" +"Kjøretid komponenten «%s» er ikke installert; besøk oppdateringssiden i " +"innstillingsvinduet for å installere." + +#: lutris/util/wine/dll_manager.py:113 +msgid "Manual" +msgstr "Manuelt" + +#: lutris/util/wine/proton.py:99 +#, python-format +msgid "Proton version '%s' is missing its wine executable and can't be used." +msgstr "Proton-versjonen «%s» mangler dens Wine-programfil og kan ikke brukes." + +#: lutris/util/wine/wine.py:173 +msgid "The Wine version must be specified." +msgstr "Wine-versjonen må oppgis." + +#: lutris/util/wine/wine.py:230 +msgid "No versions of Wine are installed." +msgstr "Ingen Wine-versjoner er installert." From d856411b880b1830d9d5aef371d74d26dc7d47c5 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 23 Apr 2025 18:10:39 -0400 Subject: [PATCH 071/126] Fix spelling mistakes --- lutris/services/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lutris/services/base.py b/lutris/services/base.py index ea6a5af0db..647eb4f434 100644 --- a/lutris/services/base.py +++ b/lutris/services/base.py @@ -457,12 +457,12 @@ def login(self, parent=None): @property def is_login_in_progress(self) -> bool: - """Set to true if the login process is underway; the credential files make be created at this + """Set to true if the login process is underway; the credential files may be created at this time, but that does not count as 'authenticated' until the login process is over. This is used by WebConnectDialog since it creates its cookies before the login is actually complete. This is recorded with a file in ~/.cache/lutris so it will persist across Lutris - restarted, just as the credentials themselves do. For this reason, we need to allow + restarts, just as the credentials themselves do. For this reason, we need to allow the user to login again even when a login is in progress.""" return self._get_login_in_progress_path().exists() From 9b4482461fc5f45a1d9a8db837d64e609571cc2b Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 23 Apr 2025 18:19:11 -0400 Subject: [PATCH 072/126] Support multiple redirect URIs for login; everybody needs a list of the things. --- lutris/gui/dialogs/webconnect_dialog.py | 2 +- lutris/services/amazon.py | 2 +- lutris/services/base.py | 1 + lutris/services/battlenet.py | 2 +- lutris/services/ea_app.py | 2 +- lutris/services/egs.py | 2 +- lutris/services/gog.py | 6 +++--- lutris/services/humblebundle.py | 2 +- lutris/services/itchio.py | 2 +- lutris/services/steamfamily.py | 2 +- lutris/services/ubisoft.py | 2 +- 11 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lutris/gui/dialogs/webconnect_dialog.py b/lutris/gui/dialogs/webconnect_dialog.py index 0061e66e10..5e8ea24849 100644 --- a/lutris/gui/dialogs/webconnect_dialog.py +++ b/lutris/gui/dialogs/webconnect_dialog.py @@ -92,7 +92,7 @@ def on_navigation(self, widget, load_event): script = self.service.scripts[url] widget.run_javascript(script, None, None) return True - if url.startswith(self.service.redirect_uri): + if any(url.startswith(r) for r in self.service.redirect_uris): if self.service.requires_login_page: resource = widget.get_main_resource() resource.get_data(None, self._get_response_data_finish, None) diff --git a/lutris/services/amazon.py b/lutris/services/amazon.py index d7c8fdac5a..e921b67291 100644 --- a/lutris/services/amazon.py +++ b/lutris/services/amazon.py @@ -90,7 +90,7 @@ class AmazonService(OnlineService): serial = None verifier = None - redirect_uri = "https://www.amazon.com/?" + redirect_uris = ["https://www.amazon.com/?"] cookies_path = os.path.join(settings.CACHE_DIR, ".amazon.auth") user_path = os.path.join(settings.CACHE_DIR, ".amazon.user") diff --git a/lutris/services/base.py b/lutris/services/base.py index 647eb4f434..cb57cd6eda 100644 --- a/lutris/services/base.py +++ b/lutris/services/base.py @@ -433,6 +433,7 @@ class OnlineService(BaseService): login_window_width = 390 login_window_height = 500 login_user_agent = settings.DEFAULT_USER_AGENT + redirect_uris = NotImplemented @property def credential_files(self): diff --git a/lutris/services/battlenet.py b/lutris/services/battlenet.py index a4b4ba04c6..cf2309e0f7 100644 --- a/lutris/services/battlenet.py +++ b/lutris/services/battlenet.py @@ -110,7 +110,7 @@ class BattleNetService(BaseService): client_installer = "battlenet" cookies_path = os.path.join(settings.CACHE_DIR, ".bnet.auth") cache_path = os.path.join(settings.CACHE_DIR, "bnet-library.json") - redirect_uri = "https://lutris.net" + redirect_uris = ["https://lutris.net"] @property def battlenet_config_path(self): diff --git a/lutris/services/ea_app.py b/lutris/services/ea_app.py index b6623867b8..ca459ca4b2 100644 --- a/lutris/services/ea_app.py +++ b/lutris/services/ea_app.py @@ -163,7 +163,7 @@ class EAAppService(OnlineService): token_path = os.path.join(settings.CACHE_DIR, "ea_app/auth_token") origin_redirect_uri = "https://www.origin.com/views/login.html" login_url = "https://www.ea.com/login" - redirect_uri = "https://www.ea.com/" + redirect_uris = ["https://www.ea.com/"] origin_login_url = ( "https://accounts.ea.com/connect/auth" "?response_type=code&client_id=ORIGIN_SPA_ID&display=originXWeb/login" diff --git a/lutris/services/egs.py b/lutris/services/egs.py index fbf811392a..eb7f0cf2f0 100644 --- a/lutris/services/egs.py +++ b/lutris/services/egs.py @@ -165,7 +165,7 @@ class EpicGamesStoreService(OnlineService): "https%3A//www.epicgames.com/id/api/redirect%3F" "clientId%3D34a02cf8f4414e29b15921876da36f9a%26responseType%3Dcode" ) - redirect_uri = "https://www.epicgames.com/id/api/redirect" + redirect_uris = ["https://www.epicgames.com/id/api/redirect"] oauth_url = "https://account-public-service-prod03.ol.epicgames.com" catalog_url = "https://catalog-public-service-prod06.ol.epicgames.com" library_url = "https://library-service.live.use1a.on.epicgames.com" diff --git a/lutris/services/gog.py b/lutris/services/gog.py index f3906f9810..e193de85d7 100644 --- a/lutris/services/gog.py +++ b/lutris/services/gog.py @@ -88,7 +88,7 @@ class GOGService(OnlineService): client_id = "46899977096215655" client_secret = "9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9" - redirect_uri = "https://embed.gog.com/on_login_success?origin=client" + redirect_uris = ["https://embed.gog.com/on_login_success?origin=client"] login_success_url = "https://www.gog.com/on_login_success" cookies_path = os.path.join(settings.CACHE_DIR, ".gog.auth") @@ -116,7 +116,7 @@ def login_url(self): params = { "client_id": self.client_id, "layout": "client2", - "redirect_uri": self.redirect_uri, + "redirect_uri": self.redirect_uris[0], "response_type": "code", } return "https://auth.gog.com/auth?" + urlencode(params) @@ -170,7 +170,7 @@ def request_token(self, url: str = "", refresh_token: str = "") -> None: return extra_params = { "code": response_params["code"], - "redirect_uri": self.redirect_uri, + "redirect_uri": self.redirect_uris[0], } params = { diff --git a/lutris/services/humblebundle.py b/lutris/services/humblebundle.py index c1ad86b082..634b1afcee 100644 --- a/lutris/services/humblebundle.py +++ b/lutris/services/humblebundle.py @@ -68,7 +68,7 @@ class HumbleBundleService(OnlineService): api_url = "https://www.humblebundle.com/" login_url = "https://www.humblebundle.com/login?goto=/home/library" - redirect_uri = "https://www.humblebundle.com/home/library" + redirect_uris = ["https://www.humblebundle.com/home/library"] cookies_path = os.path.join(settings.CACHE_DIR, ".humblebundle.auth") token_path = os.path.join(settings.CACHE_DIR, ".humblebundle.token") diff --git a/lutris/services/itchio.py b/lutris/services/itchio.py index 3978ee4df1..db2f727ac0 100644 --- a/lutris/services/itchio.py +++ b/lutris/services/itchio.py @@ -106,7 +106,7 @@ class ItchIoService(OnlineService): api_url = "https://api.itch.io" login_url = "https://itch.io/login" - redirect_uri = "https://itch.io/my-feed" + redirect_uris = ["https://itch.io/my-feed"] cookies_path = os.path.join(settings.CACHE_DIR, ".itchio.auth") cache_path = os.path.join(settings.CACHE_DIR, "itchio/api/") diff --git a/lutris/services/steamfamily.py b/lutris/services/steamfamily.py index d00f872531..d951caedeb 100644 --- a/lutris/services/steamfamily.py +++ b/lutris/services/steamfamily.py @@ -42,7 +42,7 @@ class SteamFamilyService(SteamService, OnlineService): token_path = os.path.join(settings.CACHE_DIR, ".steam.token") cache_path = os.path.join(settings.CACHE_DIR, "steam-library.json") login_url = "https://store.steampowered.com/login/?redir=/about" - redirect_uri = "https://store.steampowered.com/about" + redirect_uris = ["https://store.steampowered.com/about"] access_token_url = "https://store.steampowered.com/pointssummary/ajaxgetasyncconfig" library_url = "https://api.steampowered.com/IFamilyGroupsService/GetSharedLibraryApps/v1/" family_url = "https://api.steampowered.com/IFamilyGroupsService/GetFamilyGroupForUser/v1/" diff --git a/lutris/services/ubisoft.py b/lutris/services/ubisoft.py index 225a9f89f2..795f7b3527 100644 --- a/lutris/services/ubisoft.py +++ b/lutris/services/ubisoft.py @@ -93,7 +93,7 @@ class UbisoftConnectService(OnlineService): token_path = os.path.join(settings.CACHE_DIR, "ubisoft/.token") cache_path = os.path.join(settings.CACHE_DIR, "ubisoft/library/") login_url = consts.LOGIN_URL - redirect_uri = "https://connect.ubisoft.com/change_domain/" + redirect_uris = ["https://connect.ubisoft.com/change_domain/"] scripts = { "https://connect.ubisoft.com/ready": ('window.location.replace("https://connect.ubisoft.com/change_domain/");'), "https://connect.ubisoft.com/change_domain/": ( From 6cbc7bc587ee04752ceb1c2bc8197b6922dcb069 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 23 Apr 2025 18:21:02 -0400 Subject: [PATCH 073/126] Allow for the 'dashboard' URL redirect from Itch.IO. Itch does this redirect if your account is set for developing games and not playing them. We don't really care about that- the redirect just tells us that the credentials have been accepted. Resolves #5588 --- lutris/services/itchio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/services/itchio.py b/lutris/services/itchio.py index db2f727ac0..5135fd77e9 100644 --- a/lutris/services/itchio.py +++ b/lutris/services/itchio.py @@ -106,7 +106,7 @@ class ItchIoService(OnlineService): api_url = "https://api.itch.io" login_url = "https://itch.io/login" - redirect_uris = ["https://itch.io/my-feed"] + redirect_uris = ["https://itch.io/my-feed", "https://itch.io/dashboard"] cookies_path = os.path.join(settings.CACHE_DIR, ".itchio.auth") cache_path = os.path.join(settings.CACHE_DIR, "itchio/api/") From 3d4b0cd8800d16dfbfe68c56335392a9a04b2010 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 25 Apr 2025 21:33:16 +0700 Subject: [PATCH 074/126] add export .yml script --- lutris/game_actions.py | 77 ++++++++++++++++++++++++++++++++++++++++++ lutris/util/yaml.py | 22 ++++++++++++ 2 files changed, 99 insertions(+) diff --git a/lutris/game_actions.py b/lutris/game_actions.py index 5c5d143ff4..bbc20fceec 100644 --- a/lutris/game_actions.py +++ b/lutris/game_actions.py @@ -29,6 +29,49 @@ from lutris.util.system import path_exists +def nuke_home_paths(data): + from re import compile + + home_re = compile(r"^(/home/([^/]+))(/.*)$") + + def repl_p(path): + if isinstance(path, str) and (match := home_re.match(path)): + return f"$HOME{match.group(3)}" + return path + + def keys_targeted(d): + if not isinstance(d, dict): + return d + d = dict(d) + # check in game key + if "game" in d and isinstance(d["game"], dict): + game_d = d["game"] + for k in ["exe", "prefix", "working_dir"]: + if k in game_d: + game_d[k] = repl_p(game_d[k]) + # check in system key + if "system" in d and isinstance(d["system"], dict): + system_d = d["system"] + for k in ["antimicro_config", "sdl_gamecontrollerconfig"]: + if k in system_d: + system_d[k] = repl_p(system_d[k]) + # env + if "env" in system_d and isinstance(system_d["env"], dict): + env_d = system_d["env"] + for k, v in env_d.items(): + env_d[k] = repl_p(v) + # check in wine key + wine_d = d.get("wine") + if isinstance(wine_d, dict) and "custom_wine_path" in wine_d: + wine_d["custom_wine_path"] = repl_p(wine_d["custom_wine_path"]) + # check in script key + if "script" in d and isinstance(d["script"], dict): + d["script"] = keys_targeted(d["script"]) + return d + + return keys_targeted(data) + + class GameActions: """These classes provide a set of action to apply to a game or list of games, and can be used to populate menus. The base class handles the no-games case, for which there are no actions. But @@ -250,6 +293,7 @@ def get_game_actions(self): ("rm-steam-shortcut", _("Delete Steam shortcut"), self.on_remove_steam_shortcut), ("view", _("View on Lutris.net"), self.on_view_game), ("duplicate", _("Duplicate"), self.on_game_duplicate), + ("export_script", _("Export Script"), self.on_export_script), (None, "-", None), ("remove", _("Remove"), self.on_remove_game), ] @@ -268,6 +312,7 @@ def get_displayed_entries(self): return { "duplicate": game.is_installed, + "export_script": game.is_installed, "install": self.is_installable, "add": not game.is_installed, "play": self.is_game_launchable, @@ -436,6 +481,38 @@ def on_game_duplicate(self, _widget): # completes, no need to wait for it. AsyncCall(download_lutris_media, None, db_game["slug"]) + def on_export_script(self, _widget): + game = self.game + config_id = game.game_config_id + # db_game = get_game_by_field(game.id, "id") + # print(f"--> db:{db_game}") + from lutris.settings import GAME_CONFIG_DIR + from lutris.util.yaml import read_yaml_from_file, save_yaml_as + + _config = read_yaml_from_file(f"{GAME_CONFIG_DIR}/{config_id}.yml") + _config["slug"] = game.slug + _config["name"] = game.name + _config["game_slug"] = game.slug + _config["runner"] = get_game_by_field(game.id, "id")["runner"] + _config["description"] = _config.get("description") + _config["notes"] = _config.get("notes", f"Import {game.name} config") + _config["version"] = _config.get("Standard") + + if _script := _config.get("script"): + _config["script"] = _script + else: + _config["script"] = {"files": {}, "game": _config["game"], "installer": {}} + # _config["script"]["game"]["working_dir"] = "$GAMEDIR" + if _sys := _config.get("system"): + _config["script"]["system"] = _sys + if _wine := _config.get("wine"): + _config["script"]["wine"] = _wine + # avoid to crash `version: system` + _config["script"]["wine"].pop("version") + _config = nuke_home_paths(_config) + # print(f"-->CONFIG: {_config}") + save_yaml_as(_config, f"{config_id}.yml") + def _select_game_launch_config_name(self, game): game_config = game.config.game_level.get("game", {}) configs = game_config.get("launch_configs") diff --git a/lutris/util/yaml.py b/lutris/util/yaml.py index fec23cba7b..f9acadbcbb 100644 --- a/lutris/util/yaml.py +++ b/lutris/util/yaml.py @@ -4,6 +4,7 @@ # pylint: disable=no-member import yaml +from gi.repository import Gtk from lutris.util.log import logger from lutris.util.system import path_exists @@ -33,3 +34,24 @@ def write_yaml_to_file(config: dict, filepath: str) -> None: finally: if os.path.isfile(temp_path): os.unlink(temp_path) + + +def save_yaml_as(config: dict, default_name: str) -> None: + dialog = Gtk.FileChooserNative.new( + title="Save as", parent=None, action=Gtk.FileChooserAction.SAVE, accept_label="_Save", cancel_label="_Cancel" + ) + dialog.set_current_name(default_name) + dialog.set_do_overwrite_confirmation(True) + _yaml = Gtk.FileFilter() + _yaml.set_name("YAML files") + _yaml.add_pattern("*.yaml") + _yaml.add_pattern("*.yml") + dialog.add_filter(_yaml) + + try: + response = dialog.run() + if response == Gtk.ResponseType.ACCEPT: + if file := dialog.get_file(): + write_yaml_to_file(config, file.get_path()) + finally: + dialog.destroy() From 3e8c45e8e88528425dbaaa4696548699aa0edf7b Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Tue, 29 Apr 2025 19:50:12 +0700 Subject: [PATCH 075/126] gui: set X11 WM_CLASS to match Wayland When running Lutris on X11, the WM_CLASS is "Lutris". However, on Wayland, it is "net.lutris.Lutris". This inconsistency can cause issues with window management and icon theming, particularly in environments like GNOME. Since GTK3 (the current version used by Lutris) does not allow changing the WM_CLASS dynamically on Wayland, so the best solution is setting the WM_CLASS to "net.lutris.Lutris" when Lutris is run under X11 and change StartupWMClass in desktop file resolving issues Lutris icon not being displayed in the GNOME dash. Ref: lutris#6088 --- lutris/gui/lutriswindow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py index 65ab8a34e6..c1eed65dea 100644 --- a/lutris/gui/lutriswindow.py +++ b/lutris/gui/lutriswindow.py @@ -112,6 +112,7 @@ def __init__(self, application, **kwargs) -> None: ) update_desktop_icons() load_icon_theme() + self.set_wmclass("net.lutris.Lutris", "net.lutris.Lutris") self.application = application self.window_x, self.window_y = self.get_position() self.restore_window_position() From 84b0faf8b2184cb5b7b94f81d2ea5be70bf2b4b4 Mon Sep 17 00:00:00 2001 From: fiftydinar <65243233+fiftydinar@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:04:47 +0700 Subject: [PATCH 076/126] Update StartupWMClass in the desktop file --- share/applications/net.lutris.Lutris.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/applications/net.lutris.Lutris.desktop b/share/applications/net.lutris.Lutris.desktop index 00d27c2650..d895b7943a 100644 --- a/share/applications/net.lutris.Lutris.desktop +++ b/share/applications/net.lutris.Lutris.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Name=Lutris -StartupWMClass=Lutris +StartupWMClass=net.lutris.Lutris Comment=Video Game Preservation Platform Categories=Game;PackageManager; Keywords=gaming;wine;emulator; From d2aa047f59941d3103988e2721c5bda4a0113f40 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 2 May 2025 20:43:42 +0700 Subject: [PATCH 077/126] wine: warning when executable file path is invalid When user paste a file path in the Executable field it might contain leading or trailing whitespace, or be missing the correct path /path/to/___.ex,... This warning can help new users easily debug and fix such issues --- lutris/runners/wine.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index 21c3759c33..510e695f16 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -93,6 +93,21 @@ def _get_prefix_warning(_option_key: str, config: LutrisConfig) -> Optional[str] return _("Warning Some Wine configuration options cannot be applied, if no prefix can be found.") +def _get_exe_warning(_option_key: str, config: LutrisConfig) -> Optional[str]: + exe = config.game_config.get("exe") + if not exe: + return _("Warning No executable path specified") + _exe = exe.strip() + good_path = exe == _exe + if not _exe: + return _("Warning No executable path specified") + if good_path and os.path.isfile(_exe): + return None + elif not good_path: + return _("Warning Executable path has extra whitespace at the beginning or end") + return _("Warning Executable file does not exist") + + def _get_dxvk_warning() -> Optional[str]: if drivers.is_outdated(): driver_info = drivers.get_nvidia_driver_info() @@ -251,6 +266,7 @@ class wine(Runner): "type": "file", "label": _("Executable"), "help": _("The game's main EXE file"), + "warning": _get_exe_warning, }, { "option": "args", From 8b332c30d122e56f6867bac1b9e33c17494a7564 Mon Sep 17 00:00:00 2001 From: RoGreat Date: Tue, 29 Apr 2025 17:54:11 -0500 Subject: [PATCH 078/126] Fix Ubisoft cover art url --- lutris/services/ubisoft.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lutris/services/ubisoft.py b/lutris/services/ubisoft.py index 795f7b3527..06b242bc33 100644 --- a/lutris/services/ubisoft.py +++ b/lutris/services/ubisoft.py @@ -35,13 +35,8 @@ class UbisoftCover(ServiceMedia): size = (160, 186) dest_path = os.path.join(settings.CACHE_DIR, "ubisoft/covers") file_patterns = ["%s.jpg"] - api_field = "id" - url_pattern = "https://ubiservices.cdn.ubi.com/%s/spaceCardAsset/boxArt_mobile.jpg?imwidth=320" - - def get_media_url(self, details: Dict[str, Any]) -> Optional[str]: - if self.api_field in details: - return super().get_media_url(details) - return details["thumbImage"] + api_field = "thumbImage" + url_pattern = "https://static3.cdn.ubi.com/orbit/uplay_launcher_3_0/assets/%s" def download(self, slug, url): if url.startswith("http"): From 50f28f659cad37d67a4aaeef88ad478ac906c97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Fontaine?= Date: Fri, 2 May 2025 14:25:15 +0200 Subject: [PATCH 079/126] fix: add missing assets when creating Steam shortcut Add missing assets for the horizontal grid and icon. --- lutris/util/steam/shortcut.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lutris/util/steam/shortcut.py b/lutris/util/steam/shortcut.py index 5973666a26..2e00c06f09 100644 --- a/lutris/util/steam/shortcut.py +++ b/lutris/util/steam/shortcut.py @@ -170,17 +170,17 @@ def set_artwork(game): shortcut_id = generate_appid(game) source_cover = resources.get_cover_path(game.slug) source_banner = resources.get_banner_path(game.slug) - target_cover = os.path.join(artwork_path, "{}p.jpg".format(shortcut_id)) - target_banner = os.path.join(artwork_path, "{}_hero.jpg".format(shortcut_id)) - if not system.path_exists(target_cover, exclude_empty=True): - try: - shutil.copyfile(source_cover, target_cover) - logger.debug("Copied %s cover to %s", game, target_cover) - except FileNotFoundError as ex: - logger.error("Failed to copy cover to %s: %s", target_cover, ex) - if not system.path_exists(target_banner, exclude_empty=True): - try: - shutil.copyfile(source_banner, target_banner) - logger.debug("Copied %s cover to %s", game, target_banner) - except FileNotFoundError as ex: - logger.error("Failed to copy banner to %s: %s", target_banner, ex) + source_icon = resources.get_icon_path(game.slug) + assets = [ + ("grid horizontal", source_banner, os.path.join(artwork_path, "{}.jpg".format(shortcut_id))), + ("grid vertical", source_cover, os.path.join(artwork_path, "{}p.jpg".format(shortcut_id))), + ("hero", source_banner, os.path.join(artwork_path, "{}_hero.jpg".format(shortcut_id))), + ("icon", source_icon, os.path.join(artwork_path, "{}_icon.jpg".format(shortcut_id))), + ] + for name, source, target in assets: + if not system.path_exists(target, exclude_empty=True): + try: + shutil.copyfile(source, target) + logger.debug("Copied %s %s asset to %s", game, name, target) + except FileNotFoundError as ex: + logger.error("Failed to copy %s %s asset to %s: %s", game, name, target, ex) From 95d6be9a24444bc3aa35fe12739279fb27a35241 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Mon, 5 May 2025 16:24:25 -0400 Subject: [PATCH 080/126] Some ruffage: remove unused imports --- lutris/services/ubisoft.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lutris/services/ubisoft.py b/lutris/services/ubisoft.py index 06b242bc33..7d970278b5 100644 --- a/lutris/services/ubisoft.py +++ b/lutris/services/ubisoft.py @@ -4,7 +4,6 @@ import os import shutil from gettext import gettext as _ -from typing import Any, Dict, Optional from urllib.parse import unquote from gi.repository import Gio From 31a02e9857a84bfdc7c003284ef03b1f426c5886 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Thu, 8 May 2025 19:29:03 -0400 Subject: [PATCH 081/126] Make interpreter.revert() asynchronous. I find that with Proton 'winekill' can be pretty slow, and we don't want Wayland to kill us in our sleep. We'll just go async with 'winekill' here. --- lutris/gui/installerwindow.py | 31 ++++++++++++++++++++----------- lutris/installer/interpreter.py | 31 +++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/lutris/gui/installerwindow.py b/lutris/gui/installerwindow.py index 0f93b9ca4f..7d16f20dd1 100644 --- a/lutris/gui/installerwindow.py +++ b/lutris/gui/installerwindow.py @@ -222,6 +222,18 @@ def on_navigate_home(self, _accel_group, _window, _keyval, _modifier): def on_cancel_clicked(self, _button=None): """Ask a confirmation before cancelling the installation, if it has started.""" + + def on_cancelled(): + if self.interpreter: + self.interpreter.cleanup() # still remove temporary downloads in any case + + if self.interpreter and not self.install_in_progress: + INSTALLATION_COMPLETED.fire() + else: + INSTALLATION_FAILED.fire() + + self.destroy() + if self.install_in_progress: widgets = [] @@ -251,19 +263,16 @@ def on_cancel_clicked(self, _button=None): self.installer_files_box.stop_all() if self.interpreter: - self.interpreter.revert(remove_game_dir=remove_checkbox.get_active()) + self.interpreter.revert( + remove_game_dir=remove_checkbox.get_active(), + completion_function=on_cancelled, + error_function=self.on_signal_error, + ) + else: + on_cancelled() else: self.installer_files_box.stop_all() - - if self.interpreter: - self.interpreter.cleanup() # still remove temporary downloads in any case - - if self.interpreter and not self.install_in_progress: - INSTALLATION_COMPLETED.fire() - else: - INSTALLATION_FAILED.fire() - - self.destroy() + on_cancelled() def on_source_clicked(self, _button): InstallerSourceDialog(self.interpreter.installer.script_pretty, self.interpreter.installer.game_name, self) diff --git a/lutris/installer/interpreter.py b/lutris/installer/interpreter.py index 244a5b9ea6..40197b7498 100644 --- a/lutris/installer/interpreter.py +++ b/lutris/installer/interpreter.py @@ -385,19 +385,34 @@ def cleanup(self): os.chdir(os.path.expanduser("~")) system.delete_folder(self.cache_path) - def revert(self, remove_game_dir=True): - """Revert installation in case of an error""" + def revert(self, remove_game_dir=True, completion_function=None, error_function=None): + """Revert installation in case of an error. Since winekill can be slow, + this runs asynchronously and calls cocompletion_function() when successful, + or error_function(err) if it fails.""" logger.info("Cancelling installation of %s", self.installer.game_name) - if self.installer.runner.startswith("wine"): - self.task({"name": "winekill"}) self.cancelled = True - if self.abort_current_task: - self.abort_current_task() + def on_complete(_result, error): + if error: + error_function(error) + return + + try: + if self.abort_current_task: + self.abort_current_task() + + if self.target_path and remove_game_dir: + system.remove_folder(self.target_path) - if self.target_path and remove_game_dir: - system.remove_folder(self.target_path) + completion_function() + except Exception as ex: + error_function(ex) + + if self.installer.runner.startswith("wine"): + AsyncCall(self.task, on_complete, {"name": "winekill"}) + else: + on_complete(None, None) def _get_string_replacements(self): """Return a mapping of variables to their actual value""" From 6d06c9d1c0ecb1cf7a29d543f26ff9d988b47b26 Mon Sep 17 00:00:00 2001 From: blackPanther OS Date: Wed, 14 May 2025 07:36:20 +0200 Subject: [PATCH 082/126] Update datapath.py Logic hotfix for: ./bin/lutris -d Traceback (most recent call last): File "./bin/lutris", line 62, in from lutris.gui.application import Application # pylint: disable=no-name-in-module ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "lutris/lutris/gui/application.py", line 54, in from lutris.startup import init_lutris, run_all_checks File "lutris/lutris/startup.py", line 18, in from lutris.runners.json import load_json_runners File "lutris/lutris/runners/json.py", line 13, in os.path.join(datapath.get(), "json"), ^^^^^^^^^^^^^^ File "lutris/lutris/util/datapath.py", line 30, in get raise IOError("data_path can't be found at : %s" % data_path) OSError: data_path can't be found at : /usr/share/lutris --- lutris/util/datapath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/util/datapath.py b/lutris/util/datapath.py index e0fefce149..ce21a49e53 100644 --- a/lutris/util/datapath.py +++ b/lutris/util/datapath.py @@ -13,7 +13,7 @@ def get(): launch_path = os.path.realpath(sys.path[0]) if launch_path.startswith("/usr/local"): data_path = "/usr/local/share/lutris" - elif launch_path.startswith("/usr"): + elif launch_path.startswith("/usr") and os.path.isdir("/usr/share/lutris"): data_path = "/usr/share/lutris" elif system.path_exists(os.path.normpath(os.path.join(sys.path[0], "share"))): data_path = os.path.normpath(os.path.join(sys.path[0], "share/lutris")) From 43faca02473dcb3237390c0e8041b8a4ee2b121a Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 14 May 2025 16:55:19 -0400 Subject: [PATCH 083/126] Extent that safety check to cover /usr/local/share/lutris as well. --- lutris/util/datapath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutris/util/datapath.py b/lutris/util/datapath.py index ce21a49e53..6c85c8ccf4 100644 --- a/lutris/util/datapath.py +++ b/lutris/util/datapath.py @@ -11,7 +11,7 @@ def get(): """Return the path for the resources.""" launch_path = os.path.realpath(sys.path[0]) - if launch_path.startswith("/usr/local"): + if launch_path.startswith("/usr/local") and os.path.isdir("/usr/local/share/lutris"): data_path = "/usr/local/share/lutris" elif launch_path.startswith("/usr") and os.path.isdir("/usr/share/lutris"): data_path = "/usr/share/lutris" From 1aed8357c0d672c9d176a7a8d10257d543ebaef3 Mon Sep 17 00:00:00 2001 From: neuromancer Date: Sun, 18 May 2025 08:55:04 +0200 Subject: [PATCH 084/126] fixed two minor scummvm issues related with argument handling --- lutris/installer/commands.py | 2 +- lutris/runners/scummvm.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lutris/installer/commands.py b/lutris/installer/commands.py index 83ca976eb6..5f96d8f48b 100644 --- a/lutris/installer/commands.py +++ b/lutris/installer/commands.py @@ -565,7 +565,7 @@ def _get_scummvm_arguments(self, gog_config_path): game_id = arguments.split()[-1] arguments = " ".join(arguments.split()[:-1]) base_dir = os.path.dirname(gog_config_path) - return {"game_id": game_id, "path": base_dir, "arguments": arguments} + return {"game_id": game_id, "path": base_dir, "args": arguments} def autosetup_gog_game(self, file_id, silent=False): """Automatically guess the best way to install a GOG game by inspecting its contents. diff --git a/lutris/runners/scummvm.py b/lutris/runners/scummvm.py index 815842aacb..f2ab4c68f7 100644 --- a/lutris/runners/scummvm.py +++ b/lutris/runners/scummvm.py @@ -544,7 +544,8 @@ def play(self): args = self.game_config.get("args") or "" for arg in split_arguments(args): command.append(arg) - command.append(self.game_config.get("game_id")) + if self.game_config.get("game_id"): + command.append(self.game_config.get("game_id")) output = {"command": command} extra_libs = self.get_extra_libs() From 5c0e5d65f6ab43bcc1452336bf9d2f3593352aeb Mon Sep 17 00:00:00 2001 From: Mathieu Comandon Date: Mon, 19 May 2025 05:06:42 -0700 Subject: [PATCH 085/126] Formatting --- bin/lutris | 5 ++-- lutris/database/games.py | 2 +- lutris/exceptions.py | 2 +- lutris/gui/config/accounts_box.py | 4 +-- lutris/gui/config/boxes.py | 4 +-- lutris/gui/config/edit_category_games.py | 2 +- lutris/gui/config/runners_box.py | 2 +- lutris/gui/config/services_box.py | 2 +- lutris/gui/config/updates_box.py | 5 ++-- lutris/gui/dialogs/uninstall_dialog.py | 9 ++++--- lutris/gui/lutriswindow.py | 2 +- lutris/gui/widgets/common.py | 4 +-- lutris/installer/commands.py | 2 +- lutris/runners/commands/wine.py | 2 +- lutris/runners/dosbox.py | 4 +-- lutris/runners/easyrpg.py | 13 +++++---- lutris/runners/flatpak.py | 4 +-- lutris/runners/fsuae.py | 8 +++--- lutris/runners/linux.py | 4 +-- lutris/runners/mame.py | 12 +++------ lutris/runners/mednafen.py | 4 +-- lutris/runners/o2em.py | 2 +- lutris/runners/reicast.py | 2 +- lutris/runners/runner.py | 2 +- lutris/runners/scummvm.py | 8 +++--- lutris/runners/steam.py | 3 +-- lutris/runners/web.py | 10 +++---- lutris/runners/wine.py | 19 +++++-------- lutris/runners/zdoom.py | 2 +- lutris/services/amazon.py | 2 +- lutris/services/egs.py | 2 +- lutris/services/ubisoft.py | 2 +- lutris/startup.py | 4 +-- lutris/sysoptions.py | 23 ++++++++-------- lutris/util/display.py | 2 +- lutris/util/graphics/drivers.py | 4 +-- lutris/util/graphics/xrandr.py | 2 +- lutris/util/libretro.py | 2 +- lutris/util/steam/steamid.py | 4 +-- lutris/util/wine/fsync.py | 10 +++---- lutris/util/wine/registry.py | 3 +-- share/lutris/ui/lutris-window.ui | 34 ++---------------------- 42 files changed, 94 insertions(+), 144 deletions(-) diff --git a/bin/lutris b/bin/lutris index f3f63543b1..552e488915 100755 --- a/bin/lutris +++ b/bin/lutris @@ -12,6 +12,7 @@ # with this program. If not, see . """Main entry point for Lutris""" + import gettext import locale import os @@ -28,7 +29,7 @@ if os.environ.get("LUTRIS_ALLOW_LOCAL_PYTHON_PACKAGES") != "1": if os.path.isdir(os.path.join(LAUNCH_PATH, "../lutris")): sys.dont_write_bytecode = True - SOURCE_PATH = normpath(os.path.join(LAUNCH_PATH, '..')) + SOURCE_PATH = normpath(os.path.join(LAUNCH_PATH, "..")) sys.path.insert(0, SOURCE_PATH) else: sys.path.insert(0, os.path.normpath(os.path.join(LAUNCH_PATH, "../lib/lutris"))) @@ -50,7 +51,7 @@ try: except Exception as ex: sys.stderr.write( "Couldn't bind gettext domain, translations won't work.\n" - "LOCALE_DIR: %s\nError: %s\n" % (optional_settings.LOCALE_DIR,ex) + "LOCALE_DIR: %s\nError: %s\n" % (optional_settings.LOCALE_DIR, ex) ) except ImportError: pass diff --git a/lutris/database/games.py b/lutris/database/games.py index f803d428d1..bb8e323ce6 100644 --- a/lutris/database/games.py +++ b/lutris/database/games.py @@ -221,7 +221,7 @@ def get_used_platforms(): """Return a list of platforms currently in use""" with sql.db_cursor(settings.DB_PATH) as cursor: query = ( - "select distinct platform from games " "where platform is not null and platform is not '' order by platform" + "select distinct platform from games where platform is not null and platform is not '' order by platform" ) rows = cursor.execute(query) results = rows.fetchall() diff --git a/lutris/exceptions.py b/lutris/exceptions.py index 31ae342ad9..ba9aad7df7 100644 --- a/lutris/exceptions.py +++ b/lutris/exceptions.py @@ -122,7 +122,7 @@ class FsyncUnsupportedError(Exception): def __init__(self, message=None, *args, **kwarg): if not message: - message = _("Your kernel is not patched for fsync." " Please get a patched kernel to use fsync.") + message = _("Your kernel is not patched for fsync. Please get a patched kernel to use fsync.") super().__init__(message, *args, **kwarg) diff --git a/lutris/gui/config/accounts_box.py b/lutris/gui/config/accounts_box.py index 8639beaa7d..dffabba488 100644 --- a/lutris/gui/config/accounts_box.py +++ b/lutris/gui/config/accounts_box.py @@ -42,7 +42,7 @@ def __init__(self): self.connect("unrealize", self.on_unrealize) self.sync_frame = self._get_framed_options_list_box([self.sync_box]) - self.sync_frame.set_visible(settings.read_bool_setting("library_sync_enabled")) + self.sync_frame.set_visible(settings.read_bool_setting("library_sync_enabled", True)) self.pack_start(self.sync_frame, False, False, 0) @@ -111,7 +111,7 @@ def get_lutris_options(self): sync_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6, visible=True) sync_label = Gtk.Label(_("Keep your game library synced with Lutris.net"), visible=True) sync_switch = Gtk.Switch(visible=True) - sync_switch.set_active(settings.read_bool_setting("library_sync_enabled")) + sync_switch.set_active(settings.read_bool_setting("library_sync_enabled", default=True)) sync_switch.connect("state-set", self.on_sync_state_set) sync_box.pack_start(sync_label, False, False, 0) sync_box.pack_end(sync_switch, False, False, 0) diff --git a/lutris/gui/config/boxes.py b/lutris/gui/config/boxes.py index 81fdacd428..cf5a1b4ce2 100644 --- a/lutris/gui/config/boxes.py +++ b/lutris/gui/config/boxes.py @@ -208,7 +208,7 @@ def __init__(self, config_level: str, lutris_config: LutrisConfig, game: Game = if lutris_config.level == "game": self.generate_top_info_box( - _("If modified, these options supersede the same options from " "the base runner configuration.") + _("If modified, these options supersede the same options from the base runner configuration.") ) def generate_widgets(self): @@ -241,7 +241,7 @@ def __init__(self, config_level: str, lutris_config: LutrisConfig, **kwargs) -> ) elif runner_slug: self.generate_top_info_box( - _("If modified, these options supersede the same options from " "the global preferences.") + _("If modified, these options supersede the same options from the global preferences.") ) diff --git a/lutris/gui/config/edit_category_games.py b/lutris/gui/config/edit_category_games.py index bad4287cc9..968d11dad0 100644 --- a/lutris/gui/config/edit_category_games.py +++ b/lutris/gui/config/edit_category_games.py @@ -117,7 +117,7 @@ def on_save(self, _button: Gtk.Button) -> None: { "title": _("Merge the category '%s' into '%s'?") % (old_name, new_name), "question": _( - "If you rename this category, it will be combined with '%s'. " "Do you want to merge them?" + "If you rename this category, it will be combined with '%s'. Do you want to merge them?" ) % new_name, "parent": self, diff --git a/lutris/gui/config/runners_box.py b/lutris/gui/config/runners_box.py index e4acd4dc65..bb04cfb660 100644 --- a/lutris/gui/config/runners_box.py +++ b/lutris/gui/config/runners_box.py @@ -22,7 +22,7 @@ def __init__(self): self.add(self.get_section_label(_("Add, remove or configure runners"))) self.add( self.get_description_label( - _("Runners are programs such as emulators, engines or " "translation layers capable of running games.") + _("Runners are programs such as emulators, engines or translation layers capable of running games.") ) ) self.search_failed_label = Gtk.Label(_("No runners matched the search")) diff --git a/lutris/gui/config/services_box.py b/lutris/gui/config/services_box.py index 06324c734f..df0aa8b82e 100644 --- a/lutris/gui/config/services_box.py +++ b/lutris/gui/config/services_box.py @@ -18,7 +18,7 @@ def __init__(self): self.add(self.get_section_label(_("Enable integrations with game sources"))) self.add( self.get_description_label( - _("Access your game libraries from various sources. " "Changes require a restart to take effect.") + _("Access your game libraries from various sources. Changes require a restart to take effect.") ) ) self.frame = Gtk.Frame(visible=True, shadow_type=Gtk.ShadowType.ETCHED_IN) diff --git a/lutris/gui/config/updates_box.py b/lutris/gui/config/updates_box.py index 500d8a730f..08783fe7c2 100644 --- a/lutris/gui/config/updates_box.py +++ b/lutris/gui/config/updates_box.py @@ -93,15 +93,14 @@ def get_wine_update_texts(self) -> Tuple[str, str]: wine_version = format_runner_version(wine_version_info) if wine_version and system.path_exists(os.path.join(settings.RUNNER_DIR, "wine", wine_version)): - update_label_text = _("Your Wine version is up to date. Using: %s\n" "Last checked %s.") % ( + update_label_text = _("Your Wine version is up to date. Using: %s\nLast checked %s.") % ( wine_version_info["version"], get_runtime_versions_date_time_ago(), ) update_button_text = _("Check Again") elif not system.path_exists(os.path.join(settings.RUNNER_DIR, "wine")): update_label_text = ( - _("You don't have any Wine version installed.\n" "We recommend %s") - % wine_version_info["version"] + _("You don't have any Wine version installed.\nWe recommend %s") % wine_version_info["version"] ) update_button_text = _("Download %s") % wine_version_info["version"] else: diff --git a/lutris/gui/dialogs/uninstall_dialog.py b/lutris/gui/dialogs/uninstall_dialog.py index ad54f8c064..b06445cec3 100644 --- a/lutris/gui/dialogs/uninstall_dialog.py +++ b/lutris/gui/dialogs/uninstall_dialog.py @@ -150,7 +150,7 @@ def update_message(self) -> None: ) ) else: - messages.append(_("After you remove these games, they will no longer " "appear in the 'Games' view.")) + messages.append(_("After you remove these games, they will no longer appear in the 'Games' view.")) if self.any_shared: messages.append( @@ -258,7 +258,7 @@ def on_remove_button_clicked(self, _widget) -> None: if dirs_to_delete: if len(dirs_to_delete) == 1: - question = _("Please confirm.\nEverything under %s\n" "will be moved to the trash.") % gtk_safe( + question = _("Please confirm.\nEverything under %s\nwill be moved to the trash.") % gtk_safe( dirs_to_delete[0] ) else: @@ -278,7 +278,8 @@ def on_remove_button_clicked(self, _widget) -> None: return games_removed_from_library = [] - if settings.read_bool_setting("library_sync_enabled"): + library_sync_enabled = settings.read_bool_setting("library_sync_enabled", True) + if library_sync_enabled: library_syncer = LibrarySyncer() for row in rows: if row.remove_from_library: @@ -289,7 +290,7 @@ def on_remove_button_clicked(self, _widget) -> None: for row in rows: row.perform_removal() - if settings.read_bool_setting("library_sync_enabled") and games_removed_from_library: + if library_sync_enabled and games_removed_from_library: library_syncer.delete_from_remote_library(games_removed_from_library) self.parent.on_game_removed() self.destroy() diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py index c1eed65dea..47d73adeed 100644 --- a/lutris/gui/lutriswindow.py +++ b/lutris/gui/lutriswindow.py @@ -310,7 +310,7 @@ def on_library_synced(_result, error): if not error: sync_media() - if settings.read_bool_setting("library_sync_enabled"): + if settings.read_bool_setting("library_sync_enabled", True): AsyncCall(LibrarySyncer().sync_local_library, on_library_synced if force else None, force=force) def update_action_state(self): diff --git a/lutris/gui/widgets/common.py b/lutris/gui/widgets/common.py index 9725b7dda3..903228f801 100644 --- a/lutris/gui/widgets/common.py +++ b/lutris/gui/widgets/common.py @@ -249,7 +249,7 @@ def on_entry_changed(self, widget): if self.warn_if_non_empty and os.path.exists(path) and os.listdir(path): non_empty_label = Gtk.Label(visible=True) non_empty_label.set_markup( - _("Warning! The selected path " "contains files. Installation will not work properly.") + _("Warning! The selected path contains files. Installation will not work properly.") ) self.pack_end(non_empty_label, False, False, 10) if self.warn_if_non_writable_parent: @@ -257,7 +257,7 @@ def on_entry_changed(self, widget): if parent is not None and not os.access(parent, os.W_OK): non_writable_destination_label = Gtk.Label(visible=True) non_writable_destination_label.set_markup( - _("Warning The destination folder " "is not writable by the current user.") + _("Warning The destination folder is not writable by the current user.") ) self.pack_end(non_writable_destination_label, False, False, 10) diff --git a/lutris/installer/commands.py b/lutris/installer/commands.py index 5f96d8f48b..74a944efc7 100644 --- a/lutris/installer/commands.py +++ b/lutris/installer/commands.py @@ -105,7 +105,7 @@ def execute(self, data): self._check_required_params([("file", "command")], data, "execute") if "command" in data and "file" in data: raise ScriptingError( - _("Parameters file and command can't be used " "at the same time for the execute command"), + _("Parameters file and command can't be used at the same time for the execute command"), data, ) diff --git a/lutris/runners/commands/wine.py b/lutris/runners/commands/wine.py index b511c756a5..93bf7571d8 100644 --- a/lutris/runners/commands/wine.py +++ b/lutris/runners/commands/wine.py @@ -200,7 +200,7 @@ def create_prefix( wineboot_path = os.path.join(os.path.dirname(wine_path), "wineboot") if not system.path_exists(wineboot_path): logger.error( - "No wineboot executable found in %s, " "your wine installation is most likely broken", + "No wineboot executable found in %s, your wine installation is most likely broken", wine_path, ) return diff --git a/lutris/runners/dosbox.py b/lutris/runners/dosbox.py index 3c3d8d231d..56c9d2f481 100644 --- a/lutris/runners/dosbox.py +++ b/lutris/runners/dosbox.py @@ -54,9 +54,7 @@ class dosbox(Runner): "label": _("Working directory"), "warn_if_non_writable_parent": True, "help": _( - "The location where the game is run from.\n" - "By default, Lutris uses the directory of the " - "executable." + "The location where the game is run from.\nBy default, Lutris uses the directory of the executable." ), }, ] diff --git a/lutris/runners/easyrpg.py b/lutris/runners/easyrpg.py index c788b29263..c5ed152ebd 100644 --- a/lutris/runners/easyrpg.py +++ b/lutris/runners/easyrpg.py @@ -30,8 +30,7 @@ class easyrpg(Runner): "advanced": True, "label": _("Encoding"), "help": _( - "Instead of auto detecting the encoding or using the " - "one in RPG_RT.ini, the specified encoding is used." + "Instead of auto detecting the encoding or using the one in RPG_RT.ini, the specified encoding is used." ), "choices": [ (_("Auto"), ""), @@ -114,7 +113,7 @@ class easyrpg(Runner): "option": "load_game_id", "type": "range", "label": _("Load game ID"), - "help": _("Skip the title scene and load SaveXX.lsd.\n" "Set to 0 to disable."), + "help": _("Skip the title scene and load SaveXX.lsd.\nSet to 0 to disable."), "min": 0, "max": 99, "default": 0, @@ -249,7 +248,7 @@ class easyrpg(Runner): "advanced": True, "section": _("Engine"), "label": _("RNG seed"), - "help": _("Seeds the random number generator.\n" "Use -1 to disable."), + "help": _("Seeds the random number generator.\nUse -1 to disable."), "min": -1, "max": 2147483647, "default": -1, @@ -305,7 +304,7 @@ class easyrpg(Runner): "advanced": True, "label": _("Game resolution"), "help": _( - "Force a different game resolution.\n\n" "This is experimental and can cause glitches or break games!" + "Force a different game resolution.\n\nThis is experimental and can cause glitches or break games!" ), "choices": [ (_("320×240 (4:3, Original)"), "original"), @@ -388,14 +387,14 @@ class easyrpg(Runner): "type": "directory", "section": _("Runtime Package"), "label": _("RPG2000 RTP location"), - "help": _("Full path to a directory containing an extracted " "RPG Maker 2000 Run-Time-Package (RTP)."), + "help": _("Full path to a directory containing an extracted RPG Maker 2000 Run-Time-Package (RTP)."), }, { "option": "rpg2k3_rtp_path", "type": "directory", "section": _("Runtime Package"), "label": _("RPG2003 RTP location"), - "help": _("Full path to a directory containing an extracted " "RPG Maker 2003 Run-Time-Package (RTP)."), + "help": _("Full path to a directory containing an extracted RPG Maker 2003 Run-Time-Package (RTP)."), }, { "option": "rpg_rtp_path", diff --git a/lutris/runners/flatpak.py b/lutris/runners/flatpak.py index 644835b6a8..3361569805 100644 --- a/lutris/runners/flatpak.py +++ b/lutris/runners/flatpak.py @@ -38,7 +38,7 @@ class flatpak(Runner): "type": "string", "label": _("Architecture"), "help": _( - "The architecture to run. " "See flatpak --supported-arches for architectures supported by the host." + "The architecture to run. See flatpak --supported-arches for architectures supported by the host." ), "advanced": True, }, @@ -141,7 +141,7 @@ def play(self): if appid.count(".") < 2: raise GameConfigError( - _("Application ID is not specified in correct format." "Must be something like: tld.domain.app") + _("Application ID is not specified in correct format.Must be something like: tld.domain.app") ) if any(x in appid for x in ("--", "/")): diff --git a/lutris/runners/fsuae.py b/lutris/runners/fsuae.py index 90fd651bdc..2b1bf3eb3d 100644 --- a/lutris/runners/fsuae.py +++ b/lutris/runners/fsuae.py @@ -257,7 +257,7 @@ class fsuae(Runner): "label": _("Scanlines display style"), "type": "bool", "default": False, - "help": _("Activates a display filter adding scanlines to imitate " "the displays of yesteryear."), + "help": _("Activates a display filter adding scanlines to imitate the displays of yesteryear."), }, { "option": "grafixcard", @@ -328,7 +328,7 @@ class fsuae(Runner): "choices": flsound_choices, "default": "0", "advanced": True, - "help": _("Set volume to 0 to disable floppy drive clicks " "when the drive is empty. Max volume is 100."), + "help": _("Set volume to 0 to disable floppy drive clicks when the drive is empty. Max volume is 100."), }, { "option": "fdspeed", @@ -358,7 +358,7 @@ class fsuae(Runner): "type": "bool", "default": False, "advanced": True, - "help": _("Automatically uses Feral GameMode daemon if available. " "Set to true to disable the feature."), + "help": _("Automatically uses Feral GameMode daemon if available. Set to true to disable the feature."), }, { "option": "govwarning", @@ -367,7 +367,7 @@ class fsuae(Runner): "default": False, "advanced": True, "help": _( - "Warn if running with a CPU governor other than performance. " "Set to true to disable the warning." + "Warn if running with a CPU governor other than performance. Set to true to disable the warning." ), }, { diff --git a/lutris/runners/linux.py b/lutris/runners/linux.py index 46c71a4cac..f9f1773cef 100644 --- a/lutris/runners/linux.py +++ b/lutris/runners/linux.py @@ -38,9 +38,7 @@ class linux(Runner): "type": "directory", "label": _("Working directory"), "help": _( - "The location where the game is run from.\n" - "By default, Lutris uses the directory of the " - "executable." + "The location where the game is run from.\nBy default, Lutris uses the directory of the executable." ), }, { diff --git a/lutris/runners/mame.py b/lutris/runners/mame.py index 72dc7c9b78..3f5e8997d9 100644 --- a/lutris/runners/mame.py +++ b/lutris/runners/mame.py @@ -137,9 +137,7 @@ class mame(Runner): # pylint: disable=invalid-name "type": "string", "section": _("Autoboot"), "label": _("Autoboot command"), - "help": _( - "Autotype this command when the system has started, " "an enter keypress is automatically added." - ), + "help": _("Autotype this command when the system has started, an enter keypress is automatically added."), }, { "option": "autoboot_delay", @@ -174,7 +172,7 @@ class mame(Runner): # pylint: disable=invalid-name "type": "bool", "section": _("Graphics"), "label": _("CRT effect ()"), - "help": _("Applies a CRT effect to the screen." "Requires OpenGL renderer."), + "help": _("Applies a CRT effect to the screen.Requires OpenGL renderer."), "default": False, }, { @@ -205,9 +203,7 @@ class mame(Runner): # pylint: disable=invalid-name "type": "bool", "section": _("Graphics"), "label": _("Wait for VSync"), - "help": _( - "Enable waiting for the start of vblank before " "flipping screens; reduces tearing effects." - ), + "help": _("Enable waiting for the start of vblank before flipping screens; reduces tearing effects."), "advanced": True, "default": False, }, @@ -229,7 +225,7 @@ class mame(Runner): # pylint: disable=invalid-name ], "default": "SCRLOCK", "advanced": True, - "help": _("Key to switch between Full Keyboard Mode and " "Partial Keyboard Mode (default: Scroll Lock)"), + "help": _("Key to switch between Full Keyboard Mode and Partial Keyboard Mode (default: Scroll Lock)"), }, ] diff --git a/lutris/runners/mednafen.py b/lutris/runners/mednafen.py index 3460b24c93..5a91c20a03 100644 --- a/lutris/runners/mednafen.py +++ b/lutris/runners/mednafen.py @@ -57,9 +57,7 @@ class mednafen(Runner): "option": "main_file", "type": "file", "label": _("ROM file"), - "help": _( - "The game data, commonly called a ROM image. \n" "Mednafen supports GZIP and ZIP compressed ROMs." - ), + "help": _("The game data, commonly called a ROM image. \nMednafen supports GZIP and ZIP compressed ROMs."), }, { "option": "machine", diff --git a/lutris/runners/o2em.py b/lutris/runners/o2em.py index 4cf90bca19..8013b210e6 100644 --- a/lutris/runners/o2em.py +++ b/lutris/runners/o2em.py @@ -86,7 +86,7 @@ class o2em(Runner): "section": _("Graphics"), "label": _("Scanlines display style"), "default": False, - "help": _("Activates a display filter adding scanlines to imitate " "the displays of yesteryear."), + "help": _("Activates a display filter adding scanlines to imitate the displays of yesteryear."), }, ] diff --git a/lutris/runners/reicast.py b/lutris/runners/reicast.py index bdc2f00fec..3d7352ad39 100644 --- a/lutris/runners/reicast.py +++ b/lutris/runners/reicast.py @@ -26,7 +26,7 @@ class reicast(Runner): "option": "iso", "type": "file", "label": _("Disc image file"), - "help": _("The game data.\n" "Supported formats: ISO, CDI"), + "help": _("The game data.\nSupported formats: ISO, CDI"), } ] diff --git a/lutris/runners/runner.py b/lutris/runners/runner.py index 0e3853e9f6..3a1ff0af34 100644 --- a/lutris/runners/runner.py +++ b/lutris/runners/runner.py @@ -471,7 +471,7 @@ def install_dialog(self, ui_delegate): """ if ui_delegate.show_install_yesno_inquiry( - question=_("The required runner is not installed.\n" "Do you wish to install it now?"), + question=_("The required runner is not installed.\nDo you wish to install it now?"), title=_("Required runner unavailable"), ): if hasattr(self, "get_version"): diff --git a/lutris/runners/scummvm.py b/lutris/runners/scummvm.py index f2ab4c68f7..48d55d9cd6 100644 --- a/lutris/runners/scummvm.py +++ b/lutris/runners/scummvm.py @@ -154,7 +154,7 @@ class scummvm(Runner): ], "warning": _get_opengl_warning, "help": _( - "The algorithm used to scale up the game's base " "resolution, resulting in different visual styles. " + "The algorithm used to scale up the game's base resolution, resulting in different visual styles. " ), }, { @@ -282,8 +282,7 @@ class scummvm(Runner): "type": "string", "label": _("Engine speed"), "help": _( - "Sets frames per second limit (0 - 100) for Grim Fandango " - "or Escape from Monkey Island (default: 60)." + "Sets frames per second limit (0 - 100) for Grim Fandango or Escape from Monkey Island (default: 60)." ), "advanced": True, }, @@ -361,8 +360,7 @@ class scummvm(Runner): ("rwopl3", "rwopl3"), ], "help": _( - "Chooses which emulator is used by ScummVM when the AdLib emulator " - "is chosen as the Preferred device." + "Chooses which emulator is used by ScummVM when the AdLib emulator is chosen as the Preferred device." ), "advanced": True, }, diff --git a/lutris/runners/steam.py b/lutris/runners/steam.py index de78faa75c..f6928a4037 100644 --- a/lutris/runners/steam.py +++ b/lutris/runners/steam.py @@ -48,8 +48,7 @@ class steam(Runner): "type": "string", "label": _("Arguments"), "help": _( - "Command line arguments used when launching the game.\n" - "Ignored when Steam Big Picture mode is enabled." + "Command line arguments used when launching the game.\nIgnored when Steam Big Picture mode is enabled." ), }, { diff --git a/lutris/runners/web.py b/lutris/runners/web.py index 23df72da61..73d41a7768 100644 --- a/lutris/runners/web.py +++ b/lutris/runners/web.py @@ -76,7 +76,7 @@ class web(Runner): "label": _("Disable menu bar and default shortcuts"), "type": "bool", "default": False, - "help": _("This also disables default keyboard shortcuts, " "like copy/paste and fullscreen toggling."), + "help": _("This also disables default keyboard shortcuts, like copy/paste and fullscreen toggling."), }, { "option": "disable_scrolling", @@ -90,7 +90,7 @@ class web(Runner): "label": _("Hide mouse cursor"), "type": "bool", "default": False, - "help": _("Prevents the mouse cursor from showing " "when hovering above the window."), + "help": _("Prevents the mouse cursor from showing when hovering above the window."), }, { "option": "open_links", @@ -107,7 +107,7 @@ class web(Runner): "label": _("Remove default margin & padding"), "type": "bool", "default": False, - "help": _("Sets margin and padding to zero " "on <html> and <body> elements."), + "help": _("Sets margin and padding to zero on <html> and <body> elements."), }, { "option": "enable_flash", @@ -173,14 +173,14 @@ def get_env(self, os_env=True, disable_runtime=False): def play(self): url = self.game_config.get("main_file") if not url: - raise GameConfigError(_("The web address is empty, \n" "verify the game's configuration.")) + raise GameConfigError(_("The web address is empty, \nverify the game's configuration.")) # check if it's an url or a file is_url = urlparse(url).scheme != "" if not is_url: if not system.path_exists(url): - raise GameConfigError(_("The file %s does not exist, \n" "verify the game's configuration.") % url) + raise GameConfigError(_("The file %s does not exist, \nverify the game's configuration.") % url) url = "file://" + url game_data = get_game_by_field(self.config.game_config_id, "configpath") diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index 510e695f16..44115610f3 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -125,8 +125,7 @@ def _get_simple_vulkan_support_error(option_key: str, config: LutrisConfig, feat return None if config.runner_config.get(option_key) and not LINUX_SYSTEM.is_vulkan_supported(): return ( - _("Error Vulkan is not installed or is not supported by your system, " "%s is not available.") - % feature + _("Error Vulkan is not installed or is not supported by your system, %s is not available.") % feature ) return None @@ -280,9 +279,7 @@ class wine(Runner): "type": "directory", "label": _("Working directory"), "help": _( - "The location where the game is run from.\n" - "By default, Lutris uses the directory of the " - "executable." + "The location where the game is run from.\nBy default, Lutris uses the directory of the executable." ), }, { @@ -336,7 +333,7 @@ class wine(Runner): "label": _("Custom Wine executable"), "type": "file", "advanced": True, - "help": _("The Wine executable to be used if you have " 'selected "Custom" as the Wine version.'), + "help": _('The Wine executable to be used if you have selected "Custom" as the Wine version.'), }, { "option": "system_winetricks", @@ -381,9 +378,7 @@ class wine(Runner): "error": lambda k, c: _get_simple_vulkan_support_error(k, c, _("VKD3D")), "default": True, "active": True, - "help": _( - "Use VKD3D to enable support for Direct3D 12 " "applications by translating their calls to Vulkan." - ), + "help": _("Use VKD3D to enable support for Direct3D 12 applications by translating their calls to Vulkan."), }, { "option": "vkd3d_version", @@ -605,7 +600,7 @@ class wine(Runner): ], "default": "auto", "help": _( - "Which audio backend to use.\n" "By default, Wine automatically picks the right one " "for your system." + "Which audio backend to use.\nBy default, Wine automatically picks the right one for your system." ), }, { @@ -626,7 +621,7 @@ class wine(Runner): (_("Full (CAUTION: Will cause MASSIVE slowdown)"), "+all"), ], "default": "-all", - "help": _("Output debugging information in the game log " "(might affect performance)"), + "help": _("Output debugging information in the game log (might affect performance)"), }, { "option": "ShowCrashDialog", @@ -641,7 +636,7 @@ class wine(Runner): "label": _("Autoconfigure joypads"), "advanced": True, "default": False, - "help": _("Automatically disables one of Wine's detected joypad " "to avoid having 2 controllers detected"), + "help": _("Automatically disables one of Wine's detected joypad to avoid having 2 controllers detected"), }, ] diff --git a/lutris/runners/zdoom.py b/lutris/runners/zdoom.py index 1548cb2bfc..893923ef2b 100644 --- a/lutris/runners/zdoom.py +++ b/lutris/runners/zdoom.py @@ -32,7 +32,7 @@ class zdoom(Runner): "option": "files", "type": "multiple_file", "label": _("PWAD files"), - "help": _("Used to load one or more PWAD files which generally contain " "user-created levels."), + "help": _("Used to load one or more PWAD files which generally contain user-created levels."), }, { "option": "warp", diff --git a/lutris/services/amazon.py b/lutris/services/amazon.py index e921b67291..035c5169d5 100644 --- a/lutris/services/amazon.py +++ b/lutris/services/amazon.py @@ -516,7 +516,7 @@ def get_file_patch(self, access_token, game_id, version, file_hashes): try: return self.request_sds( - "com.amazonaws.gearbox." "softwaredistribution.service.model." "SoftwareDistributionService.GetPatches", + "com.amazonaws.gearbox.softwaredistribution.service.model.SoftwareDistributionService.GetPatches", access_token, request_data, ) diff --git a/lutris/services/egs.py b/lutris/services/egs.py index eb7f0cf2f0..85af7c2f5a 100644 --- a/lutris/services/egs.py +++ b/lutris/services/egs.py @@ -416,4 +416,4 @@ def install(self, db_game): def get_launch_arguments(app_name, action="launch"): - return ("-opengl" " -SkipBuildPatchPrereq" " -com.epicgames.launcher://apps/%s?action=%s") % (app_name, action) + return ("-opengl -SkipBuildPatchPrereq -com.epicgames.launcher://apps/%s?action=%s") % (app_name, action) diff --git a/lutris/services/ubisoft.py b/lutris/services/ubisoft.py index 7d970278b5..9b4ba4b974 100644 --- a/lutris/services/ubisoft.py +++ b/lutris/services/ubisoft.py @@ -130,7 +130,7 @@ def get_configurations(self): return base_dir = ubi_game["directory"] configurations_path = os.path.join( - base_dir, "drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/" "cache/configuration/configurations" + base_dir, "drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/cache/configuration/configurations" ) if not os.path.exists(configurations_path): return diff --git a/lutris/startup.py b/lutris/startup.py index 6d0ceda445..d00dbcad73 100644 --- a/lutris/startup.py +++ b/lutris/startup.py @@ -73,7 +73,7 @@ def check_vulkan(): library_api_version = vkquery.get_vulkan_api_version() if library_api_version and library_api_version < required_api_version: logger.warning( - "Vulkan reports an API version of %s. " "%s is required for the latest DXVK.", + "Vulkan reports an API version of %s. %s is required for the latest DXVK.", vkquery.format_version(library_api_version), vkquery.format_version(required_api_version), ) @@ -82,7 +82,7 @@ def check_vulkan(): if devices and devices[0].api_version < required_api_version: logger.warning( - "Vulkan reports that the '%s' device has API version of %s. " "%s is required for the latest DXVK.", + "Vulkan reports that the '%s' device has API version of %s. %s is required for the latest DXVK.", devices[0].name, vkquery.format_version(devices[0].api_version), vkquery.format_version(required_api_version), diff --git a/lutris/sysoptions.py b/lutris/sysoptions.py index 3138f49dab..13e0eabbb8 100644 --- a/lutris/sysoptions.py +++ b/lutris/sysoptions.py @@ -110,7 +110,7 @@ def get_output_list(): "type": "bool", "label": _("Prefer system libraries"), "default": True, - "help": _("When the runtime is enabled, prioritize the system libraries" " over the provided ones."), + "help": _("When the runtime is enabled, prioritize the system libraries over the provided ones."), }, { "section": _("Display"), @@ -154,7 +154,7 @@ def get_output_list(): "advanced": True, "available": is_display_x11, "condition": is_compositing_enabled(), - "help": _("Disable desktop effects while game is running, " "reducing stuttering and increasing performance"), + "help": _("Disable desktop effects while game is running, reducing stuttering and increasing performance"), }, { "section": _("Display"), @@ -219,7 +219,7 @@ def get_output_list(): "label": _("Enable Gamescope"), "default": False, "condition": system.can_find_executable("gamescope") and linux.LINUX_SYSTEM.nvidia_gamescope_support(), - "help": _("Use gamescope to draw the game window isolated from your desktop.\n" "Toggle fullscreen: Super + F"), + "help": _("Use gamescope to draw the game window isolated from your desktop.\nToggle fullscreen: Super + F"), }, { "section": _("Gamescope"), @@ -230,7 +230,7 @@ def get_output_list(): "default": False, "conditional_on": "gamescope", "condition": bool(system.can_find_executable("gamescope")), - "help": _("Enable HDR for games that support it.\n" "Requires Plasma 6 and VK_hdr_layer."), + "help": _("Enable HDR for games that support it.\nRequires Plasma 6 and VK_hdr_layer."), }, { "section": _("Gamescope"), @@ -271,7 +271,7 @@ def get_output_list(): "choices": DISPLAY_MANAGER.get_resolutions, "conditional_on": "gamescope", "condition": system.can_find_executable("gamescope"), - "help": _("Set the maximum resolution used by the game.\n" "\n" "Custom Resolutions: (width)x(height)"), + "help": _("Set the maximum resolution used by the game.\n\nCustom Resolutions: (width)x(height)"), }, { "section": _("Gamescope"), @@ -286,7 +286,7 @@ def get_output_list(): "default": "-f", "conditional_on": "gamescope", "condition": system.can_find_executable("gamescope"), - "help": _("Run gamescope in fullscreen, windowed or borderless mode\n" "Toggle fullscreen : Super + F"), + "help": _("Run gamescope in fullscreen, windowed or borderless mode\nToggle fullscreen : Super + F"), }, { "section": _("Gamescope"), @@ -297,7 +297,7 @@ def get_output_list(): "conditional_on": "gamescope", "condition": system.can_find_executable("gamescope"), "help": _( - "Use AMD FidelityFX™ Super Resolution 1.0 for upscaling.\n" "Upscaler sharpness from 0 (max) to 20 (min)." + "Use AMD FidelityFX™ Super Resolution 1.0 for upscaling.\nUpscaler sharpness from 0 (max) to 20 (min)." ), }, { @@ -319,7 +319,7 @@ def get_output_list(): "conditional_on": "gamescope", "condition": system.can_find_executable("gamescope"), "help": _( - "Set additional flags for gamescope (if available).\n" "See 'gamescope --help' for a full list of options." + "Set additional flags for gamescope (if available).\nSee 'gamescope --help' for a full list of options." ), }, { @@ -356,7 +356,7 @@ def get_output_list(): "default": False, "advanced": True, "condition": system.can_find_executable("pulseaudio") or system.can_find_executable("pipewire-pulse"), - "help": _("Set the environment variable PULSE_LATENCY_MSEC=60 " "to improve audio quality on some games"), + "help": _("Set the environment variable PULSE_LATENCY_MSEC=60 to improve audio quality on some games"), }, { "section": _("Input"), @@ -383,8 +383,7 @@ def get_output_list(): "label": _("SDL2 gamepad mapping"), "advanced": True, "help": _( - "SDL_GAMECONTROLLERCONFIG mapping string or path to a custom " - "gamecontrollerdb.txt file containing mappings." + "SDL_GAMECONTROLLERCONFIG mapping string or path to a custom gamecontrollerdb.txt file containing mappings." ), }, { @@ -436,7 +435,7 @@ def get_output_list(): "type": "string", "label": _("Command prefix"), "advanced": True, - "help": _("Command line instructions to add in front of the game's " "execution command."), + "help": _("Command line instructions to add in front of the game's execution command."), }, { "section": _("Game execution"), diff --git a/lutris/util/display.py b/lutris/util/display.py index e05bc72f63..3a32904592 100644 --- a/lutris/util/display.py +++ b/lutris/util/display.py @@ -658,7 +658,7 @@ def _get_screen_saver_inhibitor(): inhibitor.set_dbus_iface(name, path, interface) except GLib.Error as err: logger.warning( - "Failed to set up a DBus proxy for name %s, path %s, " "interface %s: %s", name, path, interface, err + "Failed to set up a DBus proxy for name %s, path %s, interface %s: %s", name, path, interface, err ) return inhibitor diff --git a/lutris/util/graphics/drivers.py b/lutris/util/graphics/drivers.py index d7f896a7f0..d6d31212fc 100644 --- a/lutris/util/graphics/drivers.py +++ b/lutris/util/graphics/drivers.py @@ -159,13 +159,13 @@ def is_nvidia() -> bool: try: return os.path.exists("/proc/driver/nvidia") except OSError: - logger.info("Could not determine whether /proc/driver/nvidia exists. " "Falling back to alternative method") + logger.info("Could not determine whether /proc/driver/nvidia exists. Falling back to alternative method") try: with open("/proc/modules", encoding="utf-8") as f: modules = f.read() return bool(re.search(r"^nvidia ", modules, flags=re.MULTILINE)) except OSError: - logger.error("Could not access /proc/modules to find the Nvidia drivers. " "Nvidia card may not be detected.") + logger.error("Could not access /proc/modules to find the Nvidia drivers. Nvidia card may not be detected.") glx_info = GlxInfo() return "NVIDIA" in glx_info.opengl_vendor # type: ignore[attr-defined] diff --git a/lutris/util/graphics/xrandr.py b/lutris/util/graphics/xrandr.py index 3b1cf8c4f8..cbe38f682a 100644 --- a/lutris/util/graphics/xrandr.py +++ b/lutris/util/graphics/xrandr.py @@ -51,7 +51,7 @@ def get_outputs(): # pylint: disable=too-many-locals position = "{x_pos}x{y_pos}".format(x_pos=x_pos, y_pos=y_pos) except ValueError as ex: logger.error( - "Unhandled xrandr line %s, error: %s. " "Please send your xrandr output to the dev team", line, ex + "Unhandled xrandr line %s, error: %s. Please send your xrandr output to the dev team", line, ex ) continue elif "*" in line: diff --git a/lutris/util/libretro.py b/lutris/util/libretro.py index af8655aae6..52f1346295 100644 --- a/lutris/util/libretro.py +++ b/lutris/util/libretro.py @@ -22,7 +22,7 @@ def config(self): return self._config except UnicodeDecodeError: logger.error( - "The Retroarch config in %s could not " "be read because of character encoding issues", self.config_path + "The Retroarch config in %s could not be read because of character encoding issues", self.config_path ) return [] diff --git a/lutris/util/steam/steamid.py b/lutris/util/steam/steamid.py index 227b9cd998..61d831e657 100644 --- a/lutris/util/steam/steamid.py +++ b/lutris/util/steam/steamid.py @@ -160,7 +160,7 @@ def from_community_url(cls, steam_id, universe=UNIVERSE_INDIVIDUAL): match = COMMUNITY32_REGEX.match(url.path) if match: if match.group("path") not in TYPE_URL_PATH_MAP[LETTER_TYPE_MAP[match.group("type")]]: - warnings.warn("Community URL ({}) path doesn't " "match type character".format(url.path)) + warnings.warn("Community URL ({}) path doesn't match type character".format(url.path)) steamid = int(match.group("steamid")) instance = steamid & 1 account_number = (steamid - instance) / 2 @@ -306,7 +306,7 @@ def as_32(self): return "[{}:1:{}]".format(TYPE_LETTER_MAP[self.account_type], self.get_32_bit_community_id()) except KeyError as ex: raise SteamIDError( - "Cannot create 32-bit indentifier for " "SteamID with type {}".format(self.type_name) + "Cannot create 32-bit indentifier for SteamID with type {}".format(self.type_name) ) from ex def get_32_bit_community_id(self): diff --git a/lutris/util/wine/fsync.py b/lutris/util/wine/fsync.py index 3c687c6eed..d40916d91a 100644 --- a/lutris/util/wine/fsync.py +++ b/lutris/util/wine/fsync.py @@ -88,25 +88,25 @@ def _get_syscall_nr_from_headers(syscall_name): close_fds=True, universal_newlines=True, ) as popen: - stdout, stderr = popen.communicate("#include \n" "__NR_" + syscall_name + "\n") + stdout, stderr = popen.communicate("#include \n__NR_" + syscall_name + "\n") except FileNotFoundError as ex: raise RuntimeError( - "failed to determine " + syscall_name + " syscall number: " "cpp not installed or not in PATH" + "failed to determine " + syscall_name + " syscall number: cpp not installed or not in PATH" ) from ex if popen.returncode: raise RuntimeError( - "failed to determine " + syscall_name + " syscall number: " "cpp returned nonzero exit code", stderr + "failed to determine " + syscall_name + " syscall number: cpp returned nonzero exit code", stderr ) if not stdout: - raise RuntimeError("failed to determine " + syscall_name + " syscall number: " "no output from cpp") + raise RuntimeError("failed to determine " + syscall_name + " syscall number: no output from cpp") last_line = stdout.splitlines()[-1] if last_line == "__NR_futex": raise RuntimeError( - "failed to determine " + syscall_name + " syscall number: " "__NR_" + syscall_name + " not expanded" + "failed to determine " + syscall_name + " syscall number: __NR_" + syscall_name + " not expanded" ) try: diff --git a/lutris/util/wine/registry.py b/lutris/util/wine/registry.py index 2a3a99231e..5c680f50ad 100644 --- a/lutris/util/wine/registry.py +++ b/lutris/util/wine/registry.py @@ -154,8 +154,7 @@ def save(self, path=None): prefix_path = os.path.dirname(path) if not os.path.isdir(prefix_path): raise OSError( - "Invalid Wine prefix path %s, make sure to " - "create the prefix before saving to a registry" % prefix_path + "Invalid Wine prefix path %s, make sure to create the prefix before saving to a registry" % prefix_path ) with open(path, "w", encoding="utf-8") as registry_file: registry_file.write(self.render()) diff --git a/share/lutris/ui/lutris-window.ui b/share/lutris/ui/lutris-window.ui index fff4434448..ee3add789e 100644 --- a/share/lutris/ui/lutris-window.ui +++ b/share/lutris/ui/lutris-window.ui @@ -105,48 +105,18 @@ True False - 6 True False - Login to <a href="https://lutris.net/">Lutris.net</a> to view your game library - True - - - False - True - 0 - - - - - True - False - <a href="">Login</a> + <a href="">Login</a> to Lutris.net to sync your game library True False True - end - 1 - - - - - True - False - <a href="">Turn on Library Sync</a> - True - - - - False - True - end - 2 + 0