Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restrict Game Path Access #2239

Merged
merged 6 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Snap.Hutao/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ dotnet_diagnostic.CA1849.severity = suggestion
dotnet_diagnostic.CA1852.severity = suggestion

# CA2000: 丢失范围之前释放对象
dotnet_diagnostic.CA2000.severity = suggestion
dotnet_diagnostic.CA2000.severity = none

# CA2002: 不要锁定具有弱标识的对象
dotnet_diagnostic.CA2002.severity = suggestion
Expand Down
6 changes: 3 additions & 3 deletions src/Snap.Hutao/Snap.Hutao/Core/HutaoRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ private static string InitializeDataFolder()
string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

#if IS_ALPHA_BUILD
string folderName = "HutaoAlpha";
const string FolderName = "HutaoAlpha";
#else
// 使得迁移能正常生成
string folderName = "Hutao";
const string FolderName = "Hutao";
#endif
string path = Path.GetFullPath(Path.Combine(myDocuments, folderName));
string path = Path.GetFullPath(Path.Combine(myDocuments, FolderName));
Directory.CreateDirectory(path);
return path;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Snap.Hutao/Snap.Hutao/Core/IO/Ini/IniParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ public override string ToString()
{
return $"{Key}={Value}";
}
}
}
19 changes: 10 additions & 9 deletions src/Snap.Hutao/Snap.Hutao/Core/IO/Ini/IniSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Immutable;
using System.IO;

namespace Snap.Hutao.Core.IO.Ini;

internal static class IniSerializer
{
public static List<IniElement> DeserializeFromFile(string filePath)
public static ImmutableArray<IniElement> DeserializeFromFile(string filePath)
{
using (StreamReader reader = File.OpenText(filePath))
{
return DeserializeCore(reader);
}
}

public static List<IniElement> Deserialize(Stream fileStream)
public static ImmutableArray<IniElement> Deserialize(Stream stream)
{
using (StreamReader reader = new(fileStream))
using (StreamReader reader = new(stream))
{
return DeserializeCore(reader);
}
Expand All @@ -39,9 +40,9 @@ public static void Serialize(FileStream fileStream, IEnumerable<IniElement> elem
}
}

private static List<IniElement> DeserializeCore(StreamReader reader)
private static ImmutableArray<IniElement> DeserializeCore(StreamReader reader)
{
List<IniElement> results = [];
ImmutableArray<IniElement>.Builder builder = ImmutableArray.CreateBuilder<IniElement>();
IniSection? currentSection = default;

while (reader.ReadLine() is { } line)
Expand All @@ -56,26 +57,26 @@ private static List<IniElement> DeserializeCore(StreamReader reader)
if (lineSpan[0] is '[')
{
IniSection section = new(lineSpan[1..^1].ToString());
results.Add(section);
builder.Add(section);
currentSection = section;
}

if (lineSpan[0] is ';')
{
IniComment comment = new(lineSpan[1..].ToString());
results.Add(comment);
builder.Add(comment);
currentSection?.Children.Add(comment);
}

if (lineSpan.TrySplitIntoTwo('=', out ReadOnlySpan<char> left, out ReadOnlySpan<char> right))
{
IniParameter parameter = new(left.Trim().ToString(), right.Trim().ToString());
results.Add(parameter);
builder.Add(parameter);
currentSection?.Children.Add(parameter);
}
}

return results;
return builder.ToImmutable();
}

private static void SerializeCore(StreamWriter writer, IEnumerable<IniElement> elements)
Expand Down
32 changes: 32 additions & 0 deletions src/Snap.Hutao/Snap.Hutao/Core/Threading/AsyncReaderWriterLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ public Task<Releaser> ReaderLockAsync()
}
}

public bool TryReaderLock(out Releaser releaser)
{
lock (waitingWriters)
{
if (status >= 0 && waitingWriters.Count == 0)
{
++status;
releaser = new(this, false);
return true;
}
}

releaser = default;
return false;
}

public Task<Releaser> WriterLockAsync()
{
lock (waitingWriters)
Expand All @@ -55,6 +71,22 @@ public Task<Releaser> WriterLockAsync()
}
}

public bool TryWriterLock(out Releaser releaser)
{
lock (waitingWriters)
{
if (status == 0)
{
status = -1;
releaser = new(this, true);
return true;
}
}

releaser = default;
return false;
}

private void ReaderRelease()
{
TaskCompletionSource<Releaser>? toWake = default;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using JetBrains.Annotations;
using Snap.Hutao.Core.IO;

namespace Snap.Hutao.Factory.Picker;

internal interface IFileSystemPickerInteraction
{
ValueResult<bool, ValueFile> PickFile(string? title, string? defaultFileName, (string Name, string Type)[]? filters);
ValueResult<bool, ValueFile> PickFile([LocalizationRequired] string? title, string? defaultFileName, (string Name, string Type)[]? filters);

ValueResult<bool, string> PickFolder(string? title);
ValueResult<bool, string> PickFolder([LocalizationRequired] string? title);

ValueResult<bool, ValueFile> SaveFile(string? title, string? defaultFileName, (string Name, string Type)[]? filters);
ValueResult<bool, ValueFile> SaveFile([LocalizationRequired] string? title, string? defaultFileName, (string Name, string Type)[]? filters);
}
11 changes: 6 additions & 5 deletions src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.

using CommunityToolkit.Mvvm.ComponentModel;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
Expand Down Expand Up @@ -153,25 +154,25 @@ protected T GetOption<T>(ref T? storage, string key, Func<string, T> deserialize

protected bool SetOption(ref string? storage, string key, string? value, [CallerMemberName] string? propertyName = null)
{
return SetOption(ref storage, key, value, v => v, propertyName);
return SetOption(ref storage, key, value, static v => v, propertyName);
}

protected bool SetOption(ref bool? storage, string key, bool value, [CallerMemberName] string? propertyName = null)
{
return SetOption(ref storage, key, value, v => $"{v}", propertyName);
return SetOption(ref storage, key, value, static v => $"{v}", propertyName);
}

protected bool SetOption(ref int? storage, string key, int value, [CallerMemberName] string? propertyName = null)
{
return SetOption(ref storage, key, value, v => $"{v}", propertyName);
return SetOption(ref storage, key, value, static v => $"{v}", propertyName);
}

protected bool SetOption(ref float? storage, string key, float value, [CallerMemberName] string? propertyName = null)
{
return SetOption(ref storage, key, value, v => $"{v}", propertyName);
return SetOption(ref storage, key, value, static v => $"{v}", propertyName);
}

protected bool SetOption<T>(ref T? storage, string key, T value, Func<T, string?> serializer, [CallerMemberName] string? propertyName = null)
protected bool SetOption<T>(ref T? storage, string key, T value, [RequireStaticDelegate] Func<T, string?> serializer, [CallerMemberName] string? propertyName = null)
{
if (!SetProperty(ref storage, value, propertyName))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Snap.Hutao.Core.IO.Ini;
using Snap.Hutao.Service.Game.Scheme;
using System.IO;
using System.Runtime.CompilerServices;

namespace Snap.Hutao.Service.Game.Configuration;

Expand All @@ -16,40 +17,62 @@ internal sealed partial class GameChannelOptionsService : IGameChannelOptionsSer

public ChannelOptions GetChannelOptions()
{
if (!launchOptions.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
if (!launchOptions.TryGetGameFileSystem(out IGameFileSystem? gameFileSystem))
{
return ChannelOptions.GamePathNullOrEmpty();
}

bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileSystem.GameFileName);

if (!File.Exists(gameFileSystem.GameConfigFilePath))
using (gameFileSystem)
{
// Try restore the configuration file if it does not exist
// The configuration file may be deleted by a incompatible launcher
gameConfigurationFileService.Restore(gameFileSystem.GameConfigFilePath);
}
if (!File.Exists(gameFileSystem.GetGameConfigurationFilePath()))
{
// Try restore the configuration file if it does not exist
// The configuration file may be deleted by an incompatible launcher
gameConfigurationFileService.Restore(gameFileSystem.GetGameConfigurationFilePath());
}

if (!File.Exists(gameFileSystem.ScriptVersionFilePath))
{
// Try to fix ScriptVersion by reading game_version from the configuration file
// Will check the configuration file first
// If the configuration file and ScriptVersion file are both missing, the game content is corrupted
if (!gameFileSystem.TryFixScriptVersion())
if (!File.Exists(gameFileSystem.GetScriptVersionFilePath()))
{
return ChannelOptions.GameContentCorrupted(gameFileSystem.GameDirectory);
// Try to fix ScriptVersion by reading game_version from the configuration file
// Will check the configuration file first
// If the configuration file and ScriptVersion file are both missing, the game content is corrupted
if (!gameFileSystem.TryFixScriptVersion())
{
return ChannelOptions.GameContentCorrupted(gameFileSystem.GetGameDirectory());
}
}
}

if (!File.Exists(gameFileSystem.GameConfigFilePath))
{
return ChannelOptions.ConfigurationFileNotFound(gameFileSystem.GameConfigFilePath);
}
if (!File.Exists(gameFileSystem.GetGameConfigurationFilePath()))
{
return ChannelOptions.ConfigurationFileNotFound(gameFileSystem.GetGameConfigurationFilePath());
}

string? channel = default;
string? subChannel = default;
foreach (ref readonly IniElement element in IniSerializer.DeserializeFromFile(gameFileSystem.GetGameConfigurationFilePath()).AsSpan())
{
if (element is not IniParameter parameter)
{
continue;
}

List<IniParameter> parameters = IniSerializer.DeserializeFromFile(gameFileSystem.GameConfigFilePath).OfType<IniParameter>().ToList();
string? channel = parameters.FirstOrDefault(p => p.Key is ChannelOptions.ChannelName)?.Value;
string? subChannel = parameters.FirstOrDefault(p => p.Key is ChannelOptions.SubChannelName)?.Value;
switch (parameter.Key)
{
case ChannelOptions.ChannelName:
channel = parameter.Value;
break;
case ChannelOptions.SubChannelName:
subChannel = parameter.Value;
break;
}

return new(channel, subChannel, isOversea);
if (channel is not null && subChannel is not null)
{
break;
}
}

return new(channel, subChannel, gameFileSystem.IsOversea());
}
}
}
36 changes: 14 additions & 22 deletions src/Snap.Hutao/Snap.Hutao/Service/Game/GameAudioSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,27 @@ namespace Snap.Hutao.Service.Game;

internal sealed class GameAudioSystem
{
private readonly string gameDirectory;

private bool? chinese;
private bool? english;
private bool? japanese;
private bool? korean;

public GameAudioSystem(string gameFilePath)
public GameAudioSystem(string gameDirectory)
{
string? directory = Path.GetDirectoryName(gameFilePath);
ArgumentException.ThrowIfNullOrEmpty(directory);
gameDirectory = directory;
Chinese = File.Exists(Path.Combine(gameDirectory, GameConstants.AudioChinesePkgVersion));
English = File.Exists(Path.Combine(gameDirectory, GameConstants.AudioEnglishPkgVersion));
Japanese = File.Exists(Path.Combine(gameDirectory, GameConstants.AudioJapanesePkgVersion));
Korean = File.Exists(Path.Combine(gameDirectory, GameConstants.AudioKoreanPkgVersion));
}

public GameAudioSystem(bool chinese, bool english, bool japanese, bool korean)
{
gameDirectory = default!;

this.chinese = chinese;
this.english = english;
this.japanese = japanese;
this.korean = korean;
Chinese = chinese;
English = english;
Japanese = japanese;
Korean = korean;
}

public bool Chinese { get => chinese ??= File.Exists(Path.Combine(gameDirectory, GameConstants.AudioChinesePkgVersion)); }
public bool Chinese { get; }

public bool English { get => english ??= File.Exists(Path.Combine(gameDirectory, GameConstants.AudioEnglishPkgVersion)); }
public bool English { get; }

public bool Japanese { get => japanese ??= File.Exists(Path.Combine(gameDirectory, GameConstants.AudioJapanesePkgVersion)); }
public bool Japanese { get; }

public bool Korean { get => korean ??= File.Exists(Path.Combine(gameDirectory, GameConstants.AudioKoreanPkgVersion)); }
}
public bool Korean { get; }
}
Loading