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:
TSRBerry 2024-01-29 19:58:18 +01:00 committed by GitHub
parent 7795b662a9
commit 70fcba39de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 209 additions and 117 deletions

View file

@ -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"

View file

@ -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

View file

@ -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
View 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" "$@"

View file

@ -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(

View file

@ -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))

View file

@ -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();

View file

@ -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;
} }

View file

@ -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)

View file

@ -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
} }
} }

View 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);
}
}
}

View file

@ -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

View file

@ -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))
{ {

View file

@ -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;
} }

View file

@ -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
)); ));

View file

@ -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();

View file

@ -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)

View file

@ -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.");
} }

View file

@ -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)

View file

@ -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;

View file

@ -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();

View file

@ -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);
} }