forked from Mirror/Ryujinx
Make config filename changable for releases & Log to Ryujinx directory if application directory is not writable (#4707)
* Remove GetBaseApplicationDirectory() & Move logs directory to user base path We should assume the application directory might be write-protected. * Use Ryujinx.sh in Ryujinx.desktop This desktop file isn't really used right now, so this changes effectively nothing. * Use properties in ReleaseInformation.cs and add ConfigName property * Configure config filename in Github workflows * Add a separate config step for macOS Because they use BSD sed instead of GNU sed * Keep log directory at the old location for dev environments * Add FileSystemUtils since Directory.Move() doesn't work across filesystems Steal CopyDirectory code from https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories * Fix "Open Logs folder" button pointing to the wrong directory * Add execute permissions to Ryujinx.sh * Fix missing newlines * AppDataManager: Use FileSystemUtils.MoveDirectory() * Make dotnet format happy * Add a fallback for the logging directory
This commit is contained in:
parent
7795b662a9
commit
70fcba39de
22 changed files with 209 additions and 117 deletions
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
|
@ -40,7 +40,7 @@ jobs:
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
global-json-file: global.json
|
global-json-file: global.json
|
||||||
|
|
||||||
- name: Overwrite csc problem matcher
|
- name: Overwrite csc problem matcher
|
||||||
run: echo "::add-matcher::.github/csc.json"
|
run: echo "::add-matcher::.github/csc.json"
|
||||||
|
|
||||||
|
@ -49,6 +49,16 @@ jobs:
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Change config filename
|
||||||
|
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
|
- name: Change config filename for macOS
|
||||||
|
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
if: github.event_name == 'pull_request' && matrix.os == 'macOS-latest'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||||
|
|
||||||
|
@ -135,6 +145,11 @@ jobs:
|
||||||
id: git_short_hash
|
id: git_short_hash
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Change config filename
|
||||||
|
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx.Ava
|
- name: Publish macOS Ryujinx.Ava
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -85,6 +85,7 @@ jobs:
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create output dir
|
- name: Create output dir
|
||||||
|
@ -186,6 +187,7 @@ jobs:
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx.Ava
|
- name: Publish macOS Ryujinx.Ava
|
||||||
|
|
|
@ -4,7 +4,7 @@ Name=Ryujinx
|
||||||
Type=Application
|
Type=Application
|
||||||
Icon=Ryujinx
|
Icon=Ryujinx
|
||||||
Exec=Ryujinx.sh %f
|
Exec=Ryujinx.sh %f
|
||||||
Comment=Plays Nintendo Switch applications
|
Comment=A Nintendo Switch Emulator
|
||||||
GenericName=Nintendo Switch Emulator
|
GenericName=Nintendo Switch Emulator
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=Game;Emulator;
|
Categories=Game;Emulator;
|
||||||
|
|
2
distribution/linux/Ryujinx.sh
Normal file → Executable file
2
distribution/linux/Ryujinx.sh
Normal file → Executable file
|
@ -17,4 +17,4 @@ if command -v gamemoderun > /dev/null 2>&1; then
|
||||||
COMMAND="$COMMAND gamemoderun"
|
COMMAND="$COMMAND gamemoderun"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
||||||
|
|
|
@ -665,7 +665,7 @@ namespace Ryujinx.Modules
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid())
|
if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid)
|
||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
|
@ -683,7 +683,7 @@ namespace Ryujinx.Modules
|
||||||
#else
|
#else
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
if (ReleaseInformation.IsFlatHubBuild())
|
if (ReleaseInformation.IsFlatHubBuild)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
ContentDialogHelper.CreateWarningDialog(
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace Ryujinx.Ava
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Version = ReleaseInformation.GetVersion();
|
Version = ReleaseInformation.Version;
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||||
{
|
{
|
||||||
|
@ -125,8 +125,8 @@ namespace Ryujinx.Ava
|
||||||
|
|
||||||
public static void ReloadConfig()
|
public static void ReloadConfig()
|
||||||
{
|
{
|
||||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
|
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||||
|
|
||||||
// Now load the configuration as the other subsystems are now registered
|
// Now load the configuration as the other subsystems are now registered
|
||||||
if (File.Exists(localConfigurationPath))
|
if (File.Exists(localConfigurationPath))
|
||||||
|
|
|
@ -357,7 +357,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
|
public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
|
||||||
|
|
||||||
public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild();
|
public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild;
|
||||||
|
|
||||||
public string LoadHeading
|
public string LoadHeading
|
||||||
{
|
{
|
||||||
|
@ -1350,7 +1350,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public void OpenLogsFolder()
|
public void OpenLogsFolder()
|
||||||
{
|
{
|
||||||
string logPath = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "Logs");
|
string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||||
|
|
||||||
|
if (ReleaseInformation.IsValid)
|
||||||
|
{
|
||||||
|
logPath = Path.Combine(AppDataManager.BaseDirPath, "Logs");
|
||||||
|
}
|
||||||
|
|
||||||
new DirectoryInfo(logPath).Create();
|
new DirectoryInfo(logPath).Create();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -6,8 +7,8 @@ namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
public static class AppDataManager
|
public static class AppDataManager
|
||||||
{
|
{
|
||||||
public const string DefaultBaseDir = "Ryujinx";
|
private const string DefaultBaseDir = "Ryujinx";
|
||||||
public const string DefaultPortableDir = "portable";
|
private const string DefaultPortableDir = "portable";
|
||||||
|
|
||||||
// The following 3 are always part of Base Directory
|
// The following 3 are always part of Base Directory
|
||||||
private const string GamesDir = "games";
|
private const string GamesDir = "games";
|
||||||
|
@ -109,8 +110,7 @@ namespace Ryujinx.Common.Configuration
|
||||||
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
|
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
|
||||||
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
|
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
|
||||||
{
|
{
|
||||||
CopyDirectory(oldConfigPath, BaseDirPath);
|
FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
|
||||||
Directory.Delete(oldConfigPath, true);
|
|
||||||
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
|
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,41 +127,13 @@ namespace Ryujinx.Common.Configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if existing old baseDirPath is a symlink, to prevent possible errors.
|
// Check if existing old baseDirPath is a symlink, to prevent possible errors.
|
||||||
// Should be removed, when the existance of the old directory isn't checked anymore.
|
// Should be removed, when the existence of the old directory isn't checked anymore.
|
||||||
private static bool IsPathSymlink(string path)
|
private static bool IsPathSymlink(string path)
|
||||||
{
|
{
|
||||||
FileAttributes attributes = File.GetAttributes(path);
|
FileAttributes attributes = File.GetAttributes(path);
|
||||||
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
|
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CopyDirectory(string sourceDir, string destinationDir)
|
|
||||||
{
|
|
||||||
var dir = new DirectoryInfo(sourceDir);
|
|
||||||
|
|
||||||
if (!dir.Exists)
|
|
||||||
{
|
|
||||||
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectoryInfo[] subDirs = dir.GetDirectories();
|
|
||||||
Directory.CreateDirectory(destinationDir);
|
|
||||||
|
|
||||||
foreach (FileInfo file in dir.GetFiles())
|
|
||||||
{
|
|
||||||
if (file.Name == ".DS_Store")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.CopyTo(Path.Combine(destinationDir, file.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (DirectoryInfo subDir in subDirs)
|
|
||||||
{
|
|
||||||
CopyDirectory(subDir.FullName, Path.Combine(destinationDir, subDir.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
|
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
|
||||||
public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName;
|
public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,31 +13,71 @@ namespace Ryujinx.Common.Logging.Targets
|
||||||
|
|
||||||
string ILogTarget.Name { get => _name; }
|
string ILogTarget.Name { get => _name; }
|
||||||
|
|
||||||
public FileLogTarget(string path, string name)
|
public FileLogTarget(string name, FileStream fileStream)
|
||||||
: this(path, name, FileShare.Read, FileMode.Append)
|
{
|
||||||
{ }
|
_name = name;
|
||||||
|
_logWriter = new StreamWriter(fileStream);
|
||||||
|
_formatter = new DefaultLogFormatter();
|
||||||
|
}
|
||||||
|
|
||||||
public FileLogTarget(string path, string name, FileShare fileShare, FileMode fileMode)
|
public static FileStream PrepareLogFile(string path)
|
||||||
{
|
{
|
||||||
// Ensure directory is present
|
// Ensure directory is present
|
||||||
DirectoryInfo logDir = new(Path.Combine(path, "Logs"));
|
DirectoryInfo logDir = new(Path.Combine(path, "Logs"));
|
||||||
logDir.Create();
|
try
|
||||||
|
{
|
||||||
|
logDir.Create();
|
||||||
|
}
|
||||||
|
catch (IOException exception)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}': {exception}");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up old logs, should only keep 3
|
// Clean up old logs, should only keep 3
|
||||||
FileInfo[] files = logDir.GetFiles("*.log").OrderBy((info => info.CreationTime)).ToArray();
|
FileInfo[] files = logDir.GetFiles("*.log").OrderBy((info => info.CreationTime)).ToArray();
|
||||||
for (int i = 0; i < files.Length - 2; i++)
|
for (int i = 0; i < files.Length - 2; i++)
|
||||||
{
|
{
|
||||||
files[i].Delete();
|
try
|
||||||
|
{
|
||||||
|
files[i].Delete();
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException exception)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Old log file could not be deleted '{files[i].FullName}': {exception}");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (IOException exception)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Old log file could not be deleted '{files[i].FullName}': {exception}");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string version = ReleaseInformation.GetVersion();
|
string version = ReleaseInformation.Version;
|
||||||
|
|
||||||
// Get path for the current time
|
// Get path for the current time
|
||||||
path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log");
|
path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log");
|
||||||
|
|
||||||
_name = name;
|
try
|
||||||
_logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare));
|
{
|
||||||
_formatter = new DefaultLogFormatter();
|
return File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException exception)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Log file could not be created '{path}': {exception}");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (IOException exception)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Log file could not be created '{path}': {exception}");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(object sender, LogEventArgs args)
|
public void Log(object sender, LogEventArgs args)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ryujinx.Common
|
namespace Ryujinx.Common
|
||||||
|
@ -9,50 +7,25 @@ namespace Ryujinx.Common
|
||||||
{
|
{
|
||||||
private const string FlatHubChannelOwner = "flathub";
|
private const string FlatHubChannelOwner = "flathub";
|
||||||
|
|
||||||
public const string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
|
private const string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
|
||||||
public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
private const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
||||||
public const string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%";
|
private const string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%";
|
||||||
|
private const string ConfigFileName = "%%RYUJINX_CONFIG_FILE_NAME%%";
|
||||||
|
|
||||||
public const string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%";
|
public const string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%";
|
||||||
public const string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%";
|
public const string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%";
|
||||||
|
|
||||||
public static bool IsValid()
|
public static string ConfigName => !ConfigFileName.StartsWith("%%") ? ConfigFileName : "Config.json";
|
||||||
{
|
|
||||||
return !BuildGitHash.StartsWith("%%") &&
|
|
||||||
!ReleaseChannelName.StartsWith("%%") &&
|
|
||||||
!ReleaseChannelOwner.StartsWith("%%") &&
|
|
||||||
!ReleaseChannelRepo.StartsWith("%%");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsFlatHubBuild()
|
public static bool IsValid =>
|
||||||
{
|
!BuildGitHash.StartsWith("%%") &&
|
||||||
return IsValid() && ReleaseChannelOwner.Equals(FlatHubChannelOwner);
|
!ReleaseChannelName.StartsWith("%%") &&
|
||||||
}
|
!ReleaseChannelOwner.StartsWith("%%") &&
|
||||||
|
!ReleaseChannelRepo.StartsWith("%%") &&
|
||||||
|
!ConfigFileName.StartsWith("%%");
|
||||||
|
|
||||||
public static string GetVersion()
|
public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannelOwner);
|
||||||
{
|
|
||||||
if (IsValid())
|
|
||||||
{
|
|
||||||
return BuildVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||||
}
|
|
||||||
|
|
||||||
#if FORCE_EXTERNAL_BASE_DIR
|
|
||||||
public static string GetBaseApplicationDirectory()
|
|
||||||
{
|
|
||||||
return AppDataManager.BaseDirPath;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
public static string GetBaseApplicationDirectory()
|
|
||||||
{
|
|
||||||
if (IsFlatHubBuild() || OperatingSystem.IsMacOS())
|
|
||||||
{
|
|
||||||
return AppDataManager.BaseDirPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AppDomain.CurrentDomain.BaseDirectory;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
48
src/Ryujinx.Common/Utilities/FileSystemUtils.cs
Normal file
48
src/Ryujinx.Common/Utilities/FileSystemUtils.cs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
public static class FileSystemUtils
|
||||||
|
{
|
||||||
|
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive)
|
||||||
|
{
|
||||||
|
// Get information about the source directory
|
||||||
|
var dir = new DirectoryInfo(sourceDir);
|
||||||
|
|
||||||
|
// Check if the source directory exists
|
||||||
|
if (!dir.Exists)
|
||||||
|
{
|
||||||
|
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache directories before we start copying
|
||||||
|
DirectoryInfo[] dirs = dir.GetDirectories();
|
||||||
|
|
||||||
|
// Create the destination directory
|
||||||
|
Directory.CreateDirectory(destinationDir);
|
||||||
|
|
||||||
|
// Get the files in the source directory and copy to the destination directory
|
||||||
|
foreach (FileInfo file in dir.GetFiles())
|
||||||
|
{
|
||||||
|
string targetFilePath = Path.Combine(destinationDir, file.Name);
|
||||||
|
file.CopyTo(targetFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If recursive and copying subdirectories, recursively call this method
|
||||||
|
if (recursive)
|
||||||
|
{
|
||||||
|
foreach (DirectoryInfo subDir in dirs)
|
||||||
|
{
|
||||||
|
string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
|
||||||
|
CopyDirectory(subDir.FullName, newDestinationDir, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MoveDirectory(string sourceDir, string destinationDir)
|
||||||
|
{
|
||||||
|
CopyDirectory(sourceDir, destinationDir, true);
|
||||||
|
Directory.Delete(sourceDir, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Version = ReleaseInformation.GetVersion();
|
Version = ReleaseInformation.Version;
|
||||||
|
|
||||||
// Make process DPI aware for proper window sizing on high-res screens.
|
// Make process DPI aware for proper window sizing on high-res screens.
|
||||||
ForceDpiAware.Windows();
|
ForceDpiAware.Windows();
|
||||||
|
@ -427,11 +427,26 @@ namespace Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
if (!option.DisableFileLog)
|
if (!option.DisableFileLog)
|
||||||
{
|
{
|
||||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
FileStream logFile = FileLogTarget.PrepareLogFile(AppDomain.CurrentDomain.BaseDirectory);
|
||||||
new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"),
|
|
||||||
1000,
|
if (logFile == null)
|
||||||
AsyncLogTargetOverflowAction.Block
|
{
|
||||||
));
|
logFile = FileLogTarget.PrepareLogFile(AppDataManager.BaseDirPath);
|
||||||
|
|
||||||
|
if (logFile == null)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the application directory or the Ryujinx directory is writable.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logFile != null)
|
||||||
|
{
|
||||||
|
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||||
|
new FileLogTarget("file", logFile),
|
||||||
|
1000,
|
||||||
|
AsyncLogTargetOverflowAction.Block
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup graphics configuration
|
// Setup graphics configuration
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
@ -13,8 +12,6 @@ namespace Ryujinx.SDL2.Common
|
||||||
{
|
{
|
||||||
private static SDL2Driver _instance;
|
private static SDL2Driver _instance;
|
||||||
|
|
||||||
public static bool IsInitialized => _instance != null;
|
|
||||||
|
|
||||||
public static SDL2Driver Instance
|
public static SDL2Driver Instance
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -96,7 +93,7 @@ namespace Ryujinx.SDL2.Common
|
||||||
|
|
||||||
SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE);
|
SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE);
|
||||||
|
|
||||||
string gamepadDbPath = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "SDL_GameControllerDB.txt");
|
string gamepadDbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SDL_GameControllerDB.txt");
|
||||||
|
|
||||||
if (File.Exists(gamepadDbPath))
|
if (File.Exists(gamepadDbPath))
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@ using LibHac.Ns;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
@ -105,7 +106,7 @@ namespace Ryujinx.Ui.App.Common
|
||||||
|
|
||||||
if (!Directory.Exists(appDir))
|
if (!Directory.Exists(appDir))
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{appDir}\"");
|
Logger.Warning?.Print(LogClass.Application, $"The \"game_dirs\" section in \"{ReleaseInformation.ConfigName}\" contains an invalid directory: \"{appDir}\"");
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Logging.Targets;
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
{
|
{
|
||||||
|
@ -80,8 +82,23 @@ namespace Ryujinx.Ui.Common.Configuration
|
||||||
{
|
{
|
||||||
if (e.NewValue)
|
if (e.NewValue)
|
||||||
{
|
{
|
||||||
|
FileStream logFile = FileLogTarget.PrepareLogFile(AppDomain.CurrentDomain.BaseDirectory);
|
||||||
|
|
||||||
|
if (logFile == null)
|
||||||
|
{
|
||||||
|
logFile = FileLogTarget.PrepareLogFile(AppDataManager.BaseDirPath);
|
||||||
|
|
||||||
|
if (logFile == null)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the application directory or the Ryujinx directory is writable.");
|
||||||
|
Logger.RemoveTarget("file");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||||
new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"),
|
new FileLogTarget("file", logFile),
|
||||||
1000,
|
1000,
|
||||||
AsyncLogTargetOverflowAction.Block
|
AsyncLogTargetOverflowAction.Block
|
||||||
));
|
));
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Ryujinx.Ui.Common.Helper
|
||||||
[LibraryImport("shell32.dll", SetLastError = true)]
|
[LibraryImport("shell32.dll", SetLastError = true)]
|
||||||
public static partial void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
|
public static partial void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
|
||||||
|
|
||||||
public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild();
|
public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild;
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
private static bool AreMimeTypesRegisteredLinux() => File.Exists(Path.Combine(_mimeDbPath, "packages", "Ryujinx.xml"));
|
private static bool AreMimeTypesRegisteredLinux() => File.Exists(Path.Combine(_mimeDbPath, "packages", "Ryujinx.xml"));
|
||||||
|
@ -34,7 +34,7 @@ namespace Ryujinx.Ui.Common.Helper
|
||||||
|
|
||||||
if ((uninstall && AreMimeTypesRegisteredLinux()) || (!uninstall && !AreMimeTypesRegisteredLinux()))
|
if ((uninstall && AreMimeTypesRegisteredLinux()) || (!uninstall && !AreMimeTypesRegisteredLinux()))
|
||||||
{
|
{
|
||||||
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
|
string mimeTypesFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mime", "Ryujinx.xml");
|
||||||
string additionalArgs = !uninstall ? "--novendor" : "";
|
string additionalArgs = !uninstall ? "--novendor" : "";
|
||||||
|
|
||||||
using Process mimeProcess = new();
|
using Process mimeProcess = new();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Gdk;
|
using Gdk;
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Ui;
|
using Ryujinx.Ui;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
|
@ -52,7 +53,7 @@ namespace Ryujinx.Modules
|
||||||
ProcessStartInfo processStart = new(ryuName)
|
ProcessStartInfo processStart = new(ryuName)
|
||||||
{
|
{
|
||||||
UseShellExecute = true,
|
UseShellExecute = true,
|
||||||
WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory(),
|
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (string argument in CommandLineState.Arguments)
|
foreach (string argument in CommandLineState.Arguments)
|
||||||
|
|
|
@ -532,7 +532,7 @@ namespace Ryujinx.Modules
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid())
|
if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid)
|
||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
|
@ -546,7 +546,7 @@ namespace Ryujinx.Modules
|
||||||
#else
|
#else
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
if (ReleaseInformation.IsFlatHubBuild())
|
if (ReleaseInformation.IsFlatHubBuild)
|
||||||
{
|
{
|
||||||
GtkDialog.CreateWarningDialog("Updater Disabled!", "Please update Ryujinx via FlatHub.");
|
GtkDialog.CreateWarningDialog("Updater Disabled!", "Please update Ryujinx via FlatHub.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace Ryujinx
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Version = ReleaseInformation.GetVersion();
|
Version = ReleaseInformation.Version;
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||||
{
|
{
|
||||||
|
@ -167,8 +167,8 @@ namespace Ryujinx
|
||||||
Quality = 100,
|
Quality = 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
|
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||||
|
|
||||||
// Now load the configuration as the other subsystems are now registered
|
// Now load the configuration as the other subsystems are now registered
|
||||||
ConfigurationPath = File.Exists(localConfigurationPath)
|
ConfigurationPath = File.Exists(localConfigurationPath)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -24,7 +25,7 @@ namespace Ryujinx.Ui.Helper
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\".");
|
Logger.Warning?.Print(LogClass.Application, $"The \"custom_theme_path\" section in \"{ReleaseInformation.ConfigName}\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\".");
|
||||||
|
|
||||||
ConfigurationState.Instance.Ui.CustomThemePath.Value = "";
|
ConfigurationState.Instance.Ui.CustomThemePath.Value = "";
|
||||||
ConfigurationState.Instance.Ui.EnableCustomTheme.Value = false;
|
ConfigurationState.Instance.Ui.EnableCustomTheme.Value = false;
|
||||||
|
|
|
@ -1376,7 +1376,12 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void OpenLogsFolder_Pressed(object sender, EventArgs args)
|
private void OpenLogsFolder_Pressed(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string logPath = System.IO.Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "Logs");
|
string logPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||||
|
|
||||||
|
if (ReleaseInformation.IsValid)
|
||||||
|
{
|
||||||
|
logPath = System.IO.Path.Combine(AppDataManager.BaseDirPath, "Logs");
|
||||||
|
}
|
||||||
|
|
||||||
new DirectoryInfo(logPath).Create();
|
new DirectoryInfo(logPath).Create();
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ namespace Ryujinx.Ui.Widgets
|
||||||
_extractExeFsMenuItem.Sensitive = hasNca;
|
_extractExeFsMenuItem.Sensitive = hasNca;
|
||||||
_extractLogoMenuItem.Sensitive = hasNca;
|
_extractLogoMenuItem.Sensitive = hasNca;
|
||||||
|
|
||||||
_createShortcutMenuItem.Sensitive = !ReleaseInformation.IsFlatHubBuild();
|
_createShortcutMenuItem.Sensitive = !ReleaseInformation.IsFlatHubBuild;
|
||||||
|
|
||||||
PopupAtPointer(null);
|
PopupAtPointer(null);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue