From e50883a863c50021fb0f1338cca01812d00d8e00 Mon Sep 17 00:00:00 2001 From: kenjiuno Date: Thu, 20 Jun 2024 00:10:42 +0900 Subject: [PATCH] Improve game data extraction process --- .../GameDataExtractionServiceTest.cs | 56 ++ .../OpenKh.Tests.ModsManager.csproj | 26 + .../Services/GameDataExtractionService.cs | 286 ++++++++++ .../ViewModels/SetupWizardViewModel.cs | 496 +++++------------- .../Views/SetupWizardWindow.xaml.cs | 2 + OpenKh.sln | 13 + 6 files changed, 523 insertions(+), 356 deletions(-) create mode 100644 OpenKh.Tests.ModsManager/GameDataExtractionServiceTest.cs create mode 100644 OpenKh.Tests.ModsManager/OpenKh.Tests.ModsManager.csproj create mode 100644 OpenKh.Tools.ModsManager/Services/GameDataExtractionService.cs diff --git a/OpenKh.Tests.ModsManager/GameDataExtractionServiceTest.cs b/OpenKh.Tests.ModsManager/GameDataExtractionServiceTest.cs new file mode 100644 index 000000000..e38fa47ce --- /dev/null +++ b/OpenKh.Tests.ModsManager/GameDataExtractionServiceTest.cs @@ -0,0 +1,56 @@ +using OpenKh.Tools.ModsManager.Services; +using System.Threading; +using Xunit; + +namespace OpenKh.Tests.ModsManager +{ + public class GameDataExtractionServiceTest + { + private readonly GameDataExtractionService _gameDataExtractionService = new GameDataExtractionService(); + + //[Fact] + public async Task ExtractKh2Ps2EditionAsyncTest() + { + await _gameDataExtractionService.ExtractKh2Ps2EditionAsync( + isoLocation: @"H:\CCD\KH2fm.ISO", + gameDataLocation: @"H:\Tmp\ModsManagerExtractionRoot", + onProgress: (progress) => Console.WriteLine($"{progress:P}") + ); + } + + //[Fact] + public async Task ExtractKhPcEditionAsyncTest() + { + var pcReleaseLocation = @"H:\Program Files\Epic Games/KH_1.5_2.5"; + var pcReleaseLocationKH3D = @"H:\Program Files\Epic Games/KH_2.8"; + var pcReleaseLanguage = "jp"; + var langFolder = (ConfigurationService.PCVersion == "Steam" && pcReleaseLanguage == "en") ? "dt" : pcReleaseLanguage; + + await _gameDataExtractionService.ExtractKhPcEditionAsync( + gameDataLocation: @"H:\Tmp\ModsManagerPcExtractionRoot", + onProgress: (progress) => Console.WriteLine($"{progress:P}"), + getKhFilePath: fileName + => Path.Combine( + pcReleaseLocation, + "Image", + langFolder, + fileName + ), + getKh3dFilePath: fileName + => Path.Combine( + pcReleaseLocationKH3D, + "Image", + langFolder, + fileName + ), + extractkh1: true, + extractkh2: true, + extractbbs: true, + extractrecom: true, + extractkh3d: true, + ifRetry: ex => Task.FromResult(false), + cancellationToken: CancellationToken.None + ); + } + } +} diff --git a/OpenKh.Tests.ModsManager/OpenKh.Tests.ModsManager.csproj b/OpenKh.Tests.ModsManager/OpenKh.Tests.ModsManager.csproj new file mode 100644 index 000000000..144697d24 --- /dev/null +++ b/OpenKh.Tests.ModsManager/OpenKh.Tests.ModsManager.csproj @@ -0,0 +1,26 @@ + + + + net6.0-windows + enable + enable + + false + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/OpenKh.Tools.ModsManager/Services/GameDataExtractionService.cs b/OpenKh.Tools.ModsManager/Services/GameDataExtractionService.cs new file mode 100644 index 000000000..63a365d40 --- /dev/null +++ b/OpenKh.Tools.ModsManager/Services/GameDataExtractionService.cs @@ -0,0 +1,286 @@ +using OpenKh.Common; +using OpenKh.Kh1; +using OpenKh.Kh2; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xe.IO; + +namespace OpenKh.Tools.ModsManager.Services +{ + public class GameDataExtractionService + { + private const int BufferSize = 65536; + private const string REMASTERED_FILES_FOLDER_NAME = "remastered"; + + public class BadConfigurationException : Exception + { + public BadConfigurationException(string message) : base(message) + { + + } + } + + public async Task ExtractKh2Ps2EditionAsync( + string isoLocation, + string gameDataLocation, + Action onProgress) + { + var fileBlocks = File.OpenRead(isoLocation).Using(stream => + { + var bufferedStream = new BufferedStream(stream); + var idxBlock = IsoUtility.GetFileOffset(bufferedStream, "KH2.IDX;1"); + var imgBlock = IsoUtility.GetFileOffset(bufferedStream, "KH2.IMG;1"); + return (idxBlock, imgBlock); + }); + + if (fileBlocks.idxBlock == -1 || fileBlocks.imgBlock == -1) + { + throw new BadConfigurationException( + $"Unable to find the files KH2.IDX and KH2.IMG in the ISO at '{isoLocation}'. The extraction will stop." + ); + } + + onProgress(0); + + await Task.Run(() => + { + using var isoStream = File.OpenRead(isoLocation); + + var idxOffset = fileBlocks.idxBlock * 0x800L; + var idx = Idx.Read(new SubStream(isoStream, idxOffset, isoStream.Length - idxOffset)); + + var imgOffset = fileBlocks.imgBlock * 0x800L; + var imgStream = new SubStream(isoStream, imgOffset, isoStream.Length - imgOffset); + var img = new Img(imgStream, idx, true); + + var fileCount = img.Entries.Count; + var fileProcessed = 0; + foreach (var fileEntry in img.Entries) + { + var fileName = IdxName.Lookup(fileEntry) ?? $"@{fileEntry.Hash32:08X}_{fileEntry.Hash16:04X}"; + using var stream = img.FileOpen(fileEntry); + var fileDestination = Path.Combine(gameDataLocation, "kh2", fileName); + var directoryDestination = Path.GetDirectoryName(fileDestination); + if (!Directory.Exists(directoryDestination)) + { + Directory.CreateDirectory(directoryDestination); + } + File.Create(fileDestination).Using(dstStream => stream.CopyTo(dstStream, BufferSize)); + + fileProcessed++; + onProgress((float)fileProcessed / fileCount); + } + + onProgress(1.0f); + }); + } + + public async Task ExtractKhPcEditionAsync( + string gameDataLocation, + Action onProgress, + Func getKhFilePath, + Func getKh3dFilePath, + bool extractkh1, + bool extractkh2, + bool extractbbs, + bool extractrecom, + bool extractkh3d, + Func> ifRetry, + CancellationToken cancellationToken) + { + await Task.Run(async () => + { + var _nameListkh1 = new string[] + { + "first", + "second", + "third", + "fourth", + "fifth" + }; + var _nameListkh2 = new string[] + { + "first", + "second", + "third", + "fourth", + "fifth", + "sixth" + }; + var _nameListbbs = new string[] + { + "first", + "second", + "third", + "fourth" + }; + var _nameListkh3d = new string[] + { + "first", + "second", + "third", + "fourth" + }; + + var _totalFiles = 0; + var _procTotalFiles = 0; + + onProgress(0); + + if (extractkh1) + { + for (int i = 0; i < 5; i++) + { + using var _stream = new FileStream(getKhFilePath("kh1_" + _nameListkh1[i] + ".hed"), System.IO.FileMode.Open); + var _hedFile = OpenKh.Egs.Hed.Read(_stream); + _totalFiles += _hedFile.Count(); + } + } + if (extractkh2) + { + for (int i = 0; i < 6; i++) + { + using var _stream = new FileStream(getKhFilePath("kh2_" + _nameListkh2[i] + ".hed"), System.IO.FileMode.Open); + var _hedFile = OpenKh.Egs.Hed.Read(_stream); + _totalFiles += _hedFile.Count(); + } + } + if (extractbbs) + { + for (int i = 0; i < 4; i++) + { + using var _stream = new FileStream(getKhFilePath("bbs_" + _nameListbbs[i] + ".hed"), System.IO.FileMode.Open); + var _hedFile = OpenKh.Egs.Hed.Read(_stream); + _totalFiles += _hedFile.Count(); + } + } + if (extractrecom) + { + using var _stream = new FileStream(getKhFilePath("Recom.hed"), System.IO.FileMode.Open); + var _hedFile = OpenKh.Egs.Hed.Read(_stream); + _totalFiles += _hedFile.Count(); + } + if (extractkh3d) + { + for (int i = 0; i < 4; i++) + { + using var _stream = new FileStream(getKh3dFilePath("kh3d_" + _nameListbbs[i] + ".hed"), System.IO.FileMode.Open); + var _hedFile = OpenKh.Egs.Hed.Read(_stream); + _totalFiles += _hedFile.Count(); + } + } + + async Task ProcessHedStreamAsync(string outputDir, Stream hedStream, Stream img) + { + await Task.Yield(); + + foreach (var entry in OpenKh.Egs.Hed.Read(hedStream)) + { + cancellationToken.ThrowIfCancellationRequested(); + + retry: + try + { + var hash = OpenKh.Egs.Helpers.ToString(entry.MD5); + if (!OpenKh.Egs.EgsTools.Names.TryGetValue(hash, out var fileName)) + fileName = $"{hash}.dat"; + + var outputFileName = Path.Combine(outputDir, fileName); + + OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileName); + + var hdAsset = new OpenKh.Egs.EgsHdAsset(img.SetPosition(entry.Offset)); + + File.Create(outputFileName).Using(stream => stream.Write(hdAsset.OriginalData)); + + outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName); + + foreach (var asset in hdAsset.Assets) + { + var outputFileNameRemastered = Path.Combine(OpenKh.Egs.EgsTools.GetHDAssetFolder(outputFileName), asset); + + OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileNameRemastered); + + var assetData = hdAsset.RemasteredAssetsDecompressedData[asset]; + File.Create(outputFileNameRemastered).Using(stream => stream.Write(assetData)); + } + } + catch (Exception ex) + { + if (await ifRetry(ex)) + { + goto retry; + } + } + + _procTotalFiles++; + + onProgress((float)_procTotalFiles / _totalFiles); + } + } + + if (extractkh1) + { + for (int i = 0; i < 5; i++) + { + var outputDir = Path.Combine(gameDataLocation, "kh1"); + using var hedStream = File.OpenRead(getKhFilePath("kh1_" + _nameListkh1[i] + ".hed")); + using var img = File.OpenRead(getKhFilePath("kh1_" + _nameListkh1[i] + ".pkg")); + + await ProcessHedStreamAsync(outputDir, hedStream, img); + } + } + if (extractkh2) + { + for (int i = 0; i < 6; i++) + { + var outputDir = Path.Combine(gameDataLocation, "kh2"); + using var hedStream = File.OpenRead(getKhFilePath("kh2_" + _nameListkh2[i] + ".hed")); + using var img = File.OpenRead(getKhFilePath("kh2_" + _nameListkh2[i] + ".pkg")); + + await ProcessHedStreamAsync(outputDir, hedStream, img); + } + } + if (extractbbs) + { + for (int i = 0; i < 4; i++) + { + var outputDir = Path.Combine(gameDataLocation, "bbs"); + using var hedStream = File.OpenRead(getKhFilePath("bbs_" + _nameListbbs[i] + ".hed")); + using var img = File.OpenRead(getKhFilePath("bbs_" + _nameListbbs[i] + ".pkg")); + + await ProcessHedStreamAsync(outputDir, hedStream, img); + } + } + if (extractrecom) + { + for (int i = 0; i < 1; i++) + { + var outputDir = Path.Combine(gameDataLocation, "Recom"); + using var hedStream = File.OpenRead(getKhFilePath("Recom.hed")); + using var img = File.OpenRead(getKhFilePath("Recom.pkg")); + + await ProcessHedStreamAsync(outputDir, hedStream, img); + } + } + if (extractkh3d) + { + for (int i = 0; i < 4; i++) + { + var outputDir = Path.Combine(gameDataLocation, "kh3d"); + using var hedStream = File.OpenRead(getKh3dFilePath("kh3d_" + _nameListkh3d[i] + ".hed")); + using var img = File.OpenRead(getKh3dFilePath("kh3d_" + _nameListkh3d[i] + ".pkg")); + + await ProcessHedStreamAsync(outputDir, hedStream, img); + } + } + onProgress(1); + }); + } + } +} diff --git a/OpenKh.Tools.ModsManager/ViewModels/SetupWizardViewModel.cs b/OpenKh.Tools.ModsManager/ViewModels/SetupWizardViewModel.cs index 92c91a925..f64f24b92 100644 --- a/OpenKh.Tools.ModsManager/ViewModels/SetupWizardViewModel.cs +++ b/OpenKh.Tools.ModsManager/ViewModels/SetupWizardViewModel.cs @@ -18,6 +18,8 @@ using Ionic.Zip; using System.Diagnostics; using System.Text.RegularExpressions; +using System.Threading; +using System.Runtime.ExceptionServices; namespace OpenKh.Tools.ModsManager.ViewModels { @@ -25,6 +27,7 @@ public class SetupWizardViewModel : BaseNotifyPropertyChanged { public ColorThemeService ColorTheme => ColorThemeService.Instance; private const int BufferSize = 65536; + private readonly GameDataExtractionService _gameDataExtractionService = new GameDataExtractionService(); private static readonly string PanaceaDllName = "OpenKH.Panacea.dll"; private static string ApplicationName = Utilities.GetApplicationName(); private static List _isoFilter = FileDialogFilterComposer @@ -623,8 +626,19 @@ public SetupWizardViewModel() { await ExtractGameData(IsoLocation, GameDataLocation); } - - catch (IOException _ex) + catch (OperationCanceledException) + { + // user closed the dialog + } + catch (GameDataExtractionService.BadConfigurationException _ex) + { + MessageBox.Show( + _ex.Message, + "Extraction error", + MessageBoxButton.OK, + MessageBoxImage.Error); + } + catch (Exception _ex) { var _sysMessage = MessageBox.Show(_ex.Message + "\n\nWould you like to try again?", "An Exception was Caught!", MessageBoxButton.YesNo, MessageBoxImage.Warning); @@ -1227,382 +1241,152 @@ public SetupWizardViewModel() private async Task ExtractGameData(string isoLocation, string gameDataLocation) { - switch (GameEdition) + void MarkStarted() + { + IsNotExtracting = false; + ExtractionProgress = 0; + OnPropertyChanged(nameof(IsNotExtracting)); + OnPropertyChanged(nameof(IsGameDataFound)); + OnPropertyChanged(nameof(ProgressBarVisibility)); + OnPropertyChanged(nameof(ExtractionCompleteVisibility)); + OnPropertyChanged(nameof(ExtractionProgress)); + } + + void MarkSuccessful() + { + IsNotExtracting = true; + ExtractionProgress = 1.0f; + OnPropertyChanged(nameof(IsNotExtracting)); + OnPropertyChanged(nameof(IsGameDataFound)); + OnPropertyChanged(nameof(GameDataNotFoundVisibility)); + OnPropertyChanged(nameof(GameDataFoundVisibility)); + OnPropertyChanged(nameof(ProgressBarVisibility)); + OnPropertyChanged(nameof(ExtractionCompleteVisibility)); + OnPropertyChanged(nameof(ExtractionProgress)); + } + + void MarkFailure() + { + IsNotExtracting = true; + OnPropertyChanged(nameof(IsNotExtracting)); + OnPropertyChanged(nameof(IsGameDataFound)); + OnPropertyChanged(nameof(ProgressBarVisibility)); + OnPropertyChanged(nameof(ExtractionCompleteVisibility)); + OnPropertyChanged(nameof(ExtractionProgress)); + } + + Action CreateOnProgressProcessor() { + var lastProgress = 0f; - default: + return progress => { - var fileBlocks = File.OpenRead(isoLocation).Using(stream => + if (progress == 0) { - var bufferedStream = new BufferedStream(stream); - var idxBlock = IsoUtility.GetFileOffset(bufferedStream, "KH2.IDX;1"); - var imgBlock = IsoUtility.GetFileOffset(bufferedStream, "KH2.IMG;1"); - return (idxBlock, imgBlock); - }); - - if (fileBlocks.idxBlock == -1 || fileBlocks.imgBlock == -1) + System.Windows.Application.Current.Dispatcher.Invoke(() => MarkStarted()); + } + else if (progress == 1) { - MessageBox.Show( - $"Unable to find the files KH2.IDX and KH2.IMG in the ISO at '{isoLocation}'. The extraction will stop.", - "Extraction error", - MessageBoxButton.OK, - MessageBoxImage.Error); - return; + System.Windows.Application.Current.Dispatcher.Invoke(() => MarkSuccessful()); } - - IsNotExtracting = false; - ExtractionProgress = 0; - OnPropertyChanged(nameof(IsNotExtracting)); - OnPropertyChanged(nameof(IsGameDataFound)); - OnPropertyChanged(nameof(ProgressBarVisibility)); - OnPropertyChanged(nameof(ExtractionCompleteVisibility)); - OnPropertyChanged(nameof(ExtractionProgress)); - - await Task.Run(() => + else { - using var isoStream = File.OpenRead(isoLocation); - - var idxOffset = fileBlocks.idxBlock * 0x800L; - var idx = Idx.Read(new SubStream(isoStream, idxOffset, isoStream.Length - idxOffset)); - - var imgOffset = fileBlocks.imgBlock * 0x800L; - var imgStream = new SubStream(isoStream, imgOffset, isoStream.Length - imgOffset); - var img = new Img(imgStream, idx, true); - - var fileCount = img.Entries.Count; - var fileProcessed = 0; - foreach (var fileEntry in img.Entries) + if (0.01f <= progress - lastProgress) { - var fileName = IdxName.Lookup(fileEntry) ?? $"@{fileEntry.Hash32:08X}_{fileEntry.Hash16:04X}"; - using var stream = img.FileOpen(fileEntry); - var fileDestination = Path.Combine(gameDataLocation,"kh2", fileName); - var directoryDestination = Path.GetDirectoryName(fileDestination); - if (!Directory.Exists(directoryDestination)) - Directory.CreateDirectory(directoryDestination); - File.Create(fileDestination).Using(dstStream => stream.CopyTo(dstStream, BufferSize)); + lastProgress = progress; - fileProcessed++; - ExtractionProgress = (float)fileProcessed / fileCount; - OnPropertyChanged(nameof(ExtractionProgress)); + System.Windows.Application.Current.Dispatcher.Invoke(() => + { + ExtractionProgress = progress; + OnPropertyChanged(nameof(ExtractionProgress)); + }); } + } + }; + } - System.Windows.Application.Current.Dispatcher.Invoke(() => - { - IsNotExtracting = true; - ExtractionProgress = 1.0f; - OnPropertyChanged(nameof(IsNotExtracting)); - OnPropertyChanged(nameof(IsGameDataFound)); - OnPropertyChanged(nameof(GameDataNotFoundVisibility)); - OnPropertyChanged(nameof(GameDataFoundVisibility)); - OnPropertyChanged(nameof(ProgressBarVisibility)); - OnPropertyChanged(nameof(ExtractionCompleteVisibility)); - OnPropertyChanged(nameof(ExtractionProgress)); - }); - }); - } - break; - - case PC: + try + { + switch (GameEdition) { - IsNotExtracting = false; - ExtractionProgress = 0; - OnPropertyChanged(nameof(IsNotExtracting)); - OnPropertyChanged(nameof(IsGameDataFound)); - OnPropertyChanged(nameof(ProgressBarVisibility)); - OnPropertyChanged(nameof(ExtractionCompleteVisibility)); - OnPropertyChanged(nameof(ExtractionProgress)); - - await Task.Run(() => + default: { - var _nameListkh1 = new string[] - { - "first", - "second", - "third", - "fourth", - "fifth" - }; - var _nameListkh2 = new string[] - { - "first", - "second", - "third", - "fourth", - "fifth", - "sixth" - }; - var _nameListbbs = new string[] - { - "first", - "second", - "third", - "fourth" - }; - var _nameListkh3d = new string[] - { - "first", - "second", - "third", - "fourth" - }; - - var _totalFiles = 0; - var _procTotalFiles = 0; - - if (ConfigurationService.Extractkh1) - { - for (int i = 0; i < 5; i++) - { - using var _stream = new FileStream(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh1_" + _nameListkh1[i] + ".hed"), System.IO.FileMode.Open); - var _hedFile = OpenKh.Egs.Hed.Read(_stream); - _totalFiles += _hedFile.Count(); - } - } - if (ConfigurationService.Extractkh2) - { - for (int i = 0; i < 6; i++) - { - using var _stream = new FileStream(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh2_" + _nameListkh2[i] + ".hed"), System.IO.FileMode.Open); - var _hedFile = OpenKh.Egs.Hed.Read(_stream); - _totalFiles += _hedFile.Count(); - } - } - if (ConfigurationService.Extractbbs) - { - for (int i = 0; i < 4; i++) - { - using var _stream = new FileStream(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "bbs_" + _nameListbbs[i] + ".hed"), System.IO.FileMode.Open); - var _hedFile = OpenKh.Egs.Hed.Read(_stream); - _totalFiles += _hedFile.Count(); - } - } - if (ConfigurationService.Extractrecom) - { - using var _stream = new FileStream(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "Recom.hed"), System.IO.FileMode.Open); - var _hedFile = OpenKh.Egs.Hed.Read(_stream); - _totalFiles += _hedFile.Count(); - } - if (ConfigurationService.Extractkh3d) - { - for (int i = 0; i < 4; i++) - { - using var _stream = new FileStream(Path.Combine(_pcReleaseLocationKH3D, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh3d_" + _nameListbbs[i] + ".hed"), System.IO.FileMode.Open); - var _hedFile = OpenKh.Egs.Hed.Read(_stream); - _totalFiles += _hedFile.Count(); - } - } - - if (ConfigurationService.Extractkh1) - { - for (int i = 0; i < 5; i++) - { - var outputDir = Path.Combine(gameDataLocation, "kh1"); - using var hedStream = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh1_" + _nameListkh1[i] + ".hed")); - using var img = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh1_" + _nameListkh1[i] + ".pkg")); - - foreach (var entry in OpenKh.Egs.Hed.Read(hedStream)) - { - var hash = OpenKh.Egs.Helpers.ToString(entry.MD5); - if (!OpenKh.Egs.EgsTools.Names.TryGetValue(hash, out var fileName)) - fileName = $"{hash}.dat"; - - var outputFileName = Path.Combine(outputDir, fileName); - - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileName); - - var hdAsset = new OpenKh.Egs.EgsHdAsset(img.SetPosition(entry.Offset)); - - File.Create(outputFileName).Using(stream => stream.Write(hdAsset.OriginalData)); - - outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName); - - foreach (var asset in hdAsset.Assets) - { - var outputFileNameRemastered = Path.Combine(OpenKh.Egs.EgsTools.GetHDAssetFolder(outputFileName), asset); - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileNameRemastered); - - var assetData = hdAsset.RemasteredAssetsDecompressedData[asset]; - File.Create(outputFileNameRemastered).Using(stream => stream.Write(assetData)); - } - _procTotalFiles++; - - ExtractionProgress = (float)_procTotalFiles / _totalFiles; - OnPropertyChanged(nameof(ExtractionProgress)); - } - } - } - if(ConfigurationService.Extractkh2) - { - for (int i = 0; i < 6; i++) - { - var outputDir = Path.Combine(gameDataLocation, "kh2"); - using var hedStream = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh2_" + _nameListkh2[i] + ".hed")); - using var img = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh2_" + _nameListkh2[i] + ".pkg")); - - foreach (var entry in OpenKh.Egs.Hed.Read(hedStream)) - { - var hash = OpenKh.Egs.Helpers.ToString(entry.MD5); - if (!OpenKh.Egs.EgsTools.Names.TryGetValue(hash, out var fileName)) - fileName = $"{hash}.dat"; - - var outputFileName = Path.Combine(outputDir, fileName); - - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileName); - - var hdAsset = new OpenKh.Egs.EgsHdAsset(img.SetPosition(entry.Offset)); - - File.Create(outputFileName).Using(stream => stream.Write(hdAsset.OriginalData)); - - outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName); - - foreach (var asset in hdAsset.Assets) - { - var outputFileNameRemastered = Path.Combine(OpenKh.Egs.EgsTools.GetHDAssetFolder(outputFileName), asset); - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileNameRemastered); - - var assetData = hdAsset.RemasteredAssetsDecompressedData[asset]; - File.Create(outputFileNameRemastered).Using(stream => stream.Write(assetData)); - } - _procTotalFiles++; - - ExtractionProgress = (float)_procTotalFiles / _totalFiles; - OnPropertyChanged(nameof(ExtractionProgress)); - } - } - } - if(ConfigurationService.Extractbbs) - { - for (int i = 0; i < 4; i++) - { - var outputDir = Path.Combine(gameDataLocation, "bbs"); - using var hedStream = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "bbs_" + _nameListbbs[i] + ".hed")); - using var img = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "bbs_" + _nameListbbs[i] + ".pkg")); - - foreach (var entry in OpenKh.Egs.Hed.Read(hedStream)) - { - var hash = OpenKh.Egs.Helpers.ToString(entry.MD5); - if (!OpenKh.Egs.EgsTools.Names.TryGetValue(hash, out var fileName)) - fileName = $"{hash}.dat"; - - var outputFileName = Path.Combine(outputDir, fileName); - - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileName); - - var hdAsset = new OpenKh.Egs.EgsHdAsset(img.SetPosition(entry.Offset)); - - File.Create(outputFileName).Using(stream => stream.Write(hdAsset.OriginalData)); - - outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName); - - foreach (var asset in hdAsset.Assets) - { - var outputFileNameRemastered = Path.Combine(OpenKh.Egs.EgsTools.GetHDAssetFolder(outputFileName), asset); - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileNameRemastered); - - var assetData = hdAsset.RemasteredAssetsDecompressedData[asset]; - File.Create(outputFileNameRemastered).Using(stream => stream.Write(assetData)); - } - _procTotalFiles++; - - ExtractionProgress = (float)_procTotalFiles / _totalFiles; - OnPropertyChanged(nameof(ExtractionProgress)); - } - } - } - if (ConfigurationService.Extractrecom) - { - for (int i = 0; i < 1; i++) - { - var outputDir = Path.Combine(gameDataLocation, "Recom"); - using var hedStream = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "Recom.hed")); - using var img = File.OpenRead(Path.Combine(_pcReleaseLocation, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "Recom.pkg")); - - foreach (var entry in OpenKh.Egs.Hed.Read(hedStream)) - { - var hash = OpenKh.Egs.Helpers.ToString(entry.MD5); - if (!OpenKh.Egs.EgsTools.Names.TryGetValue(hash, out var fileName)) - fileName = $"{hash}.dat"; - - var outputFileName = Path.Combine(outputDir, fileName); - - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileName); - - var hdAsset = new OpenKh.Egs.EgsHdAsset(img.SetPosition(entry.Offset)); - - File.Create(outputFileName).Using(stream => stream.Write(hdAsset.OriginalData)); - - outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName); - - foreach (var asset in hdAsset.Assets) - { - var outputFileNameRemastered = Path.Combine(OpenKh.Egs.EgsTools.GetHDAssetFolder(outputFileName), asset); - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileNameRemastered); - - var assetData = hdAsset.RemasteredAssetsDecompressedData[asset]; - File.Create(outputFileNameRemastered).Using(stream => stream.Write(assetData)); - } - _procTotalFiles++; + await _gameDataExtractionService.ExtractKh2Ps2EditionAsync( + isoLocation: isoLocation, + gameDataLocation: gameDataLocation, + onProgress: CreateOnProgressProcessor() + ); + break; + } - ExtractionProgress = (float)_procTotalFiles / _totalFiles; - OnPropertyChanged(nameof(ExtractionProgress)); - } - } - } - if (ConfigurationService.Extractkh3d) - { - for (int i = 0; i < 4; i++) + case PC: + { + var langFolder = (ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en") ? "dt" : _pcReleaseLanguage; + + await _gameDataExtractionService.ExtractKhPcEditionAsync( + gameDataLocation: @"H:\Tmp\ModsManagerPcExtractionRoot", + onProgress: CreateOnProgressProcessor(), + getKhFilePath: fileName + => Path.Combine( + _pcReleaseLocation, + "Image", + langFolder, + fileName + ), + getKh3dFilePath: fileName + => Path.Combine( + _pcReleaseLocationKH3D, + "Image", + langFolder, + fileName + ), + extractkh1: ConfigurationService.Extractkh1, + extractkh2: ConfigurationService.Extractkh2, + extractbbs: ConfigurationService.Extractbbs, + extractrecom: ConfigurationService.Extractrecom, + extractkh3d: ConfigurationService.Extractkh3d, + ifRetry: async ex => { - var outputDir = Path.Combine(gameDataLocation, "kh3d"); - using var hedStream = File.OpenRead(Path.Combine(_pcReleaseLocationKH3D, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage, "kh3d_" + _nameListkh3d[i] + ".hed")); - using var img = File.OpenRead(Path.Combine(_pcReleaseLocationKH3D, "Image", ConfigurationService.PCVersion == "Steam" && _pcReleaseLanguage == "en" ? "dt" : _pcReleaseLanguage , "kh3d_" + _nameListkh3d[i] + ".pkg")); - - foreach (var entry in OpenKh.Egs.Hed.Read(hedStream)) - { - var hash = OpenKh.Egs.Helpers.ToString(entry.MD5); - if (!OpenKh.Egs.EgsTools.Names.TryGetValue(hash, out var fileName)) - fileName = $"{hash}.dat"; - - var outputFileName = Path.Combine(outputDir, fileName); - - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileName); + var delayedResult = new TaskCompletionSource(); - var hdAsset = new OpenKh.Egs.EgsHdAsset(img.SetPosition(entry.Offset)); - - File.Create(outputFileName).Using(stream => stream.Write(hdAsset.OriginalData)); - - outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName); - - foreach (var asset in hdAsset.Assets) + await System.Windows.Application.Current.Dispatcher.InvokeAsync( + () => { - var outputFileNameRemastered = Path.Combine(OpenKh.Egs.EgsTools.GetHDAssetFolder(outputFileName), asset); - OpenKh.Egs.EgsTools.CreateDirectoryForFile(outputFileNameRemastered); + var selection = MessageBox.Show(ex.Message + "\n\nWould you like to retry again?", "An Exception was Caught!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning); - var assetData = hdAsset.RemasteredAssetsDecompressedData[asset]; - File.Create(outputFileNameRemastered).Using(stream => stream.Write(assetData)); + switch (selection) + { + case MessageBoxResult.Yes: + delayedResult.SetResult(true); + return; + case MessageBoxResult.No: + delayedResult.SetResult(false); + return; + default: + delayedResult.SetException(ex); + return; + } } - _procTotalFiles++; + ) + .Task; - ExtractionProgress = (float)_procTotalFiles / _totalFiles; - OnPropertyChanged(nameof(ExtractionProgress)); - } - } - } - System.Windows.Application.Current.Dispatcher.Invoke(() => - { - IsNotExtracting = true; - ExtractionProgress = 1.0f; - OnPropertyChanged(nameof(IsNotExtracting)); - OnPropertyChanged(nameof(IsGameDataFound)); - OnPropertyChanged(nameof(GameDataNotFoundVisibility)); - OnPropertyChanged(nameof(GameDataFoundVisibility)); - OnPropertyChanged(nameof(ProgressBarVisibility)); - OnPropertyChanged(nameof(ExtractionCompleteVisibility)); - OnPropertyChanged(nameof(ExtractionProgress)); - }); - }); + return await delayedResult.Task; + }, + cancellationToken: Abort + ); + break; + } } - break; + } + catch + { + System.Windows.Application.Current.Dispatcher.Invoke(() => MarkFailure()); + throw; } } + + private CancellationTokenSource _abort = new CancellationTokenSource(); + public CancellationToken Abort => _abort.Token; + public void SetAborted() => _abort.Cancel(); } } diff --git a/OpenKh.Tools.ModsManager/Views/SetupWizardWindow.xaml.cs b/OpenKh.Tools.ModsManager/Views/SetupWizardWindow.xaml.cs index 76393e69c..32e4ef69e 100644 --- a/OpenKh.Tools.ModsManager/Views/SetupWizardWindow.xaml.cs +++ b/OpenKh.Tools.ModsManager/Views/SetupWizardWindow.xaml.cs @@ -25,6 +25,8 @@ public SetupWizardWindow() _vm.LastPage = LastPage; _vm.PageStack.OnPageChanged(wizard.CurrentPage); + + Closed += (sender, e) => _vm.SetAborted(); } public string ConfigIsoLocation { get => _vm.IsoLocation; set => _vm.IsoLocation = value; } diff --git a/OpenKh.sln b/OpenKh.sln index a6b5b9601..a24320005 100644 --- a/OpenKh.sln +++ b/OpenKh.sln @@ -222,6 +222,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenKh.Tools.KhModels", "Op EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModelingToolkit", "ModelingToolkit\ModelingToolkit\ModelingToolkit.csproj", "{3603444C-3A1B-487D-AA6B-9AACB5530DD6}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Graphical tool tests", "Graphical tool tests", "{EC12F6FC-5753-430C-AE13-09EFD219E432}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenKh.Tests.ModsManager", "OpenKh.Tests.ModsManager\OpenKh.Tests.ModsManager.csproj", "{788CDE3D-77D8-4910-8D1A-C829EFD591D2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution .NET Debug|Any CPU = .NET Debug|Any CPU @@ -900,6 +904,14 @@ Global {3603444C-3A1B-487D-AA6B-9AACB5530DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU {3603444C-3A1B-487D-AA6B-9AACB5530DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU {3603444C-3A1B-487D-AA6B-9AACB5530DD6}.Release|Any CPU.Build.0 = Release|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}..NET Debug|Any CPU.ActiveCfg = Debug|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}..NET Debug|Any CPU.Build.0 = Debug|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}..NET Release|Any CPU.ActiveCfg = Release|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}..NET Release|Any CPU.Build.0 = Release|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {788CDE3D-77D8-4910-8D1A-C829EFD591D2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -993,6 +1005,7 @@ Global {A635E041-7F5D-4CD5-BB87-0F692EA1AD3E} = {4FBC958A-4685-4DF2-8D4F-98850BAE61B3} {E9AE2FAE-4335-4C75-9272-3B6C6D70072B} = {402B2669-D594-4DFA-965B-02A92626ADC6} {3603444C-3A1B-487D-AA6B-9AACB5530DD6} = {4FBC958A-4685-4DF2-8D4F-98850BAE61B3} + {788CDE3D-77D8-4910-8D1A-C829EFD591D2} = {EC12F6FC-5753-430C-AE13-09EFD219E432} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1DF3960B-4FE5-4E56-92D4-2FC0A912E823}