diff --git a/ErogeHelper.Model/Services/Interface/IWindowsGuid.cs b/ErogeHelper.Model/Services/Interface/IWindowsGuid.cs new file mode 100644 index 0000000..87f2313 --- /dev/null +++ b/ErogeHelper.Model/Services/Interface/IWindowsGuid.cs @@ -0,0 +1,6 @@ +namespace ErogeHelper.Model.Services.Interface; + +public interface IWindowsGuid +{ + string MachineGuid { get; } +} diff --git a/ErogeHelper.Model/Services/SavedataSyncService.cs b/ErogeHelper.Model/Services/SavedataSyncService.cs index 50d49a9..53a1502 100644 --- a/ErogeHelper.Model/Services/SavedataSyncService.cs +++ b/ErogeHelper.Model/Services/SavedataSyncService.cs @@ -5,65 +5,133 @@ using System.Threading.Tasks; using ErogeHelper.Model.Repositories.Interface; using ErogeHelper.Model.Services.Interface; +using ErogeHelper.Shared; +using ErogeHelper.Shared.Contracts; +using ErogeHelper.Shared.Entities; +using Newtonsoft.Json; +using Vanara.PInvoke; namespace ErogeHelper.Model.Services; public class SavedataSyncService : ISavedataSyncService { - //private readonly IEhConfigRepository _ehConfigDataService; - //private readonly INetworkListManager _networkListManager; - //private bool IsNetConnected => _networkListManager.IsConnectedToInternet; - //private string GameMd5 => _gameDataService.Md5; - //private string LocalSavedataFolder => _ehDbRepository.GameInfo?.SavedataPath ?? string.Empty; - //private string CloudSavedataFolder => Path.Combine( - // _ehConfigDataService.ExternalSharedDrivePath, Path.GetFileNameWithoutExtension(LocalSavedataFolder)); - //private string CloudDbFilePath => Path.Combine( - // _ehConfigDataService.ExternalSharedDrivePath, ConstantValues.EhCloudDbFilename); - - //public string[] ExcludeFiles { get; } + public static Func MessageBox { get; set; } = null!; + + private string FolderName => Path.GetFileNameWithoutExtension(LocalSavedataFolder); + private string LocalSavedataFolder => _gameInfoRepository.GameInfo.SaveDataPath; + private string CloudGameSaveFolder => Path.Combine( + _ehConfigRepository.ExternalSharedDrivePath, FolderName); + private string CloudDBFilePath => Path.Combine( + _ehConfigRepository.ExternalSharedDrivePath, ConstantValue.CloudDbFilename); + private readonly IGameInfoRepository _gameInfoRepository; + private readonly IEHConfigRepository _ehConfigRepository; public SavedataSyncService( - IGameInfoRepository? gameInfoRepository = null) + IGameInfoRepository? gameInfoRepository = null, + IEHConfigRepository? ehConfigRepository = null) { _gameInfoRepository = gameInfoRepository ?? Shared.DependencyResolver.GetService(); + _ehConfigRepository = ehConfigRepository ?? Shared.DependencyResolver.GetService(); } - //public void InitGameData() - //{ - // var cloudGameDatas = File.Exists(CloudDbFilePath) ? - // JsonConvert.DeserializeObject>(File.ReadAllText(CloudDbFilePath)) : - // new(); + // 打开设置switch上传时 + // 云端没有 就上传当前存档 + // 云端有了 -> 对比。。。 - // var currentData = CreateGameData(); - // cloudGameDatas.Add(currentData); - // Directory.CreateDirectory(CloudSavedataFolder); - // File.WriteAllText(CloudDbFilePath, JsonConvert.SerializeObject(cloudGameDatas)); + // 游戏启动时 + // 连不上网 None + // 找不到数据库|找不到游戏存档 throw Toast提示 + // 连得上网 -> 对比。。。 - // Task.Run(UploadFiles); + // 游戏结束时 + // 连不上网 None + // 找不到数据库|找不到游戏存档 throw 弹窗提示或直接报错 + // 连得上网 -> 对比。。。 + + // 对比时需要询问用户输入,使用 回调 或 IObservable 或 Interaction + + //public void UpdateSync() + //{ + // if (!WinINet.InternetGetConnectedState(out _)) + // { + // return; + // } + // var cloadDBFilePath = CloudDBFilePath; + // if (File.Exists(cloadDBFilePath)) + // { + // throw new FileNotFoundException(cloadDBFilePath); + // } + // var localSavedataFolder = LocalSavedataFolder; + // if (File.Exists(localSavedataFolder)) + // { + // throw new FileNotFoundException(localSavedataFolder); + // } - // //if (File.Exists(cloudDbFile)) - // //{ - // // var cloudGameDatas = - // // JsonConvert.DeserializeObject>(File.ReadAllText(cloudDbFile)) - // // .CheckNull(); - // // var currentData = CreateGameData(); - // // cloudGameDatas.Add(currentData); + // var gamedata = GetCurrentGameData(FolderName, cloadDBFilePath); - // // File.WriteAllText(cloudDbFile, JsonConvert.SerializeObject(cloudGameDatas)); - // //} - // //else - // //{ - // // var currentData = CreateGameData(); - // // var cloudGameDatas = new List { currentData }; + // if (gamedata is null) + // { + // // 没有数据库 或 当前游戏信息 + // // first time + // return; + // } - // // File.WriteAllText(cloudDbFile, JsonConvert.SerializeObject(cloudGameDatas)); - // //} + // var guid = DependencyResolver.GetService().MachineGuid; + // var saveTime = GetLastDirectoryModifiedTime(localSavedataFolder); + // if (gamedata.PCId.Equals(guid, StringComparison.Ordinal) && gamedata.LastTimeModified < saveTime) + // { + // UploadFiles(localSavedataFolder, CloudGameSaveFolder); + // UpdateLastModifiedTime(saveTime); + // } + // else if (!gamedata.PCId.Equals(guid, StringComparison.Ordinal)) + // { + // var result = MessageBox(); + // if (result == true) + // { + // UploadFiles(localSavedataFolder, CloudGameSaveFolder); + // UpdateSavedataInfo(); + // } + // } //} - //public CloudSaveDataEntity CreateGameData() => - // new(GameMd5, + + ////public void InitGameData() + ////{ + //// var cloudGameDatas = File.Exists(CloudDbFilePath) ? + //// JsonConvert.DeserializeObject>(File.ReadAllText(CloudDbFilePath)) : + //// new(); + + //// var currentData = CreateGameData(); + //// cloudGameDatas.Add(currentData); + //// Directory.CreateDirectory(CloudSavedataFolder); + //// File.WriteAllText(CloudDbFilePath, JsonConvert.SerializeObject(cloudGameDatas)); + + //// Task.Run(UploadFiles); + + //// //if (File.Exists(cloudDbFile)) + //// //{ + //// // var cloudGameDatas = + //// // JsonConvert.DeserializeObject>(File.ReadAllText(cloudDbFile)) + //// // .CheckNull(); + + //// // var currentData = CreateGameData(); + //// // cloudGameDatas.Add(currentData); + + //// // File.WriteAllText(cloudDbFile, JsonConvert.SerializeObject(cloudGameDatas)); + //// //} + //// //else + //// //{ + //// // var currentData = CreateGameData(); + //// // var cloudGameDatas = new List { currentData }; + + //// // File.WriteAllText(cloudDbFile, JsonConvert.SerializeObject(cloudGameDatas)); + //// //} + ////} + + //public CloudSaveDataTerm CreateGameData() => + // new(FolderName, // LocalSavedataFolder, // GetLastDirectoryModifiedTime(LocalSavedataFolder), // ExcludeFiles, @@ -73,11 +141,65 @@ public SavedataSyncService( ///// ///// Get savedata info from cloud ///// - //public CloudSaveDataEntity? GetCurrentGameData() => - // !File.Exists(CloudDbFilePath) - // ? null - // : JsonConvert.DeserializeObject>(File.ReadAllText(CloudDbFilePath)) - // .FirstOrDefault(g => g.Md5.Equals(GameMd5, StringComparison.Ordinal)); + //// TODO: Use System.Text.Json + //private static CloudSaveDataTerm? GetCurrentGameData(string folderName, string cloudDBFilePath) => + // JsonConvert.DeserializeObject>(File.ReadAllText(cloudDBFilePath)) + // ?.FirstOrDefault(g => g.FolderName.Equals(folderName, StringComparison.Ordinal)); + + //private static DateTime GetLastDirectoryModifiedTime(string path) => + // new DirectoryInfo(path) + // .EnumerateFileSystemInfos() + // .Max(i => i.LastWriteTime); + + //private static void UploadFiles(string localSavedataFolder, string cloudSavedataFolder) => + // DirectoryCopy(localSavedataFolder, cloudSavedataFolder, true, true); + + //private void UpdateLastModifiedTime(string cloudDBFilePath, DateTime time) + //{ + // var cloudGameDatas = + // JsonConvert.DeserializeObject>(File.ReadAllText(cloudDBFilePath)) + // ?? new(); + + // var currentData = CreateGameData(); + + // cloudGameDatas.RemoveAll(item => item.Md5.Equals(currentData.Md5, StringComparison.Ordinal)); + // cloudGameDatas.Add(currentData with { LastTimeModified = time }); + // File.WriteAllText(cloudDBFilePath, JsonConvert.SerializeObject(cloudGameDatas)); + //} + + //private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs, bool overwrite = false) + //{ + // // 不需要的文件 + // // md5相同的文件 变化的 + // DirectoryInfo dir = new(sourceDirName); + + // if (!dir.Exists) + // { + // throw new DirectoryNotFoundException( + // "Source directory does not exist or could not be found: " + // + sourceDirName); + // } + + // var dirs = dir.GetDirectories(); + + // Directory.CreateDirectory(destDirName); + + // var files = dir.GetFiles(); + // foreach (var file in files) + // { + // var tempPath = Path.Combine(destDirName, file.Name); + // file.CopyTo(tempPath, overwrite); + // } + + // if (copySubDirs) + // { + // foreach (var subdir in dirs) + // { + // var tempPath = Path.Combine(destDirName, subdir.Name); + // DirectoryCopy(subdir.FullName, tempPath, copySubDirs, overwrite); + // } + // } + //} //public void DownloadSync() //{ @@ -130,40 +252,6 @@ public SavedataSyncService( // } //} - //public void UpdateSync() - //{ - // try - // { - // if (!IsNetConnected) - // { - // return; - // } - - // CloudSaveDataEntity gamedata = GetCurrentGameData() ?? throw new ArgumentNullException(nameof(gamedata)); - - // var savedataPath = _ehDbRepository.GameInfo?.SavedataPath ?? string.Empty; - // var saveTime = GetLastDirectoryModifiedTime(savedataPath); - // if (gamedata.PCId.Equals(Utils.MachineGuid, StringComparison.Ordinal) && gamedata.LastTimeModified < saveTime) - // { - // UploadFiles(); - // UpdateLastModifiedTime(saveTime); - // } - // else if (!gamedata.PCId.Equals(Utils.MachineGuid, StringComparison.Ordinal)) - // { - // var result = ModernWpf.MessageBox.Show("Upload savedata to cloud?", "Warning", System.Windows.MessageBoxButton.YesNo); - // if (result == System.Windows.MessageBoxResult.Yes) - // { - // UploadFiles(); - // UpdateSavedataInfo(); - // } - // } - // } - // catch (Exception ex) - // { - // this.Log().Debug(ex); - // }; - //} - //private void UpdateSavedataInfo() //{ // CloudDbFilePath.CheckFileExist(); @@ -179,60 +267,7 @@ public SavedataSyncService( // File.WriteAllText(CloudDbFilePath, JsonConvert.SerializeObject(cloudGameDatas)); //} - //private void UpdateLastModifiedTime(DateTime time) - //{ - // CloudDbFilePath.CheckFileExist(); - // var cloudGameDatas = - // JsonConvert.DeserializeObject>(File.ReadAllText(CloudDbFilePath)) - // ?? new(); - - // var currentData = CreateGameData(); - - // cloudGameDatas.RemoveAll(item => item.Md5.Equals(currentData.Md5, StringComparison.Ordinal)); - // cloudGameDatas.Add(currentData with { LastTimeModified = time }); - // File.WriteAllText(CloudDbFilePath, JsonConvert.SerializeObject(cloudGameDatas)); - //} //private void DownloadFiles() => DirectoryCopy(CloudSavedataFolder, LocalSavedataFolder, true, true); - //private void UploadFiles() => DirectoryCopy(LocalSavedataFolder, CloudSavedataFolder, true, true); - - //private static DateTime GetLastDirectoryModifiedTime(string path) => - // new DirectoryInfo(path) - // .EnumerateFileSystemInfos() - // .Max(i => i.LastWriteTime); - - //private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs, bool overwrite = false) - //{ - // // 不需要的文件 - // // md5相同的文件 变化的 - // DirectoryInfo dir = new(sourceDirName); - - // if (!dir.Exists) - // { - // throw new DirectoryNotFoundException( - // "Source directory does not exist or could not be found: " - // + sourceDirName); - // } - - // var dirs = dir.GetDirectories(); - - // Directory.CreateDirectory(destDirName); - - // var files = dir.GetFiles(); - // foreach (var file in files) - // { - // var tempPath = Path.Combine(destDirName, file.Name); - // file.CopyTo(tempPath, overwrite); - // } - - // if (copySubDirs) - // { - // foreach (var subdir in dirs) - // { - // var tempPath = Path.Combine(destDirName, subdir.Name); - // DirectoryCopy(subdir.FullName, tempPath, copySubDirs, overwrite); - // } - // } - //} } diff --git a/ErogeHelper.Shared/Entities/CloudSaveDataTerm.cs b/ErogeHelper.Shared/Entities/CloudSaveDataTerm.cs index 8165f3f..f66c470 100644 --- a/ErogeHelper.Shared/Entities/CloudSaveDataTerm.cs +++ b/ErogeHelper.Shared/Entities/CloudSaveDataTerm.cs @@ -1,41 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ErogeHelper.Shared.Entities; - -public record CloudSaveDataTerm -{ - public CloudSaveDataTerm( - string md5, - string localPath, - DateTime lastTimeModified, - string[] excludeFiles, - string pcName, - string pcId) - { - Md5 = md5; - FolderName = Path.GetFileNameWithoutExtension(localPath) ?? string.Empty; - LocalPath = localPath; - LastTimeModified = lastTimeModified; - ExcludeFiles = excludeFiles; - PCName = pcName; - PCId = pcId; - } - - public string Md5 { get; init; } - - public string FolderName { get; init; } - - public string LocalPath { get; init; } - - public DateTime LastTimeModified { get; init; } - - public string PCName { get; init; } - - public string PCId { get; init; } - - public string[] ExcludeFiles { get; init; } -} +namespace ErogeHelper.Shared.Entities; + +public record CloudSaveDataTerm( + string FolderName, + string LocalPath, + DateTime LastTimeModified, + string[] ExcludeFiles, + string PCName, + string PCId); diff --git a/ErogeHelper.ViewModel/CloudSave/CloudSaveViewModel.cs b/ErogeHelper.ViewModel/CloudSave/CloudSaveViewModel.cs index 9c350fc..e701c39 100644 --- a/ErogeHelper.ViewModel/CloudSave/CloudSaveViewModel.cs +++ b/ErogeHelper.ViewModel/CloudSave/CloudSaveViewModel.cs @@ -70,8 +70,8 @@ public CloudSaveViewModel( .Subscribe(hasPath => CanEnable = hasPath); this.WhenAnyValue(x => x.IsSwitchOn) + .ObserveOn(RxApp.TaskpoolScheduler) .Subscribe(useCloudsave => gameInfoRepository.UpdateCloudStatus(useCloudsave)); - //_ehDbRepository.UpdateCloudStatus(value); //if (value) //{ // var savedataInfo = _savedataSyncService.GetCurrentGameData(); diff --git a/ErogeHelper/AppLauncher.cs b/ErogeHelper/AppLauncher.cs index 2841afb..d7b9e58 100644 --- a/ErogeHelper/AppLauncher.cs +++ b/ErogeHelper/AppLauncher.cs @@ -116,9 +116,10 @@ private static void InitializeGameData( Path.Combine(gameDir, Path.GetFileNameWithoutExtension(gamePath) + ".exe"))); } - // Note: There is temporal coupling of md5 + // Note: There is temporal coupling of md5 and GameInfo.SaveDataPath gameDataService.InitGameMd5AndPath(md5, gamePath); var gameInfoRepository = DependencyResolver.GetService(); + var savedataSyncSerivce = DependencyResolver.GetService(); // NOTE: There is a >5mb large object heap allocate var gameInfo = gameInfoRepository.TryGetGameInfo(); @@ -170,10 +171,17 @@ private static void InitializeGameData( .Select(_ => WpfHelper.IsGameForegroundFullscreen(gameDataService.GameRealWindowHandle)) .DistinctUntilChanged()); - var savedataSyncSerivce = DependencyResolver.GetService(); + void CheckSyncCloud() + { + if (gameInfoRepository.GameInfo.UseCloudSave) + { + + } + } + gameWindowHooker.WhenViewOperated .Where(op => op == ViewOperation.TerminateApp) - .Do(_ => CheckSyncCloud(gameInfoRepository)) + .Do(_ => CheckSyncCloud()) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => App.Terminate()); @@ -223,12 +231,4 @@ private static void InitializeGameData( return leProc; } - - private static void CheckSyncCloud(IGameInfoRepository gameInfoRepository) - { - if (gameInfoRepository.GameInfo.UseCloudSave) - { - - } - } } diff --git a/ErogeHelper/Platform/DI.cs b/ErogeHelper/Platform/DI.cs index 274608a..dde4847 100644 --- a/ErogeHelper/Platform/DI.cs +++ b/ErogeHelper/Platform/DI.cs @@ -77,6 +77,10 @@ public static void RegisterServices() // Model->ViewModel data flow gameWindowHookerService.BringKeyboardWindowTopDataFlow .Subscribe(_ => User32.BringWindowToTop(State.VirtualKeyboardWindowHandle)); + + // Model->View ask for interactions + SavedataSyncService.MessageBox = () => + ShowMessageBox("Upload savedata to cloud?", "Warning", MessageBoxButton.YesNo) == MessageBoxResult.Yes; } /// @@ -93,6 +97,7 @@ private static void RegisterDataServices(IEHConfigRepository ehConfigRepository) new RefitSettings { ContentSerializer = new XmlContentSerializer() })); Locator.CurrentMutable.Register( () => RestService.For(ehConfigRepository.ServerBaseUrl)); + Locator.CurrentMutable.RegisterLazySingleton(() => new WindowsGuid()); // In memory state Locator.CurrentMutable.RegisterLazySingleton(() => new GameDataService()); @@ -265,12 +270,15 @@ private static void RegisterInteractions() }); } - public static MessageBoxResult? ShowMessageBox(string messageBoxText, string caption = "Eroge Helper") + public static MessageBoxResult? ShowMessageBox( + string messageBoxText, + string caption = "Eroge Helper", + MessageBoxButton buttons = MessageBoxButton.OK) { var owner = Application.Current.Windows.Cast().FirstOrDefault((Window window) => window.IsActive && window.ShowActivated); - var messageBoxWindow = new MessageBoxWindow(messageBoxText, caption, MessageBoxButton.OK, null) + var messageBoxWindow = new MessageBoxWindow(messageBoxText, caption, buttons, null) { Owner = owner, WindowStartupLocation = (owner == null) diff --git a/ErogeHelper/Platform/Windows/WindowsGuid.cs b/ErogeHelper/Platform/Windows/WindowsGuid.cs new file mode 100644 index 0000000..589f1d9 --- /dev/null +++ b/ErogeHelper/Platform/Windows/WindowsGuid.cs @@ -0,0 +1,39 @@ +using Microsoft.Win32; +using ErogeHelper.Model.Services.Interface; + +namespace ErogeHelper.Platform.Windows; + +internal class WindowsGuid : IWindowsGuid +{ + public string MachineGuid { get; } = GetMachineGuid(); + + private static string GetMachineGuid() + { + if (Environment.Is64BitOperatingSystem) + { + var keyBaseX64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); + var keyX64 = keyBaseX64.OpenSubKey( + @"SOFTWARE\Microsoft\Cryptography", RegistryKeyPermissionCheck.ReadSubTree); + var resultObjX64 = keyX64?.GetValue("MachineGuid", string.Empty); + + if (resultObjX64 is not null) + { + return resultObjX64.ToString() ?? string.Empty; + } + } + else + { + var keyBaseX86 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); + var keyX86 = keyBaseX86.OpenSubKey( + @"SOFTWARE\Microsoft\Cryptography", RegistryKeyPermissionCheck.ReadSubTree); + var resultObjX86 = keyX86?.GetValue("MachineGuid", string.Empty); + + if (resultObjX86 != null) + { + return resultObjX86.ToString() ?? string.Empty; + } + } + + return string.Empty; + } +} diff --git a/ErogeHelper/libs/aarch64/libwtfdanmaku.dll b/ErogeHelper/libs/aarch64/libwtfdanmaku.dll new file mode 100644 index 0000000..59c9c50 Binary files /dev/null and b/ErogeHelper/libs/aarch64/libwtfdanmaku.dll differ diff --git a/ErogeHelper/libs/x64/libwtfdanmaku.dll b/ErogeHelper/libs/x64/libwtfdanmaku.dll new file mode 100644 index 0000000..b9878fa Binary files /dev/null and b/ErogeHelper/libs/x64/libwtfdanmaku.dll differ diff --git a/ErogeHelper/libs/x86/libwtfdanmaku.dll b/ErogeHelper/libs/x86/libwtfdanmaku.dll new file mode 100644 index 0000000..2d8d91f Binary files /dev/null and b/ErogeHelper/libs/x86/libwtfdanmaku.dll differ