Linux flatpak. (#51)

* Add flatpak support.

* Add game install directory override for flatpak.

* Flatpak'ing.

* Flatpak it some more.

* We flat it, we pak it.

* Flatpak'd.

* The Marvelous Misadventures of Flatpak.

* Attempt to change logic of NFD and show error.

* Flattenpakken.

* Use game install directory instead of current path.

* Attempt to fix line endings.
This commit is contained in:
Darío 2024-12-18 14:54:46 -03:00 committed by GitHub
parent 279390f1fe
commit bbbcdf1566
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 148 additions and 23 deletions

View file

@ -5,6 +5,10 @@ if (WIN32)
option(SWA_D3D12 "Add D3D12 support for rendering" ON) option(SWA_D3D12 "Add D3D12 support for rendering" ON)
endif() endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
option(SWA_FLATPAK "Configure the build for Flatpak compatibility." OFF)
endif()
option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF) option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF)
function(BIN2C) function(BIN2C)
@ -259,6 +263,10 @@ endif()
set_target_properties(UnleashedRecomp PROPERTIES OUTPUT_NAME ${TARGET_NAME}) set_target_properties(UnleashedRecomp PROPERTIES OUTPUT_NAME ${TARGET_NAME})
if (SWA_FLATPAK)
target_compile_definitions(UnleashedRecomp PRIVATE "GAME_INSTALL_DIRECTORY=\"/var/data\"")
endif()
if (SWA_D3D12) if (SWA_D3D12)
find_package(directx-headers CONFIG REQUIRED) find_package(directx-headers CONFIG REQUIRED)
find_package(directx12-agility CONFIG REQUIRED) find_package(directx12-agility CONFIG REQUIRED)

View file

@ -313,11 +313,11 @@ SWA_API uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName
} }
else if (pContentData->dwContentType == XCONTENTTYPE_DLC) else if (pContentData->dwContentType == XCONTENTTYPE_DLC)
{ {
root = "./dlc"; root = GAME_INSTALL_DIRECTORY "/dlc";
} }
else else
{ {
root = "."; root = GAME_INSTALL_DIRECTORY;
} }
XamRegisterContent(*pContentData, root); XamRegisterContent(*pContentData, root);

View file

@ -50,8 +50,8 @@ void KiSystemStartup()
{ {
const auto gameContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Game"); const auto gameContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Game");
const auto updateContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Update"); const auto updateContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Update");
XamRegisterContent(gameContent, std::filesystem::exists("./game") ? "./game" : "."); XamRegisterContent(gameContent, GAME_INSTALL_DIRECTORY "/game");
XamRegisterContent(updateContent, "./update"); XamRegisterContent(updateContent, GAME_INSTALL_DIRECTORY "/update");
const auto savePath = GetSavePath(); const auto savePath = GetSavePath();
const auto saveName = "SYS-DATA"; const auto saveName = "SYS-DATA";
@ -70,7 +70,7 @@ void KiSystemStartup()
XamContentCreateEx(0, "D", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr); XamContentCreateEx(0, "D", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr);
std::error_code ec; std::error_code ec;
for (auto& file : std::filesystem::directory_iterator("./dlc", ec)) for (auto& file : std::filesystem::directory_iterator(GAME_INSTALL_DIRECTORY "/dlc", ec))
{ {
if (file.is_directory()) if (file.is_directory())
{ {
@ -151,13 +151,13 @@ int main(int argc, char *argv[])
HostStartup(); HostStartup();
bool isGameInstalled = Installer::checkGameInstall("."); bool isGameInstalled = Installer::checkGameInstall(GAME_INSTALL_DIRECTORY);
bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled;
if (runInstallerWizard) if (runInstallerWizard)
{ {
Video::CreateHostDevice(); Video::CreateHostDevice();
if (!InstallerWizard::Run(isGameInstalled && forceDLCInstaller)) if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller))
{ {
return 1; return 1;
} }

View file

@ -96,7 +96,7 @@ static double g_appearTime = 0.0;
static double g_disappearTime = DBL_MAX; static double g_disappearTime = DBL_MAX;
static bool g_isDisappearing = false; static bool g_isDisappearing = false;
static std::filesystem::path g_installPath = "."; static std::filesystem::path g_installPath;
static std::filesystem::path g_gameSourcePath; static std::filesystem::path g_gameSourcePath;
static std::filesystem::path g_updateSourcePath; static std::filesystem::path g_updateSourcePath;
static std::array<std::filesystem::path, int(DLC::Count)> g_dlcSourcePaths; static std::array<std::filesystem::path, int(DLC::Count)> g_dlcSourcePaths;
@ -135,6 +135,7 @@ static std::string g_currentMessagePrompt = "";
static bool g_currentMessagePromptConfirmation = false; static bool g_currentMessagePromptConfirmation = false;
static std::list<std::filesystem::path> g_currentPickerResults; static std::list<std::filesystem::path> g_currentPickerResults;
static std::atomic<bool> g_currentPickerResultsReady = false; static std::atomic<bool> g_currentPickerResultsReady = false;
static std::string g_currentPickerErrorMessage;
static std::unique_ptr<std::thread> g_currentPickerThread; static std::unique_ptr<std::thread> g_currentPickerThread;
static bool g_currentPickerVisible = false; static bool g_currentPickerVisible = false;
static bool g_currentPickerFolderMode = false; static bool g_currentPickerFolderMode = false;
@ -872,15 +873,15 @@ static bool ConvertPathSet(const nfdpathset_t *pathSet, std::list<std::filesyste
for (nfdpathsetsize_t i = 0; i < pathSetCount; i++) for (nfdpathsetsize_t i = 0; i < pathSetCount; i++)
{ {
char *pathSetPath = nullptr; nfdnchar_t *pathSetPath = nullptr;
if (NFD_PathSet_GetPathU8(pathSet, i, &pathSetPath) != NFD_OKAY) if (NFD_PathSet_GetPathN(pathSet, i, &pathSetPath) != NFD_OKAY)
{ {
filePaths.clear(); filePaths.clear();
return false; return false;
} }
filePaths.emplace_back(std::filesystem::path(std::u8string_view((const char8_t *)(pathSetPath)))); filePaths.emplace_back(std::filesystem::path(pathSetPath));
NFD_PathSet_FreePathU8(pathSetPath); NFD_PathSet_FreePathN(pathSetPath);
} }
return true; return true;
@ -892,13 +893,11 @@ static void PickerThreadProcess()
nfdresult_t result = NFD_ERROR; nfdresult_t result = NFD_ERROR;
if (g_currentPickerFolderMode) if (g_currentPickerFolderMode)
{ {
nfdpickfolderu8args_t openArgs = {}; result = NFD_PickFolderMultipleN(&pathSet, nullptr);
result = NFD_PickFolderMultipleU8_With(&pathSet, &openArgs);
} }
else else
{ {
nfdopendialogu8args_t openArgs = {}; result = NFD_OpenDialogMultipleN(&pathSet, nullptr, 0, nullptr);
result = NFD_OpenDialogMultipleU8_With(&pathSet, &openArgs);
} }
if (result == NFD_OKAY) if (result == NFD_OKAY)
@ -906,6 +905,10 @@ static void PickerThreadProcess()
bool pathsConverted = ConvertPathSet(pathSet, g_currentPickerResults); bool pathsConverted = ConvertPathSet(pathSet, g_currentPickerResults);
NFD_PathSet_Free(pathSet); NFD_PathSet_Free(pathSet);
} }
else if (result == NFD_ERROR)
{
g_currentPickerErrorMessage = NFD_GetError();
}
g_currentPickerResultsReady = true; g_currentPickerResultsReady = true;
g_currentPickerVisible = false; g_currentPickerVisible = false;
@ -1343,6 +1346,13 @@ static void CheckPickerResults()
return; return;
} }
if (!g_currentPickerErrorMessage.empty())
{
g_currentMessagePrompt = g_currentPickerErrorMessage;
g_currentMessagePromptConfirmation = false;
g_currentPickerErrorMessage.clear();
}
ParseSourcePaths(g_currentPickerResults); ParseSourcePaths(g_currentPickerResults);
g_currentPickerResultsReady = false; g_currentPickerResultsReady = false;
g_currentPickerVisible = false; g_currentPickerVisible = false;
@ -1426,8 +1436,10 @@ void InstallerWizard::Shutdown()
} }
} }
bool InstallerWizard::Run(bool skipGame) bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame)
{ {
g_installPath = installPath;
EmbeddedPlayer::Init(); EmbeddedPlayer::Init();
NFD_Init(); NFD_Init();

View file

@ -9,5 +9,5 @@ struct InstallerWizard
static void Init(); static void Init();
static void Draw(); static void Draw();
static void Shutdown(); static void Shutdown();
static bool Run(bool skipGame); static bool Run(std::filesystem::path installPath, bool skipGame);
}; };

View file

@ -50,7 +50,7 @@ public:
#define WINDOWPOS_CENTRED 0x2FFF0000 #define WINDOWPOS_CENTRED 0x2FFF0000
static inline std::vector<IConfigDef*> g_configDefinitions{}; inline std::vector<IConfigDef*> g_configDefinitions;
CONFIG_DEFINE_ENUM_TEMPLATE(ELanguage) CONFIG_DEFINE_ENUM_TEMPLATE(ELanguage)
{ {

View file

@ -2,15 +2,19 @@
#define USER_DIRECTORY "SWA" #define USER_DIRECTORY "SWA"
#ifndef GAME_INSTALL_DIRECTORY
#define GAME_INSTALL_DIRECTORY "."
#endif
inline std::filesystem::path GetGamePath() inline std::filesystem::path GetGamePath()
{ {
return std::filesystem::current_path(); return GAME_INSTALL_DIRECTORY;
} }
inline std::filesystem::path GetUserPath() inline std::filesystem::path GetUserPath()
{ {
if (std::filesystem::exists("portable.txt")) if (std::filesystem::exists(GAME_INSTALL_DIRECTORY "portable.txt"))
return std::filesystem::current_path(); return GAME_INSTALL_DIRECTORY;
std::filesystem::path userPath; std::filesystem::path userPath;

10
flatpak/README.md Normal file
View file

@ -0,0 +1,10 @@
Build
```sh
flatpak-builder --force-clean --user --install-deps-from=flathub --repo=repo --install builddir io.github.hedge_dev.unleashedrecomp.json
```
Bundle
```sh
flatpak build-bundle repo io.github.hedge_dev.unleashedrecomp.flatpak io.github.hedge_dev.unleashedrecomp --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
```

View file

@ -0,0 +1,7 @@
[Desktop Entry]
Name=Unleashed Recompiled
Exec=/app/bin/SWA
Type=Application
Icon=io.github.hedge_dev.unleashedrecomp
Categories=Game;
Comment=Static recompilation of Sonic Unleashed.

View file

@ -0,0 +1,58 @@
{
"id": "io.github.hedge_dev.unleashedrecomp",
"runtime": "org.freedesktop.Platform",
"runtime-version": "23.08",
"sdk": "org.freedesktop.Sdk",
"sdk-extensions" : [ "org.freedesktop.Sdk.Extension.llvm18" ],
"finish-args": [
"--share=network",
"--socket=wayland",
"--socket=fallback-x11",
"--socket=pulseaudio",
"--device=all",
"--filesystem=host",
"--filesystem=/media",
"--filesystem=/run/media",
"--filesystem=/mnt"
],
"modules": [
{
"name": "UnleashedRecomp",
"buildsystem": "simple",
"build-commands": [
"cmake --preset linux-release -DSWA_FLATPAK=ON",
"cmake --build out/build/linux-release",
"mkdir -p /app/bin",
"cp out/build/linux-release/UnleashedRecomp/SWA /app/bin/SWA",
"install -Dm644 UnleashedRecompResources/images/game_icon.png /app/share/icons/hicolor/128x128/apps/${FLATPAK_ID}.png",
"install -Dm644 flatpak/io.github.hedge_dev.unleashedrecomp.metainfo.xml /app/share/metainfo/${FLATPAK_ID}.metainfo.xml",
"install -Dm644 flatpak/io.github.hedge_dev.unleashedrecomp.desktop /app/share/applications/${FLATPAK_ID}.desktop"
],
"sources": [
{
"type": "git",
"branch": "linux-flatpak",
"disable-shallow-clone": true,
"url": "https://github.com/hedge-dev/UnleashedRecomp.git"
},
{
"type": "file",
"path": "default.xex",
"dest": "UnleashedRecompLib/private"
},
{
"type": "file",
"path": "shader.ar",
"dest": "UnleashedRecompLib/private"
}
],
"build-options": {
"append-path": "/usr/lib/sdk/llvm18/bin",
"prepend-ld-library-path": "/usr/lib/sdk/llvm18/lib",
"build-args": [
"--share=network"
]
}
}
]
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>io.github.hedge_dev.unleashedrecomp</id>
<name>Unleashed Recompiled</name>
<summary>Static recompilation of Sonic Unleashed.</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</supports>
<description>
<p>
A native PC port of Sonic Unleashed for Xbox 360 achieved through static recompilation.
https://github.com/hedge-dev/UnleashedRecomp
</p>
</description>
<launchable type="desktop-id">io.github.hedge_dev.unleashedrecomp.desktop</launchable>
</component>