Skip to content

Commit f7a7448

Browse files
feat(linux): hide Windows-only Tools menu and bundle the 7-Zip Linux fallback
- Tools are Windows executables only, so the Avalonia app now hides the whole Tools menu on non-Windows (ToolsMenuItem.IsVisible = OperatingSystem.IsWindows()) with a matching guard in LaunchTool_Click. - 7-Zip extraction fallback: ExtractionService picks 7za(.exe)/7za_arm64.exe on Windows and the extension-less 7zz/7zz_arm64 on Linux/macOS (GetSevenZipExecutableName, covered by ExtractionServiceTests). - Bundled the official 7-Zip 26.03 static Linux builds as tools/SevenZip/7zz (x64) and 7zz_arm64, plus License.txt; SHA256 verified against the upstream release. - Packaging: both scripts keep only the matching architecture and stamp 0755 on Linux; Windows payloads drop the Linux-only 7zz binaries. - RetroAchievementsSharp already ships per-arch Linux ELF binaries from CSharp_RetroAchievementsSharp (scan --console/--out), so RA hashing needed no change. - Docs updated (06, 07, 11, 15, manual test plan); verified on Ubuntu 24.04 Wayland (no Tools menu, bundled 7zz extracts a real .7z) and by running both packaging scripts.
1 parent faa065b commit f7a7448

14 files changed

Lines changed: 215 additions & 24 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Runtime.InteropServices;
2+
using SimpleLauncher.Core.Services.ExtractFiles;
3+
4+
namespace SimpleLauncher.Avalonia.Tests;
5+
6+
/// <summary>
7+
/// Tests for the 7-Zip fallback executable selection in <see cref="ExtractionService" /> —
8+
/// Windows bundles 7za.exe, Linux/macOS bundle the extension-less 7zz binaries.
9+
/// </summary>
10+
public class ExtractionServiceTests
11+
{
12+
[Theory]
13+
[InlineData(Architecture.X64, true, "7za.exe")]
14+
[InlineData(Architecture.Arm64, true, "7za_arm64.exe")]
15+
[InlineData(Architecture.X64, false, "7zz")]
16+
[InlineData(Architecture.Arm64, false, "7zz_arm64")]
17+
public void GetSevenZipExecutableName_PicksBinaryForPlatform(
18+
Architecture architecture, bool isWindows, string expected)
19+
{
20+
Assert.Equal(expected, ExtractionService.GetSevenZipExecutableName(architecture, isWindows));
21+
}
22+
}

‎SimpleLauncher.Avalonia/MainWindow.axaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@
563563
</MenuItem>
564564

565565
<!-- ── TOOLS ── -->
566-
<MenuItem Header="{ext:Translate Tools}" ToolTip.Tip="{ext:Translate TooltipToolsMenu}">
566+
<MenuItem x:Name="ToolsMenuItem" Header="{ext:Translate Tools}" ToolTip.Tip="{ext:Translate TooltipToolsMenu}">
567567
<MenuItem Header="{ext:Translate BatchConvertIsoToXiso}" Tag="BatchConvertIsoToXiso" Click="LaunchTool_Click"
568568
ToolTip.Tip="{ext:Translate TooltipToolsMenu}">
569569
<MenuItem.Icon>

‎SimpleLauncher.Avalonia/MainWindow.axaml.cs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ public MainWindow(
128128

129129
InitializeComponent();
130130

131+
// The bundled external tools are Windows executables only.
132+
ToolsMenuItem.IsVisible = OperatingSystem.IsWindows();
133+
131134
// Wire the extracted services to this host (WPF parity)
132135
_uiResetService.Initialize(this);
133136
_systemSelectionOrchestrator.Initialize(this);
@@ -2949,6 +2952,8 @@ private async void LaunchTool_Click(object? sender, RoutedEventArgs e)
29492952
{
29502953
if (sender is not MenuItem { Tag: string toolTag }) return;
29512954

2955+
if (!OperatingSystem.IsWindows()) return;
2956+
29522957
tool = toolTag;
29532958

29542959
Log.Information("Launching external tool {Tool}", tool);

‎SimpleLauncher.Core/Services/ExtractFiles/ExtractionService.cs‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace SimpleLauncher.Core.Services.ExtractFiles;
1313

1414
/// <summary>
1515
/// Extracts compressed game archives (7z, ZIP, RAR) to temporary or permanent locations,
16-
/// with support for path traversal protection, disk space checks, and 7za fallback.
16+
/// with support for path traversal protection, disk space checks, and a 7-Zip fallback.
1717
/// </summary>
1818
public class ExtractionService : IExtractionService
1919
{
@@ -488,10 +488,17 @@ await Task.Run(() =>
488488
}
489489
}
490490

491+
internal static string GetSevenZipExecutableName(Architecture architecture, bool isWindows)
492+
{
493+
if (isWindows)
494+
return architecture == Architecture.Arm64 ? "7za_arm64.exe" : "7za.exe";
495+
496+
return architecture == Architecture.Arm64 ? "7zz_arm64" : "7zz";
497+
}
498+
491499
private async Task<bool> ExtractWith7ZipAsync(string archivePath, string destinationFolder)
492500
{
493-
var arch = RuntimeInformation.ProcessArchitecture;
494-
var exeName = arch == Architecture.Arm64 ? "7za_arm64.exe" : "7za.exe";
501+
var exeName = GetSevenZipExecutableName(RuntimeInformation.ProcessArchitecture, OperatingSystem.IsWindows());
495502
var exePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tools", "SevenZip", exeName);
496503

497504
if (!File.Exists(exePath))

‎SimpleLauncher/tools/SevenZip/7zz‎

3.59 MB
Binary file not shown.
2.97 MB
Binary file not shown.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
7-Zip for Linux and macOS
2+
~~~~~~~~~~~~~~~~~~~~~~~~~
3+
License for use and distribution
4+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5+
6+
7-Zip Copyright (C) 1999-2026 Igor Pavlov.
7+
8+
The licenses for 7zz and 7zzs files are:
9+
10+
- The "GNU LGPL" as main license for most of the code
11+
- The "GNU LGPL" with "unRAR license restriction" for some code
12+
- The "BSD 3-clause License" for some code
13+
- The "BSD 2-clause License" for some code
14+
15+
Redistributions in binary form must reproduce related license information from this file.
16+
17+
Note:
18+
You can use 7-Zip on any computer, including a computer in a commercial
19+
organization. You don't need to register or pay for 7-Zip.
20+
21+
22+
GNU LGPL information
23+
--------------------
24+
25+
This library is free software; you can redistribute it and/or
26+
modify it under the terms of the GNU Lesser General Public
27+
License as published by the Free Software Foundation; either
28+
version 2.1 of the License, or (at your option) any later version.
29+
30+
This library is distributed in the hope that it will be useful,
31+
but WITHOUT ANY WARRANTY; without even the implied warranty of
32+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33+
Lesser General Public License for more details.
34+
35+
You can receive a copy of the GNU Lesser General Public License from
36+
http://www.gnu.org/
37+
38+
39+
40+
41+
BSD 3-clause License in 7-Zip code
42+
----------------------------------
43+
44+
The "BSD 3-clause License" is used for the following code in 7z.dll
45+
1) LZFSE data decompression.
46+
That code was derived from the code in the "LZFSE compression library" developed by Apple Inc,
47+
that also uses the "BSD 3-clause License".
48+
2) ZSTD data decompression.
49+
that code was developed using original zstd decoder code as reference code.
50+
The original zstd decoder code was developed by Facebook Inc,
51+
that also uses the "BSD 3-clause License".
52+
53+
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
54+
Copyright (c) Facebook, Inc. All rights reserved.
55+
Copyright (c) 2023-2026 Igor Pavlov.
56+
57+
Text of the "BSD 3-clause License"
58+
----------------------------------
59+
60+
Redistribution and use in source and binary forms, with or without modification,
61+
are permitted provided that the following conditions are met:
62+
63+
1. Redistributions of source code must retain the above copyright notice, this
64+
list of conditions and the following disclaimer.
65+
66+
2. Redistributions in binary form must reproduce the above copyright notice,
67+
this list of conditions and the following disclaimer in the documentation
68+
and/or other materials provided with the distribution.
69+
70+
3. Neither the name of the copyright holder nor the names of its contributors may
71+
be used to endorse or promote products derived from this software without
72+
specific prior written permission.
73+
74+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
75+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
76+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
77+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
78+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
79+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
80+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
81+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
82+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
83+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
84+
85+
---
86+
87+
88+
89+
90+
BSD 2-clause License in 7-Zip code
91+
----------------------------------
92+
93+
The "BSD 2-clause License" is used for the XXH64 code in 7-Zip.
94+
95+
XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet.
96+
97+
Copyright (c) 2012-2021 Yann Collet.
98+
Copyright (c) 2023-2026 Igor Pavlov.
99+
100+
Text of the "BSD 2-clause License"
101+
----------------------------------
102+
103+
Redistribution and use in source and binary forms, with or without modification,
104+
are permitted provided that the following conditions are met:
105+
106+
1. Redistributions of source code must retain the above copyright notice, this
107+
list of conditions and the following disclaimer.
108+
109+
2. Redistributions in binary form must reproduce the above copyright notice,
110+
this list of conditions and the following disclaimer in the documentation
111+
and/or other materials provided with the distribution.
112+
113+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
114+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
115+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
116+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
117+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
118+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
119+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
120+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
121+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
122+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
123+
124+
---
125+
126+
127+
128+
129+
unRAR license restriction
130+
-------------------------
131+
132+
The decompression engine for RAR archives was developed using source
133+
code of unRAR program.
134+
All copyrights to original unRAR code are owned by Alexander Roshal.
135+
136+
The license for original unRAR code has the following restriction:
137+
138+
The unRAR sources cannot be used to re-create the RAR compression algorithm,
139+
which is proprietary. Distribution of modified unRAR sources in separate form
140+
or as a part of other software is permitted, provided that it is clearly
141+
stated in the documentation and source comments that the code may
142+
not be used to develop a RAR (WinRAR) compatible archiver.
143+
144+
--

‎docs/06-systems-and-launch.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Ordered by `Priority`; first `IsMatch` wins:
8787
- **`.extraction_in_progress` marker** written before extract, removed on success. On failure only the marker plus the files written by that run are deleted — destination folders are never wiped (CORE-01), and tracked files are removed before the marker.
8888
- **Disk-space check:** estimated size × 1.2 vs `DriveInfo.AvailableFreeSpace` → `DiskSpaceErrorMessageBoxAsync` + IOException (`:165-196`).
8989
- **Path-traversal guard:** every entry must resolve under the destination root (`:199-219`, `:364-382`); random temp names (`:351-354`).
90-
- **7za fallback:** SharpCompress failure on `.7z` → `tools\SevenZip\7za.exe`/`_arm64.exe` (`:446-448`), args `x -o"dest" -y "archive"` (`:460`), **30-minute timeout** with kill (`:485-503`).
90+
- **7-Zip fallback:** SharpCompress failure on `.7z` → `tools\SevenZip\7za.exe`/`_arm64.exe` on Windows, `tools/SevenZip/7zz`/`7zz_arm64` on Linux/macOS (`GetSevenZipExecutableName`, `:490-497`), args `x -o"dest" -y "archive"`, **30-minute timeout** with kill.
9191
- Temp root: `%TEMP%\SimpleLauncher` (`:20`); `ValidateAndFindGameFileAsync` (`:540`) searches `FileFormatsToLaunch` then any file.
9292

9393
## Mounting (Core `GameLauncher\MountFiles\`)

‎docs/07-core-services.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ All Core services follow the same conventions: Serilog `ILogger` injected (globa
4545
| `GameLauncher\MountFiles\FindEbootBin`, `FindDefaultXbe`, `FindDefaultXex`, `FindImageIso`, `FindBinFile`, `FindCueFile`, `FileFinderService` | Launch-file discovery inside mounted volumes |
4646
| `GameLauncher\MountFiles\DokanValidation` | P/Invoke `dokan2.dll` version check |
4747
| `GameLauncher\ValidateBatchFile` | Pre-execution validation of batch files (missing paths) |
48-
| `ExtractFiles\ExtractionService` | Archive extraction: lock retry, disk-space check, path-traversal guard, 7za fallback, `.extraction_in_progress` marker. See [06](06-systems-and-launch.md#extraction) |
48+
| `ExtractFiles\ExtractionService` | Archive extraction: lock retry, disk-space check, path-traversal guard, 7-Zip fallback, `.extraction_in_progress` marker. See [06](06-systems-and-launch.md#extraction) |
4949
| `Converters\DiscConverter` | CHD→ISO/CUE-BIN, PBP→CUE-BIN, RVZ/WBFS/GCZ→ISO via bundled tools (5-min timeouts) |
5050
| `ExternalToolLauncher\ExternalToolLauncherService` | Launch bundled tools: arch-aware paths, PE validation, per-tool methods. See [11](11-bundled-tools.md) |
5151

‎docs/11-bundled-tools.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
66
## Shipped payloads (`SimpleLauncher\tools\`)
77

8-
Every tool ships x64 + ARM64 variants (`X.exe` + `X_arm64.exe`) unless noted. All are launched through `ExternalToolLauncherService` (below). In `BatchConvertIsoToXiso`, the `bchunk.exe`, `extract-xiso.exe` and `xdvdfs.exe` helpers are x64-only and are invoked by the wrapper (on Windows 11 ARM64 they run under x64 emulation).
8+
Every tool ships x64 + ARM64 variants (`X.exe` + `X_arm64.exe`) unless noted. All are launched through `ExternalToolLauncherService` (below). The whole Tools menu is Windows-only: the Avalonia app hides it on Linux/macOS (`MainWindow` sets `ToolsMenuItem.IsVisible = OperatingSystem.IsWindows()`), and the Linux packaging script prunes every `tools/**/*.exe` and `tools/**/*.dll`. In `BatchConvertIsoToXiso`, the `bchunk.exe`, `extract-xiso.exe` and `xdvdfs.exe` helpers are x64-only and are invoked by the wrapper (on Windows 11 ARM64 they run under x64 emulation).
99

1010
| Folder | Executables | Purpose |
1111
|---|---|---|
12-
| `SevenZip` | `7za.exe`, `7za_arm64.exe` | Fallback 7z extraction (SharpCompress failure path) |
12+
| `SevenZip` | `7za.exe`, `7za_arm64.exe` (Windows) / `7zz`, `7zz_arm64` (Linux, static 7-Zip 26.03 builds) + `License.txt` | Fallback 7z extraction (SharpCompress failure path) — `ExtractionService` picks the binary for the running OS/architecture |
1313
| `CHDMounter` | `CHDMounter.exe`, `_arm64.exe` (+ README) | Mounts CHD files as virtual drives for emulators without native CHD support |
1414
| `SimpleXisoDrive` | `SimpleXisoDrive.exe`, `_arm64.exe` | Mounts XISO images (Dokan) |
1515
| `SimpleZipDrive` | `SimpleZipDrive.exe`, `_arm64.exe` (+ ReadMe) | Mounts ZIP archives (Dokan) |

0 commit comments

Comments
 (0)