From 722ac0cf338d659e2d0553431285fbfb0cd978b5 Mon Sep 17 00:00:00 2001 From: Janleyx <242423638+Janleyx@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:08:42 -0400 Subject: add reproducible Armada ARM64 support --- py_modules/lsfg_vk/installation.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'py_modules') diff --git a/py_modules/lsfg_vk/installation.py b/py_modules/lsfg_vk/installation.py index 4f01be1..29259f7 100644 --- a/py_modules/lsfg_vk/installation.py +++ b/py_modules/lsfg_vk/installation.py @@ -53,7 +53,7 @@ class InstallationService(BaseService): if self._is_arm_architecture(): self.log.info("Detected ARM architecture, using ARM binary") arm_so_path = plugin_dir / BIN_DIR / ARM_LIB_FILENAME - shutil.copy2(arm_so_path, self.lib_file) + self._copy_plugin_file(arm_so_path, self.lib_file) self.log.info(f"Overwrote with ARM binary: {self.lib_file}") self._create_config_file() @@ -78,7 +78,35 @@ class InstallationService(BaseService): Returns: True if running on ARM (aarch64), False otherwise """ - return platform.machine().lower() == 'aarch64' + if platform.machine().lower() in ('aarch64', 'arm64'): + return True + + # Decky runs through FEX on Armada, so Python reports x86_64 even + # though the host is AArch64. Armada exposes this native helper only + # on its ARM image, including inside Decky's FEX rootfs. + if Path('/usr/libexec/armada/device-env').is_file(): + self.log.info("Detected native AArch64 Armada host through device-env") + return True + + # Fall back to the native PID 1 ELF header. e_machine 183 is AArch64. + try: + with Path('/proc/1/exe').open('rb') as host_executable: + elf_header = host_executable.read(20) + if elf_header[:4] == b'\x7fELF' and elf_header[5] in (1, 2): + byte_order = 'little' if elf_header[5] == 1 else 'big' + if int.from_bytes(elf_header[18:20], byte_order) == 183: + self.log.info("Detected native AArch64 host through PID 1") + return True + except OSError as e: + self.log.debug(f"Could not inspect native host architecture: {e}") + + return False + + @staticmethod + def _copy_plugin_file(src_file: Path, dst_file: Path) -> None: + """Copy plugin content without preserving FEX-incompatible metadata.""" + shutil.copyfile(src_file, dst_file) + dst_file.chmod(0o644) def _extract_and_install_files(self, zip_path: Path) -> None: """Extract zip file and install files to appropriate locations @@ -117,7 +145,7 @@ class InstallationService(BaseService): if file_path.suffix == JSON_EXT and file == JSON_FILENAME: self._copy_and_fix_json_file(src_file, dst_file) else: - shutil.copy2(src_file, dst_file) + self._copy_plugin_file(src_file, dst_file) self.log.info(f"Copied {file} to {dst_file}") @@ -147,7 +175,7 @@ class InstallationService(BaseService): except (json.JSONDecodeError, KeyError, OSError) as e: self.log.error(f"Error fixing JSON file {src_file}: {e}") # Fallback to simple copy if JSON modification fails - shutil.copy2(src_file, dst_file) + self._copy_plugin_file(src_file, dst_file) def _create_config_file(self) -> None: """Create or update the TOML config file in ~/.config/lsfg-vk with default configuration and detected DLL path -- cgit v1.2.3 From e205effa83dbbcb9c13f3f11a493a525e989d7b6 Mon Sep 17 00:00:00 2001 From: Janleyx <242423638+Janleyx@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:20:20 -0400 Subject: preserve Armada game launch wrapper --- py_modules/lsfg_vk/constants.py | 2 ++ py_modules/lsfg_vk/plugin.py | 8 ++++++++ 2 files changed, 10 insertions(+) (limited to 'py_modules') diff --git a/py_modules/lsfg_vk/constants.py b/py_modules/lsfg_vk/constants.py index 795c1bf..a2140b3 100644 --- a/py_modules/lsfg_vk/constants.py +++ b/py_modules/lsfg_vk/constants.py @@ -25,6 +25,8 @@ JSON_EXT = ".json" BIN_DIR = "bin" +ARMADA_GAME_LAUNCH = Path("/usr/libexec/armada/armada-game-launch") + STEAM_COMMON_PATH = Path("steamapps/common/Lossless Scaling") LOSSLESS_DLL_NAME = "Lossless.dll" diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index cb59b4f..2cb08f4 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -18,6 +18,7 @@ from .dll_detection import DllDetectionService from .configuration import ConfigurationService from .config_schema import ConfigurationManager from .flatpak_service import FlatpakService +from .constants import ARMADA_GAME_LAUNCH class Plugin: @@ -255,6 +256,13 @@ class Plugin: Returns: Dict containing the launch option string and instructions """ + if ARMADA_GAME_LAUNCH.is_file(): + return { + "launch_option": f"~/lsfg {ARMADA_GAME_LAUNCH} %command%", + "instructions": "Use this combined command in Steam Properties, preserving Armada's game-launch wrapper", + "explanation": "LSFG sets its environment first, then Armada applies the game's FEX profile and launches Proton" + } + return { "launch_option": "~/lsfg %command%", "instructions": "Add this to your game's launch options in Steam Properties", -- cgit v1.2.3 From bbd959cee4d4b3ac784cc5543464775c10a0b981 Mon Sep 17 00:00:00 2001 From: Janleyx <242423638+Janleyx@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:21:48 -0400 Subject: document Armada-only launch behavior --- py_modules/lsfg_vk/plugin.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'py_modules') diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 2cb08f4..dee2650 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -256,6 +256,8 @@ class Plugin: Returns: Dict containing the launch option string and instructions """ + # Armada-specific: other distributions do not ship this wrapper and + # continue through the unchanged generic launch option below. if ARMADA_GAME_LAUNCH.is_file(): return { "launch_option": f"~/lsfg {ARMADA_GAME_LAUNCH} %command%", -- cgit v1.2.3 From 84c79a3158d596af97dd89c1abffa2dad8ab899c Mon Sep 17 00:00:00 2001 From: Janleyx <242423638+Janleyx@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:27:34 -0400 Subject: gate launch wrapper on Armada host --- py_modules/lsfg_vk/constants.py | 1 + py_modules/lsfg_vk/installation.py | 4 ++-- py_modules/lsfg_vk/plugin.py | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'py_modules') diff --git a/py_modules/lsfg_vk/constants.py b/py_modules/lsfg_vk/constants.py index a2140b3..023894f 100644 --- a/py_modules/lsfg_vk/constants.py +++ b/py_modules/lsfg_vk/constants.py @@ -25,6 +25,7 @@ JSON_EXT = ".json" BIN_DIR = "bin" +ARMADA_DEVICE_ENV = Path("/usr/libexec/armada/device-env") ARMADA_GAME_LAUNCH = Path("/usr/libexec/armada/armada-game-launch") STEAM_COMMON_PATH = Path("steamapps/common/Lossless Scaling") diff --git a/py_modules/lsfg_vk/installation.py b/py_modules/lsfg_vk/installation.py index 29259f7..49ed711 100644 --- a/py_modules/lsfg_vk/installation.py +++ b/py_modules/lsfg_vk/installation.py @@ -15,7 +15,7 @@ from typing import Dict, Any from .base_service import BaseService from .constants import ( LIB_FILENAME, JSON_FILENAME, ZIP_FILENAME, BIN_DIR, - SO_EXT, JSON_EXT, ARM_LIB_FILENAME + SO_EXT, JSON_EXT, ARM_LIB_FILENAME, ARMADA_DEVICE_ENV ) from .config_schema import ConfigurationManager from .types import InstallationResponse, UninstallationResponse, InstallationCheckResponse @@ -84,7 +84,7 @@ class InstallationService(BaseService): # Decky runs through FEX on Armada, so Python reports x86_64 even # though the host is AArch64. Armada exposes this native helper only # on its ARM image, including inside Decky's FEX rootfs. - if Path('/usr/libexec/armada/device-env').is_file(): + if ARMADA_DEVICE_ENV.is_file(): self.log.info("Detected native AArch64 Armada host through device-env") return True diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index dee2650..74c1677 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -18,7 +18,7 @@ from .dll_detection import DllDetectionService from .configuration import ConfigurationService from .config_schema import ConfigurationManager from .flatpak_service import FlatpakService -from .constants import ARMADA_GAME_LAUNCH +from .constants import ARMADA_DEVICE_ENV, ARMADA_GAME_LAUNCH class Plugin: @@ -256,9 +256,9 @@ class Plugin: Returns: Dict containing the launch option string and instructions """ - # Armada-specific: other distributions do not ship this wrapper and - # continue through the unchanged generic launch option below. - if ARMADA_GAME_LAUNCH.is_file(): + # Decky runs through FEX on Armada, so the guest OS identity is not a + # reliable host check. Require both Armada's native marker and wrapper. + if ARMADA_DEVICE_ENV.is_file() and ARMADA_GAME_LAUNCH.is_file(): return { "launch_option": f"~/lsfg {ARMADA_GAME_LAUNCH} %command%", "instructions": "Use this combined command in Steam Properties, preserving Armada's game-launch wrapper", -- cgit v1.2.3 From 1260aa400e6d19e8d1cbc3d887c8ddcff3058be9 Mon Sep 17 00:00:00 2001 From: Janleyx <242423638+Janleyx@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:35:23 -0400 Subject: wrap Armada launch inside lsfg script --- py_modules/lsfg_vk/configuration.py | 25 +++++++++++++++++-------- py_modules/lsfg_vk/plugin.py | 10 ---------- 2 files changed, 17 insertions(+), 18 deletions(-) (limited to 'py_modules') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index e479f43..f7a171a 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -8,6 +8,7 @@ from typing import Dict, Any from .base_service import BaseService from .config_schema import ConfigurationManager, CONFIG_SCHEMA, ProfileData, DEFAULT_PROFILE_NAME from .config_schema_generated import ConfigurationData, get_script_generation_logic +from .constants import ARMADA_DEVICE_ENV, ARMADA_GAME_LAUNCH from .types import ConfigurationResponse, ProfilesResponse, ProfileResponse @@ -123,10 +124,8 @@ class ConfigurationService(BaseService): generate_script_lines = get_script_generation_logic() lines.extend(generate_script_lines(config)) - lines.extend([ - "export LSFG_PROCESS=decky-lsfg-vk", - 'exec "$@"' - ]) + lines.append("export LSFG_PROCESS=decky-lsfg-vk") + lines.extend(self._generate_game_launch_lines()) return "\n".join(lines) + "\n" @@ -154,12 +153,22 @@ class ConfigurationService(BaseService): generate_script_lines = get_script_generation_logic() lines.extend(generate_script_lines(merged_config)) - lines.extend([ - f"export LSFG_PROCESS={current_profile}", - 'exec "$@"' - ]) + lines.append(f"export LSFG_PROCESS={current_profile}") + lines.extend(self._generate_game_launch_lines()) return "\n".join(lines) + "\n" + + @staticmethod + def _generate_game_launch_lines() -> list[str]: + """Generate a portable exec block with Armada's host wrapper.""" + device_env = ARMADA_DEVICE_ENV.as_posix() + game_launch = ARMADA_GAME_LAUNCH.as_posix() + return [ + f'if [ -f "{device_env}" ] && [ -x "{game_launch}" ]; then', + f' exec "{game_launch}" "$@"', + "fi", + 'exec "$@"', + ] def _get_profile_data(self) -> ProfileData: """Get current profile data from config file""" diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 74c1677..cb59b4f 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -18,7 +18,6 @@ from .dll_detection import DllDetectionService from .configuration import ConfigurationService from .config_schema import ConfigurationManager from .flatpak_service import FlatpakService -from .constants import ARMADA_DEVICE_ENV, ARMADA_GAME_LAUNCH class Plugin: @@ -256,15 +255,6 @@ class Plugin: Returns: Dict containing the launch option string and instructions """ - # Decky runs through FEX on Armada, so the guest OS identity is not a - # reliable host check. Require both Armada's native marker and wrapper. - if ARMADA_DEVICE_ENV.is_file() and ARMADA_GAME_LAUNCH.is_file(): - return { - "launch_option": f"~/lsfg {ARMADA_GAME_LAUNCH} %command%", - "instructions": "Use this combined command in Steam Properties, preserving Armada's game-launch wrapper", - "explanation": "LSFG sets its environment first, then Armada applies the game's FEX profile and launches Proton" - } - return { "launch_option": "~/lsfg %command%", "instructions": "Add this to your game's launch options in Steam Properties", -- cgit v1.2.3 From 2e55d0cb3205cf0770ec93610b5d3df18deacd34 Mon Sep 17 00:00:00 2001 From: Janleyx <242423638+Janleyx@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:54:22 -0400 Subject: avoid double Armada launch wrapping --- py_modules/lsfg_vk/configuration.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'py_modules') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index f7a171a..fce3738 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -164,8 +164,14 @@ class ConfigurationService(BaseService): device_env = ARMADA_DEVICE_ENV.as_posix() game_launch = ARMADA_GAME_LAUNCH.as_posix() return [ - f'if [ -f "{device_env}" ] && [ -x "{game_launch}" ]; then', - f' exec "{game_launch}" "$@"', + f'armada_game_launch="{game_launch}"', + 'for argument in "$@"; do', + ' if [ "$argument" = "$armada_game_launch" ]; then', + ' exec "$@"', + " fi", + "done", + f'if [ -f "{device_env}" ] && [ -x "$armada_game_launch" ]; then', + ' exec "$armada_game_launch" "$@"', "fi", 'exec "$@"', ] -- cgit v1.2.3