From 9a429ce439c4239b6a09bf349de334bbaf7ac865 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 9 Nov 2024 01:37:59 +0000 Subject: [PATCH] Full code refactor and clean-up. --- RomRepoMgr.Core/Aaru/FAT.cs | 406 +-- RomRepoMgr.Core/Base32.cs | 300 +- .../Checksums/BigEndianBitConverter.cs | 539 ++-- RomRepoMgr.Core/Checksums/CRC32Context.cs | 371 +-- RomRepoMgr.Core/Checksums/CRC64Context.cs | 373 +-- RomRepoMgr.Core/Checksums/IChecksum.cs | 29 +- RomRepoMgr.Core/Checksums/MD5Context.cs | 176 +- RomRepoMgr.Core/Checksums/SHA1Context.cs | 176 +- RomRepoMgr.Core/Checksums/SHA256Context.cs | 176 +- RomRepoMgr.Core/Checksums/SHA384Context.cs | 176 +- RomRepoMgr.Core/Checksums/SHA512Context.cs | 176 +- RomRepoMgr.Core/Checksums/SpamSumContext.cs | 788 +++--- RomRepoMgr.Core/EventArgs/ErrorEventArgs.cs | 9 +- .../EventArgs/ImportedRomEventArgs.cs | 9 +- RomRepoMgr.Core/EventArgs/MessageEventArgs.cs | 9 +- .../EventArgs/ProgressBoundsEventArgs.cs | 11 +- .../EventArgs/ProgressEventArgs.cs | 9 +- RomRepoMgr.Core/EventArgs/RomSetEventArgs.cs | 9 +- RomRepoMgr.Core/EventArgs/RomSetsEventArgs.cs | 9 +- RomRepoMgr.Core/Filesystem/Fuse.cs | 1473 +++++----- RomRepoMgr.Core/Filesystem/Vfs.cs | 1333 +++++---- RomRepoMgr.Core/Filesystem/Winfsp.cs | 1139 ++++---- RomRepoMgr.Core/ForcedSeekStream.cs | 286 +- RomRepoMgr.Core/Models/AssemblyModel.cs | 11 +- RomRepoMgr.Core/Models/ImportRomItem.cs | 11 +- RomRepoMgr.Core/Models/RomSetModel.cs | 41 +- .../Resources/Localization.Designer.cs | 832 +++--- .../Resources/Localization.es.resx | 142 +- RomRepoMgr.Core/Resources/Localization.resx | 151 +- RomRepoMgr.Core/RomRepoMgr.Core.csproj | 62 +- RomRepoMgr.Core/StreamWithLength.cs | 55 +- RomRepoMgr.Core/Workers/Checksum.cs | 289 +- RomRepoMgr.Core/Workers/Compression.cs | 354 +-- RomRepoMgr.Core/Workers/DatImporter.cs | 2210 ++++++++------- RomRepoMgr.Core/Workers/FileExporter.cs | 1100 ++++---- RomRepoMgr.Core/Workers/FileImporter.cs | 2475 +++++++++-------- RomRepoMgr.Database/Context.cs | 268 +- RomRepoMgr.Database/ContextFactory.cs | 9 +- RomRepoMgr.Database/Models/BaseModel.cs | 15 +- RomRepoMgr.Database/Models/DbDisk.cs | 25 +- RomRepoMgr.Database/Models/DbFile.cs | 43 +- RomRepoMgr.Database/Models/DbMedia.cs | 31 +- RomRepoMgr.Database/Models/DiskByMachine.cs | 27 +- RomRepoMgr.Database/Models/FileByMachine.cs | 33 +- RomRepoMgr.Database/Models/Machine.cs | 23 +- RomRepoMgr.Database/Models/MediaByMachine.cs | 27 +- RomRepoMgr.Database/Models/RomSet.cs | 38 +- RomRepoMgr.Database/Models/RomSetStat.cs | 25 +- .../Resources/Localization.Designer.cs | 41 +- .../Resources/Localization.es.resx | 30 +- .../Resources/Localization.resx | 37 +- .../RomRepoMgr.Database.csproj | 66 +- RomRepoMgr.Settings/Interop/DetectOS.cs | 670 ++--- RomRepoMgr.Settings/Interop/PlatformID.cs | 156 +- RomRepoMgr.Settings/Interop/Version.cs | 54 +- .../Resources/Localization.Designer.cs | 41 +- .../Resources/Localization.es.resx | 30 +- .../Resources/Localization.resx | 37 +- .../RomRepoMgr.Settings.csproj | 42 +- RomRepoMgr.Settings/Settings.cs | 505 ++-- RomRepoMgr/App.xaml | 15 +- RomRepoMgr/App.xaml.cs | 114 +- RomRepoMgr/Assets/emblem-checked.svg | 19 +- RomRepoMgr/Assets/emblem-error.svg | 19 +- RomRepoMgr/Assets/emblem-question.svg | 6 +- RomRepoMgr/Program.cs | 23 +- RomRepoMgr/Resources/Localization.es.resx | 264 +- RomRepoMgr/Resources/Localization.resx | 271 +- RomRepoMgr/RomRepoMgr.csproj | 84 +- RomRepoMgr/ViewLocator.cs | 31 +- RomRepoMgr/ViewModels/AboutViewModel.cs | 230 +- RomRepoMgr/ViewModels/EditDatViewModel.cs | 409 ++- RomRepoMgr/ViewModels/ExportDatViewModel.cs | 216 +- RomRepoMgr/ViewModels/ExportRomsViewModel.cs | 385 ++- .../ViewModels/ImportDatFolderViewModel.cs | 596 ++-- RomRepoMgr/ViewModels/ImportDatViewModel.cs | 261 +- .../ViewModels/ImportRomFolderViewModel.cs | 506 ++-- RomRepoMgr/ViewModels/MainWindowViewModel.cs | 540 ++-- RomRepoMgr/ViewModels/RemoveDatViewModel.cs | 145 +- RomRepoMgr/ViewModels/SettingsViewModel.cs | 538 ++-- .../ViewModels/SplashWindowViewModel.cs | 658 ++--- RomRepoMgr/ViewModels/UpdateStatsViewModel.cs | 392 +-- RomRepoMgr/ViewModels/ViewModelBase.cs | 7 +- RomRepoMgr/Views/About.xaml | 91 +- RomRepoMgr/Views/About.xaml.cs | 13 +- RomRepoMgr/Views/EditDat.xaml | 239 +- RomRepoMgr/Views/EditDat.xaml.cs | 13 +- RomRepoMgr/Views/ExportDat.xaml | 35 +- RomRepoMgr/Views/ExportDat.xaml.cs | 21 +- RomRepoMgr/Views/ExportRoms.xaml | 60 +- RomRepoMgr/Views/ExportRoms.xaml.cs | 21 +- RomRepoMgr/Views/ImportDat.xaml | 40 +- RomRepoMgr/Views/ImportDat.xaml.cs | 21 +- RomRepoMgr/Views/ImportDatFolder.xaml | 94 +- RomRepoMgr/Views/ImportDatFolder.xaml.cs | 21 +- RomRepoMgr/Views/ImportRomFolder.xaml | 84 +- RomRepoMgr/Views/ImportRomFolder.xaml.cs | 13 +- RomRepoMgr/Views/MainWindow.xaml | 123 +- RomRepoMgr/Views/MainWindow.xaml.cs | 23 +- RomRepoMgr/Views/RemoveDat.xaml | 25 +- RomRepoMgr/Views/RemoveDat.xaml.cs | 21 +- RomRepoMgr/Views/SettingsDialog.xaml | 125 +- RomRepoMgr/Views/SettingsDialog.xaml.cs | 13 +- RomRepoMgr/Views/SplashWindow.xaml | 120 +- RomRepoMgr/Views/SplashWindow.xaml.cs | 21 +- RomRepoMgr/Views/UpdateStats.xaml | 96 +- RomRepoMgr/Views/UpdateStats.xaml.cs | 21 +- RomRepoMgr/nuget.config | 2 +- 108 files changed, 12878 insertions(+), 12195 deletions(-) diff --git a/RomRepoMgr.Core/Aaru/FAT.cs b/RomRepoMgr.Core/Aaru/FAT.cs index 3bead02..bc6dfaa 100644 --- a/RomRepoMgr.Core/Aaru/FAT.cs +++ b/RomRepoMgr.Core/Aaru/FAT.cs @@ -35,201 +35,227 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; -namespace RomRepoMgr.Core.Aaru +namespace RomRepoMgr.Core.Aaru; + +// TODO: This should be taken from Aaru as a nuget package in the future +public static class FAT { - // TODO: This should be taken from Aaru as a nuget package in the future - public static class FAT + static int CountBits(uint number) { - static int CountBits(uint number) - { - number -= (number >> 1) & 0x55555555; - number = (number & 0x33333333) + ((number >> 2) & 0x33333333); + number -= number >> 1 & 0x55555555; + number = (number & 0x33333333) + (number >> 2 & 0x33333333); - return (int)((((number + (number >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24); + return (int)((number + (number >> 4) & 0x0F0F0F0F) * 0x01010101 >> 24); + } + + public static bool Identify(string path) + { + try + { + return Identify(new FileStream(path, FileMode.Open, FileAccess.Read)); } - - public static bool Identify(string path) + catch(Exception e) { - try - { - return Identify(new FileStream(path, FileMode.Open, FileAccess.Read)); - } - catch(Exception e) - { - return false; - } - } - - [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] - static bool Identify(Stream imageStream) - { - ushort bps; - byte spc; - byte numberOfFats; - ushort reservedSecs; - ushort rootEntries; - ushort sectors; - ushort fatSectors; - uint bigSectors; - byte bpbSignature; - byte fat32Signature; - ulong hugeSectors; - byte[] fat32Id = new byte[8]; - byte[] msxId = new byte[6]; - byte[] dosOem = new byte[8]; - byte[] atariOem = new byte[6]; - ushort bootable = 0; - - byte[] bpbSector = new byte[512]; - byte[] fatSector = new byte[512]; - imageStream.Position = 0; - imageStream.Read(bpbSector, 0, 512); - imageStream.Read(fatSector, 0, 512); - - Array.Copy(bpbSector, 0x02, atariOem, 0, 6); - Array.Copy(bpbSector, 0x03, dosOem, 0, 8); - bps = BitConverter.ToUInt16(bpbSector, 0x00B); - spc = bpbSector[0x00D]; - reservedSecs = BitConverter.ToUInt16(bpbSector, 0x00E); - numberOfFats = bpbSector[0x010]; - rootEntries = BitConverter.ToUInt16(bpbSector, 0x011); - sectors = BitConverter.ToUInt16(bpbSector, 0x013); - fatSectors = BitConverter.ToUInt16(bpbSector, 0x016); - Array.Copy(bpbSector, 0x052, msxId, 0, 6); - bigSectors = BitConverter.ToUInt32(bpbSector, 0x020); - bpbSignature = bpbSector[0x026]; - fat32Signature = bpbSector[0x042]; - Array.Copy(bpbSector, 0x052, fat32Id, 0, 8); - hugeSectors = BitConverter.ToUInt64(bpbSector, 0x052); - int bitsInBps = CountBits(bps); - - bootable = BitConverter.ToUInt16(bpbSector, 0x1FE); - - bool correctSpc = spc == 1 || spc == 2 || spc == 4 || spc == 8 || spc == 16 || spc == 32 || spc == 64; - string msxString = Encoding.ASCII.GetString(msxId); - string fat32String = Encoding.ASCII.GetString(fat32Id); - - string oemString = Encoding.ASCII.GetString(dosOem); - - ushort apricotBps = BitConverter.ToUInt16(bpbSector, 0x50); - byte apricotSpc = bpbSector[0x52]; - ushort apricotReservedSecs = BitConverter.ToUInt16(bpbSector, 0x53); - byte apricotFatsNo = bpbSector[0x55]; - ushort apricotRootEntries = BitConverter.ToUInt16(bpbSector, 0x56); - ushort apricotSectors = BitConverter.ToUInt16(bpbSector, 0x58); - ushort apricotFatSectors = BitConverter.ToUInt16(bpbSector, 0x5B); - - bool apricotCorrectSpc = apricotSpc == 1 || apricotSpc == 2 || apricotSpc == 4 || apricotSpc == 8 || - apricotSpc == 16 || apricotSpc == 32 || apricotSpc == 64; - - int bitsInApricotBps = CountBits(apricotBps); - byte apricotPartitions = bpbSector[0x0C]; - - switch(oemString) - { - // exFAT - case "EXFAT ": return false; - - // NTFS - case "NTFS " when bootable == 0xAA55 && numberOfFats == 0 && fatSectors == 0: return false; - - // QNX4 - case "FQNX4FS ": return false; - } - - ulong imageSectors = (ulong)imageStream.Length / 512; - - switch(bitsInBps) - { - // FAT32 for sure - case 1 when correctSpc && numberOfFats <= 2 && sectors == 0 && fatSectors == 0 && - fat32Signature == 0x29 && fat32String == "FAT32 ": return true; - - // short FAT32 - case 1 - when correctSpc && numberOfFats <= 2 && sectors == 0 && fatSectors == 0 && fat32Signature == 0x28: - return bigSectors == 0 ? hugeSectors <= imageSectors : bigSectors <= imageSectors; - - // MSX-DOS FAT12 - case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && sectors <= imageSectors && - fatSectors > 0 && msxString == "VOL_ID": return true; - - // EBPB - case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && fatSectors > 0 && - (bpbSignature == 0x28 || bpbSignature == 0x29): - return sectors == 0 ? bigSectors <= imageSectors : sectors <= imageSectors; - - // BPB - case 1 when correctSpc && reservedSecs < imageSectors - 1 && numberOfFats <= 2 && rootEntries > 0 && - fatSectors > 0: return sectors == 0 ? bigSectors <= imageSectors : sectors <= imageSectors; - } - - // Apricot BPB - if(bitsInApricotBps == 1 && - apricotCorrectSpc && - apricotReservedSecs < imageSectors - 1 && - apricotFatsNo <= 2 && - apricotRootEntries > 0 && - apricotFatSectors > 0 && - apricotSectors <= imageSectors && - apricotPartitions == 0) - return true; - - // DEC Rainbow, lacks a BPB but has a very concrete structure... - if(imageSectors != 800) - return false; - - // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) - byte z80Di = bpbSector[0]; - - // First FAT1 sector resides at LBA 0x14 - byte[] fat1Sector0 = new byte[512]; - imageStream.Position = 0x14 * 512; - imageStream.Read(fat1Sector0, 0, 512); - - // First FAT2 sector resides at LBA 0x1A - byte[] fat2Sector0 = new byte[512]; - imageStream.Position = 0x1A * 512; - imageStream.Read(fat2Sector0, 0, 512); - bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; - - // Volume is software interleaved 2:1 - var rootMs = new MemoryStream(); - - byte[] tmp = new byte[512]; - - foreach(long position in new long[] - { - 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 - }) - { - imageStream.Position = position * 512; - imageStream.Read(tmp, 0, 512); - rootMs.Write(tmp, 0, tmp.Length); - } - - byte[] rootDir = rootMs.ToArray(); - bool validRootDir = true; - - // Iterate all root directory - for(int e = 0; e < 96 * 32; e += 32) - { - for(int c = 0; c < 11; c++) - if((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) || - rootDir[c + e] == 0xFF || - rootDir[c + e] == 0x2E) - { - validRootDir = false; - - break; - } - - if(!validRootDir) - break; - } - - return z80Di == 0xF3 && equalFatIds && (fat1Sector0[0] & 0xF0) == 0xF0 && fat1Sector0[1] == 0xFF && - validRootDir; + return false; } } + + [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] + static bool Identify(Stream imageStream) + { + ushort bps; + byte spc; + byte numberOfFats; + ushort reservedSecs; + ushort rootEntries; + ushort sectors; + ushort fatSectors; + uint bigSectors; + byte bpbSignature; + byte fat32Signature; + ulong hugeSectors; + var fat32Id = new byte[8]; + var msxId = new byte[6]; + var dosOem = new byte[8]; + var atariOem = new byte[6]; + ushort bootable = 0; + + var bpbSector = new byte[512]; + var fatSector = new byte[512]; + imageStream.Position = 0; + imageStream.Read(bpbSector, 0, 512); + imageStream.Read(fatSector, 0, 512); + + Array.Copy(bpbSector, 0x02, atariOem, 0, 6); + Array.Copy(bpbSector, 0x03, dosOem, 0, 8); + bps = BitConverter.ToUInt16(bpbSector, 0x00B); + spc = bpbSector[0x00D]; + reservedSecs = BitConverter.ToUInt16(bpbSector, 0x00E); + numberOfFats = bpbSector[0x010]; + rootEntries = BitConverter.ToUInt16(bpbSector, 0x011); + sectors = BitConverter.ToUInt16(bpbSector, 0x013); + fatSectors = BitConverter.ToUInt16(bpbSector, 0x016); + Array.Copy(bpbSector, 0x052, msxId, 0, 6); + bigSectors = BitConverter.ToUInt32(bpbSector, 0x020); + bpbSignature = bpbSector[0x026]; + fat32Signature = bpbSector[0x042]; + Array.Copy(bpbSector, 0x052, fat32Id, 0, 8); + hugeSectors = BitConverter.ToUInt64(bpbSector, 0x052); + int bitsInBps = CountBits(bps); + + bootable = BitConverter.ToUInt16(bpbSector, 0x1FE); + + bool correctSpc = spc == 1 || spc == 2 || spc == 4 || spc == 8 || spc == 16 || spc == 32 || spc == 64; + string msxString = Encoding.ASCII.GetString(msxId); + string fat32String = Encoding.ASCII.GetString(fat32Id); + + string oemString = Encoding.ASCII.GetString(dosOem); + + var apricotBps = BitConverter.ToUInt16(bpbSector, 0x50); + byte apricotSpc = bpbSector[0x52]; + var apricotReservedSecs = BitConverter.ToUInt16(bpbSector, 0x53); + byte apricotFatsNo = bpbSector[0x55]; + var apricotRootEntries = BitConverter.ToUInt16(bpbSector, 0x56); + var apricotSectors = BitConverter.ToUInt16(bpbSector, 0x58); + var apricotFatSectors = BitConverter.ToUInt16(bpbSector, 0x5B); + + bool apricotCorrectSpc = apricotSpc == 1 || + apricotSpc == 2 || + apricotSpc == 4 || + apricotSpc == 8 || + apricotSpc == 16 || + apricotSpc == 32 || + apricotSpc == 64; + + int bitsInApricotBps = CountBits(apricotBps); + byte apricotPartitions = bpbSector[0x0C]; + + switch(oemString) + { + // exFAT + case "EXFAT ": + return false; + + // NTFS + case "NTFS " when bootable == 0xAA55 && numberOfFats == 0 && fatSectors == 0: + return false; + + // QNX4 + case "FQNX4FS ": + return false; + } + + ulong imageSectors = (ulong)imageStream.Length / 512; + + switch(bitsInBps) + { + // FAT32 for sure + case 1 when correctSpc && + numberOfFats <= 2 && + sectors == 0 && + fatSectors == 0 && + fat32Signature == 0x29 && + fat32String == "FAT32 ": + return true; + + // short FAT32 + case 1 when correctSpc && numberOfFats <= 2 && sectors == 0 && fatSectors == 0 && fat32Signature == 0x28: + return bigSectors == 0 ? hugeSectors <= imageSectors : bigSectors <= imageSectors; + + // MSX-DOS FAT12 + case 1 when correctSpc && + numberOfFats <= 2 && + rootEntries > 0 && + sectors <= imageSectors && + fatSectors > 0 && + msxString == "VOL_ID": + return true; + + // EBPB + case 1 when correctSpc && + numberOfFats <= 2 && + rootEntries > 0 && + fatSectors > 0 && + (bpbSignature == 0x28 || bpbSignature == 0x29): + return sectors == 0 ? bigSectors <= imageSectors : sectors <= imageSectors; + + // BPB + case 1 when correctSpc && + reservedSecs < imageSectors - 1 && + numberOfFats <= 2 && + rootEntries > 0 && + fatSectors > 0: + return sectors == 0 ? bigSectors <= imageSectors : sectors <= imageSectors; + } + + // Apricot BPB + if(bitsInApricotBps == 1 && + apricotCorrectSpc && + apricotReservedSecs < imageSectors - 1 && + apricotFatsNo <= 2 && + apricotRootEntries > 0 && + apricotFatSectors > 0 && + apricotSectors <= imageSectors && + apricotPartitions == 0) + return true; + + // DEC Rainbow, lacks a BPB but has a very concrete structure... + if(imageSectors != 800) return false; + + // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) + byte z80Di = bpbSector[0]; + + // First FAT1 sector resides at LBA 0x14 + var fat1Sector0 = new byte[512]; + imageStream.Position = 0x14 * 512; + imageStream.Read(fat1Sector0, 0, 512); + + // First FAT2 sector resides at LBA 0x1A + var fat2Sector0 = new byte[512]; + imageStream.Position = 0x1A * 512; + imageStream.Read(fat2Sector0, 0, 512); + bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; + + // Volume is software interleaved 2:1 + var rootMs = new MemoryStream(); + + var tmp = new byte[512]; + + foreach(long position in new long[] + { + 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 + }) + { + imageStream.Position = position * 512; + imageStream.Read(tmp, 0, 512); + rootMs.Write(tmp, 0, tmp.Length); + } + + byte[] rootDir = rootMs.ToArray(); + var validRootDir = true; + + // Iterate all root directory + for(var e = 0; e < 96 * 32; e += 32) + { + for(var c = 0; c < 11; c++) + { + if(rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05 || + rootDir[c + e] == 0xFF || + rootDir[c + e] == 0x2E) + { + validRootDir = false; + + break; + } + } + + if(!validRootDir) break; + } + + return z80Di == 0xF3 && + equalFatIds && + (fat1Sector0[0] & 0xF0) == 0xF0 && + fat1Sector0[1] == 0xFF && + validRootDir; + } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Base32.cs b/RomRepoMgr.Core/Base32.cs index dd264cf..99c3b6b 100644 --- a/RomRepoMgr.Core/Base32.cs +++ b/RomRepoMgr.Core/Base32.cs @@ -8,98 +8,76 @@ using System; using System.Text; using RomRepoMgr.Core.Resources; -namespace RomRepoMgr.Core +namespace RomRepoMgr.Core; + +/// Class used for conversion between byte array and Base32 notation +public sealed class Base32 { - /// Class used for conversion between byte array and Base32 notation - public sealed class Base32 + /// Size of the regular byte in bits + const int _inByteSize = 8; + + /// Size of converted byte in bits + const int _outByteSize = 5; + + /// Alphabet + const string _base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + /// Convert byte array to Base32 format + /// An array of bytes to convert to Base32 format + /// Returns a string representing byte array + public static string ToBase32String(byte[] bytes) { - /// Size of the regular byte in bits - const int _inByteSize = 8; + // Check if byte array is null + if(bytes == null) return null; - /// Size of converted byte in bits - const int _outByteSize = 5; + // Check if empty - /// Alphabet - const string _base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + if(bytes.Length == 0) return string.Empty; - /// Convert byte array to Base32 format - /// An array of bytes to convert to Base32 format - /// Returns a string representing byte array - public static string ToBase32String(byte[] bytes) + // Prepare container for the final value + var builder = new StringBuilder(bytes.Length * _inByteSize / _outByteSize); + + // Position in the input buffer + var bytesPosition = 0; + + // Offset inside a single byte that points to (from left to right) + // 0 - highest bit, 7 - lowest bit + var bytesSubPosition = 0; + + // Byte to look up in the dictionary + byte outputBase32Byte = 0; + + // The number of bits filled in the current output byte + var outputBase32BytePosition = 0; + + // Iterate through input buffer until we reach past the end of it + while(bytesPosition < bytes.Length) { - // Check if byte array is null - if(bytes == null) - return null; + // Calculate the number of bits we can extract out of current input byte to fill missing bits in the output byte + int bitsAvailableInByte = Math.Min(_inByteSize - bytesSubPosition, _outByteSize - outputBase32BytePosition); - // Check if empty + // Make space in the output byte + outputBase32Byte <<= bitsAvailableInByte; - if(bytes.Length == 0) - return string.Empty; + // Extract the part of the input byte and move it to the output byte + outputBase32Byte |= (byte)(bytes[bytesPosition] >> _inByteSize - (bytesSubPosition + bitsAvailableInByte)); - // Prepare container for the final value - var builder = new StringBuilder((bytes.Length * _inByteSize) / _outByteSize); + // Update current sub-byte position + bytesSubPosition += bitsAvailableInByte; - // Position in the input buffer - int bytesPosition = 0; - - // Offset inside a single byte that points to (from left to right) - // 0 - highest bit, 7 - lowest bit - int bytesSubPosition = 0; - - // Byte to look up in the dictionary - byte outputBase32Byte = 0; - - // The number of bits filled in the current output byte - int outputBase32BytePosition = 0; - - // Iterate through input buffer until we reach past the end of it - while(bytesPosition < bytes.Length) + // Check overflow + if(bytesSubPosition >= _inByteSize) { - // Calculate the number of bits we can extract out of current input byte to fill missing bits in the output byte - int bitsAvailableInByte = - Math.Min(_inByteSize - bytesSubPosition, _outByteSize - outputBase32BytePosition); - - // Make space in the output byte - outputBase32Byte <<= bitsAvailableInByte; - - // Extract the part of the input byte and move it to the output byte - outputBase32Byte |= - (byte)(bytes[bytesPosition] >> (_inByteSize - (bytesSubPosition + bitsAvailableInByte))); - - // Update current sub-byte position - bytesSubPosition += bitsAvailableInByte; - - // Check overflow - if(bytesSubPosition >= _inByteSize) - { - // Move to the next byte - bytesPosition++; - bytesSubPosition = 0; - } - - // Update current base32 byte completion - outputBase32BytePosition += bitsAvailableInByte; - - // Check overflow or end of input array - if(outputBase32BytePosition < _outByteSize) - continue; - - // Drop the overflow bits - outputBase32Byte &= 0x1F; // 0x1F = 00011111 in binary - - // Add current Base32 byte and convert it to character - builder.Append(_base32Alphabet[outputBase32Byte]); - // Move to the next byte - outputBase32BytePosition = 0; + bytesPosition++; + bytesSubPosition = 0; } - // Check if we have a remainder - if(outputBase32BytePosition <= 0) - return builder.ToString(); + // Update current base32 byte completion + outputBase32BytePosition += bitsAvailableInByte; - // Move to the right bits - outputBase32Byte <<= _outByteSize - outputBase32BytePosition; + // Check overflow or end of input array + if(outputBase32BytePosition < _outByteSize) continue; // Drop the overflow bits outputBase32Byte &= 0x1F; // 0x1F = 00011111 in binary @@ -107,91 +85,103 @@ namespace RomRepoMgr.Core // Add current Base32 byte and convert it to character builder.Append(_base32Alphabet[outputBase32Byte]); - return builder.ToString(); + // Move to the next byte + outputBase32BytePosition = 0; } - /// Convert base32 string to array of bytes - /// Base32 string to convert - /// Returns a byte array converted from the string - public static byte[] FromBase32String(string base32String) + // Check if we have a remainder + if(outputBase32BytePosition <= 0) return builder.ToString(); + + // Move to the right bits + outputBase32Byte <<= _outByteSize - outputBase32BytePosition; + + // Drop the overflow bits + outputBase32Byte &= 0x1F; // 0x1F = 00011111 in binary + + // Add current Base32 byte and convert it to character + builder.Append(_base32Alphabet[outputBase32Byte]); + + return builder.ToString(); + } + + /// Convert base32 string to array of bytes + /// Base32 string to convert + /// Returns a byte array converted from the string + public static byte[] FromBase32String(string base32String) + { + // Check if string is null + if(base32String == null) return null; + + // Check if empty + if(base32String == string.Empty) return new byte[0]; + + // Convert to upper-case + string base32StringUpperCase = base32String.ToUpperInvariant(); + + // Prepare output byte array + var outputBytes = new byte[base32StringUpperCase.Length * _outByteSize / _inByteSize]; + + // Check the size + if(outputBytes.Length == 0) throw new ArgumentException(Localization.Base32_Not_enought_data); + + // Position in the string + var base32Position = 0; + + // Offset inside the character in the string + var base32SubPosition = 0; + + // Position within outputBytes array + var outputBytePosition = 0; + + // The number of bits filled in the current output byte + var outputByteSubPosition = 0; + + // Normally we would iterate on the input array but in this case we actually iterate on the output array + // We do it because output array doesn't have overflow bits, while input does and it will cause output array overflow if we don''t stop in time + while(outputBytePosition < outputBytes.Length) { - // Check if string is null - if(base32String == null) - return null; + // Look up current character in the dictionary to convert it to byte + int currentBase32Byte = _base32Alphabet.IndexOf(base32StringUpperCase[base32Position]); - // Check if empty - if(base32String == string.Empty) - return new byte[0]; - - // Convert to upper-case - string base32StringUpperCase = base32String.ToUpperInvariant(); - - // Prepare output byte array - byte[] outputBytes = new byte[(base32StringUpperCase.Length * _outByteSize) / _inByteSize]; - - // Check the size - if(outputBytes.Length == 0) - throw new ArgumentException(Localization.Base32_Not_enought_data); - - // Position in the string - int base32Position = 0; - - // Offset inside the character in the string - int base32SubPosition = 0; - - // Position within outputBytes array - int outputBytePosition = 0; - - // The number of bits filled in the current output byte - int outputByteSubPosition = 0; - - // Normally we would iterate on the input array but in this case we actually iterate on the output array - // We do it because output array doesn't have overflow bits, while input does and it will cause output array overflow if we don''t stop in time - while(outputBytePosition < outputBytes.Length) + // Check if found + if(currentBase32Byte < 0) { - // Look up current character in the dictionary to convert it to byte - int currentBase32Byte = _base32Alphabet.IndexOf(base32StringUpperCase[base32Position]); - - // Check if found - if(currentBase32Byte < 0) - throw new ArgumentException(string.Format(Localization.Base32_Invalid_format, - base32String[base32Position])); - - // Calculate the number of bits we can extract out of current input character to fill missing bits in the output byte - int bitsAvailableInByte = - Math.Min(_outByteSize - base32SubPosition, _inByteSize - outputByteSubPosition); - - // Make space in the output byte - outputBytes[outputBytePosition] <<= bitsAvailableInByte; - - // Extract the part of the input character and move it to the output byte - outputBytes[outputBytePosition] |= - (byte)(currentBase32Byte >> (_outByteSize - (base32SubPosition + bitsAvailableInByte))); - - // Update current sub-byte position - outputByteSubPosition += bitsAvailableInByte; - - // Check overflow - if(outputByteSubPosition >= _inByteSize) - { - // Move to the next byte - outputBytePosition++; - outputByteSubPosition = 0; - } - - // Update current base32 byte completion - base32SubPosition += bitsAvailableInByte; - - // Check overflow or end of input array - if(base32SubPosition < _outByteSize) - continue; - - // Move to the next character - base32Position++; - base32SubPosition = 0; + throw new ArgumentException(string.Format(Localization.Base32_Invalid_format, + base32String[base32Position])); } - return outputBytes; + // Calculate the number of bits we can extract out of current input character to fill missing bits in the output byte + int bitsAvailableInByte = Math.Min(_outByteSize - base32SubPosition, _inByteSize - outputByteSubPosition); + + // Make space in the output byte + outputBytes[outputBytePosition] <<= bitsAvailableInByte; + + // Extract the part of the input character and move it to the output byte + outputBytes[outputBytePosition] |= + (byte)(currentBase32Byte >> _outByteSize - (base32SubPosition + bitsAvailableInByte)); + + // Update current sub-byte position + outputByteSubPosition += bitsAvailableInByte; + + // Check overflow + if(outputByteSubPosition >= _inByteSize) + { + // Move to the next byte + outputBytePosition++; + outputByteSubPosition = 0; + } + + // Update current base32 byte completion + base32SubPosition += bitsAvailableInByte; + + // Check overflow or end of input array + if(base32SubPosition < _outByteSize) continue; + + // Move to the next character + base32Position++; + base32SubPosition = 0; } + + return outputBytes; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/BigEndianBitConverter.cs b/RomRepoMgr.Core/Checksums/BigEndianBitConverter.cs index 2d4a7ab..c91c305 100644 --- a/RomRepoMgr.Core/Checksums/BigEndianBitConverter.cs +++ b/RomRepoMgr.Core/Checksums/BigEndianBitConverter.cs @@ -33,288 +33,287 @@ using System; using System.Linq; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// +/// Converts base data types to an array of bytes, and an array of bytes to base data types. All info taken from +/// the meta data of System.BitConverter. This implementation allows for Endianness consideration. +/// +public static class BigEndianBitConverter { + /// Converts the specified double-precision floating point number to a 64-bit signed integer. + /// The number to convert. + /// A 64-bit signed integer whose value is equivalent to value. + /// It is not currently implemented + public static long DoubleToInt64Bits(double value) => throw new NotImplementedException(); + + /// Returns the specified Boolean value as an array of bytes. + /// A Boolean value. + /// An array of bytes with length 1. + public static byte[] GetBytes(bool value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified Unicode character value as an array of bytes. + /// A character to convert. + /// An array of bytes with length 2. + public static byte[] GetBytes(char value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified double-precision floating point value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 8. + public static byte[] GetBytes(double value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified single-precision floating point value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 4. + public static byte[] GetBytes(float value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 32-bit signed integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 4. + public static byte[] GetBytes(int value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 64-bit signed integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 8. + public static byte[] GetBytes(long value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 16-bit signed integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 2. + public static byte[] GetBytes(short value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 32-bit unsigned integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 4. + public static byte[] GetBytes(uint value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 64-bit unsigned integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 8. + public static byte[] GetBytes(ulong value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 16-bit unsigned integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 2. + public static byte[] GetBytes(ushort value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Converts the specified 64-bit signed integer to a double-precision floating point number. + /// The number to convert. + /// A double-precision floating point number whose value is equivalent to value. + public static double Int64BitsToDouble(long value) => throw new NotImplementedException(); + + /// Returns a Boolean value converted from one byte at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// true if the byte at in value is nonzero; otherwise, false. + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static bool ToBoolean(byte[] value, int startIndex) => throw new NotImplementedException(); + + /// Returns a Unicode character converted from two bytes at a specified position in a byte array. + /// An array. + /// The starting position within value. + /// A character formed by two bytes beginning at . + /// equals the length of value minus 1. + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static char ToChar(byte[] value, int startIndex) => throw new NotImplementedException(); + /// - /// Converts base data types to an array of bytes, and an array of bytes to base data types. All info taken from - /// the meta data of System.BitConverter. This implementation allows for Endianness consideration. + /// Returns a double-precision floating point number converted from eight bytes at a specified position in a byte + /// array. /// - public static class BigEndianBitConverter - { - /// Converts the specified double-precision floating point number to a 64-bit signed integer. - /// The number to convert. - /// A 64-bit signed integer whose value is equivalent to value. - /// It is not currently implemented - public static long DoubleToInt64Bits(double value) => throw new NotImplementedException(); + /// An array of bytes. + /// The starting position within value. + /// A double precision floating point number formed by eight bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 7, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static double ToDouble(byte[] value, int startIndex) => throw new NotImplementedException(); - /// Returns the specified Boolean value as an array of bytes. - /// A Boolean value. - /// An array of bytes with length 1. - public static byte[] GetBytes(bool value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 16-bit signed integer formed by two bytes beginning at . + /// equals the length of value minus 1. + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static short ToInt16(byte[] value, int startIndex) => + BitConverter.ToInt16(value.Reverse().ToArray(), value.Length - sizeof(short) - startIndex); - /// Returns the specified Unicode character value as an array of bytes. - /// A character to convert. - /// An array of bytes with length 2. - public static byte[] GetBytes(char value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 32-bit signed integer formed by four bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 3, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static int ToInt32(byte[] value, int startIndex) => + BitConverter.ToInt32(value.Reverse().ToArray(), value.Length - sizeof(int) - startIndex); - /// Returns the specified double-precision floating point value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 8. - public static byte[] GetBytes(double value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 64-bit signed integer formed by eight bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 7, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static long ToInt64(byte[] value, int startIndex) => + BitConverter.ToInt64(value.Reverse().ToArray(), value.Length - sizeof(long) - startIndex); - /// Returns the specified single-precision floating point value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 4. - public static byte[] GetBytes(float value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte + /// array. + /// + /// An array of bytes. + /// The starting position within value. + /// A single-precision floating point number formed by four bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 3, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static float ToSingle(byte[] value, int startIndex) => + BitConverter.ToSingle(value.Reverse().ToArray(), value.Length - sizeof(float) - startIndex); - /// Returns the specified 32-bit signed integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 4. - public static byte[] GetBytes(int value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string + /// representation. + /// + /// An array of bytes. + /// + /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding + /// element in value; for example, "7F-2C-4A". + /// + /// value is null. + public static string ToString(byte[] value) => BitConverter.ToString(value.Reverse().ToArray()); - /// Returns the specified 64-bit signed integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 8. - public static byte[] GetBytes(long value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal + /// string representation. + /// + /// An array of bytes. + /// The starting position within value. + /// + /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding + /// element in a subarray of value; for example, "7F-2C-4A". + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static string ToString(byte[] value, int startIndex) => + BitConverter.ToString(value.Reverse().ToArray(), startIndex); - /// Returns the specified 16-bit signed integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 2. - public static byte[] GetBytes(short value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal + /// string representation. + /// + /// An array of bytes. + /// The starting position within value. + /// The number of array elements in value to convert. + /// + /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding + /// element in a subarray of value; for example, "7F-2C-4A". + /// + /// value is null. + /// + /// startIndex or length is less than zero. -or- startIndex is greater + /// than zero and is greater than or equal to the length of value. + /// + /// + /// The combination of startIndex and length does not specify a position within + /// value; that is, the startIndex parameter is greater than the length of value minus the length parameter. + /// + public static string ToString(byte[] value, int startIndex, int length) => + BitConverter.ToString(value.Reverse().ToArray(), startIndex, length); - /// Returns the specified 32-bit unsigned integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 4. - public static byte[] GetBytes(uint value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. + /// The array of bytes. + /// The starting position within value. + /// A 16-bit unsigned integer formed by two bytes beginning at startIndex. + /// startIndex equals the length of value minus 1. + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static ushort ToUInt16(byte[] value, int startIndex) => + BitConverter.ToUInt16(value.Reverse().ToArray(), value.Length - sizeof(ushort) - startIndex); - /// Returns the specified 64-bit unsigned integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 8. - public static byte[] GetBytes(ulong value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 32-bit unsigned integer formed by four bytes beginning at startIndex. + /// + /// startIndex is greater than or equal to the length of value minus 3, and is + /// less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static uint ToUInt32(byte[] value, int startIndex) => + BitConverter.ToUInt32(value.Reverse().ToArray(), value.Length - sizeof(uint) - startIndex); - /// Returns the specified 16-bit unsigned integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 2. - public static byte[] GetBytes(ushort value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 64-bit unsigned integer formed by the eight bytes beginning at startIndex. + /// + /// startIndex is greater than or equal to the length of value minus 7, and is + /// less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static ulong ToUInt64(byte[] value, int startIndex) => + BitConverter.ToUInt64(value.Reverse().ToArray(), value.Length - sizeof(ulong) - startIndex); - /// Converts the specified 64-bit signed integer to a double-precision floating point number. - /// The number to convert. - /// A double-precision floating point number whose value is equivalent to value. - public static double Int64BitsToDouble(long value) => throw new NotImplementedException(); - - /// Returns a Boolean value converted from one byte at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// true if the byte at in value is nonzero; otherwise, false. - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static bool ToBoolean(byte[] value, int startIndex) => throw new NotImplementedException(); - - /// Returns a Unicode character converted from two bytes at a specified position in a byte array. - /// An array. - /// The starting position within value. - /// A character formed by two bytes beginning at . - /// equals the length of value minus 1. - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static char ToChar(byte[] value, int startIndex) => throw new NotImplementedException(); - - /// - /// Returns a double-precision floating point number converted from eight bytes at a specified position in a byte - /// array. - /// - /// An array of bytes. - /// The starting position within value. - /// A double precision floating point number formed by eight bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 7, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static double ToDouble(byte[] value, int startIndex) => throw new NotImplementedException(); - - /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 16-bit signed integer formed by two bytes beginning at . - /// equals the length of value minus 1. - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static short ToInt16(byte[] value, int startIndex) => - BitConverter.ToInt16(value.Reverse().ToArray(), value.Length - sizeof(short) - startIndex); - - /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 32-bit signed integer formed by four bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 3, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static int ToInt32(byte[] value, int startIndex) => - BitConverter.ToInt32(value.Reverse().ToArray(), value.Length - sizeof(int) - startIndex); - - /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 64-bit signed integer formed by eight bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 7, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static long ToInt64(byte[] value, int startIndex) => - BitConverter.ToInt64(value.Reverse().ToArray(), value.Length - sizeof(long) - startIndex); - - /// - /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte - /// array. - /// - /// An array of bytes. - /// The starting position within value. - /// A single-precision floating point number formed by four bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 3, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static float ToSingle(byte[] value, int startIndex) => - BitConverter.ToSingle(value.Reverse().ToArray(), value.Length - sizeof(float) - startIndex); - - /// - /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string - /// representation. - /// - /// An array of bytes. - /// - /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding - /// element in value; for example, "7F-2C-4A". - /// - /// value is null. - public static string ToString(byte[] value) => BitConverter.ToString(value.Reverse().ToArray()); - - /// - /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal - /// string representation. - /// - /// An array of bytes. - /// The starting position within value. - /// - /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding - /// element in a subarray of value; for example, "7F-2C-4A". - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static string ToString(byte[] value, int startIndex) => - BitConverter.ToString(value.Reverse().ToArray(), startIndex); - - /// - /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal - /// string representation. - /// - /// An array of bytes. - /// The starting position within value. - /// The number of array elements in value to convert. - /// - /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding - /// element in a subarray of value; for example, "7F-2C-4A". - /// - /// value is null. - /// - /// startIndex or length is less than zero. -or- startIndex is greater - /// than zero and is greater than or equal to the length of value. - /// - /// - /// The combination of startIndex and length does not specify a position within - /// value; that is, the startIndex parameter is greater than the length of value minus the length parameter. - /// - public static string ToString(byte[] value, int startIndex, int length) => - BitConverter.ToString(value.Reverse().ToArray(), startIndex, length); - - /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. - /// The array of bytes. - /// The starting position within value. - /// A 16-bit unsigned integer formed by two bytes beginning at startIndex. - /// startIndex equals the length of value minus 1. - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static ushort ToUInt16(byte[] value, int startIndex) => - BitConverter.ToUInt16(value.Reverse().ToArray(), value.Length - sizeof(ushort) - startIndex); - - /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 32-bit unsigned integer formed by four bytes beginning at startIndex. - /// - /// startIndex is greater than or equal to the length of value minus 3, and is - /// less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static uint ToUInt32(byte[] value, int startIndex) => - BitConverter.ToUInt32(value.Reverse().ToArray(), value.Length - sizeof(uint) - startIndex); - - /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 64-bit unsigned integer formed by the eight bytes beginning at startIndex. - /// - /// startIndex is greater than or equal to the length of value minus 7, and is - /// less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static ulong ToUInt64(byte[] value, int startIndex) => - BitConverter.ToUInt64(value.Reverse().ToArray(), value.Length - sizeof(ulong) - startIndex); - - public static Guid ToGuid(byte[] value, int startIndex) => new Guid(ToUInt32(value, 0 + startIndex), - ToUInt16(value, 4 + startIndex), - ToUInt16(value, 6 + startIndex), - value[8 + startIndex + 0], - value[8 + startIndex + 1], - value[8 + startIndex + 2], - value[8 + startIndex + 3], - value[8 + startIndex + 5], - value[8 + startIndex + 5], - value[8 + startIndex + 6], - value[8 + startIndex + 7]); - } + public static Guid ToGuid(byte[] value, int startIndex) => new(ToUInt32(value, 0 + startIndex), + ToUInt16(value, 4 + startIndex), + ToUInt16(value, 6 + startIndex), + value[8 + startIndex + 0], + value[8 + startIndex + 1], + value[8 + startIndex + 2], + value[8 + startIndex + 3], + value[8 + startIndex + 5], + value[8 + startIndex + 5], + value[8 + startIndex + 6], + value[8 + startIndex + 7]); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/CRC32Context.cs b/RomRepoMgr.Core/Checksums/CRC32Context.cs index eadf5cb..ca50745 100644 --- a/RomRepoMgr.Core/Checksums/CRC32Context.cs +++ b/RomRepoMgr.Core/Checksums/CRC32Context.cs @@ -35,199 +35,202 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Implements a CRC32 algorithm +public sealed class Crc32Context : IChecksum { - /// Implements a CRC32 algorithm - public sealed class Crc32Context : IChecksum + const uint CRC32_ISO_POLY = 0xEDB88320; + const uint CRC32_ISO_SEED = 0xFFFFFFFF; + + readonly uint _finalSeed; + readonly uint[] _table; + uint _hashInt; + + /// Initializes the CRC32 table and seed as CRC32-ISO + public Crc32Context() { - const uint CRC32_ISO_POLY = 0xEDB88320; - const uint CRC32_ISO_SEED = 0xFFFFFFFF; + _hashInt = CRC32_ISO_SEED; + _finalSeed = CRC32_ISO_SEED; - readonly uint _finalSeed; - readonly uint[] _table; - uint _hashInt; + _table = new uint[256]; - /// Initializes the CRC32 table and seed as CRC32-ISO - public Crc32Context() + for(var i = 0; i < 256; i++) { - _hashInt = CRC32_ISO_SEED; - _finalSeed = CRC32_ISO_SEED; + var entry = (uint)i; - _table = new uint[256]; - - for(int i = 0; i < 256; i++) + for(var j = 0; j < 8; j++) { - uint entry = (uint)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ CRC32_ISO_POLY; - else - entry = entry >> 1; - - _table[i] = entry; - } - } - - /// Initializes the CRC32 table with a custom polynomial and seed - public Crc32Context(uint polynomial, uint seed) - { - _hashInt = seed; - _finalSeed = seed; - - _table = new uint[256]; - - for(int i = 0; i < 256; i++) - { - uint entry = (uint)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ polynomial; - else - entry = entry >> 1; - - _table[i] = entry; - } - } - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) - { - for(int i = 0; i < len; i++) - _hashInt = (_hashInt >> 8) ^ _table[data[i] ^ (_hashInt & 0xff)]; - } - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed); - - /// - /// Returns a hexadecimal representation of the hash value. - public string End() - { - var crc32Output = new StringBuilder(); - - for(int i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed).Length; i++) - crc32Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed)[i].ToString("x2")); - - return crc32Output.ToString(); - } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) - { - File(filename, out byte[] hash); - - return hash; - } - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) => - File(filename, out hash, CRC32_ISO_POLY, CRC32_ISO_SEED); - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - /// CRC polynomial - /// CRC seed - public static string File(string filename, out byte[] hash, uint polynomial, uint seed) - { - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - - uint localhashInt = seed; - - uint[] localTable = new uint[256]; - - for(int i = 0; i < 256; i++) - { - uint entry = (uint)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ polynomial; - else - entry = entry >> 1; - - localTable[i] = entry; + if((entry & 1) == 1) + entry = entry >> 1 ^ CRC32_ISO_POLY; + else + entry = entry >> 1; } - for(int i = 0; i < fileStream.Length; i++) - localhashInt = (localhashInt >> 8) ^ localTable[fileStream.ReadByte() ^ (localhashInt & 0xff)]; - - localhashInt ^= seed; - hash = BigEndianBitConverter.GetBytes(localhashInt); - - var crc32Output = new StringBuilder(); - - foreach(byte h in hash) - crc32Output.Append(h.ToString("x2")); - - fileStream.Close(); - - return crc32Output.ToString(); + _table[i] = entry; } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - public static string Data(byte[] data, uint len, out byte[] hash) => - Data(data, len, out hash, CRC32_ISO_POLY, CRC32_ISO_SEED); - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - /// CRC polynomial - /// CRC seed - public static string Data(byte[] data, uint len, out byte[] hash, uint polynomial, uint seed) - { - uint localhashInt = seed; - - uint[] localTable = new uint[256]; - - for(int i = 0; i < 256; i++) - { - uint entry = (uint)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ polynomial; - else - entry = entry >> 1; - - localTable[i] = entry; - } - - for(int i = 0; i < len; i++) - localhashInt = (localhashInt >> 8) ^ localTable[data[i] ^ (localhashInt & 0xff)]; - - localhashInt ^= seed; - hash = BigEndianBitConverter.GetBytes(localhashInt); - - var crc32Output = new StringBuilder(); - - foreach(byte h in hash) - crc32Output.Append(h.ToString("x2")); - - return crc32Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Byte array of the hash value. - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } + + /// Initializes the CRC32 table with a custom polynomial and seed + public Crc32Context(uint polynomial, uint seed) + { + _hashInt = seed; + _finalSeed = seed; + + _table = new uint[256]; + + for(var i = 0; i < 256; i++) + { + var entry = (uint)i; + + for(var j = 0; j < 8; j++) + { + if((entry & 1) == 1) + entry = entry >> 1 ^ polynomial; + else + entry = entry >> 1; + } + + _table[i] = entry; + } + } + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + for(var i = 0; i < len; i++) _hashInt = _hashInt >> 8 ^ _table[data[i] ^ _hashInt & 0xff]; + } + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed); + + /// + /// Returns a hexadecimal representation of the hash value. + public string End() + { + var crc32Output = new StringBuilder(); + + for(var i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed).Length; i++) + crc32Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed)[i].ToString("x2")); + + return crc32Output.ToString(); + } + + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) + { + File(filename, out byte[] hash); + + return hash; + } + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) => + File(filename, out hash, CRC32_ISO_POLY, CRC32_ISO_SEED); + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + /// CRC polynomial + /// CRC seed + public static string File(string filename, out byte[] hash, uint polynomial, uint seed) + { + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + + uint localhashInt = seed; + + var localTable = new uint[256]; + + for(var i = 0; i < 256; i++) + { + var entry = (uint)i; + + for(var j = 0; j < 8; j++) + { + if((entry & 1) == 1) + entry = entry >> 1 ^ polynomial; + else + entry = entry >> 1; + } + + localTable[i] = entry; + } + + for(var i = 0; i < fileStream.Length; i++) + localhashInt = localhashInt >> 8 ^ localTable[fileStream.ReadByte() ^ localhashInt & 0xff]; + + localhashInt ^= seed; + hash = BigEndianBitConverter.GetBytes(localhashInt); + + var crc32Output = new StringBuilder(); + + foreach(byte h in hash) crc32Output.Append(h.ToString("x2")); + + fileStream.Close(); + + return crc32Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) => + Data(data, len, out hash, CRC32_ISO_POLY, CRC32_ISO_SEED); + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + /// CRC polynomial + /// CRC seed + public static string Data(byte[] data, uint len, out byte[] hash, uint polynomial, uint seed) + { + uint localhashInt = seed; + + var localTable = new uint[256]; + + for(var i = 0; i < 256; i++) + { + var entry = (uint)i; + + for(var j = 0; j < 8; j++) + { + if((entry & 1) == 1) + entry = entry >> 1 ^ polynomial; + else + entry = entry >> 1; + } + + localTable[i] = entry; + } + + for(var i = 0; i < len; i++) localhashInt = localhashInt >> 8 ^ localTable[data[i] ^ localhashInt & 0xff]; + + localhashInt ^= seed; + hash = BigEndianBitConverter.GetBytes(localhashInt); + + var crc32Output = new StringBuilder(); + + foreach(byte h in hash) crc32Output.Append(h.ToString("x2")); + + return crc32Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/CRC64Context.cs b/RomRepoMgr.Core/Checksums/CRC64Context.cs index 95fd3cb..ffddb0d 100644 --- a/RomRepoMgr.Core/Checksums/CRC64Context.cs +++ b/RomRepoMgr.Core/Checksums/CRC64Context.cs @@ -35,201 +35,204 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Implements a CRC64 algorithm +public sealed class Crc64Context : IChecksum { - /// Implements a CRC64 algorithm - public sealed class Crc64Context : IChecksum + public const ulong CRC64_ECMA_POLY = 0xC96C5795D7870F42; + public const ulong CRC64_ECMA_SEED = 0xFFFFFFFFFFFFFFFF; + + readonly ulong _finalSeed; + readonly ulong[] _table; + ulong _hashInt; + + /// Initializes the CRC64 table and seed as CRC64-ECMA + public Crc64Context() { - public const ulong CRC64_ECMA_POLY = 0xC96C5795D7870F42; - public const ulong CRC64_ECMA_SEED = 0xFFFFFFFFFFFFFFFF; + _hashInt = CRC64_ECMA_SEED; - readonly ulong _finalSeed; - readonly ulong[] _table; - ulong _hashInt; + _table = new ulong[256]; - /// Initializes the CRC64 table and seed as CRC64-ECMA - public Crc64Context() + for(var i = 0; i < 256; i++) { - _hashInt = CRC64_ECMA_SEED; + var entry = (ulong)i; - _table = new ulong[256]; - - for(int i = 0; i < 256; i++) + for(var j = 0; j < 8; j++) { - ulong entry = (ulong)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ CRC64_ECMA_POLY; - else - entry = entry >> 1; - - _table[i] = entry; + if((entry & 1) == 1) + entry = entry >> 1 ^ CRC64_ECMA_POLY; + else + entry = entry >> 1; } - _finalSeed = CRC64_ECMA_SEED; + _table[i] = entry; } - /// Initializes the CRC16 table with a custom polynomial and seed - public Crc64Context(ulong polynomial, ulong seed) - { - _hashInt = seed; - - _table = new ulong[256]; - - for(int i = 0; i < 256; i++) - { - ulong entry = (ulong)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ polynomial; - else - entry = entry >> 1; - - _table[i] = entry; - } - - _finalSeed = seed; - } - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) - { - for(int i = 0; i < len; i++) - _hashInt = (_hashInt >> 8) ^ _table[data[i] ^ (_hashInt & 0xff)]; - } - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed); - - /// - /// Returns a hexadecimal representation of the hash value. - public string End() - { - var crc64Output = new StringBuilder(); - - for(int i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed).Length; i++) - crc64Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed)[i].ToString("x2")); - - return crc64Output.ToString(); - } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) - { - File(filename, out byte[] localHash); - - return localHash; - } - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) => - File(filename, out hash, CRC64_ECMA_POLY, CRC64_ECMA_SEED); - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - /// CRC polynomial - /// CRC seed - public static string File(string filename, out byte[] hash, ulong polynomial, ulong seed) - { - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - - ulong localhashInt = seed; - - ulong[] localTable = new ulong[256]; - - for(int i = 0; i < 256; i++) - { - ulong entry = (ulong)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ polynomial; - else - entry = entry >> 1; - - localTable[i] = entry; - } - - for(int i = 0; i < fileStream.Length; i++) - localhashInt = (localhashInt >> 8) ^ localTable[(ulong)fileStream.ReadByte() ^ (localhashInt & 0xffL)]; - - localhashInt ^= seed; - hash = BigEndianBitConverter.GetBytes(localhashInt); - - var crc64Output = new StringBuilder(); - - foreach(byte h in hash) - crc64Output.Append(h.ToString("x2")); - - fileStream.Close(); - - return crc64Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - public static string Data(byte[] data, uint len, out byte[] hash) => - Data(data, len, out hash, CRC64_ECMA_POLY, CRC64_ECMA_SEED); - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - /// CRC polynomial - /// CRC seed - public static string Data(byte[] data, uint len, out byte[] hash, ulong polynomial, ulong seed) - { - ulong localhashInt = seed; - - ulong[] localTable = new ulong[256]; - - for(int i = 0; i < 256; i++) - { - ulong entry = (ulong)i; - - for(int j = 0; j < 8; j++) - if((entry & 1) == 1) - entry = (entry >> 1) ^ polynomial; - else - entry = entry >> 1; - - localTable[i] = entry; - } - - for(int i = 0; i < len; i++) - localhashInt = (localhashInt >> 8) ^ localTable[data[i] ^ (localhashInt & 0xff)]; - - localhashInt ^= seed; - hash = BigEndianBitConverter.GetBytes(localhashInt); - - var crc64Output = new StringBuilder(); - - foreach(byte h in hash) - crc64Output.Append(h.ToString("x2")); - - return crc64Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Byte array of the hash value. - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + _finalSeed = CRC64_ECMA_SEED; } + + /// Initializes the CRC16 table with a custom polynomial and seed + public Crc64Context(ulong polynomial, ulong seed) + { + _hashInt = seed; + + _table = new ulong[256]; + + for(var i = 0; i < 256; i++) + { + var entry = (ulong)i; + + for(var j = 0; j < 8; j++) + { + if((entry & 1) == 1) + entry = entry >> 1 ^ polynomial; + else + entry = entry >> 1; + } + + _table[i] = entry; + } + + _finalSeed = seed; + } + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + for(var i = 0; i < len; i++) _hashInt = _hashInt >> 8 ^ _table[data[i] ^ _hashInt & 0xff]; + } + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed); + + /// + /// Returns a hexadecimal representation of the hash value. + public string End() + { + var crc64Output = new StringBuilder(); + + for(var i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed).Length; i++) + crc64Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed)[i].ToString("x2")); + + return crc64Output.ToString(); + } + + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) + { + File(filename, out byte[] localHash); + + return localHash; + } + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) => + File(filename, out hash, CRC64_ECMA_POLY, CRC64_ECMA_SEED); + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + /// CRC polynomial + /// CRC seed + public static string File(string filename, out byte[] hash, ulong polynomial, ulong seed) + { + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + + ulong localhashInt = seed; + + var localTable = new ulong[256]; + + for(var i = 0; i < 256; i++) + { + var entry = (ulong)i; + + for(var j = 0; j < 8; j++) + { + if((entry & 1) == 1) + entry = entry >> 1 ^ polynomial; + else + entry = entry >> 1; + } + + localTable[i] = entry; + } + + for(var i = 0; i < fileStream.Length; i++) + localhashInt = localhashInt >> 8 ^ localTable[(ulong)fileStream.ReadByte() ^ localhashInt & 0xffL]; + + localhashInt ^= seed; + hash = BigEndianBitConverter.GetBytes(localhashInt); + + var crc64Output = new StringBuilder(); + + foreach(byte h in hash) crc64Output.Append(h.ToString("x2")); + + fileStream.Close(); + + return crc64Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) => + Data(data, len, out hash, CRC64_ECMA_POLY, CRC64_ECMA_SEED); + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + /// CRC polynomial + /// CRC seed + public static string Data(byte[] data, uint len, out byte[] hash, ulong polynomial, ulong seed) + { + ulong localhashInt = seed; + + var localTable = new ulong[256]; + + for(var i = 0; i < 256; i++) + { + var entry = (ulong)i; + + for(var j = 0; j < 8; j++) + { + if((entry & 1) == 1) + entry = entry >> 1 ^ polynomial; + else + entry = entry >> 1; + } + + localTable[i] = entry; + } + + for(var i = 0; i < len; i++) localhashInt = localhashInt >> 8 ^ localTable[data[i] ^ localhashInt & 0xff]; + + localhashInt ^= seed; + hash = BigEndianBitConverter.GetBytes(localhashInt); + + var crc64Output = new StringBuilder(); + + foreach(byte h in hash) crc64Output.Append(h.ToString("x2")); + + return crc64Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/IChecksum.cs b/RomRepoMgr.Core/Checksums/IChecksum.cs index 6b40c4a..996b63f 100644 --- a/RomRepoMgr.Core/Checksums/IChecksum.cs +++ b/RomRepoMgr.Core/Checksums/IChecksum.cs @@ -36,23 +36,22 @@ // Copyright © 2011-2024 Natalia Portillo // ****************************************************************************/ -namespace Aaru.CommonTypes.Interfaces +namespace Aaru.CommonTypes.Interfaces; + +public interface IChecksum { - public interface IChecksum - { - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - void Update(byte[] data, uint len); + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + void Update(byte[] data, uint len); - /// Updates the hash with data. - /// Data buffer. - void Update(byte[] data); + /// Updates the hash with data. + /// Data buffer. + void Update(byte[] data); - /// Returns a byte array of the hash value. - byte[] Final(); + /// Returns a byte array of the hash value. + byte[] Final(); - /// Returns a hexadecimal representation of the hash value. - string End(); - } + /// Returns a hexadecimal representation of the hash value. + string End(); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/MD5Context.cs b/RomRepoMgr.Core/Checksums/MD5Context.cs index 70fb836..4f9e748 100644 --- a/RomRepoMgr.Core/Checksums/MD5Context.cs +++ b/RomRepoMgr.Core/Checksums/MD5Context.cs @@ -35,98 +35,94 @@ using System.Security.Cryptography; using System.Text; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Wraps up .NET MD5 implementation to a Init(), Update(), Final() context. +public sealed class Md5Context : IChecksum { - /// Wraps up .NET MD5 implementation to a Init(), Update(), Final() context. - public sealed class Md5Context : IChecksum + readonly MD5 _provider; + + /// Initializes the MD5 hash provider + public Md5Context() => _provider = MD5.Create(); + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() { - readonly MD5 _provider; + _provider.TransformFinalBlock(new byte[0], 0, 0); - /// Initializes the MD5 hash provider - public Md5Context() => _provider = MD5.Create(); - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - - return _provider.Hash; - } - - /// - /// Returns a hexadecimal representation of the hash value. - public string End() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - var md5Output = new StringBuilder(); - - foreach(byte h in _provider.Hash) - md5Output.Append(h.ToString("x2")); - - return md5Output.ToString(); - } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) - { - var localMd5Provider = MD5.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - byte[] result = localMd5Provider.ComputeHash(fileStream); - fileStream.Close(); - - return result; - } - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) - { - var localMd5Provider = MD5.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - hash = localMd5Provider.ComputeHash(fileStream); - var md5Output = new StringBuilder(); - - foreach(byte h in hash) - md5Output.Append(h.ToString("x2")); - - fileStream.Close(); - - return md5Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - public static string Data(byte[] data, uint len, out byte[] hash) - { - var localMd5Provider = MD5.Create(); - hash = localMd5Provider.ComputeHash(data, 0, (int)len); - var md5Output = new StringBuilder(); - - foreach(byte h in hash) - md5Output.Append(h.ToString("x2")); - - return md5Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Byte array of the hash value. - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + return _provider.Hash; } + + /// + /// Returns a hexadecimal representation of the hash value. + public string End() + { + _provider.TransformFinalBlock(new byte[0], 0, 0); + var md5Output = new StringBuilder(); + + foreach(byte h in _provider.Hash) md5Output.Append(h.ToString("x2")); + + return md5Output.ToString(); + } + + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) + { + var localMd5Provider = MD5.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + byte[] result = localMd5Provider.ComputeHash(fileStream); + fileStream.Close(); + + return result; + } + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + var localMd5Provider = MD5.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + hash = localMd5Provider.ComputeHash(fileStream); + var md5Output = new StringBuilder(); + + foreach(byte h in hash) md5Output.Append(h.ToString("x2")); + + fileStream.Close(); + + return md5Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + var localMd5Provider = MD5.Create(); + hash = localMd5Provider.ComputeHash(data, 0, (int)len); + var md5Output = new StringBuilder(); + + foreach(byte h in hash) md5Output.Append(h.ToString("x2")); + + return md5Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/SHA1Context.cs b/RomRepoMgr.Core/Checksums/SHA1Context.cs index 78e913b..242b265 100644 --- a/RomRepoMgr.Core/Checksums/SHA1Context.cs +++ b/RomRepoMgr.Core/Checksums/SHA1Context.cs @@ -35,98 +35,94 @@ using System.Security.Cryptography; using System.Text; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Wraps up .NET SHA1 implementation to a Init(), Update(), Final() context. +public sealed class Sha1Context : IChecksum { - /// Wraps up .NET SHA1 implementation to a Init(), Update(), Final() context. - public sealed class Sha1Context : IChecksum + readonly SHA1 _provider; + + /// Initializes the SHA1 hash provider + public Sha1Context() => _provider = SHA1.Create(); + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() { - readonly SHA1 _provider; + _provider.TransformFinalBlock(new byte[0], 0, 0); - /// Initializes the SHA1 hash provider - public Sha1Context() => _provider = SHA1.Create(); - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - - return _provider.Hash; - } - - /// - /// Returns a hexadecimal representation of the hash value. - public string End() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - var sha1Output = new StringBuilder(); - - foreach(byte h in _provider.Hash) - sha1Output.Append(h.ToString("x2")); - - return sha1Output.ToString(); - } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) - { - var localSha1Provider = SHA1.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - byte[] result = localSha1Provider.ComputeHash(fileStream); - fileStream.Close(); - - return result; - } - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) - { - var localSha1Provider = SHA1.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - hash = localSha1Provider.ComputeHash(fileStream); - var sha1Output = new StringBuilder(); - - foreach(byte h in hash) - sha1Output.Append(h.ToString("x2")); - - fileStream.Close(); - - return sha1Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - public static string Data(byte[] data, uint len, out byte[] hash) - { - var localSha1Provider = SHA1.Create(); - hash = localSha1Provider.ComputeHash(data, 0, (int)len); - var sha1Output = new StringBuilder(); - - foreach(byte h in hash) - sha1Output.Append(h.ToString("x2")); - - return sha1Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Byte array of the hash value. - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + return _provider.Hash; } + + /// + /// Returns a hexadecimal representation of the hash value. + public string End() + { + _provider.TransformFinalBlock(new byte[0], 0, 0); + var sha1Output = new StringBuilder(); + + foreach(byte h in _provider.Hash) sha1Output.Append(h.ToString("x2")); + + return sha1Output.ToString(); + } + + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) + { + var localSha1Provider = SHA1.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + byte[] result = localSha1Provider.ComputeHash(fileStream); + fileStream.Close(); + + return result; + } + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + var localSha1Provider = SHA1.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + hash = localSha1Provider.ComputeHash(fileStream); + var sha1Output = new StringBuilder(); + + foreach(byte h in hash) sha1Output.Append(h.ToString("x2")); + + fileStream.Close(); + + return sha1Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + var localSha1Provider = SHA1.Create(); + hash = localSha1Provider.ComputeHash(data, 0, (int)len); + var sha1Output = new StringBuilder(); + + foreach(byte h in hash) sha1Output.Append(h.ToString("x2")); + + return sha1Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/SHA256Context.cs b/RomRepoMgr.Core/Checksums/SHA256Context.cs index 273ec9c..7df7414 100644 --- a/RomRepoMgr.Core/Checksums/SHA256Context.cs +++ b/RomRepoMgr.Core/Checksums/SHA256Context.cs @@ -35,98 +35,94 @@ using System.Security.Cryptography; using System.Text; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Wraps up .NET SHA256 implementation to a Init(), Update(), Final() context. +public sealed class Sha256Context : IChecksum { - /// Wraps up .NET SHA256 implementation to a Init(), Update(), Final() context. - public sealed class Sha256Context : IChecksum + readonly SHA256 _provider; + + /// Initializes the SHA256 hash provider + public Sha256Context() => _provider = SHA256.Create(); + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() { - readonly SHA256 _provider; + _provider.TransformFinalBlock(new byte[0], 0, 0); - /// Initializes the SHA256 hash provider - public Sha256Context() => _provider = SHA256.Create(); - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - - return _provider.Hash; - } - - /// - /// Returns a hexadecimal representation of the hash value. - public string End() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - var sha256Output = new StringBuilder(); - - foreach(byte h in _provider.Hash) - sha256Output.Append(h.ToString("x2")); - - return sha256Output.ToString(); - } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) - { - var localSha256Provider = SHA256.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - byte[] result = localSha256Provider.ComputeHash(fileStream); - fileStream.Close(); - - return result; - } - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) - { - var localSha256Provider = SHA256.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - hash = localSha256Provider.ComputeHash(fileStream); - var sha256Output = new StringBuilder(); - - foreach(byte h in hash) - sha256Output.Append(h.ToString("x2")); - - fileStream.Close(); - - return sha256Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - public static string Data(byte[] data, uint len, out byte[] hash) - { - var localSha256Provider = SHA256.Create(); - hash = localSha256Provider.ComputeHash(data, 0, (int)len); - var sha256Output = new StringBuilder(); - - foreach(byte h in hash) - sha256Output.Append(h.ToString("x2")); - - return sha256Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Byte array of the hash value. - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + return _provider.Hash; } + + /// + /// Returns a hexadecimal representation of the hash value. + public string End() + { + _provider.TransformFinalBlock(new byte[0], 0, 0); + var sha256Output = new StringBuilder(); + + foreach(byte h in _provider.Hash) sha256Output.Append(h.ToString("x2")); + + return sha256Output.ToString(); + } + + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) + { + var localSha256Provider = SHA256.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + byte[] result = localSha256Provider.ComputeHash(fileStream); + fileStream.Close(); + + return result; + } + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + var localSha256Provider = SHA256.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + hash = localSha256Provider.ComputeHash(fileStream); + var sha256Output = new StringBuilder(); + + foreach(byte h in hash) sha256Output.Append(h.ToString("x2")); + + fileStream.Close(); + + return sha256Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + var localSha256Provider = SHA256.Create(); + hash = localSha256Provider.ComputeHash(data, 0, (int)len); + var sha256Output = new StringBuilder(); + + foreach(byte h in hash) sha256Output.Append(h.ToString("x2")); + + return sha256Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/SHA384Context.cs b/RomRepoMgr.Core/Checksums/SHA384Context.cs index 280e889..bfe47a9 100644 --- a/RomRepoMgr.Core/Checksums/SHA384Context.cs +++ b/RomRepoMgr.Core/Checksums/SHA384Context.cs @@ -35,98 +35,94 @@ using System.Security.Cryptography; using System.Text; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Wraps up .NET SHA384 implementation to a Init(), Update(), Final() context. +public sealed class Sha384Context : IChecksum { - /// Wraps up .NET SHA384 implementation to a Init(), Update(), Final() context. - public sealed class Sha384Context : IChecksum + readonly SHA384 _provider; + + /// Initializes the SHA384 hash provider + public Sha384Context() => _provider = SHA384.Create(); + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() { - readonly SHA384 _provider; + _provider.TransformFinalBlock(new byte[0], 0, 0); - /// Initializes the SHA384 hash provider - public Sha384Context() => _provider = SHA384.Create(); - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - - return _provider.Hash; - } - - /// - /// Returns a hexadecimal representation of the hash value. - public string End() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - var sha384Output = new StringBuilder(); - - foreach(byte h in _provider.Hash) - sha384Output.Append(h.ToString("x2")); - - return sha384Output.ToString(); - } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) - { - var localSha384Provider = SHA384.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - byte[] result = localSha384Provider.ComputeHash(fileStream); - fileStream.Close(); - - return result; - } - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) - { - var localSha384Provider = SHA384.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - hash = localSha384Provider.ComputeHash(fileStream); - var sha384Output = new StringBuilder(); - - foreach(byte h in hash) - sha384Output.Append(h.ToString("x2")); - - fileStream.Close(); - - return sha384Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - public static string Data(byte[] data, uint len, out byte[] hash) - { - var localSha384Provider = SHA384.Create(); - hash = localSha384Provider.ComputeHash(data, 0, (int)len); - var sha384Output = new StringBuilder(); - - foreach(byte h in hash) - sha384Output.Append(h.ToString("x2")); - - return sha384Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Byte array of the hash value. - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + return _provider.Hash; } + + /// + /// Returns a hexadecimal representation of the hash value. + public string End() + { + _provider.TransformFinalBlock(new byte[0], 0, 0); + var sha384Output = new StringBuilder(); + + foreach(byte h in _provider.Hash) sha384Output.Append(h.ToString("x2")); + + return sha384Output.ToString(); + } + + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) + { + var localSha384Provider = SHA384.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + byte[] result = localSha384Provider.ComputeHash(fileStream); + fileStream.Close(); + + return result; + } + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + var localSha384Provider = SHA384.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + hash = localSha384Provider.ComputeHash(fileStream); + var sha384Output = new StringBuilder(); + + foreach(byte h in hash) sha384Output.Append(h.ToString("x2")); + + fileStream.Close(); + + return sha384Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + var localSha384Provider = SHA384.Create(); + hash = localSha384Provider.ComputeHash(data, 0, (int)len); + var sha384Output = new StringBuilder(); + + foreach(byte h in hash) sha384Output.Append(h.ToString("x2")); + + return sha384Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/SHA512Context.cs b/RomRepoMgr.Core/Checksums/SHA512Context.cs index 0fb7d7b..7ed9df5 100644 --- a/RomRepoMgr.Core/Checksums/SHA512Context.cs +++ b/RomRepoMgr.Core/Checksums/SHA512Context.cs @@ -35,98 +35,94 @@ using System.Security.Cryptography; using System.Text; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Wraps up .NET SHA512 implementation to a Init(), Update(), Final() context. +public sealed class Sha512Context : IChecksum { - /// Wraps up .NET SHA512 implementation to a Init(), Update(), Final() context. - public sealed class Sha512Context : IChecksum + readonly SHA512 _provider; + + /// Initializes the SHA512 hash provider + public Sha512Context() => _provider = SHA512.Create(); + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() { - readonly SHA512 _provider; + _provider.TransformFinalBlock(new byte[0], 0, 0); - /// Initializes the SHA512 hash provider - public Sha512Context() => _provider = SHA512.Create(); - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0); - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - - return _provider.Hash; - } - - /// - /// Returns a hexadecimal representation of the hash value. - public string End() - { - _provider.TransformFinalBlock(new byte[0], 0, 0); - var sha512Output = new StringBuilder(); - - foreach(byte h in _provider.Hash) - sha512Output.Append(h.ToString("x2")); - - return sha512Output.ToString(); - } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) - { - var localSha512Provider = SHA512.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - byte[] result = localSha512Provider.ComputeHash(fileStream); - fileStream.Close(); - - return result; - } - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) - { - var localSha512Provider = SHA512.Create(); - var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - hash = localSha512Provider.ComputeHash(fileStream); - var sha512Output = new StringBuilder(); - - foreach(byte h in hash) - sha512Output.Append(h.ToString("x2")); - - fileStream.Close(); - - return sha512Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// Byte array of the hash value. - public static string Data(byte[] data, uint len, out byte[] hash) - { - var localSha512Provider = SHA512.Create(); - hash = localSha512Provider.ComputeHash(data, 0, (int)len); - var sha512Output = new StringBuilder(); - - foreach(byte h in hash) - sha512Output.Append(h.ToString("x2")); - - return sha512Output.ToString(); - } - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Byte array of the hash value. - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + return _provider.Hash; } + + /// + /// Returns a hexadecimal representation of the hash value. + public string End() + { + _provider.TransformFinalBlock(new byte[0], 0, 0); + var sha512Output = new StringBuilder(); + + foreach(byte h in _provider.Hash) sha512Output.Append(h.ToString("x2")); + + return sha512Output.ToString(); + } + + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) + { + var localSha512Provider = SHA512.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + byte[] result = localSha512Provider.ComputeHash(fileStream); + fileStream.Close(); + + return result; + } + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + var localSha512Provider = SHA512.Create(); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + hash = localSha512Provider.ComputeHash(fileStream); + var sha512Output = new StringBuilder(); + + foreach(byte h in hash) sha512Output.Append(h.ToString("x2")); + + fileStream.Close(); + + return sha512Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + var localSha512Provider = SHA512.Create(); + hash = localSha512Provider.ComputeHash(data, 0, (int)len); + var sha512Output = new StringBuilder(); + + foreach(byte h in hash) sha512Output.Append(h.ToString("x2")); + + return sha512Output.ToString(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); } \ No newline at end of file diff --git a/RomRepoMgr.Core/Checksums/SpamSumContext.cs b/RomRepoMgr.Core/Checksums/SpamSumContext.cs index a56ec5c..d655f9b 100644 --- a/RomRepoMgr.Core/Checksums/SpamSumContext.cs +++ b/RomRepoMgr.Core/Checksums/SpamSumContext.cs @@ -45,283 +45,316 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using RomRepoMgr.Core.Resources; -namespace Aaru.Checksums +namespace Aaru.Checksums; + +/// Implements the SpamSum fuzzy hashing algorithm. +public sealed class SpamSumContext : IChecksum { - /// Implements the SpamSum fuzzy hashing algorithm. - public sealed class SpamSumContext : IChecksum + const uint ROLLING_WINDOW = 7; + const uint MIN_BLOCKSIZE = 3; + const uint HASH_PRIME = 0x01000193; + const uint HASH_INIT = 0x28021967; + const uint NUM_BLOCKHASHES = 31; + const uint SPAMSUM_LENGTH = 64; + const uint FUZZY_MAX_RESULT = 2 * SPAMSUM_LENGTH + 20; + + //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + readonly byte[] _b64 = { - const uint ROLLING_WINDOW = 7; - const uint MIN_BLOCKSIZE = 3; - const uint HASH_PRIME = 0x01000193; - const uint HASH_INIT = 0x28021967; - const uint NUM_BLOCKHASHES = 31; - const uint SPAMSUM_LENGTH = 64; - const uint FUZZY_MAX_RESULT = (2 * SPAMSUM_LENGTH) + 20; + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F + }; - //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - readonly byte[] _b64 = + FuzzyState _self; + + /// Initializes the SpamSum structures + public SpamSumContext() + { + _self = new FuzzyState { - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, - 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, - 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F + Bh = new BlockhashContext[NUM_BLOCKHASHES] }; - FuzzyState _self; + for(var i = 0; i < NUM_BLOCKHASHES; i++) _self.Bh[i].Digest = new byte[SPAMSUM_LENGTH]; - /// Initializes the SpamSum structures - public SpamSumContext() + _self.Bhstart = 0; + _self.Bhend = 1; + _self.Bh[0].H = HASH_INIT; + _self.Bh[0].Halfh = HASH_INIT; + _self.Bh[0].Digest[0] = 0; + _self.Bh[0].Halfdigest = 0; + _self.Bh[0].Dlen = 0; + _self.TotalSize = 0; + roll_init(); + } + + /// + /// Updates the hash with data. + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + _self.TotalSize += len; + + for(var i = 0; i < len; i++) fuzzy_engine_step(data[i]); + } + + /// + /// Updates the hash with data. + /// Data buffer. + public void Update(byte[] data) => Update(data, (uint)data.Length); + + /// + /// Returns a byte array of the hash value. + public byte[] Final() => throw new NotImplementedException(Localization.Spamsum_no_binary); + + /// + /// Returns a base64 representation of the hash value. + public string End() + { + FuzzyDigest(out byte[] result); + + return CToString(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void roll_init() => _self.Roll = new RollState + { + Window = new byte[ROLLING_WINDOW] + }; + + /* + * a rolling hash, based on the Adler checksum. By using a rolling hash + * we can perform auto resynchronisation after inserts/deletes + + * internally, h1 is the sum of the bytes in the window and h2 + * is the sum of the bytes times the index + + * h3 is a shift/xor based rolling hash, and is mostly needed to ensure that + * we can cope with large blocksize values + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void roll_hash(byte c) + { + _self.Roll.H2 -= _self.Roll.H1; + _self.Roll.H2 += ROLLING_WINDOW * c; + + _self.Roll.H1 += c; + _self.Roll.H1 -= _self.Roll.Window[_self.Roll.N % ROLLING_WINDOW]; + + _self.Roll.Window[_self.Roll.N % ROLLING_WINDOW] = c; + _self.Roll.N++; + + /* The original spamsum AND'ed this value with 0xFFFFFFFF which + * in theory should have no effect. This AND has been removed + * for performance (jk) */ + _self.Roll.H3 <<= 5; + _self.Roll.H3 ^= c; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + uint roll_sum() => _self.Roll.H1 + _self.Roll.H2 + _self.Roll.H3; + + /* A simple non-rolling hash, based on the FNV hash. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint sum_hash(byte c, uint h) => h * HASH_PRIME ^ c; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint SSDEEP_BS(uint index) => MIN_BLOCKSIZE << (int)index; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void fuzzy_try_fork_blockhash() + { + if(_self.Bhend >= NUM_BLOCKHASHES) return; + + if(_self.Bhend == 0) // assert + throw new Exception(Localization.Assertion_failed); + + uint obh = _self.Bhend - 1; + uint nbh = _self.Bhend; + _self.Bh[nbh].H = _self.Bh[obh].H; + _self.Bh[nbh].Halfh = _self.Bh[obh].Halfh; + _self.Bh[nbh].Digest[0] = 0; + _self.Bh[nbh].Halfdigest = 0; + _self.Bh[nbh].Dlen = 0; + ++_self.Bhend; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void fuzzy_try_reduce_blockhash() + { + if(_self.Bhstart >= _self.Bhend) throw new Exception(Localization.Assertion_failed); + + if(_self.Bhend - _self.Bhstart < 2) + /* Need at least two working hashes. */ + return; + + if((ulong)SSDEEP_BS(_self.Bhstart) * SPAMSUM_LENGTH >= _self.TotalSize) + /* Initial blocksize estimate would select this or a smaller + * blocksize. */ + return; + + if(_self.Bh[_self.Bhstart + 1].Dlen < SPAMSUM_LENGTH / 2) + /* Estimate adjustment would select this blocksize. */ + return; + + /* At this point we are clearly no longer interested in the + * start_blocksize. Get rid of it. */ + ++_self.Bhstart; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void fuzzy_engine_step(byte c) + { + uint i; + /* At each character we update the rolling hash and the normal hashes. + * When the rolling hash hits a reset value then we emit a normal hash + * as a element of the signature and reset the normal hash. */ + roll_hash(c); + ulong h = roll_sum(); + + for(i = _self.Bhstart; i < _self.Bhend; ++i) { - _self = new FuzzyState + _self.Bh[i].H = sum_hash(c, _self.Bh[i].H); + _self.Bh[i].Halfh = sum_hash(c, _self.Bh[i].Halfh); + } + + for(i = _self.Bhstart; i < _self.Bhend; ++i) + { + /* With growing blocksize almost no runs fail the next test. */ + if(h % SSDEEP_BS(i) != SSDEEP_BS(i) - 1) + /* Once this condition is false for one bs, it is + * automatically false for all further bs. I.e. if + * h === -1 (mod 2*bs) then h === -1 (mod bs). */ + break; + + /* We have hit a reset point. We now emit hashes which are + * based on all characters in the piece of the message between + * the last reset point and this one */ + if(0 == _self.Bh[i].Dlen) fuzzy_try_fork_blockhash(); + + _self.Bh[i].Digest[_self.Bh[i].Dlen] = _b64[_self.Bh[i].H % 64]; + _self.Bh[i].Halfdigest = _b64[_self.Bh[i].Halfh % 64]; + + if(_self.Bh[i].Dlen < SPAMSUM_LENGTH - 1) { - Bh = new BlockhashContext[NUM_BLOCKHASHES] - }; + /* We can have a problem with the tail overflowing. The + * easiest way to cope with this is to only reset the + * normal hash if we have room for more characters in + * our signature. This has the effect of combining the + * last few pieces of the message into a single piece + * */ + _self.Bh[i].Digest[++_self.Bh[i].Dlen] = 0; + _self.Bh[i].H = HASH_INIT; - for(int i = 0; i < NUM_BLOCKHASHES; i++) - _self.Bh[i].Digest = new byte[SPAMSUM_LENGTH]; + if(_self.Bh[i].Dlen >= SPAMSUM_LENGTH / 2) continue; - _self.Bhstart = 0; - _self.Bhend = 1; - _self.Bh[0].H = HASH_INIT; - _self.Bh[0].Halfh = HASH_INIT; - _self.Bh[0].Digest[0] = 0; - _self.Bh[0].Halfdigest = 0; - _self.Bh[0].Dlen = 0; - _self.TotalSize = 0; - roll_init(); - } - - /// - /// Updates the hash with data. - /// Data buffer. - /// Length of buffer to hash. - public void Update(byte[] data, uint len) - { - _self.TotalSize += len; - - for(int i = 0; i < len; i++) - fuzzy_engine_step(data[i]); - } - - /// - /// Updates the hash with data. - /// Data buffer. - public void Update(byte[] data) => Update(data, (uint)data.Length); - - /// - /// Returns a byte array of the hash value. - public byte[] Final() => throw new NotImplementedException(Localization.Spamsum_no_binary); - - /// - /// Returns a base64 representation of the hash value. - public string End() - { - FuzzyDigest(out byte[] result); - - return CToString(result); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void roll_init() => _self.Roll = new RollState - { - Window = new byte[ROLLING_WINDOW] - }; - - /* - * a rolling hash, based on the Adler checksum. By using a rolling hash - * we can perform auto resynchronisation after inserts/deletes - - * internally, h1 is the sum of the bytes in the window and h2 - * is the sum of the bytes times the index - - * h3 is a shift/xor based rolling hash, and is mostly needed to ensure that - * we can cope with large blocksize values - */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void roll_hash(byte c) - { - _self.Roll.H2 -= _self.Roll.H1; - _self.Roll.H2 += ROLLING_WINDOW * c; - - _self.Roll.H1 += c; - _self.Roll.H1 -= _self.Roll.Window[_self.Roll.N % ROLLING_WINDOW]; - - _self.Roll.Window[_self.Roll.N % ROLLING_WINDOW] = c; - _self.Roll.N++; - - /* The original spamsum AND'ed this value with 0xFFFFFFFF which - * in theory should have no effect. This AND has been removed - * for performance (jk) */ - _self.Roll.H3 <<= 5; - _self.Roll.H3 ^= c; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - uint roll_sum() => _self.Roll.H1 + _self.Roll.H2 + _self.Roll.H3; - - /* A simple non-rolling hash, based on the FNV hash. */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static uint sum_hash(byte c, uint h) => (h * HASH_PRIME) ^ c; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static uint SSDEEP_BS(uint index) => MIN_BLOCKSIZE << (int)index; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void fuzzy_try_fork_blockhash() - { - if(_self.Bhend >= NUM_BLOCKHASHES) - return; - - if(_self.Bhend == 0) // assert - throw new Exception(Localization.Assertion_failed); - - uint obh = _self.Bhend - 1; - uint nbh = _self.Bhend; - _self.Bh[nbh].H = _self.Bh[obh].H; - _self.Bh[nbh].Halfh = _self.Bh[obh].Halfh; - _self.Bh[nbh].Digest[0] = 0; - _self.Bh[nbh].Halfdigest = 0; - _self.Bh[nbh].Dlen = 0; - ++_self.Bhend; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void fuzzy_try_reduce_blockhash() - { - if(_self.Bhstart >= _self.Bhend) - throw new Exception(Localization.Assertion_failed); - - if(_self.Bhend - _self.Bhstart < 2) - /* Need at least two working hashes. */ - return; - - if((ulong)SSDEEP_BS(_self.Bhstart) * SPAMSUM_LENGTH >= _self.TotalSize) - /* Initial blocksize estimate would select this or a smaller - * blocksize. */ - return; - - if(_self.Bh[_self.Bhstart + 1].Dlen < SPAMSUM_LENGTH / 2) - /* Estimate adjustment would select this blocksize. */ - return; - - /* At this point we are clearly no longer interested in the - * start_blocksize. Get rid of it. */ - ++_self.Bhstart; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void fuzzy_engine_step(byte c) - { - uint i; - /* At each character we update the rolling hash and the normal hashes. - * When the rolling hash hits a reset value then we emit a normal hash - * as a element of the signature and reset the normal hash. */ - roll_hash(c); - ulong h = roll_sum(); - - for(i = _self.Bhstart; i < _self.Bhend; ++i) - { - _self.Bh[i].H = sum_hash(c, _self.Bh[i].H); - _self.Bh[i].Halfh = sum_hash(c, _self.Bh[i].Halfh); + _self.Bh[i].Halfh = HASH_INIT; + _self.Bh[i].Halfdigest = 0; } + else + fuzzy_try_reduce_blockhash(); + } + } - for(i = _self.Bhstart; i < _self.Bhend; ++i) + // CLAUNIA: Flags seems to never be used in ssdeep, so I just removed it for code simplicity + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void FuzzyDigest(out byte[] result) + { + var sb = new StringBuilder(); + uint bi = _self.Bhstart; + uint h = roll_sum(); + var remain = (int)(FUZZY_MAX_RESULT - 1); /* Exclude terminating '\0'. */ + result = new byte[FUZZY_MAX_RESULT]; + + /* Verify that our elimination was not overeager. */ + if(!(bi == 0 || (ulong)SSDEEP_BS(bi) / 2 * SPAMSUM_LENGTH < _self.TotalSize)) + throw new Exception(Localization.Assertion_failed); + + var resultOff = 0; + + /* Initial blocksize guess. */ + while((ulong)SSDEEP_BS(bi) * SPAMSUM_LENGTH < _self.TotalSize) + { + ++bi; + + if(bi >= NUM_BLOCKHASHES) throw new OverflowException(Localization.Spamsum_Input_exceeds_data); + } + + /* Adapt blocksize guess to actual digest length. */ + while(bi >= _self.Bhend) --bi; + + while(bi > _self.Bhstart && _self.Bh[bi].Dlen < SPAMSUM_LENGTH / 2) --bi; + + if(bi > 0 && _self.Bh[bi].Dlen < SPAMSUM_LENGTH / 2) throw new Exception(Localization.Assertion_failed); + + sb.AppendFormat("{0}:", SSDEEP_BS(bi)); + int i = Encoding.ASCII.GetBytes(sb.ToString()).Length; + + if(i <= 0) + /* Maybe snprintf has set errno here? */ + throw new OverflowException(Localization.Spamsum_Input_exceeds_data); + + if(i >= remain) throw new Exception(Localization.Assertion_failed); + + remain -= i; + + Array.Copy(Encoding.ASCII.GetBytes(sb.ToString()), 0, result, 0, i); + + resultOff = i; + + i = (int)_self.Bh[bi].Dlen; + + if(i > remain) throw new Exception(Localization.Assertion_failed); + + Array.Copy(_self.Bh[bi].Digest, 0, result, resultOff, i); + resultOff += i; + remain -= i; + + if(h != 0) + { + if(remain <= 0) throw new Exception(Localization.Assertion_failed); + + result[resultOff] = _b64[_self.Bh[bi].H % 64]; + + if(i < 3 || + result[resultOff] != result[resultOff - 1] || + result[resultOff] != result[resultOff - 2] || + result[resultOff] != result[resultOff - 3]) { - /* With growing blocksize almost no runs fail the next test. */ - if(h % SSDEEP_BS(i) != SSDEEP_BS(i) - 1) - /* Once this condition is false for one bs, it is - * automatically false for all further bs. I.e. if - * h === -1 (mod 2*bs) then h === -1 (mod bs). */ - break; + ++resultOff; + --remain; + } + } + else if(_self.Bh[bi].Digest[i] != 0) + { + if(remain <= 0) throw new Exception(Localization.Assertion_failed); - /* We have hit a reset point. We now emit hashes which are - * based on all characters in the piece of the message between - * the last reset point and this one */ - if(0 == _self.Bh[i].Dlen) - fuzzy_try_fork_blockhash(); + result[resultOff] = _self.Bh[bi].Digest[i]; - _self.Bh[i].Digest[_self.Bh[i].Dlen] = _b64[_self.Bh[i].H % 64]; - _self.Bh[i].Halfdigest = _b64[_self.Bh[i].Halfh % 64]; - - if(_self.Bh[i].Dlen < SPAMSUM_LENGTH - 1) - { - /* We can have a problem with the tail overflowing. The - * easiest way to cope with this is to only reset the - * normal hash if we have room for more characters in - * our signature. This has the effect of combining the - * last few pieces of the message into a single piece - * */ - _self.Bh[i].Digest[++_self.Bh[i].Dlen] = 0; - _self.Bh[i].H = HASH_INIT; - - if(_self.Bh[i].Dlen >= SPAMSUM_LENGTH / 2) - continue; - - _self.Bh[i].Halfh = HASH_INIT; - _self.Bh[i].Halfdigest = 0; - } - else - fuzzy_try_reduce_blockhash(); + if(i < 3 || + result[resultOff] != result[resultOff - 1] || + result[resultOff] != result[resultOff - 2] || + result[resultOff] != result[resultOff - 3]) + { + ++resultOff; + --remain; } } - // CLAUNIA: Flags seems to never be used in ssdeep, so I just removed it for code simplicity - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void FuzzyDigest(out byte[] result) + if(remain <= 0) throw new Exception(Localization.Assertion_failed); + + result[resultOff++] = 0x3A; // ':' + --remain; + + if(bi < _self.Bhend - 1) { - var sb = new StringBuilder(); - uint bi = _self.Bhstart; - uint h = roll_sum(); - int remain = (int)(FUZZY_MAX_RESULT - 1); /* Exclude terminating '\0'. */ - result = new byte[FUZZY_MAX_RESULT]; - - /* Verify that our elimination was not overeager. */ - if(!(bi == 0 || ((ulong)SSDEEP_BS(bi) / 2) * SPAMSUM_LENGTH < _self.TotalSize)) - throw new Exception(Localization.Assertion_failed); - - int resultOff = 0; - - /* Initial blocksize guess. */ - while((ulong)SSDEEP_BS(bi) * SPAMSUM_LENGTH < _self.TotalSize) - { - ++bi; - - if(bi >= NUM_BLOCKHASHES) - throw new OverflowException(Localization.Spamsum_Input_exceeds_data); - } - - /* Adapt blocksize guess to actual digest length. */ - while(bi >= _self.Bhend) - --bi; - - while(bi > _self.Bhstart && - _self.Bh[bi].Dlen < SPAMSUM_LENGTH / 2) - --bi; - - if(bi > 0 && - _self.Bh[bi].Dlen < SPAMSUM_LENGTH / 2) - throw new Exception(Localization.Assertion_failed); - - sb.AppendFormat("{0}:", SSDEEP_BS(bi)); - int i = Encoding.ASCII.GetBytes(sb.ToString()).Length; - - if(i <= 0) - /* Maybe snprintf has set errno here? */ - throw new OverflowException(Localization.Spamsum_Input_exceeds_data); - - if(i >= remain) - throw new Exception(Localization.Assertion_failed); - - remain -= i; - - Array.Copy(Encoding.ASCII.GetBytes(sb.ToString()), 0, result, 0, i); - - resultOff = i; - + ++bi; i = (int)_self.Bh[bi].Dlen; - if(i > remain) - throw new Exception(Localization.Assertion_failed); + if(i > remain) throw new Exception(Localization.Assertion_failed); Array.Copy(_self.Bh[bi].Digest, 0, result, resultOff, i); resultOff += i; @@ -329,10 +362,10 @@ namespace Aaru.Checksums if(h != 0) { - if(remain <= 0) - throw new Exception(Localization.Assertion_failed); + if(remain <= 0) throw new Exception(Localization.Assertion_failed); - result[resultOff] = _b64[_self.Bh[bi].H % 64]; + h = _self.Bh[bi].Halfh; + result[resultOff] = _b64[h % 64]; if(i < 3 || result[resultOff] != result[resultOff - 1] || @@ -343,48 +376,15 @@ namespace Aaru.Checksums --remain; } } - else if(_self.Bh[bi].Digest[i] != 0) + else { - if(remain <= 0) - throw new Exception(Localization.Assertion_failed); + i = _self.Bh[bi].Halfdigest; - result[resultOff] = _self.Bh[bi].Digest[i]; - - if(i < 3 || - result[resultOff] != result[resultOff - 1] || - result[resultOff] != result[resultOff - 2] || - result[resultOff] != result[resultOff - 3]) + if(i != 0) { - ++resultOff; - --remain; - } - } + if(remain <= 0) throw new Exception(Localization.Assertion_failed); - if(remain <= 0) - throw new Exception(Localization.Assertion_failed); - - result[resultOff++] = 0x3A; // ':' - --remain; - - if(bi < _self.Bhend - 1) - { - ++bi; - i = (int)_self.Bh[bi].Dlen; - - if(i > remain) - throw new Exception(Localization.Assertion_failed); - - Array.Copy(_self.Bh[bi].Digest, 0, result, resultOff, i); - resultOff += i; - remain -= i; - - if(h != 0) - { - if(remain <= 0) - throw new Exception(Localization.Assertion_failed); - - h = _self.Bh[bi].Halfh; - result[resultOff] = _b64[h % 64]; + result[resultOff] = (byte)i; if(i < 3 || result[resultOff] != result[resultOff - 1] || @@ -395,132 +395,108 @@ namespace Aaru.Checksums --remain; } } - else - { - i = _self.Bh[bi].Halfdigest; - - if(i != 0) - { - if(remain <= 0) - throw new Exception(Localization.Assertion_failed); - - result[resultOff] = (byte)i; - - if(i < 3 || - result[resultOff] != result[resultOff - 1] || - result[resultOff] != result[resultOff - 2] || - result[resultOff] != result[resultOff - 3]) - { - ++resultOff; - --remain; - } - } - } } - else if(h != 0) - { - if(_self.Bh[bi].Dlen != 0) - throw new Exception(Localization.Assertion_failed); - - if(remain <= 0) - throw new Exception(Localization.Assertion_failed); - - result[resultOff++] = _b64[_self.Bh[bi].H % 64]; - /* No need to bother with FUZZY_FLAG_ELIMSEQ, because this - * digest has length 1. */ - --remain; - } - - result[resultOff] = 0; } - - /// Gets the hash of a file - /// File path. - public static byte[] File(string filename) => throw new NotImplementedException(Localization.Spamsum_no_binary); - - /// Gets the hash of a file in hexadecimal and as a byte array. - /// File path. - /// Byte array of the hash value. - public static string File(string filename, out byte[] hash) => - throw new NotImplementedException(Localization.Not_yet_implemented); - - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// Length of the data buffer to hash. - /// null - /// Base64 representation of SpamSum $blocksize:$hash:$hash - public static string Data(byte[] data, uint len, out byte[] hash) + else if(h != 0) { - var fuzzyContext = new SpamSumContext(); + if(_self.Bh[bi].Dlen != 0) throw new Exception(Localization.Assertion_failed); - fuzzyContext.Update(data, len); + if(remain <= 0) throw new Exception(Localization.Assertion_failed); - hash = null; - - return fuzzyContext.End(); + result[resultOff++] = _b64[_self.Bh[bi].H % 64]; + /* No need to bother with FUZZY_FLAG_ELIMSEQ, because this + * digest has length 1. */ + --remain; } - /// Gets the hash of the specified data buffer. - /// Data buffer. - /// null - /// Base64 representation of SpamSum $blocksize:$hash:$hash - public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + result[resultOff] = 0; + } - // Converts an ASCII null-terminated string to .NET string - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static string CToString(byte[] cString) + /// Gets the hash of a file + /// File path. + public static byte[] File(string filename) => throw new NotImplementedException(Localization.Spamsum_no_binary); + + /// Gets the hash of a file in hexadecimal and as a byte array. + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) => + throw new NotImplementedException(Localization.Not_yet_implemented); + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// Length of the data buffer to hash. + /// null + /// Base64 representation of SpamSum $blocksize:$hash:$hash + public static string Data(byte[] data, uint len, out byte[] hash) + { + var fuzzyContext = new SpamSumContext(); + + fuzzyContext.Update(data, len); + + hash = null; + + return fuzzyContext.End(); + } + + /// Gets the hash of the specified data buffer. + /// Data buffer. + /// null + /// Base64 representation of SpamSum $blocksize:$hash:$hash + public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash); + + // Converts an ASCII null-terminated string to .NET string + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static string CToString(byte[] cString) + { + var count = 0; + + // ReSharper disable once LoopCanBeConvertedToQuery + // LINQ is six times slower + foreach(byte c in cString) { - int count = 0; + if(c == 0) break; - // ReSharper disable once LoopCanBeConvertedToQuery - // LINQ is six times slower - foreach(byte c in cString) - { - if(c == 0) - break; - - count++; - } - - return Encoding.ASCII.GetString(cString, 0, count); + count++; } - struct RollState - { - public byte[] Window; + return Encoding.ASCII.GetString(cString, 0, count); + } - // ROLLING_WINDOW - public uint H1; - public uint H2; - public uint H3; - public uint N; - } + struct RollState + { + public byte[] Window; - /* A blockhash contains a signature state for a specific (implicit) blocksize. - * The blocksize is given by SSDEEP_BS(index). The h and halfh members are the - * FNV hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2 - * long. The halfh hash is needed be able to truncate digest for the second - * output hash to stay compatible with ssdeep output. */ - struct BlockhashContext - { - public uint H; - public uint Halfh; - public byte[] Digest; + // ROLLING_WINDOW + public uint H1; + public uint H2; + public uint H3; + public uint N; + } - // SPAMSUM_LENGTH - public byte Halfdigest; - public uint Dlen; - } + /* A blockhash contains a signature state for a specific (implicit) blocksize. + * The blocksize is given by SSDEEP_BS(index). The h and halfh members are the + * FNV hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2 + * long. The halfh hash is needed be able to truncate digest for the second + * output hash to stay compatible with ssdeep output. */ + struct BlockhashContext + { + public uint H; + public uint Halfh; + public byte[] Digest; - struct FuzzyState - { - public uint Bhstart; - public uint Bhend; - public BlockhashContext[] Bh; + // SPAMSUM_LENGTH + public byte Halfdigest; + public uint Dlen; + } - //NUM_BLOCKHASHES - public ulong TotalSize; - public RollState Roll; - } + struct FuzzyState + { + public uint Bhstart; + public uint Bhend; + public BlockhashContext[] Bh; + + //NUM_BLOCKHASHES + public ulong TotalSize; + public RollState Roll; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/EventArgs/ErrorEventArgs.cs b/RomRepoMgr.Core/EventArgs/ErrorEventArgs.cs index 8db94d7..d63bbd6 100644 --- a/RomRepoMgr.Core/EventArgs/ErrorEventArgs.cs +++ b/RomRepoMgr.Core/EventArgs/ErrorEventArgs.cs @@ -23,10 +23,9 @@ // Copyright © 2020-2024 Natalia Portillo *******************************************************************************/ -namespace RomRepoMgr.Core.EventArgs +namespace RomRepoMgr.Core.EventArgs; + +public sealed class ErrorEventArgs : System.EventArgs { - public sealed class ErrorEventArgs : System.EventArgs - { - public string Message { get; set; } - } + public string Message { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/EventArgs/ImportedRomEventArgs.cs b/RomRepoMgr.Core/EventArgs/ImportedRomEventArgs.cs index 0329cb8..34fa2e5 100644 --- a/RomRepoMgr.Core/EventArgs/ImportedRomEventArgs.cs +++ b/RomRepoMgr.Core/EventArgs/ImportedRomEventArgs.cs @@ -25,10 +25,9 @@ using RomRepoMgr.Core.Models; -namespace RomRepoMgr.Core.EventArgs +namespace RomRepoMgr.Core.EventArgs; + +public sealed class ImportedRomItemEventArgs : System.EventArgs { - public sealed class ImportedRomItemEventArgs : System.EventArgs - { - public ImportRomItem Item { get; set; } - } + public ImportRomItem Item { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/EventArgs/MessageEventArgs.cs b/RomRepoMgr.Core/EventArgs/MessageEventArgs.cs index 539483e..ba557d9 100644 --- a/RomRepoMgr.Core/EventArgs/MessageEventArgs.cs +++ b/RomRepoMgr.Core/EventArgs/MessageEventArgs.cs @@ -23,10 +23,9 @@ // Copyright © 2020-2024 Natalia Portillo *******************************************************************************/ -namespace RomRepoMgr.Core.EventArgs +namespace RomRepoMgr.Core.EventArgs; + +public sealed class MessageEventArgs : System.EventArgs { - public sealed class MessageEventArgs : System.EventArgs - { - public string Message { get; set; } - } + public string Message { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/EventArgs/ProgressBoundsEventArgs.cs b/RomRepoMgr.Core/EventArgs/ProgressBoundsEventArgs.cs index c1951ae..c0ab420 100644 --- a/RomRepoMgr.Core/EventArgs/ProgressBoundsEventArgs.cs +++ b/RomRepoMgr.Core/EventArgs/ProgressBoundsEventArgs.cs @@ -23,11 +23,10 @@ // Copyright © 2020-2024 Natalia Portillo *******************************************************************************/ -namespace RomRepoMgr.Core.EventArgs +namespace RomRepoMgr.Core.EventArgs; + +public sealed class ProgressBoundsEventArgs : System.EventArgs { - public sealed class ProgressBoundsEventArgs : System.EventArgs - { - public double Minimum { get; set; } - public double Maximum { get; set; } - } + public double Minimum { get; set; } + public double Maximum { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/EventArgs/ProgressEventArgs.cs b/RomRepoMgr.Core/EventArgs/ProgressEventArgs.cs index 402744b..c87f272 100644 --- a/RomRepoMgr.Core/EventArgs/ProgressEventArgs.cs +++ b/RomRepoMgr.Core/EventArgs/ProgressEventArgs.cs @@ -23,10 +23,9 @@ // Copyright © 2020-2024 Natalia Portillo *******************************************************************************/ -namespace RomRepoMgr.Core.EventArgs +namespace RomRepoMgr.Core.EventArgs; + +public sealed class ProgressEventArgs : System.EventArgs { - public sealed class ProgressEventArgs : System.EventArgs - { - public double Value { get; set; } - } + public double Value { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/EventArgs/RomSetEventArgs.cs b/RomRepoMgr.Core/EventArgs/RomSetEventArgs.cs index b2a48e0..da3a600 100644 --- a/RomRepoMgr.Core/EventArgs/RomSetEventArgs.cs +++ b/RomRepoMgr.Core/EventArgs/RomSetEventArgs.cs @@ -25,10 +25,9 @@ using RomRepoMgr.Core.Models; -namespace RomRepoMgr.Core.EventArgs +namespace RomRepoMgr.Core.EventArgs; + +public sealed class RomSetEventArgs : System.EventArgs { - public sealed class RomSetEventArgs : System.EventArgs - { - public RomSetModel RomSet { get; set; } - } + public RomSetModel RomSet { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/EventArgs/RomSetsEventArgs.cs b/RomRepoMgr.Core/EventArgs/RomSetsEventArgs.cs index a9b4610..07ecfd4 100644 --- a/RomRepoMgr.Core/EventArgs/RomSetsEventArgs.cs +++ b/RomRepoMgr.Core/EventArgs/RomSetsEventArgs.cs @@ -26,10 +26,9 @@ using System.Collections.Generic; using RomRepoMgr.Core.Models; -namespace RomRepoMgr.Core.EventArgs +namespace RomRepoMgr.Core.EventArgs; + +public sealed class RomSetsEventArgs : System.EventArgs { - public sealed class RomSetsEventArgs : System.EventArgs - { - public List RomSets { get; set; } - } + public List RomSets { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Filesystem/Fuse.cs b/RomRepoMgr.Core/Filesystem/Fuse.cs index d20243d..64f7688 100644 --- a/RomRepoMgr.Core/Filesystem/Fuse.cs +++ b/RomRepoMgr.Core/Filesystem/Fuse.cs @@ -10,166 +10,298 @@ using Mono.Fuse.NETStandard; using Mono.Unix.Native; using RomRepoMgr.Database.Models; -namespace RomRepoMgr.Core.Filesystem +namespace RomRepoMgr.Core.Filesystem; + +// TODO: Last handle goes negative +[SupportedOSPlatform("Linux")] +[SupportedOSPlatform("macOS")] +public sealed class Fuse : FileSystem { - // TODO: Last handle goes negative - [SupportedOSPlatform("Linux")] - [SupportedOSPlatform("macOS")] - public sealed class Fuse : FileSystem + readonly ConcurrentDictionary> _directoryCache; + readonly ConcurrentDictionary _fileStatHandleCache; + readonly Vfs _vfs; + long _lastHandle; + string _umountToken; + + public Fuse(Vfs vfs) { - readonly ConcurrentDictionary> _directoryCache; - readonly ConcurrentDictionary _fileStatHandleCache; - readonly Vfs _vfs; - long _lastHandle; - string _umountToken; + _directoryCache = new ConcurrentDictionary>(); + _lastHandle = 0; + _fileStatHandleCache = new ConcurrentDictionary(); + Name = "romrepombgrfs"; + _vfs = vfs; + } - public Fuse(Vfs vfs) + public static bool IsAvailable + { + get { - _directoryCache = new ConcurrentDictionary>(); - _lastHandle = 0; - _fileStatHandleCache = new ConcurrentDictionary(); - Name = "romrepombgrfs"; - _vfs = vfs; - } - - public static bool IsAvailable - { - get + try { - try + IntPtr fuse = dlopen("libfuse.so.2", 2); + + if(fuse == IntPtr.Zero) return false; + + dlclose(fuse); + + IntPtr helper = dlopen("libMonoFuseHelper.so", 2); + + if(helper == IntPtr.Zero) { - IntPtr fuse = dlopen("libfuse.so.2", 2); + helper = dlopen("./libMonoFuseHelper.so", 2); - if(fuse == IntPtr.Zero) - return false; - - dlclose(fuse); - - IntPtr helper = dlopen("libMonoFuseHelper.so", 2); - - if(helper == IntPtr.Zero) - { - helper = dlopen("./libMonoFuseHelper.so", 2); - - if(helper == IntPtr.Zero) - return false; - } - - dlclose(helper); - - return true; - } - catch(Exception e) - { - return false; + if(helper == IntPtr.Zero) return false; } + + dlclose(helper); + + return true; + } + catch(Exception e) + { + return false; } } + } - [DllImport("libdl")] - static extern IntPtr dlopen(string filename, int flags); + [DllImport("libdl")] + static extern IntPtr dlopen(string filename, int flags); - [DllImport("libdl")] - static extern int dlclose(IntPtr handle); + [DllImport("libdl")] + static extern int dlclose(IntPtr handle); - protected override Errno OnGetPathStatus(string path, out Stat stat) + protected override Errno OnGetPathStatus(string path, out Stat stat) + { + stat = new Stat(); + + string[] pieces = _vfs.SplitPath(path); + + if(pieces.Length == 0) { - stat = new Stat(); + stat.st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555"); + stat.st_nlink = 2; - string[] pieces = _vfs.SplitPath(path); + return 0; + } - if(pieces.Length == 0) + long romSetId = _vfs.GetRomSetId(pieces[0]); + + if(romSetId <= 0) + { + if(pieces[0] != ".fuse_umount" || _umountToken == null) return Errno.ENOENT; + + stat = new Stat { - stat.st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555"); - stat.st_nlink = 2; + st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), + st_nlink = 1, + st_ctime = NativeConvert.ToTimeT(DateTime.UtcNow), + st_mtime = NativeConvert.ToTimeT(DateTime.UtcNow), + st_blksize = 0, + st_blocks = 0, + st_ino = 0, + st_size = 0 + }; - return 0; - } + return 0; + } - long romSetId = _vfs.GetRomSetId(pieces[0]); + RomSet romSet = _vfs.GetRomSet(romSetId); - if(romSetId <= 0) + if(romSet == null) return Errno.ENOENT; + + if(pieces.Length == 1) + { + stat.st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555"); + stat.st_nlink = 2; + stat.st_ctime = NativeConvert.ToTimeT(romSet.CreatedOn.ToUniversalTime()); + stat.st_mtime = NativeConvert.ToTimeT(romSet.UpdatedOn.ToUniversalTime()); + + return 0; + } + + CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + + if(machine == null) return Errno.ENOENT; + + if(pieces.Length == 2) + { + stat = new Stat { - if(pieces[0] != ".fuse_umount" || - _umountToken == null) - return Errno.ENOENT; + st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555"), + st_nlink = 2, + st_ctime = NativeConvert.ToTimeT(machine.CreationDate.ToUniversalTime()), + st_mtime = NativeConvert.ToTimeT(machine.ModificationDate.ToUniversalTime()) + }; - stat = new Stat - { - st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), - st_nlink = 1, - st_ctime = NativeConvert.ToTimeT(DateTime.UtcNow), - st_mtime = NativeConvert.ToTimeT(DateTime.UtcNow), - st_blksize = 0, - st_blocks = 0, - st_ino = 0, - st_size = 0 - }; + return 0; + } - return 0; - } + CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - RomSet romSet = _vfs.GetRomSet(romSetId); + if(file != null) + { + if(pieces.Length != 3) return Errno.ENOSYS; - if(romSet == null) - return Errno.ENOENT; - - if(pieces.Length == 1) + stat = new Stat { - stat.st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555"); - stat.st_nlink = 2; - stat.st_ctime = NativeConvert.ToTimeT(romSet.CreatedOn.ToUniversalTime()); - stat.st_mtime = NativeConvert.ToTimeT(romSet.UpdatedOn.ToUniversalTime()); + st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), + st_nlink = 1, + st_ctime = NativeConvert.ToTimeT(file.CreatedOn.ToUniversalTime()), + st_mtime = + NativeConvert.ToTimeT(file.FileLastModification?.ToUniversalTime() ?? + file.UpdatedOn.ToUniversalTime()), + st_blksize = 512, + st_blocks = (long)(file.Size / 512), + st_ino = file.Id, + st_size = (long)file.Size + }; - return 0; - } + return 0; + } - CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); - if(machine == null) - return Errno.ENOENT; + if(disk != null) + { + if(pieces.Length != 3) return Errno.ENOSYS; - if(pieces.Length == 2) + stat = new Stat { - stat = new Stat - { - st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555"), - st_nlink = 2, - st_ctime = NativeConvert.ToTimeT(machine.CreationDate.ToUniversalTime()), - st_mtime = NativeConvert.ToTimeT(machine.ModificationDate.ToUniversalTime()) - }; + st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), + st_nlink = 1, + st_ctime = NativeConvert.ToTimeT(disk.CreatedOn.ToUniversalTime()), + st_mtime = NativeConvert.ToTimeT(disk.UpdatedOn.ToUniversalTime()), + st_blksize = 512, + st_blocks = (long)(disk.Size / 512), + st_ino = disk.Id, + st_size = (long)disk.Size + }; - return 0; - } + return 0; + } - CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); + CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - if(file != null) + if(media == null) return Errno.ENOENT; + + if(pieces.Length != 3) return Errno.ENOSYS; + + stat = new Stat + { + st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), + st_nlink = 1, + st_ctime = NativeConvert.ToTimeT(media.CreatedOn.ToUniversalTime()), + st_mtime = NativeConvert.ToTimeT(media.UpdatedOn.ToUniversalTime()), + st_blksize = 512, + st_blocks = (long)(media.Size / 512), + st_ino = media.Id, + st_size = (long)media.Size + }; + + return 0; + } + + protected override Errno OnReadSymbolicLink(string link, out string target) + { + target = null; + + return Errno.EOPNOTSUPP; + } + + protected override Errno OnCreateSpecialFile(string file, FilePermissions perms, ulong dev) => Errno.EROFS; + + protected override Errno OnCreateDirectory(string directory, FilePermissions mode) => Errno.EROFS; + + protected override Errno OnRemoveFile(string file) => Errno.EROFS; + + protected override Errno OnRemoveDirectory(string directory) => Errno.EROFS; + + protected override Errno OnCreateSymbolicLink(string target, string link) => Errno.EROFS; + + protected override Errno OnRenamePath(string oldPath, string newPath) => Errno.EROFS; + + protected override Errno OnCreateHardLink(string oldPath, string link) => Errno.EROFS; + + protected override Errno OnChangePathPermissions(string path, FilePermissions mode) => Errno.EROFS; + + protected override Errno OnChangePathOwner(string path, long owner, long group) => Errno.EROFS; + + protected override Errno OnTruncateFile(string file, long length) => Errno.EROFS; + + protected override Errno OnChangePathTimes(string path, ref Utimbuf buf) => Errno.EROFS; + + protected override Errno OnOpenHandle(string path, OpenedPathInfo info) + { + string[] pieces = _vfs.SplitPath(path); + + if(pieces.Length == 0) return Errno.EISDIR; + + long romSetId = _vfs.GetRomSetId(pieces[0]); + + if(romSetId <= 0) return Errno.ENOENT; + + RomSet romSet = _vfs.GetRomSet(romSetId); + + if(romSet == null) return Errno.ENOENT; + + if(pieces.Length == 1) return Errno.EISDIR; + + CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + + if(machine == null) return Errno.ENOENT; + + if(pieces.Length == 2) return Errno.EISDIR; + + long handle = 0; + Stat stat; + + CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); + + if(file != null) + { + if(pieces.Length > 3) return Errno.ENOSYS; + + if(file.Sha384 == null) return Errno.ENOENT; + + if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) || + info.OpenAccess.HasFlag(OpenFlags.O_CREAT) || + info.OpenAccess.HasFlag(OpenFlags.O_EXCL) || + info.OpenAccess.HasFlag(OpenFlags.O_TRUNC)) + return Errno.EROFS; + + handle = _vfs.Open(file.Sha384, (long)file.Size); + + stat = new Stat { - if(pieces.Length != 3) - return Errno.ENOSYS; - - stat = new Stat - { - st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), - st_nlink = 1, - st_ctime = NativeConvert.ToTimeT(file.CreatedOn.ToUniversalTime()), - st_mtime = NativeConvert.ToTimeT(file.FileLastModification?.ToUniversalTime() ?? file.UpdatedOn.ToUniversalTime()), - st_blksize = 512, - st_blocks = (long)(file.Size / 512), - st_ino = file.Id, - st_size = (long)file.Size - }; - - return 0; - } - + st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), + st_nlink = 1, + st_ctime = NativeConvert.ToTimeT(file.CreatedOn.ToUniversalTime()), + st_mtime = + NativeConvert.ToTimeT(file.FileLastModification?.ToUniversalTime() ?? + file.UpdatedOn.ToUniversalTime()), + st_blksize = 512, + st_blocks = (long)(file.Size / 512), + st_ino = file.Id, + st_size = (long)file.Size + }; + } + else + { CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); if(disk != null) { - if(pieces.Length != 3) - return Errno.ENOSYS; + if(pieces.Length > 3) return Errno.ENOSYS; + + if(disk.Sha1 == null && disk.Md5 == null) return Errno.ENOENT; + + if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) || + info.OpenAccess.HasFlag(OpenFlags.O_CREAT) || + info.OpenAccess.HasFlag(OpenFlags.O_EXCL) || + info.OpenAccess.HasFlag(OpenFlags.O_TRUNC)) + return Errno.EROFS; + + handle = _vfs.OpenDisk(disk.Sha1, disk.Md5); stat = new Stat { @@ -182,102 +314,16 @@ namespace RomRepoMgr.Core.Filesystem st_ino = disk.Id, st_size = (long)disk.Size }; - - return 0; } - - CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - - if(media == null) - return Errno.ENOENT; - - if(pieces.Length != 3) - return Errno.ENOSYS; - - stat = new Stat + else { - st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), - st_nlink = 1, - st_ctime = NativeConvert.ToTimeT(media.CreatedOn.ToUniversalTime()), - st_mtime = NativeConvert.ToTimeT(media.UpdatedOn.ToUniversalTime()), - st_blksize = 512, - st_blocks = (long)(media.Size / 512), - st_ino = media.Id, - st_size = (long)media.Size - }; + CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - return 0; - } + if(media == null) return Errno.ENOENT; - protected override Errno OnReadSymbolicLink(string link, out string target) - { - target = null; + if(pieces.Length > 3) return Errno.ENOSYS; - return Errno.EOPNOTSUPP; - } - - protected override Errno OnCreateSpecialFile(string file, FilePermissions perms, ulong dev) => Errno.EROFS; - - protected override Errno OnCreateDirectory(string directory, FilePermissions mode) => Errno.EROFS; - - protected override Errno OnRemoveFile(string file) => Errno.EROFS; - - protected override Errno OnRemoveDirectory(string directory) => Errno.EROFS; - - protected override Errno OnCreateSymbolicLink(string target, string link) => Errno.EROFS; - - protected override Errno OnRenamePath(string oldPath, string newPath) => Errno.EROFS; - - protected override Errno OnCreateHardLink(string oldPath, string link) => Errno.EROFS; - - protected override Errno OnChangePathPermissions(string path, FilePermissions mode) => Errno.EROFS; - - protected override Errno OnChangePathOwner(string path, long owner, long group) => Errno.EROFS; - - protected override Errno OnTruncateFile(string file, long length) => Errno.EROFS; - - protected override Errno OnChangePathTimes(string path, ref Utimbuf buf) => Errno.EROFS; - - protected override Errno OnOpenHandle(string path, OpenedPathInfo info) - { - string[] pieces = _vfs.SplitPath(path); - - if(pieces.Length == 0) - return Errno.EISDIR; - - long romSetId = _vfs.GetRomSetId(pieces[0]); - - if(romSetId <= 0) - return Errno.ENOENT; - - RomSet romSet = _vfs.GetRomSet(romSetId); - - if(romSet == null) - return Errno.ENOENT; - - if(pieces.Length == 1) - return Errno.EISDIR; - - CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); - - if(machine == null) - return Errno.ENOENT; - - if(pieces.Length == 2) - return Errno.EISDIR; - - long handle = 0; - Stat stat; - - CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - - if(file != null) - { - if(pieces.Length > 3) - return Errno.ENOSYS; - - if(file.Sha384 == null) - return Errno.ENOENT; + if(media.Sha256 == null && media.Sha1 == null && media.Md5 == null) return Errno.ENOENT; if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) || info.OpenAccess.HasFlag(OpenFlags.O_CREAT) || @@ -285,672 +331,535 @@ namespace RomRepoMgr.Core.Filesystem info.OpenAccess.HasFlag(OpenFlags.O_TRUNC)) return Errno.EROFS; - handle = _vfs.Open(file.Sha384, (long)file.Size); + handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5); stat = new Stat { st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), st_nlink = 1, - st_ctime = NativeConvert.ToTimeT(file.CreatedOn.ToUniversalTime()), - st_mtime = NativeConvert.ToTimeT(file.FileLastModification?.ToUniversalTime() ?? file.UpdatedOn.ToUniversalTime()), + st_ctime = NativeConvert.ToTimeT(media.CreatedOn.ToUniversalTime()), + st_mtime = NativeConvert.ToTimeT(media.UpdatedOn.ToUniversalTime()), st_blksize = 512, - st_blocks = (long)(file.Size / 512), - st_ino = file.Id, - st_size = (long)file.Size + st_blocks = (long)(media.Size / 512), + st_ino = media.Id, + st_size = (long)media.Size }; } - else - { - CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); - - if(disk != null) - { - if(pieces.Length > 3) - return Errno.ENOSYS; - - if(disk.Sha1 == null && - disk.Md5 == null) - return Errno.ENOENT; - - if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) || - info.OpenAccess.HasFlag(OpenFlags.O_CREAT) || - info.OpenAccess.HasFlag(OpenFlags.O_EXCL) || - info.OpenAccess.HasFlag(OpenFlags.O_TRUNC)) - return Errno.EROFS; - - handle = _vfs.OpenDisk(disk.Sha1, disk.Md5); - - stat = new Stat - { - st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), - st_nlink = 1, - st_ctime = NativeConvert.ToTimeT(disk.CreatedOn.ToUniversalTime()), - st_mtime = NativeConvert.ToTimeT(disk.UpdatedOn.ToUniversalTime()), - st_blksize = 512, - st_blocks = (long)(disk.Size / 512), - st_ino = disk.Id, - st_size = (long)disk.Size - }; - } - else - { - CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - - if(media == null) - return Errno.ENOENT; - - if(pieces.Length > 3) - return Errno.ENOSYS; - - if(media.Sha256 == null && - media.Sha1 == null && - media.Md5 == null) - return Errno.ENOENT; - - if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) || - info.OpenAccess.HasFlag(OpenFlags.O_CREAT) || - info.OpenAccess.HasFlag(OpenFlags.O_EXCL) || - info.OpenAccess.HasFlag(OpenFlags.O_TRUNC)) - return Errno.EROFS; - - handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5); - - stat = new Stat - { - st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), - st_nlink = 1, - st_ctime = NativeConvert.ToTimeT(media.CreatedOn.ToUniversalTime()), - st_mtime = NativeConvert.ToTimeT(media.UpdatedOn.ToUniversalTime()), - st_blksize = 512, - st_blocks = (long)(media.Size / 512), - st_ino = media.Id, - st_size = (long)media.Size - }; - } - } - - if(handle <= 0) - return Errno.ENOENT; - - info.Handle = new IntPtr(handle); - - _fileStatHandleCache[handle] = stat; - - return 0; } - protected override Errno OnReadHandle(string file, OpenedPathInfo info, byte[] buf, long offset, - out int bytesWritten) + if(handle <= 0) return Errno.ENOENT; + + info.Handle = new IntPtr(handle); + + _fileStatHandleCache[handle] = stat; + + return 0; + } + + protected override Errno OnReadHandle(string file, OpenedPathInfo info, byte[] buf, long offset, + out int bytesWritten) + { + bytesWritten = _vfs.Read(info.Handle.ToInt64(), buf, offset); + + if(bytesWritten >= 0) return 0; + + bytesWritten = 0; + + return Errno.EBADF; + } + + protected override Errno OnWriteHandle(string file, OpenedPathInfo info, byte[] buf, long offset, out int bytesRead) + { + bytesRead = 0; + + return Errno.EROFS; + } + + protected override Errno OnGetFileSystemStatus(string path, out Statvfs buf) + { + _vfs.GetInfo(out ulong files, out ulong totalSize); + + buf = new Statvfs { - bytesWritten = _vfs.Read(info.Handle.ToInt64(), buf, offset); + f_bsize = 512, + f_frsize = 512, + f_blocks = totalSize / 512, + f_bavail = 0, + f_files = files, + f_ffree = 0, + f_favail = 0, + f_fsid = 0xFFFFFFFF, + f_flag = 0, + f_namemax = 255 + }; - if(bytesWritten >= 0) - return 0; + return 0; + } - bytesWritten = 0; + protected override Errno OnFlushHandle(string file, OpenedPathInfo info) => Errno.ENOSYS; - return Errno.EBADF; - } + protected override Errno OnReleaseHandle(string file, OpenedPathInfo info) + { + if(!_vfs.Close(info.Handle.ToInt64())) return Errno.EBADF; - protected override Errno OnWriteHandle(string file, OpenedPathInfo info, byte[] buf, long offset, - out int bytesRead) + _fileStatHandleCache.TryRemove(info.Handle.ToInt64(), out _); + + return 0; + } + + protected override Errno OnSynchronizeHandle(string file, OpenedPathInfo info, bool onlyUserData) => + Errno.EOPNOTSUPP; + + protected override Errno OnSetPathExtendedAttribute(string path, string name, byte[] value, XattrFlags flags) + { + if(_umountToken == null) return Errno.EROFS; + + if(path != "/.fuse_umount") return Errno.EROFS; + + if(name != _umountToken) return Errno.EROFS; + + if(value?.Length != 0) return Errno.EROFS; + + _umountToken = null; + Stop(); + + return 0; + } + + protected override Errno OnGetPathExtendedAttribute(string path, string name, byte[] value, out int bytesWritten) + { + bytesWritten = 0; + + string[] pieces = _vfs.SplitPath(path); + + if(pieces.Length == 0) return Errno.ENODATA; + + long romSetId = _vfs.GetRomSetId(pieces[0]); + + if(romSetId <= 0) return Errno.ENOENT; + + RomSet romSet = _vfs.GetRomSet(romSetId); + + if(romSet == null) return Errno.ENOENT; + + if(pieces.Length == 1) return Errno.ENODATA; + + CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + + if(machine == null) return Errno.ENOENT; + + if(pieces.Length == 2) return Errno.ENODATA; + + CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); + + string hash = null; + + if(file != null) { - bytesRead = 0; + if(pieces.Length > 3) return Errno.ENOSYS; - return Errno.EROFS; - } - - protected override Errno OnGetFileSystemStatus(string path, out Statvfs buf) - { - _vfs.GetInfo(out ulong files, out ulong totalSize); - - buf = new Statvfs + switch(name) { - f_bsize = 512, - f_frsize = 512, - f_blocks = totalSize / 512, - f_bavail = 0, - f_files = files, - f_ffree = 0, - f_favail = 0, - f_fsid = 0xFFFFFFFF, - f_flag = 0, - f_namemax = 255 - }; + case "user.crc32": + hash = file.Crc32; - return 0; - } + break; + case "user.md5": + hash = file.Md5; - protected override Errno OnFlushHandle(string file, OpenedPathInfo info) => Errno.ENOSYS; + break; + case "user.sha1": + hash = file.Sha1; - protected override Errno OnReleaseHandle(string file, OpenedPathInfo info) - { - if(!_vfs.Close(info.Handle.ToInt64())) - return Errno.EBADF; + break; + case "user.sha256": + hash = file.Sha256; - _fileStatHandleCache.TryRemove(info.Handle.ToInt64(), out _); + break; + case "user.sha384": + hash = file.Sha384; - return 0; - } + break; + case "user.sha512": + hash = file.Sha512; - protected override Errno OnSynchronizeHandle(string file, OpenedPathInfo info, bool onlyUserData) => - Errno.EOPNOTSUPP; - - protected override Errno OnSetPathExtendedAttribute(string path, string name, byte[] value, XattrFlags flags) - { - if(_umountToken == null) - return Errno.EROFS; - - if(path != "/.fuse_umount") - return Errno.EROFS; - - if(name != _umountToken) - return Errno.EROFS; - - if(value?.Length != 0) - return Errno.EROFS; - - _umountToken = null; - Stop(); - - return 0; - } - - protected override Errno OnGetPathExtendedAttribute(string path, string name, byte[] value, - out int bytesWritten) - { - bytesWritten = 0; - - string[] pieces = _vfs.SplitPath(path); - - if(pieces.Length == 0) - return Errno.ENODATA; - - long romSetId = _vfs.GetRomSetId(pieces[0]); - - if(romSetId <= 0) - return Errno.ENOENT; - - RomSet romSet = _vfs.GetRomSet(romSetId); - - if(romSet == null) - return Errno.ENOENT; - - if(pieces.Length == 1) - return Errno.ENODATA; - - CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); - - if(machine == null) - return Errno.ENOENT; - - if(pieces.Length == 2) - return Errno.ENODATA; - - CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - - string hash = null; - - if(file != null) - { - if(pieces.Length > 3) - return Errno.ENOSYS; - - switch(name) - { - case "user.crc32": - hash = file.Crc32; - - break; - case "user.md5": - hash = file.Md5; - - break; - case "user.sha1": - hash = file.Sha1; - - break; - case "user.sha256": - hash = file.Sha256; - - break; - case "user.sha384": - hash = file.Sha384; - - break; - case "user.sha512": - hash = file.Sha512; - - break; - } + break; } - else - { - CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); - - if(disk != null) - { - switch(name) - { - case "user.md5": - hash = disk.Md5; - - break; - case "user.sha1": - hash = disk.Sha1; - - break; - } - } - else - { - CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - - if(media == null) - return Errno.ENOENT; - - switch(name) - { - case "user.md5": - hash = media.Md5; - - break; - case "user.sha1": - hash = media.Sha1; - - break; - case "user.sha256": - hash = media.Sha256; - - break; - case "user.spamsum": - hash = media.SpamSum; - - break; - } - } - } - - if(hash == null) - return Errno.ENODATA; - - byte[] xattr = null; - - if(name == "user.spamsum") - { - xattr = Encoding.ASCII.GetBytes(hash); - } - else - { - xattr = new byte[hash.Length / 2]; - - for(int i = 0; i < xattr.Length; i++) - { - if(hash[i * 2] >= 0x30 && - hash[i * 2] <= 0x39) - xattr[i] = (byte)((hash[i * 2] - 0x30) * 0x10); - else if(hash[i * 2] >= 0x41 && - hash[i * 2] <= 0x46) - xattr[i] = (byte)((hash[i * 2] - 0x37) * 0x10); - else if(hash[i * 2] >= 0x61 && - hash[i * 2] <= 0x66) - xattr[i] = (byte)((hash[i * 2] - 0x57) * 0x10); - - if(hash[(i * 2) + 1] >= 0x30 && - hash[(i * 2) + 1] <= 0x39) - xattr[i] += (byte)(hash[(i * 2) + 1] - 0x30); - else if(hash[(i * 2) + 1] >= 0x41 && - hash[(i * 2) + 1] <= 0x46) - xattr[i] += (byte)(hash[(i * 2) + 1] - 0x37); - else if(hash[(i * 2) + 1] >= 0x61 && - hash[(i * 2) + 1] <= 0x66) - xattr[i] += (byte)(hash[(i * 2) + 1] - 0x57); - } - } - - if(value == null) - { - bytesWritten = xattr.Length; - - return 0; - } - - int maxSize = value.Length > xattr.Length ? xattr.Length : value.Length; - - Array.Copy(xattr, 0, value, 0, maxSize); - bytesWritten = maxSize; - - return 0; } - - protected override Errno OnListPathExtendedAttributes(string path, out string[] names) + else { - names = null; - - string[] pieces = _vfs.SplitPath(path); - - if(pieces.Length == 0) - return 0; - - long romSetId = _vfs.GetRomSetId(pieces[0]); - - if(romSetId <= 0) - return Errno.ENOENT; - - RomSet romSet = _vfs.GetRomSet(romSetId); - - if(romSet == null) - return Errno.ENOENT; - - if(pieces.Length == 1) - return 0; - - CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); - - if(machine == null) - return Errno.ENOENT; - - if(pieces.Length == 2) - return 0; - - List xattrs = new List(); - - CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - - if(file != null) - { - if(pieces.Length > 3) - return Errno.ENOSYS; - - if(file.Crc32 != null) - xattrs.Add("user.crc32"); - - if(file.Md5 != null) - xattrs.Add("user.md5"); - - if(file.Sha1 != null) - xattrs.Add("user.sha1"); - - if(file.Sha256 != null) - xattrs.Add("user.sha256"); - - if(file.Sha384 != null) - xattrs.Add("user.sha384"); - - if(file.Sha512 != null) - xattrs.Add("user.sha512"); - - names = xattrs.ToArray(); - - return 0; - } - CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); if(disk != null) { - if(pieces.Length > 3) - return Errno.ENOSYS; + switch(name) + { + case "user.md5": + hash = disk.Md5; - if(disk.Md5 != null) - xattrs.Add("user.md5"); + break; + case "user.sha1": + hash = disk.Sha1; - if(disk.Sha1 != null) - xattrs.Add("user.sha1"); - - names = xattrs.ToArray(); - - return 0; + break; + } } + else + { + CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); + if(media == null) return Errno.ENOENT; - if(media == null) - return Errno.ENOENT; + switch(name) + { + case "user.md5": + hash = media.Md5; - if(pieces.Length > 3) - return Errno.ENOSYS; + break; + case "user.sha1": + hash = media.Sha1; - if(media.Md5 != null) - xattrs.Add("user.md5"); + break; + case "user.sha256": + hash = media.Sha256; - if(media.Sha1 != null) - xattrs.Add("user.sha1"); + break; + case "user.spamsum": + hash = media.SpamSum; - if(media.Sha256 != null) - xattrs.Add("user.sha256"); + break; + } + } + } - if(media.SpamSum != null) - xattrs.Add("user.spamsum"); + if(hash == null) return Errno.ENODATA; + + byte[] xattr = null; + + if(name == "user.spamsum") + xattr = Encoding.ASCII.GetBytes(hash); + else + { + xattr = new byte[hash.Length / 2]; + + for(var i = 0; i < xattr.Length; i++) + { + if(hash[i * 2] >= 0x30 && hash[i * 2] <= 0x39) + xattr[i] = (byte)((hash[i * 2] - 0x30) * 0x10); + else if(hash[i * 2] >= 0x41 && hash[i * 2] <= 0x46) + xattr[i] = (byte)((hash[i * 2] - 0x37) * 0x10); + else if(hash[i * 2] >= 0x61 && hash[i * 2] <= 0x66) xattr[i] = (byte)((hash[i * 2] - 0x57) * 0x10); + + if(hash[i * 2 + 1] >= 0x30 && hash[i * 2 + 1] <= 0x39) + xattr[i] += (byte)(hash[i * 2 + 1] - 0x30); + else if(hash[i * 2 + 1] >= 0x41 && hash[i * 2 + 1] <= 0x46) + xattr[i] += (byte)(hash[i * 2 + 1] - 0x37); + else if(hash[i * 2 + 1] >= 0x61 && hash[i * 2 + 1] <= 0x66) xattr[i] += (byte)(hash[i * 2 + 1] - 0x57); + } + } + + if(value == null) + { + bytesWritten = xattr.Length; + + return 0; + } + + int maxSize = value.Length > xattr.Length ? xattr.Length : value.Length; + + Array.Copy(xattr, 0, value, 0, maxSize); + bytesWritten = maxSize; + + return 0; + } + + protected override Errno OnListPathExtendedAttributes(string path, out string[] names) + { + names = null; + + string[] pieces = _vfs.SplitPath(path); + + if(pieces.Length == 0) return 0; + + long romSetId = _vfs.GetRomSetId(pieces[0]); + + if(romSetId <= 0) return Errno.ENOENT; + + RomSet romSet = _vfs.GetRomSet(romSetId); + + if(romSet == null) return Errno.ENOENT; + + if(pieces.Length == 1) return 0; + + CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + + if(machine == null) return Errno.ENOENT; + + if(pieces.Length == 2) return 0; + + var xattrs = new List(); + + CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); + + if(file != null) + { + if(pieces.Length > 3) return Errno.ENOSYS; + + if(file.Crc32 != null) xattrs.Add("user.crc32"); + + if(file.Md5 != null) xattrs.Add("user.md5"); + + if(file.Sha1 != null) xattrs.Add("user.sha1"); + + if(file.Sha256 != null) xattrs.Add("user.sha256"); + + if(file.Sha384 != null) xattrs.Add("user.sha384"); + + if(file.Sha512 != null) xattrs.Add("user.sha512"); names = xattrs.ToArray(); return 0; } - protected override Errno OnRemovePathExtendedAttribute(string path, string name) => Errno.EROFS; + CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); - protected override Errno OnOpenDirectory(string directory, OpenedPathInfo info) + if(disk != null) { - try - { - if(directory == "/") - { - List entries = new List - { - new DirectoryEntry("."), - new DirectoryEntry("..") - }; + if(pieces.Length > 3) return Errno.ENOSYS; - entries.AddRange(_vfs.GetRootEntries().Select(e => new DirectoryEntry(e))); + if(disk.Md5 != null) xattrs.Add("user.md5"); - _lastHandle++; - info.Handle = new IntPtr(_lastHandle); + if(disk.Sha1 != null) xattrs.Add("user.sha1"); - _directoryCache[_lastHandle] = entries; - - return 0; - } - - string[] pieces = directory.Split("/", StringSplitOptions.RemoveEmptyEntries); - - if(pieces.Length == 0) - return Errno.ENOENT; - - long romSetId = _vfs.GetRomSetId(pieces[0]); - - if(romSetId <= 0) - return Errno.ENOENT; - - RomSet romSet = _vfs.GetRomSet(romSetId); - - if(romSet == null) - return Errno.ENOENT; - - ConcurrentDictionary machines = _vfs.GetMachinesFromRomSet(romSetId); - - if(pieces.Length == 1) - { - List entries = new List - { - new DirectoryEntry("."), - new DirectoryEntry("..") - }; - - entries.AddRange(machines.Select(mach => new DirectoryEntry(mach.Key))); - - _lastHandle++; - info.Handle = new IntPtr(_lastHandle); - - _directoryCache[_lastHandle] = entries; - - return 0; - } - - CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); - - if(machine == null) - return Errno.ENOENT; - - ConcurrentDictionary cachedMachineFiles = _vfs.GetFilesFromMachine(machine.Id); - ConcurrentDictionary cachedMachineDisks = _vfs.GetDisksFromMachine(machine.Id); - ConcurrentDictionary cachedMachineMedias = _vfs.GetMediasFromMachine(machine.Id); - - if(pieces.Length == 2) - { - List entries = new List - { - new DirectoryEntry("."), - new DirectoryEntry("..") - }; - - entries.AddRange(cachedMachineFiles.Select(file => new DirectoryEntry(file.Key))); - entries.AddRange(cachedMachineDisks.Select(disk => new DirectoryEntry(disk.Key + ".chd"))); - entries.AddRange(cachedMachineMedias.Select(media => new DirectoryEntry(media.Key + ".aif"))); - - _lastHandle++; - info.Handle = new IntPtr(_lastHandle); - - _directoryCache[_lastHandle] = entries; - - return 0; - } - - // TODO: DATs with subfolders as game name - if(pieces.Length >= 3) - return Errno.EISDIR; - - return Errno.ENOENT; - } - catch(Exception e) - { - Console.WriteLine(e); - - throw; - } - } - - protected override Errno OnReadDirectory(string directory, OpenedPathInfo info, - out IEnumerable paths) - { - paths = null; - - if(!_directoryCache.TryGetValue(info.Handle.ToInt64(), out List cache)) - return Errno.EBADF; - - paths = cache; + names = xattrs.ToArray(); return 0; } - protected override Errno OnReleaseDirectory(string directory, OpenedPathInfo info) + CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); + + if(media == null) return Errno.ENOENT; + + if(pieces.Length > 3) return Errno.ENOSYS; + + if(media.Md5 != null) xattrs.Add("user.md5"); + + if(media.Sha1 != null) xattrs.Add("user.sha1"); + + if(media.Sha256 != null) xattrs.Add("user.sha256"); + + if(media.SpamSum != null) xattrs.Add("user.spamsum"); + + names = xattrs.ToArray(); + + return 0; + } + + protected override Errno OnRemovePathExtendedAttribute(string path, string name) => Errno.EROFS; + + protected override Errno OnOpenDirectory(string directory, OpenedPathInfo info) + { + try { - if(!_directoryCache.TryGetValue(info.Handle.ToInt64(), out _)) - return Errno.EBADF; + if(directory == "/") + { + var entries = new List + { + new("."), + new("..") + }; - _directoryCache.Remove(info.Handle.ToInt64(), out _); + entries.AddRange(_vfs.GetRootEntries().Select(e => new DirectoryEntry(e))); - return 0; - } + _lastHandle++; + info.Handle = new IntPtr(_lastHandle); - protected override Errno OnSynchronizeDirectory(string directory, OpenedPathInfo info, bool onlyUserData) => - Errno.ENOSYS; + _directoryCache[_lastHandle] = entries; - protected override Errno OnAccessPath(string path, AccessModes mode) - { - string[] pieces = _vfs.SplitPath(path); + return 0; + } - if(pieces.Length == 0) - return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; + string[] pieces = directory.Split("/", StringSplitOptions.RemoveEmptyEntries); + + if(pieces.Length == 0) return Errno.ENOENT; long romSetId = _vfs.GetRomSetId(pieces[0]); - if(romSetId <= 0) - return Errno.ENOENT; + if(romSetId <= 0) return Errno.ENOENT; RomSet romSet = _vfs.GetRomSet(romSetId); - if(romSet == null) - return Errno.ENOENT; + if(romSet == null) return Errno.ENOENT; + + ConcurrentDictionary machines = _vfs.GetMachinesFromRomSet(romSetId); if(pieces.Length == 1) - return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; + { + var entries = new List + { + new("."), + new("..") + }; + + entries.AddRange(machines.Select(mach => new DirectoryEntry(mach.Key))); + + _lastHandle++; + info.Handle = new IntPtr(_lastHandle); + + _directoryCache[_lastHandle] = entries; + + return 0; + } CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); - if(machine == null) - return Errno.ENOENT; + if(machine == null) return Errno.ENOENT; + + ConcurrentDictionary cachedMachineFiles = _vfs.GetFilesFromMachine(machine.Id); + ConcurrentDictionary cachedMachineDisks = _vfs.GetDisksFromMachine(machine.Id); + ConcurrentDictionary cachedMachineMedias = _vfs.GetMediasFromMachine(machine.Id); if(pieces.Length == 2) - return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; - - CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - - if(file != null) { - if(pieces.Length > 3) - return Errno.ENOSYS; + var entries = new List + { + new("."), + new("..") + }; - return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; + entries.AddRange(cachedMachineFiles.Select(file => new DirectoryEntry(file.Key))); + entries.AddRange(cachedMachineDisks.Select(disk => new DirectoryEntry(disk.Key + ".chd"))); + entries.AddRange(cachedMachineMedias.Select(media => new DirectoryEntry(media.Key + ".aif"))); + + _lastHandle++; + info.Handle = new IntPtr(_lastHandle); + + _directoryCache[_lastHandle] = entries; + + return 0; } - CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); + // TODO: DATs with subfolders as game name + if(pieces.Length >= 3) return Errno.EISDIR; - if(disk != null) - { - if(pieces.Length > 3) - return Errno.ENOSYS; + return Errno.ENOENT; + } + catch(Exception e) + { + Console.WriteLine(e); - return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; - } + throw; + } + } - CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); + protected override Errno OnReadDirectory(string directory, OpenedPathInfo info, + out IEnumerable paths) + { + paths = null; - if(media == null) - return Errno.ENOENT; + if(!_directoryCache.TryGetValue(info.Handle.ToInt64(), out List cache)) return Errno.EBADF; - if(pieces.Length > 3) - return Errno.ENOSYS; + paths = cache; + + return 0; + } + + protected override Errno OnReleaseDirectory(string directory, OpenedPathInfo info) + { + if(!_directoryCache.TryGetValue(info.Handle.ToInt64(), out _)) return Errno.EBADF; + + _directoryCache.Remove(info.Handle.ToInt64(), out _); + + return 0; + } + + protected override Errno OnSynchronizeDirectory(string directory, OpenedPathInfo info, bool onlyUserData) => + Errno.ENOSYS; + + protected override Errno OnAccessPath(string path, AccessModes mode) + { + string[] pieces = _vfs.SplitPath(path); + + if(pieces.Length == 0) return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; + + long romSetId = _vfs.GetRomSetId(pieces[0]); + + if(romSetId <= 0) return Errno.ENOENT; + + RomSet romSet = _vfs.GetRomSet(romSetId); + + if(romSet == null) return Errno.ENOENT; + + if(pieces.Length == 1) return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; + + CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + + if(machine == null) return Errno.ENOENT; + + if(pieces.Length == 2) return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; + + CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); + + if(file != null) + { + if(pieces.Length > 3) return Errno.ENOSYS; return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; } - protected override Errno OnCreateHandle(string file, OpenedPathInfo info, FilePermissions mode) => Errno.EROFS; + CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); - protected override Errno OnTruncateHandle(string file, OpenedPathInfo info, long length) => Errno.EROFS; - - protected override Errno OnGetHandleStatus(string file, OpenedPathInfo info, out Stat buf) + if(disk != null) { - buf = new Stat(); + if(pieces.Length > 3) return Errno.ENOSYS; - if(!_fileStatHandleCache.TryGetValue(info.Handle.ToInt64(), out Stat fileStat)) - return Errno.EBADF; - - buf = fileStat; - - return 0; + return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; } - protected override Errno OnLockHandle(string file, OpenedPathInfo info, FcntlCommand cmd, ref Flock @lock) => - Errno.EOPNOTSUPP; + CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - protected override Errno OnMapPathLogicalToPhysicalIndex(string path, ulong logical, out ulong physical) - { - physical = ulong.MaxValue; + if(media == null) return Errno.ENOENT; - return Errno.EOPNOTSUPP; - } + if(pieces.Length > 3) return Errno.ENOSYS; - [DllImport("libc", SetLastError = true)] - static extern int setxattr(string path, string name, IntPtr value, long size, int flags); + return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0; + } - public void Umount() - { - var rnd = new Random(); - byte[] token = new byte[64]; - rnd.NextBytes(token); - _umountToken = Base32.ToBase32String(token); - setxattr(Path.Combine(MountPoint, ".fuse_umount"), _umountToken, IntPtr.Zero, 0, 0); - } + protected override Errno OnCreateHandle(string file, OpenedPathInfo info, FilePermissions mode) => Errno.EROFS; + + protected override Errno OnTruncateHandle(string file, OpenedPathInfo info, long length) => Errno.EROFS; + + protected override Errno OnGetHandleStatus(string file, OpenedPathInfo info, out Stat buf) + { + buf = new Stat(); + + if(!_fileStatHandleCache.TryGetValue(info.Handle.ToInt64(), out Stat fileStat)) return Errno.EBADF; + + buf = fileStat; + + return 0; + } + + protected override Errno OnLockHandle(string file, OpenedPathInfo info, FcntlCommand cmd, ref Flock @lock) => + Errno.EOPNOTSUPP; + + protected override Errno OnMapPathLogicalToPhysicalIndex(string path, ulong logical, out ulong physical) + { + physical = ulong.MaxValue; + + return Errno.EOPNOTSUPP; + } + + [DllImport("libc", SetLastError = true)] + static extern int setxattr(string path, string name, IntPtr value, long size, int flags); + + public void Umount() + { + var rnd = new Random(); + var token = new byte[64]; + rnd.NextBytes(token); + _umountToken = Base32.ToBase32String(token); + setxattr(Path.Combine(MountPoint, ".fuse_umount"), _umountToken, IntPtr.Zero, 0, 0); } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Filesystem/Vfs.cs b/RomRepoMgr.Core/Filesystem/Vfs.cs index db04249..a2dd94d 100644 --- a/RomRepoMgr.Core/Filesystem/Vfs.cs +++ b/RomRepoMgr.Core/Filesystem/Vfs.cs @@ -3,727 +3,698 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Threading.Tasks; using RomRepoMgr.Database; using RomRepoMgr.Database.Models; using SharpCompress.Compressors; using SharpCompress.Compressors.LZMA; -namespace RomRepoMgr.Core.Filesystem +namespace RomRepoMgr.Core.Filesystem; + +// TODO: Last handle goes negative +// TODO: Invalidate caches +// TODO: Mount options +// TODO: Do not show machines or romsets with no ROMs in repo +public class Vfs : IDisposable { - // TODO: Last handle goes negative - // TODO: Invalidate caches - // TODO: Mount options - // TODO: Do not show machines or romsets with no ROMs in repo - public class Vfs : IDisposable + readonly ConcurrentDictionary> _machineDisksCache; + readonly ConcurrentDictionary> _machineFilesCache; + readonly ConcurrentDictionary> _machineMediasCache; + readonly ConcurrentDictionary> _machinesStatCache; + readonly ConcurrentDictionary _romSetsCache; + readonly ConcurrentDictionary _streamsCache; + Fuse _fuse; + long _lastHandle; + ConcurrentDictionary _rootDirectoryCache; + Winfsp _winfsp; + + public Vfs() { - readonly ConcurrentDictionary> _machineDisksCache; - readonly ConcurrentDictionary> _machineFilesCache; - readonly ConcurrentDictionary> _machineMediasCache; - readonly ConcurrentDictionary> _machinesStatCache; - readonly ConcurrentDictionary _romSetsCache; - readonly ConcurrentDictionary _streamsCache; - Fuse _fuse; - long _lastHandle; - ConcurrentDictionary _rootDirectoryCache; - Winfsp _winfsp; + _rootDirectoryCache = new ConcurrentDictionary(); + _romSetsCache = new ConcurrentDictionary(); + _machinesStatCache = new ConcurrentDictionary>(); + _machineFilesCache = new ConcurrentDictionary>(); + _machineDisksCache = new ConcurrentDictionary>(); + _machineMediasCache = new ConcurrentDictionary>(); + _streamsCache = new ConcurrentDictionary(); + _lastHandle = 0; + } - public Vfs() + public static bool IsAvailable => OperatingSystem.IsMacOS() || OperatingSystem.IsLinux() + ? Fuse.IsAvailable + : OperatingSystem.IsWindows() && Winfsp.IsAvailable; + + public void Dispose() => Umount(); + + public event EventHandler Umounted; + + public void MountTo(string mountPoint) + { + if((OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) && Fuse.IsAvailable) { - _rootDirectoryCache = new ConcurrentDictionary(); - _romSetsCache = new ConcurrentDictionary(); - _machinesStatCache = new ConcurrentDictionary>(); - _machineFilesCache = new ConcurrentDictionary>(); - _machineDisksCache = new ConcurrentDictionary>(); - _machineMediasCache = new ConcurrentDictionary>(); - _streamsCache = new ConcurrentDictionary(); - _lastHandle = 0; + _fuse = new Fuse(this) + { + MountPoint = mountPoint + }; + + Task.Run(() => + { + _fuse.Start(); + + CleanUp(); + }); + } + else if(OperatingSystem.IsWindows() && Winfsp.IsAvailable) + { + _winfsp = new Winfsp(this); + bool ret = _winfsp.Mount(mountPoint); + + if(ret) return; + + _winfsp = null; + CleanUp(); + } + else + CleanUp(); + } + + public void Umount() + { + if(OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) + { + _fuse?.Umount(); + _fuse = null; } - public static bool IsAvailable => OperatingSystem.IsMacOS() || - OperatingSystem.IsLinux() ? Fuse.IsAvailable - : OperatingSystem.IsWindows() && Winfsp.IsAvailable; - - public void Dispose() => Umount(); - - public event EventHandler Umounted; - - public void MountTo(string mountPoint) + if(OperatingSystem.IsWindows()) { - if((OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) && Fuse.IsAvailable) - { - _fuse = new Fuse(this) - { - MountPoint = mountPoint - }; - - Task.Run(() => - { - _fuse.Start(); - - CleanUp(); - }); - } - else if(OperatingSystem.IsWindows() && Winfsp.IsAvailable) - { - _winfsp = new Winfsp(this); - bool ret = _winfsp.Mount(mountPoint); - - if(ret) - return; - - _winfsp = null; - CleanUp(); - } - else - CleanUp(); + _winfsp?.Umount(); + _winfsp = null; } - public void Umount() + CleanUp(); + } + + public void CleanUp() + { + foreach(KeyValuePair handle in _streamsCache) handle.Value.Close(); + + _streamsCache.Clear(); + _lastHandle = 0; + + Umounted?.Invoke(this, System.EventArgs.Empty); + } + + internal void GetInfo(out ulong files, out ulong totalSize) + { + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + totalSize = (ulong)(ctx.Files.Where(f => f.IsInRepo).Sum(f => (double)f.Size) + + ctx.Disks.Where(f => f.IsInRepo).Sum(f => (double)f.Size) + + ctx.Medias.Where(f => f.IsInRepo).Sum(f => (double)f.Size)); + + files = (ulong)(ctx.Files.Count(f => f.IsInRepo) + + ctx.Disks.Count(f => f.IsInRepo) + + ctx.Medias.Count(f => f.IsInRepo)); + } + + internal string[] SplitPath(string path) => + path.Split(OperatingSystem.IsWindows() ? "\\" : "/", StringSplitOptions.RemoveEmptyEntries); + + void FillRootDirectoryCache() + { + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + var rootCache = new ConcurrentDictionary(); + + foreach(RomSet set in ctx.RomSets) { - if(OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) - { - _fuse?.Umount(); - _fuse = null; - } + string name; if(OperatingSystem.IsWindows()) { - _winfsp?.Umount(); - _winfsp = null; - } + name = set.Name.Replace('/', '∕') + .Replace('<', '\uFF1C') + .Replace('>', '\uFF1E') + .Replace(':', '\uFF1A') + .Replace('"', '\u2033') + .Replace('\\', '\') + .Replace('|', '|') + .Replace('?', '?') + .Replace('*', '*'); - CleanUp(); - } - - public void CleanUp() - { - foreach(KeyValuePair handle in _streamsCache) - handle.Value.Close(); - - _streamsCache.Clear(); - _lastHandle = 0; - - Umounted?.Invoke(this, System.EventArgs.Empty); - } - - internal void GetInfo(out ulong files, out ulong totalSize) - { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - totalSize = (ulong)(ctx.Files.Where(f => f.IsInRepo).Sum(f => (double)f.Size) + - ctx.Disks.Where(f => f.IsInRepo).Sum(f => (double)f.Size) + - ctx.Medias.Where(f => f.IsInRepo).Sum(f => (double)f.Size)); - - files = (ulong)(ctx.Files.Count(f => f.IsInRepo) + ctx.Disks.Count(f => f.IsInRepo) + - ctx.Medias.Count(f => f.IsInRepo)); - } - - internal string[] SplitPath(string path) => - path.Split(OperatingSystem.IsWindows() ? "\\" : "/", - StringSplitOptions.RemoveEmptyEntries); - - void FillRootDirectoryCache() - { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - ConcurrentDictionary rootCache = new ConcurrentDictionary(); - - foreach(RomSet set in ctx.RomSets) - { - string name; - - if(OperatingSystem.IsWindows()) + if(rootCache.ContainsKey(name)) { - name = set.Name.Replace('/', '∕').Replace('<', '\uFF1C').Replace('>', '\uFF1E'). - Replace(':', '\uFF1A').Replace('"', '\u2033').Replace('\\', '\').Replace('|', '|'). - Replace('?', '?').Replace('*', '*'); - - if(rootCache.ContainsKey(name)) - name = Path.GetFileNameWithoutExtension(set.Filename)?.Replace('/', '∕').Replace('<', '\uFF1C'). - Replace('>', '\uFF1E').Replace(':', '\uFF1A').Replace('"', '\u2033'). - Replace('\\', '\').Replace('|', '|').Replace('?', '?').Replace('*', '*'); + name = Path.GetFileNameWithoutExtension(set.Filename) + ?.Replace('/', '∕') + .Replace('<', '\uFF1C') + .Replace('>', '\uFF1E') + .Replace(':', '\uFF1A') + .Replace('"', '\u2033') + .Replace('\\', '\') + .Replace('|', '|') + .Replace('?', '?') + .Replace('*', '*'); } - else - { - name = set.Name.Replace('/', '∕'); - - if(rootCache.ContainsKey(name)) - name = Path.GetFileNameWithoutExtension(set.Filename)?.Replace('/', '∕'); - } - - if(name == null || - rootCache.ContainsKey(name)) - name = Path.GetFileNameWithoutExtension(set.Sha384); - - if(name == null) - continue; - - rootCache[name] = set.Id; - _romSetsCache[set.Id] = set; } - - _rootDirectoryCache = rootCache; - } - - internal long GetRomSetId(string name) - { - if(_rootDirectoryCache.Count == 0) - FillRootDirectoryCache(); - - if(!_rootDirectoryCache.TryGetValue(name, out long romSetId)) - return -1; - - return romSetId; - } - - internal RomSet GetRomSet(long id) - { - if(_romSetsCache.TryGetValue(id, out RomSet romSet)) - return romSet; - - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - romSet = ctx.RomSets.Find(id); - - if(romSet == null) - return null; - - _romSetsCache[id] = romSet; - - return romSet; - } - - internal ConcurrentDictionary GetMachinesFromRomSet(long id) - { - _machinesStatCache.TryGetValue(id, out ConcurrentDictionary cachedMachines); - - if(cachedMachines != null) - return cachedMachines; - - cachedMachines = new ConcurrentDictionary(); - - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - foreach(Machine mach in ctx.Machines.Where(m => m.RomSet.Id == id)) + else { - cachedMachines[mach.Name] = new CachedMachine - { - Id = mach.Id, - CreationDate = mach.CreatedOn, - ModificationDate = mach.UpdatedOn - }; + name = set.Name.Replace('/', '∕'); + + if(rootCache.ContainsKey(name)) + name = Path.GetFileNameWithoutExtension(set.Filename)?.Replace('/', '∕'); } - _machinesStatCache[id] = cachedMachines; + if(name == null || rootCache.ContainsKey(name)) name = Path.GetFileNameWithoutExtension(set.Sha384); - return cachedMachines; + if(name == null) continue; + + rootCache[name] = set.Id; + _romSetsCache[set.Id] = set; } - internal CachedMachine GetMachine(long romSetId, string name) - { - ConcurrentDictionary cachedMachines = GetMachinesFromRomSet(romSetId); - - if(cachedMachines == null || - !cachedMachines.TryGetValue(name, out CachedMachine machine)) - return null; - - return machine; - } - - internal ConcurrentDictionary GetFilesFromMachine(ulong id) - { - _machineFilesCache.TryGetValue(id, out ConcurrentDictionary cachedMachineFiles); - - if(cachedMachineFiles != null) - return cachedMachineFiles; - - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - cachedMachineFiles = new ConcurrentDictionary(); - - foreach(FileByMachine machineFile in ctx.FilesByMachines.Where(fbm => fbm.Machine.Id == id && - fbm.File.IsInRepo)) - { - var cachedFile = new CachedFile - { - Id = machineFile.File.Id, - Crc32 = machineFile.File.Crc32, - Md5 = machineFile.File.Md5, - Sha1 = machineFile.File.Sha1, - Sha256 = machineFile.File.Sha256, - Sha384 = machineFile.File.Sha384, - Sha512 = machineFile.File.Sha512, - Size = machineFile.File.Size, - CreatedOn = machineFile.File.CreatedOn, - UpdatedOn = machineFile.File.UpdatedOn, - FileLastModification = machineFile.FileLastModification - }; - - cachedMachineFiles[machineFile.Name] = cachedFile; - } - - _machineFilesCache[id] = cachedMachineFiles; - - return cachedMachineFiles; - } - - internal ConcurrentDictionary GetDisksFromMachine(ulong id) - { - _machineDisksCache.TryGetValue(id, out ConcurrentDictionary cachedMachineDisks); - - if(cachedMachineDisks != null) - return cachedMachineDisks; - - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - cachedMachineDisks = new ConcurrentDictionary(); - - foreach(DiskByMachine machineDisk in ctx.DisksByMachines.Where(dbm => dbm.Machine.Id == id && - dbm.Disk.IsInRepo && - dbm.Disk.Size != null)) - { - var cachedDisk = new CachedDisk - { - Id = machineDisk.Disk.Id, - Md5 = machineDisk.Disk.Md5, - Sha1 = machineDisk.Disk.Sha1, - Size = machineDisk.Disk.Size ?? 0, - CreatedOn = machineDisk.Disk.CreatedOn, - UpdatedOn = machineDisk.Disk.UpdatedOn - }; - - cachedMachineDisks[machineDisk.Name] = cachedDisk; - } - - _machineDisksCache[id] = cachedMachineDisks; - - return cachedMachineDisks; - } - - internal ConcurrentDictionary GetMediasFromMachine(ulong id) - { - _machineMediasCache.TryGetValue(id, out ConcurrentDictionary cachedMachineMedias); - - if(cachedMachineMedias != null) - return cachedMachineMedias; - - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - cachedMachineMedias = new ConcurrentDictionary(); - - foreach(MediaByMachine machineMedia in ctx.MediasByMachines.Where(mbm => mbm.Machine.Id == id && - mbm.Media.IsInRepo && - mbm.Media.Size != null)) - { - var cachedDisk = new CachedMedia - { - Id = machineMedia.Media.Id, - Md5 = machineMedia.Media.Md5, - Sha1 = machineMedia.Media.Sha1, - Sha256 = machineMedia.Media.Sha256, - SpamSum = machineMedia.Media.SpamSum, - Size = machineMedia.Media.Size ?? 0, - CreatedOn = machineMedia.Media.CreatedOn, - UpdatedOn = machineMedia.Media.UpdatedOn - }; - - cachedMachineMedias[machineMedia.Name] = cachedDisk; - } - - _machineMediasCache[id] = cachedMachineMedias; - - return cachedMachineMedias; - } - - internal CachedFile GetFile(ulong machineId, string name) - { - ConcurrentDictionary cachedFiles = GetFilesFromMachine(machineId); - - if(cachedFiles == null || - !cachedFiles.TryGetValue(name, out CachedFile file)) - return null; - - return file; - } - - internal CachedDisk GetDisk(ulong machineId, string name) - { - if(name.EndsWith(".chd", StringComparison.OrdinalIgnoreCase)) - name = name.Substring(0, name.Length - 4); - - ConcurrentDictionary cachedDisks = GetDisksFromMachine(machineId); - - if(cachedDisks == null || - !cachedDisks.TryGetValue(name, out CachedDisk disk)) - return null; - - return disk; - } - - internal CachedMedia GetMedia(ulong machineId, string name) - { - if(name.EndsWith(".aif", StringComparison.OrdinalIgnoreCase)) - name = name.Substring(0, name.Length - 4); - - ConcurrentDictionary cachedMedias = GetMediasFromMachine(machineId); - - if(cachedMedias == null || - !cachedMedias.TryGetValue(name, out CachedMedia media)) - return null; - - return media; - } - - internal long Open(string sha384, long fileSize) - { - byte[] sha384Bytes = new byte[48]; - - for(int i = 0; i < 48; i++) - { - if(sha384[i * 2] >= 0x30 && - sha384[i * 2] <= 0x39) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); - else if(sha384[i * 2] >= 0x41 && - sha384[i * 2] <= 0x46) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); - else if(sha384[i * 2] >= 0x61 && - sha384[i * 2] <= 0x66) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); - - if(sha384[(i * 2) + 1] >= 0x30 && - sha384[(i * 2) + 1] <= 0x39) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30); - else if(sha384[(i * 2) + 1] >= 0x41 && - sha384[(i * 2) + 1] <= 0x46) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37); - else if(sha384[(i * 2) + 1] >= 0x61 && - sha384[(i * 2) + 1] <= 0x66) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57); - } - - string sha384B32 = Base32.ToBase32String(sha384Bytes); - - string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "files", sha384B32[0].ToString(), - sha384B32[1].ToString(), sha384B32[2].ToString(), sha384B32[3].ToString(), - sha384B32[4].ToString(), sha384B32 + ".lz"); - - if(!File.Exists(repoPath)) - return -1; - - _lastHandle++; - long handle = _lastHandle; - - _streamsCache[handle] = - Stream.Synchronized(new ForcedSeekStream(fileSize, - new FileStream(repoPath, FileMode.Open, - FileAccess.Read), - CompressionMode.Decompress)); - - return handle; - } - - internal int Read(long handle, byte[] buf, long offset) - { - if(!_streamsCache.TryGetValue(handle, out Stream stream)) - return -1; - - lock(stream) - { - stream.Position = offset; - - return stream.Read(buf, 0, buf.Length); - } - } - - internal bool Close(long handle) - { - if(!_streamsCache.TryGetValue(handle, out Stream stream)) - return false; - - stream.Close(); - _streamsCache.TryRemove(handle, out _); - - return true; - } - - internal IEnumerable GetRootEntries() - { - if(_rootDirectoryCache.Count == 0) - FillRootDirectoryCache(); - - return _rootDirectoryCache.Keys.ToArray(); - } - - public long OpenDisk(string sha1, string md5) - { - if(sha1 == null && - md5 == null) - return -1; - - string repoPath = null; - string md5Path = null; - string sha1Path = null; - - if(sha1 != null) - { - byte[] sha1Bytes = new byte[20]; - - for(int i = 0; i < 20; i++) - { - if(sha1[i * 2] >= 0x30 && - sha1[i * 2] <= 0x39) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); - else if(sha1[i * 2] >= 0x41 && - sha1[i * 2] <= 0x46) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); - else if(sha1[i * 2] >= 0x61 && - sha1[i * 2] <= 0x66) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); - - if(sha1[(i * 2) + 1] >= 0x30 && - sha1[(i * 2) + 1] <= 0x39) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x30); - else if(sha1[(i * 2) + 1] >= 0x41 && - sha1[(i * 2) + 1] <= 0x46) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x37); - else if(sha1[(i * 2) + 1] >= 0x61 && - sha1[(i * 2) + 1] <= 0x66) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x57); - } - - string sha1B32 = Base32.ToBase32String(sha1Bytes); - - sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "sha1", sha1B32[0].ToString(), - sha1B32[1].ToString(), sha1B32[2].ToString(), sha1B32[3].ToString(), - sha1B32[4].ToString(), sha1B32 + ".chd"); - } - - if(md5 != null) - { - byte[] md5Bytes = new byte[16]; - - for(int i = 0; i < 16; i++) - { - if(md5[i * 2] >= 0x30 && - md5[i * 2] <= 0x39) - md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); - else if(md5[i * 2] >= 0x41 && - md5[i * 2] <= 0x46) - md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); - else if(md5[i * 2] >= 0x61 && - md5[i * 2] <= 0x66) - md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); - - if(md5[(i * 2) + 1] >= 0x30 && - md5[(i * 2) + 1] <= 0x39) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x30); - else if(md5[(i * 2) + 1] >= 0x41 && - md5[(i * 2) + 1] <= 0x46) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x37); - else if(md5[(i * 2) + 1] >= 0x61 && - md5[(i * 2) + 1] <= 0x66) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x57); - } - - string md5B32 = Base32.ToBase32String(md5Bytes); - - md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "md5", md5B32[0].ToString(), - md5B32[1].ToString(), md5B32[2].ToString(), md5B32[3].ToString(), - md5B32[4].ToString(), md5B32 + ".chd"); - } - - if(File.Exists(sha1Path)) - repoPath = sha1Path; - else if(File.Exists(md5Path)) - repoPath = md5Path; - - if(repoPath == null) - return -1; - - _lastHandle++; - long handle = _lastHandle; - - _streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read)); - - return handle; - } - - public long OpenMedia(string sha256, string sha1, string md5) - { - if(sha256 == null && - sha1 == null && - md5 == null) - return -1; - - string repoPath = null; - string md5Path = null; - string sha1Path = null; - string sha256Path = null; - - if(sha256 != null) - { - byte[] sha256Bytes = new byte[32]; - - for(int i = 0; i < 32; i++) - { - if(sha256[i * 2] >= 0x30 && - sha256[i * 2] <= 0x39) - sha256Bytes[i] = (byte)((sha256[i * 2] - 0x30) * 0x10); - else if(sha256[i * 2] >= 0x41 && - sha256[i * 2] <= 0x46) - sha256Bytes[i] = (byte)((sha256[i * 2] - 0x37) * 0x10); - else if(sha256[i * 2] >= 0x61 && - sha256[i * 2] <= 0x66) - sha256Bytes[i] = (byte)((sha256[i * 2] - 0x57) * 0x10); - - if(sha256[(i * 2) + 1] >= 0x30 && - sha256[(i * 2) + 1] <= 0x39) - sha256Bytes[i] += (byte)(sha256[(i * 2) + 1] - 0x30); - else if(sha256[(i * 2) + 1] >= 0x41 && - sha256[(i * 2) + 1] <= 0x46) - sha256Bytes[i] += (byte)(sha256[(i * 2) + 1] - 0x37); - else if(sha256[(i * 2) + 1] >= 0x61 && - sha256[(i * 2) + 1] <= 0x66) - sha256Bytes[i] += (byte)(sha256[(i * 2) + 1] - 0x57); - } - - string sha256B32 = Base32.ToBase32String(sha256Bytes); - - sha256Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "sha256", - sha256B32[0].ToString(), sha256B32[1].ToString(), sha256B32[2].ToString(), - sha256B32[3].ToString(), sha256B32[4].ToString(), sha256B32 + ".aif"); - } - - if(sha1 != null) - { - byte[] sha1Bytes = new byte[20]; - - for(int i = 0; i < 20; i++) - { - if(sha1[i * 2] >= 0x30 && - sha1[i * 2] <= 0x39) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); - else if(sha1[i * 2] >= 0x41 && - sha1[i * 2] <= 0x46) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); - else if(sha1[i * 2] >= 0x61 && - sha1[i * 2] <= 0x66) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); - - if(sha1[(i * 2) + 1] >= 0x30 && - sha1[(i * 2) + 1] <= 0x39) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x30); - else if(sha1[(i * 2) + 1] >= 0x41 && - sha1[(i * 2) + 1] <= 0x46) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x37); - else if(sha1[(i * 2) + 1] >= 0x61 && - sha1[(i * 2) + 1] <= 0x66) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x57); - } - - string sha1B32 = Base32.ToBase32String(sha1Bytes); - - sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "sha1", sha1B32[0].ToString(), - sha1B32[1].ToString(), sha1B32[2].ToString(), sha1B32[3].ToString(), - sha1B32[4].ToString(), sha1B32 + ".aif"); - } - - if(md5 != null) - { - byte[] md5Bytes = new byte[16]; - - for(int i = 0; i < 16; i++) - { - if(md5[i * 2] >= 0x30 && - md5[i * 2] <= 0x39) - md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); - else if(md5[i * 2] >= 0x41 && - md5[i * 2] <= 0x46) - md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); - else if(md5[i * 2] >= 0x61 && - md5[i * 2] <= 0x66) - md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); - - if(md5[(i * 2) + 1] >= 0x30 && - md5[(i * 2) + 1] <= 0x39) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x30); - else if(md5[(i * 2) + 1] >= 0x41 && - md5[(i * 2) + 1] <= 0x46) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x37); - else if(md5[(i * 2) + 1] >= 0x61 && - md5[(i * 2) + 1] <= 0x66) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x57); - } - - string md5B32 = Base32.ToBase32String(md5Bytes); - - md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "md5", md5B32[0].ToString(), - md5B32[1].ToString(), md5B32[2].ToString(), md5B32[3].ToString(), - md5B32[4].ToString(), md5B32 + ".aif"); - } - - if(File.Exists(sha256Path)) - repoPath = sha256Path; - else if(File.Exists(sha1Path)) - repoPath = sha1Path; - else if(File.Exists(md5Path)) - repoPath = md5Path; - - if(repoPath == null) - return -1; - - _lastHandle++; - long handle = _lastHandle; - - _streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read)); - - return handle; - } + _rootDirectoryCache = rootCache; } - internal sealed class CachedMachine + internal long GetRomSetId(string name) { - public ulong Id { get; set; } - public DateTime CreationDate { get; set; } - public DateTime ModificationDate { get; set; } + if(_rootDirectoryCache.Count == 0) FillRootDirectoryCache(); + + if(!_rootDirectoryCache.TryGetValue(name, out long romSetId)) return -1; + + return romSetId; } - internal sealed class CachedFile + internal RomSet GetRomSet(long id) { - public ulong Id { get; set; } - public ulong Size { get; set; } - public string Crc32 { get; set; } - public string Md5 { get; set; } - public string Sha1 { get; set; } - public string Sha256 { get; set; } - public string Sha384 { get; set; } - public string Sha512 { get; set; } - public DateTime CreatedOn { get; set; } - public DateTime UpdatedOn { get; set; } - public DateTime? FileLastModification { get; set; } + if(_romSetsCache.TryGetValue(id, out RomSet romSet)) return romSet; + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + romSet = ctx.RomSets.Find(id); + + if(romSet == null) return null; + + _romSetsCache[id] = romSet; + + return romSet; } - internal sealed class CachedDisk + internal ConcurrentDictionary GetMachinesFromRomSet(long id) { - public ulong Id { get; set; } - public ulong Size { get; set; } - public string Md5 { get; set; } - public string Sha1 { get; set; } - public DateTime CreatedOn { get; set; } - public DateTime UpdatedOn { get; set; } + _machinesStatCache.TryGetValue(id, out ConcurrentDictionary cachedMachines); + + if(cachedMachines != null) return cachedMachines; + + cachedMachines = new ConcurrentDictionary(); + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + foreach(Machine mach in ctx.Machines.Where(m => m.RomSet.Id == id)) + { + cachedMachines[mach.Name] = new CachedMachine + { + Id = mach.Id, + CreationDate = mach.CreatedOn, + ModificationDate = mach.UpdatedOn + }; + } + + _machinesStatCache[id] = cachedMachines; + + return cachedMachines; } - internal sealed class CachedMedia + internal CachedMachine GetMachine(long romSetId, string name) { - public ulong Id { get; set; } - public ulong Size { get; set; } - public string Md5 { get; set; } - public string Sha1 { get; set; } - public string Sha256 { get; set; } - public string SpamSum { get; set; } - public DateTime CreatedOn { get; set; } - public DateTime UpdatedOn { get; set; } + ConcurrentDictionary cachedMachines = GetMachinesFromRomSet(romSetId); + + if(cachedMachines == null || !cachedMachines.TryGetValue(name, out CachedMachine machine)) return null; + + return machine; } + + internal ConcurrentDictionary GetFilesFromMachine(ulong id) + { + _machineFilesCache.TryGetValue(id, out ConcurrentDictionary cachedMachineFiles); + + if(cachedMachineFiles != null) return cachedMachineFiles; + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + cachedMachineFiles = new ConcurrentDictionary(); + + foreach(FileByMachine machineFile in + ctx.FilesByMachines.Where(fbm => fbm.Machine.Id == id && fbm.File.IsInRepo)) + { + var cachedFile = new CachedFile + { + Id = machineFile.File.Id, + Crc32 = machineFile.File.Crc32, + Md5 = machineFile.File.Md5, + Sha1 = machineFile.File.Sha1, + Sha256 = machineFile.File.Sha256, + Sha384 = machineFile.File.Sha384, + Sha512 = machineFile.File.Sha512, + Size = machineFile.File.Size, + CreatedOn = machineFile.File.CreatedOn, + UpdatedOn = machineFile.File.UpdatedOn, + FileLastModification = machineFile.FileLastModification + }; + + cachedMachineFiles[machineFile.Name] = cachedFile; + } + + _machineFilesCache[id] = cachedMachineFiles; + + return cachedMachineFiles; + } + + internal ConcurrentDictionary GetDisksFromMachine(ulong id) + { + _machineDisksCache.TryGetValue(id, out ConcurrentDictionary cachedMachineDisks); + + if(cachedMachineDisks != null) return cachedMachineDisks; + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + cachedMachineDisks = new ConcurrentDictionary(); + + foreach(DiskByMachine machineDisk in ctx.DisksByMachines.Where(dbm => dbm.Machine.Id == id && + dbm.Disk.IsInRepo && + dbm.Disk.Size != null)) + { + var cachedDisk = new CachedDisk + { + Id = machineDisk.Disk.Id, + Md5 = machineDisk.Disk.Md5, + Sha1 = machineDisk.Disk.Sha1, + Size = machineDisk.Disk.Size ?? 0, + CreatedOn = machineDisk.Disk.CreatedOn, + UpdatedOn = machineDisk.Disk.UpdatedOn + }; + + cachedMachineDisks[machineDisk.Name] = cachedDisk; + } + + _machineDisksCache[id] = cachedMachineDisks; + + return cachedMachineDisks; + } + + internal ConcurrentDictionary GetMediasFromMachine(ulong id) + { + _machineMediasCache.TryGetValue(id, out ConcurrentDictionary cachedMachineMedias); + + if(cachedMachineMedias != null) return cachedMachineMedias; + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + cachedMachineMedias = new ConcurrentDictionary(); + + foreach(MediaByMachine machineMedia in ctx.MediasByMachines.Where(mbm => mbm.Machine.Id == id && + mbm.Media.IsInRepo && + mbm.Media.Size != null)) + { + var cachedDisk = new CachedMedia + { + Id = machineMedia.Media.Id, + Md5 = machineMedia.Media.Md5, + Sha1 = machineMedia.Media.Sha1, + Sha256 = machineMedia.Media.Sha256, + SpamSum = machineMedia.Media.SpamSum, + Size = machineMedia.Media.Size ?? 0, + CreatedOn = machineMedia.Media.CreatedOn, + UpdatedOn = machineMedia.Media.UpdatedOn + }; + + cachedMachineMedias[machineMedia.Name] = cachedDisk; + } + + _machineMediasCache[id] = cachedMachineMedias; + + return cachedMachineMedias; + } + + internal CachedFile GetFile(ulong machineId, string name) + { + ConcurrentDictionary cachedFiles = GetFilesFromMachine(machineId); + + if(cachedFiles == null || !cachedFiles.TryGetValue(name, out CachedFile file)) return null; + + return file; + } + + internal CachedDisk GetDisk(ulong machineId, string name) + { + if(name.EndsWith(".chd", StringComparison.OrdinalIgnoreCase)) name = name.Substring(0, name.Length - 4); + + ConcurrentDictionary cachedDisks = GetDisksFromMachine(machineId); + + if(cachedDisks == null || !cachedDisks.TryGetValue(name, out CachedDisk disk)) return null; + + return disk; + } + + internal CachedMedia GetMedia(ulong machineId, string name) + { + if(name.EndsWith(".aif", StringComparison.OrdinalIgnoreCase)) name = name.Substring(0, name.Length - 4); + + ConcurrentDictionary cachedMedias = GetMediasFromMachine(machineId); + + if(cachedMedias == null || !cachedMedias.TryGetValue(name, out CachedMedia media)) return null; + + return media; + } + + internal long Open(string sha384, long fileSize) + { + var sha384Bytes = new byte[48]; + + for(var i = 0; i < 48; i++) + { + if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); + else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); + else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); + + if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30); + else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37); + else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57); + } + + string sha384B32 = Base32.ToBase32String(sha384Bytes); + + string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, + "files", + sha384B32[0].ToString(), + sha384B32[1].ToString(), + sha384B32[2].ToString(), + sha384B32[3].ToString(), + sha384B32[4].ToString(), + sha384B32 + ".lz"); + + if(!File.Exists(repoPath)) return -1; + + _lastHandle++; + long handle = _lastHandle; + + _streamsCache[handle] = + Stream.Synchronized(new ForcedSeekStream(fileSize, + new FileStream(repoPath, + FileMode.Open, + FileAccess.Read), + CompressionMode.Decompress)); + + return handle; + } + + internal int Read(long handle, byte[] buf, long offset) + { + if(!_streamsCache.TryGetValue(handle, out Stream stream)) return -1; + + lock(stream) + { + stream.Position = offset; + + return stream.Read(buf, 0, buf.Length); + } + } + + internal bool Close(long handle) + { + if(!_streamsCache.TryGetValue(handle, out Stream stream)) return false; + + stream.Close(); + _streamsCache.TryRemove(handle, out _); + + return true; + } + + internal IEnumerable GetRootEntries() + { + if(_rootDirectoryCache.Count == 0) FillRootDirectoryCache(); + + return _rootDirectoryCache.Keys.ToArray(); + } + + public long OpenDisk(string sha1, string md5) + { + if(sha1 == null && md5 == null) return -1; + + string repoPath = null; + string md5Path = null; + string sha1Path = null; + + if(sha1 != null) + { + var sha1Bytes = new byte[20]; + + for(var i = 0; i < 20; i++) + { + if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); + else if(sha1[i * 2] >= 0x41 && sha1[i * 2] <= 0x46) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); + else if(sha1[i * 2] >= 0x61 && sha1[i * 2] <= 0x66) sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); + + if(sha1[i * 2 + 1] >= 0x30 && sha1[i * 2 + 1] <= 0x39) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x30); + else if(sha1[i * 2 + 1] >= 0x41 && sha1[i * 2 + 1] <= 0x46) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x37); + else if(sha1[i * 2 + 1] >= 0x61 && sha1[i * 2 + 1] <= 0x66) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x57); + } + + string sha1B32 = Base32.ToBase32String(sha1Bytes); + + sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "chd", + "sha1", + sha1B32[0].ToString(), + sha1B32[1].ToString(), + sha1B32[2].ToString(), + sha1B32[3].ToString(), + sha1B32[4].ToString(), + sha1B32 + ".chd"); + } + + if(md5 != null) + { + var md5Bytes = new byte[16]; + + for(var i = 0; i < 16; i++) + { + if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39) + md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); + else if(md5[i * 2] >= 0x41 && md5[i * 2] <= 0x46) + md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); + else if(md5[i * 2] >= 0x61 && md5[i * 2] <= 0x66) md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); + + if(md5[i * 2 + 1] >= 0x30 && md5[i * 2 + 1] <= 0x39) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x30); + else if(md5[i * 2 + 1] >= 0x41 && md5[i * 2 + 1] <= 0x46) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x37); + else if(md5[i * 2 + 1] >= 0x61 && md5[i * 2 + 1] <= 0x66) md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x57); + } + + string md5B32 = Base32.ToBase32String(md5Bytes); + + md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "chd", + "md5", + md5B32[0].ToString(), + md5B32[1].ToString(), + md5B32[2].ToString(), + md5B32[3].ToString(), + md5B32[4].ToString(), + md5B32 + ".chd"); + } + + if(File.Exists(sha1Path)) + repoPath = sha1Path; + else if(File.Exists(md5Path)) repoPath = md5Path; + + if(repoPath == null) return -1; + + _lastHandle++; + long handle = _lastHandle; + + _streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read)); + + return handle; + } + + public long OpenMedia(string sha256, string sha1, string md5) + { + if(sha256 == null && sha1 == null && md5 == null) return -1; + + string repoPath = null; + string md5Path = null; + string sha1Path = null; + string sha256Path = null; + + if(sha256 != null) + { + var sha256Bytes = new byte[32]; + + for(var i = 0; i < 32; i++) + { + if(sha256[i * 2] >= 0x30 && sha256[i * 2] <= 0x39) + sha256Bytes[i] = (byte)((sha256[i * 2] - 0x30) * 0x10); + else if(sha256[i * 2] >= 0x41 && sha256[i * 2] <= 0x46) + sha256Bytes[i] = (byte)((sha256[i * 2] - 0x37) * 0x10); + else if(sha256[i * 2] >= 0x61 && sha256[i * 2] <= 0x66) + sha256Bytes[i] = (byte)((sha256[i * 2] - 0x57) * 0x10); + + if(sha256[i * 2 + 1] >= 0x30 && sha256[i * 2 + 1] <= 0x39) + sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x30); + else if(sha256[i * 2 + 1] >= 0x41 && sha256[i * 2 + 1] <= 0x46) + sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x37); + else if(sha256[i * 2 + 1] >= 0x61 && sha256[i * 2 + 1] <= 0x66) + sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x57); + } + + string sha256B32 = Base32.ToBase32String(sha256Bytes); + + sha256Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "sha256", + sha256B32[0].ToString(), + sha256B32[1].ToString(), + sha256B32[2].ToString(), + sha256B32[3].ToString(), + sha256B32[4].ToString(), + sha256B32 + ".aif"); + } + + if(sha1 != null) + { + var sha1Bytes = new byte[20]; + + for(var i = 0; i < 20; i++) + { + if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); + else if(sha1[i * 2] >= 0x41 && sha1[i * 2] <= 0x46) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); + else if(sha1[i * 2] >= 0x61 && sha1[i * 2] <= 0x66) sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); + + if(sha1[i * 2 + 1] >= 0x30 && sha1[i * 2 + 1] <= 0x39) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x30); + else if(sha1[i * 2 + 1] >= 0x41 && sha1[i * 2 + 1] <= 0x46) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x37); + else if(sha1[i * 2 + 1] >= 0x61 && sha1[i * 2 + 1] <= 0x66) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x57); + } + + string sha1B32 = Base32.ToBase32String(sha1Bytes); + + sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "sha1", + sha1B32[0].ToString(), + sha1B32[1].ToString(), + sha1B32[2].ToString(), + sha1B32[3].ToString(), + sha1B32[4].ToString(), + sha1B32 + ".aif"); + } + + if(md5 != null) + { + var md5Bytes = new byte[16]; + + for(var i = 0; i < 16; i++) + { + if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39) + md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); + else if(md5[i * 2] >= 0x41 && md5[i * 2] <= 0x46) + md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); + else if(md5[i * 2] >= 0x61 && md5[i * 2] <= 0x66) md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); + + if(md5[i * 2 + 1] >= 0x30 && md5[i * 2 + 1] <= 0x39) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x30); + else if(md5[i * 2 + 1] >= 0x41 && md5[i * 2 + 1] <= 0x46) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x37); + else if(md5[i * 2 + 1] >= 0x61 && md5[i * 2 + 1] <= 0x66) md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x57); + } + + string md5B32 = Base32.ToBase32String(md5Bytes); + + md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "md5", + md5B32[0].ToString(), + md5B32[1].ToString(), + md5B32[2].ToString(), + md5B32[3].ToString(), + md5B32[4].ToString(), + md5B32 + ".aif"); + } + + if(File.Exists(sha256Path)) + repoPath = sha256Path; + else if(File.Exists(sha1Path)) + repoPath = sha1Path; + else if(File.Exists(md5Path)) repoPath = md5Path; + + if(repoPath == null) return -1; + + _lastHandle++; + long handle = _lastHandle; + + _streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read)); + + return handle; + } +} + +internal sealed class CachedMachine +{ + public ulong Id { get; set; } + public DateTime CreationDate { get; set; } + public DateTime ModificationDate { get; set; } +} + +internal sealed class CachedFile +{ + public ulong Id { get; set; } + public ulong Size { get; set; } + public string Crc32 { get; set; } + public string Md5 { get; set; } + public string Sha1 { get; set; } + public string Sha256 { get; set; } + public string Sha384 { get; set; } + public string Sha512 { get; set; } + public DateTime CreatedOn { get; set; } + public DateTime UpdatedOn { get; set; } + public DateTime? FileLastModification { get; set; } +} + +internal sealed class CachedDisk +{ + public ulong Id { get; set; } + public ulong Size { get; set; } + public string Md5 { get; set; } + public string Sha1 { get; set; } + public DateTime CreatedOn { get; set; } + public DateTime UpdatedOn { get; set; } +} + +internal sealed class CachedMedia +{ + public ulong Id { get; set; } + public ulong Size { get; set; } + public string Md5 { get; set; } + public string Sha1 { get; set; } + public string Sha256 { get; set; } + public string SpamSum { get; set; } + public DateTime CreatedOn { get; set; } + public DateTime UpdatedOn { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Filesystem/Winfsp.cs b/RomRepoMgr.Core/Filesystem/Winfsp.cs index b91b29f..f2271af 100644 --- a/RomRepoMgr.Core/Filesystem/Winfsp.cs +++ b/RomRepoMgr.Core/Filesystem/Winfsp.cs @@ -11,361 +11,262 @@ using Fsp.Interop; using RomRepoMgr.Database.Models; using FileInfo = Fsp.Interop.FileInfo; -namespace RomRepoMgr.Core.Filesystem +namespace RomRepoMgr.Core.Filesystem; + +[SupportedOSPlatform("windows")] +public class Winfsp : FileSystemBase { - [SupportedOSPlatform("windows")] - public class Winfsp : FileSystemBase + readonly Vfs _vfs; + FileSystemHost _host; + + public Winfsp(Vfs vfs) => _vfs = vfs; + + public static bool IsAvailable { - readonly Vfs _vfs; - FileSystemHost _host; - - public Winfsp(Vfs vfs) => _vfs = vfs; - - public static bool IsAvailable + get { - get + try { - try - { - Version winfspVersion = FileSystemHost.Version(); + Version winfspVersion = FileSystemHost.Version(); - if(winfspVersion == null) - return false; + if(winfspVersion == null) return false; - return winfspVersion.Major == 1 && winfspVersion.Minor >= 7; - } - catch(Exception) - { - return false; - } + return winfspVersion.Major == 1 && winfspVersion.Minor >= 7; + } + catch(Exception) + { + return false; } } + } - internal bool Mount(string mountPoint) + internal bool Mount(string mountPoint) + { + _host = new FileSystemHost(this) { - _host = new FileSystemHost(this) + SectorSize = 512, + CasePreservedNames = true, + CaseSensitiveSearch = true, + FileSystemName = "romrepomgrfs", + MaxComponentLength = 255, + UnicodeOnDisk = true, + SectorsPerAllocationUnit = 1 + }; + + if(Directory.Exists(mountPoint)) Directory.Delete(mountPoint); + + int ret = _host.Mount(mountPoint); + + if(ret == STATUS_SUCCESS) return true; + + _host = null; + + return false; + } + + internal void Umount() => _host?.Unmount(); + + public override int SetVolumeLabel(string volumeLabel, out VolumeInfo volumeInfo) + { + volumeInfo = default(VolumeInfo); + + return STATUS_MEDIA_WRITE_PROTECTED; + } + + public override int Create(string fileName, uint createOptions, uint grantedAccess, uint fileAttributes, + byte[] securityDescriptor, ulong allocationSize, out object fileNode, + out object fileDesc, out FileInfo fileInfo, out string normalizedName) + { + fileNode = default(object); + fileDesc = default(object); + fileInfo = default(FileInfo); + normalizedName = default(string); + + return STATUS_MEDIA_WRITE_PROTECTED; + } + + public override int Overwrite(object fileNode, object fileDesc, uint fileAttributes, bool replaceFileAttributes, + ulong allocationSize, out FileInfo fileInfo) + { + fileInfo = default(FileInfo); + + return STATUS_MEDIA_WRITE_PROTECTED; + } + + public override int Write(object fileNode, object fileDesc, IntPtr buffer, ulong offset, uint length, + bool writeToEndOfFile, bool constrainedIo, out uint bytesTransferred, + out FileInfo fileInfo) + { + bytesTransferred = default(uint); + fileInfo = default(FileInfo); + + return STATUS_MEDIA_WRITE_PROTECTED; + } + + public override int SetBasicInfo(object fileNode, object fileDesc, uint fileAttributes, ulong creationTime, + ulong lastAccessTime, ulong lastWriteTime, ulong changeTime, out FileInfo fileInfo) + { + fileInfo = default(FileInfo); + + return STATUS_MEDIA_WRITE_PROTECTED; + } + + public override int SetFileSize(object fileNode, object fileDesc, ulong newSize, bool setAllocationSize, + out FileInfo fileInfo) + { + fileInfo = default(FileInfo); + + return STATUS_MEDIA_WRITE_PROTECTED; + } + + public override int CanDelete(object fileNode, object fileDesc, string fileName) => STATUS_MEDIA_WRITE_PROTECTED; + + public override int Rename(object fileNode, object fileDesc, string fileName, string newFileName, + bool replaceIfExists) => STATUS_MEDIA_WRITE_PROTECTED; + + public override int GetVolumeInfo(out VolumeInfo volumeInfo) + { + volumeInfo = new VolumeInfo(); + + _vfs.GetInfo(out _, out ulong totalSize); + + volumeInfo.FreeSize = 0; + volumeInfo.TotalSize = totalSize; + + return STATUS_SUCCESS; + } + + public override int Open(string fileName, uint createOptions, uint grantedAccess, out object fileNode, + out object fileDesc, out FileInfo fileInfo, out string normalizedName) + { + fileNode = default(object); + fileDesc = default(object); + fileInfo = default(FileInfo); + normalizedName = default(string); + + string[] pieces = _vfs.SplitPath(fileName); + + // Root directory + if(pieces.Length == 0) + { + fileInfo = new FileInfo { - SectorSize = 512, - CasePreservedNames = true, - CaseSensitiveSearch = true, - FileSystemName = "romrepomgrfs", - MaxComponentLength = 255, - UnicodeOnDisk = true, - SectorsPerAllocationUnit = 1 + CreationTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), + FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), + LastWriteTime = (ulong)DateTime.UtcNow.ToFileTimeUtc() }; - if(Directory.Exists(mountPoint)) - Directory.Delete(mountPoint); + normalizedName = ""; - int ret = _host.Mount(mountPoint); - - if(ret == STATUS_SUCCESS) - return true; - - _host = null; - - return false; - } - - internal void Umount() => _host?.Unmount(); - - public override int SetVolumeLabel(string volumeLabel, out VolumeInfo volumeInfo) - { - volumeInfo = default; - - return STATUS_MEDIA_WRITE_PROTECTED; - } - - public override int Create(string fileName, uint createOptions, uint grantedAccess, uint fileAttributes, - byte[] securityDescriptor, ulong allocationSize, out object fileNode, - out object fileDesc, out FileInfo fileInfo, out string normalizedName) - { - fileNode = default; - fileDesc = default; - fileInfo = default; - normalizedName = default; - - return STATUS_MEDIA_WRITE_PROTECTED; - } - - public override int Overwrite(object fileNode, object fileDesc, uint fileAttributes, bool replaceFileAttributes, - ulong allocationSize, out FileInfo fileInfo) - { - fileInfo = default; - - return STATUS_MEDIA_WRITE_PROTECTED; - } - - public override int Write(object fileNode, object fileDesc, IntPtr buffer, ulong offset, uint length, - bool writeToEndOfFile, bool constrainedIo, out uint bytesTransferred, - out FileInfo fileInfo) - { - bytesTransferred = default; - fileInfo = default; - - return STATUS_MEDIA_WRITE_PROTECTED; - } - - public override int SetBasicInfo(object fileNode, object fileDesc, uint fileAttributes, ulong creationTime, - ulong lastAccessTime, ulong lastWriteTime, ulong changeTime, - out FileInfo fileInfo) - { - fileInfo = default; - - return STATUS_MEDIA_WRITE_PROTECTED; - } - - public override int SetFileSize(object fileNode, object fileDesc, ulong newSize, bool setAllocationSize, - out FileInfo fileInfo) - { - fileInfo = default; - - return STATUS_MEDIA_WRITE_PROTECTED; - } - - public override int CanDelete(object fileNode, object fileDesc, string fileName) => - STATUS_MEDIA_WRITE_PROTECTED; - - public override int Rename(object fileNode, object fileDesc, string fileName, string newFileName, - bool replaceIfExists) => STATUS_MEDIA_WRITE_PROTECTED; - - public override int GetVolumeInfo(out VolumeInfo volumeInfo) - { - volumeInfo = new VolumeInfo(); - - _vfs.GetInfo(out _, out ulong totalSize); - - volumeInfo.FreeSize = 0; - volumeInfo.TotalSize = totalSize; + fileNode = new FileNode + { + FileName = normalizedName, + IsDirectory = true, + Info = fileInfo, + Path = fileName + }; return STATUS_SUCCESS; } - public override int Open(string fileName, uint createOptions, uint grantedAccess, out object fileNode, - out object fileDesc, out FileInfo fileInfo, out string normalizedName) + long romSetId = _vfs.GetRomSetId(pieces[0]); + + if(romSetId <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; + + RomSet romSet = _vfs.GetRomSet(romSetId); + + if(romSet == null) return STATUS_OBJECT_NAME_NOT_FOUND; + + // ROM Set + if(pieces.Length == 1) { - fileNode = default; - fileDesc = default; - fileInfo = default; - normalizedName = default; - - string[] pieces = _vfs.SplitPath(fileName); - - // Root directory - if(pieces.Length == 0) + fileInfo = new FileInfo { - fileInfo = new FileInfo + CreationTime = (ulong)romSet.CreatedOn.ToUniversalTime().ToFileTimeUtc(), + FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), + LastWriteTime = (ulong)romSet.UpdatedOn.ToUniversalTime().ToFileTimeUtc() + }; + + normalizedName = Path.GetFileName(fileName); + + fileNode = new FileNode + { + FileName = normalizedName, + IsDirectory = true, + Info = fileInfo, + Path = fileName, + RomSetId = romSet.Id, + ParentInfo = new FileInfo { CreationTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), LastWriteTime = (ulong)DateTime.UtcNow.ToFileTimeUtc() - }; + } + }; - normalizedName = ""; + return STATUS_SUCCESS; + } - fileNode = new FileNode - { - FileName = normalizedName, - IsDirectory = true, - Info = fileInfo, - Path = fileName - }; + CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); - return STATUS_SUCCESS; - } + if(machine == null) return STATUS_OBJECT_NAME_NOT_FOUND; - long romSetId = _vfs.GetRomSetId(pieces[0]); - - if(romSetId <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; - - RomSet romSet = _vfs.GetRomSet(romSetId); - - if(romSet == null) - return STATUS_OBJECT_NAME_NOT_FOUND; - - // ROM Set - if(pieces.Length == 1) + // Machine + if(pieces.Length == 2) + { + fileInfo = new FileInfo { - fileInfo = new FileInfo + CreationTime = (ulong)machine.CreationDate.ToUniversalTime().ToFileTimeUtc(), + FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), + LastWriteTime = (ulong)machine.ModificationDate.ToUniversalTime().ToFileTimeUtc() + }; + + normalizedName = Path.GetFileName(fileName); + + fileNode = new FileNode + { + FileName = normalizedName, + IsDirectory = true, + Info = fileInfo, + Path = fileName, + MachineId = machine.Id, + ParentInfo = new FileInfo { CreationTime = (ulong)romSet.CreatedOn.ToUniversalTime().ToFileTimeUtc(), FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), LastWriteTime = (ulong)romSet.UpdatedOn.ToUniversalTime().ToFileTimeUtc() - }; + } + }; - normalizedName = Path.GetFileName(fileName); + return STATUS_SUCCESS; + } - fileNode = new FileNode - { - FileName = normalizedName, - IsDirectory = true, - Info = fileInfo, - Path = fileName, - RomSetId = romSet.Id, - ParentInfo = new FileInfo - { - CreationTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), - FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), - LastWriteTime = (ulong)DateTime.UtcNow.ToFileTimeUtc() - } - }; + long handle = 0; + CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - return STATUS_SUCCESS; - } + if(file != null) + { + if(pieces.Length > 3) return STATUS_INVALID_DEVICE_REQUEST; - CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + if(file.Sha384 == null) return STATUS_OBJECT_NAME_NOT_FOUND; - if(machine == null) - return STATUS_OBJECT_NAME_NOT_FOUND; + handle = _vfs.Open(file.Sha384, (long)file.Size); - // Machine - if(pieces.Length == 2) - { - fileInfo = new FileInfo - { - CreationTime = (ulong)machine.CreationDate.ToUniversalTime().ToFileTimeUtc(), - FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), - LastWriteTime = (ulong)machine.ModificationDate.ToUniversalTime().ToFileTimeUtc() - }; - - normalizedName = Path.GetFileName(fileName); - - fileNode = new FileNode - { - FileName = normalizedName, - IsDirectory = true, - Info = fileInfo, - Path = fileName, - MachineId = machine.Id, - ParentInfo = new FileInfo - { - CreationTime = (ulong)romSet.CreatedOn.ToUniversalTime().ToFileTimeUtc(), - FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), - LastWriteTime = (ulong)romSet.UpdatedOn.ToUniversalTime().ToFileTimeUtc() - } - }; - - return STATUS_SUCCESS; - } - - long handle = 0; - CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - - if(file != null) - { - if(pieces.Length > 3) - return STATUS_INVALID_DEVICE_REQUEST; - - if(file.Sha384 == null) - return STATUS_OBJECT_NAME_NOT_FOUND; - - handle = _vfs.Open(file.Sha384, (long)file.Size); - - if(handle <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; - - normalizedName = Path.GetFileName(fileName); - - // TODO: Real allocation size - fileInfo = new FileInfo - { - ChangeTime = (ulong)file.UpdatedOn.ToFileTimeUtc(), - AllocationSize = (file.Size + 511) / 512, - FileSize = file.Size, - CreationTime = (ulong)file.CreatedOn.ToFileTimeUtc(), - FileAttributes = - (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), - IndexNumber = file.Id, - LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), - LastWriteTime = (ulong)(file.FileLastModification?.ToFileTimeUtc() ?? file.UpdatedOn.ToFileTimeUtc()) - }; - - fileNode = new FileNode - { - FileName = normalizedName, - Info = fileInfo, - Path = fileName, - Handle = handle - }; - - return STATUS_SUCCESS; - } - - CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); - - if(disk != null) - { - if(pieces.Length > 3) - return STATUS_INVALID_DEVICE_REQUEST; - - if(disk.Sha1 == null && - disk.Md5 == null) - return STATUS_OBJECT_NAME_NOT_FOUND; - - handle = _vfs.OpenDisk(disk.Sha1, disk.Md5); - - if(handle <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; - - normalizedName = Path.GetFileName(fileName); - - // TODO: Real allocation size - fileInfo = new FileInfo - { - ChangeTime = (ulong)disk.UpdatedOn.ToFileTimeUtc(), - AllocationSize = (disk.Size + 511) / 512, - FileSize = disk.Size, - CreationTime = (ulong)disk.CreatedOn.ToFileTimeUtc(), - FileAttributes = - (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), - IndexNumber = disk.Id, - LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), - LastWriteTime = (ulong)disk.UpdatedOn.ToFileTimeUtc() - }; - - fileNode = new FileNode - { - FileName = normalizedName, - Info = fileInfo, - Path = fileName, - Handle = handle - }; - - return STATUS_SUCCESS; - } - - CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - - if(media == null) - return STATUS_OBJECT_NAME_NOT_FOUND; - - if(pieces.Length > 3) - return STATUS_INVALID_DEVICE_REQUEST; - - if(media.Sha256 == null && - media.Sha1 == null && - media.Md5 == null) - return STATUS_OBJECT_NAME_NOT_FOUND; - - handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5); - - if(handle <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; + if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; normalizedName = Path.GetFileName(fileName); // TODO: Real allocation size fileInfo = new FileInfo { - ChangeTime = (ulong)media.UpdatedOn.ToFileTimeUtc(), - AllocationSize = (media.Size + 511) / 512, - FileSize = media.Size, - CreationTime = (ulong)media.CreatedOn.ToFileTimeUtc(), + ChangeTime = (ulong)file.UpdatedOn.ToFileTimeUtc(), + AllocationSize = (file.Size + 511) / 512, + FileSize = file.Size, + CreationTime = (ulong)file.CreatedOn.ToFileTimeUtc(), FileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), - IndexNumber = media.Id, + IndexNumber = file.Id, LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), - LastWriteTime = (ulong)media.UpdatedOn.ToFileTimeUtc() + LastWriteTime = (ulong)(file.FileLastModification?.ToFileTimeUtc() ?? file.UpdatedOn.ToFileTimeUtc()) }; fileNode = new FileNode @@ -379,365 +280,411 @@ namespace RomRepoMgr.Core.Filesystem return STATUS_SUCCESS; } - public override void Close(object fileNode, object fileDesc) + CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); + + if(disk != null) { - if(!(fileNode is FileNode node)) - return; + if(pieces.Length > 3) return STATUS_INVALID_DEVICE_REQUEST; - if(node.Handle <= 0) - return; + if(disk.Sha1 == null && disk.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND; - _vfs.Close(node.Handle); - } + handle = _vfs.OpenDisk(disk.Sha1, disk.Md5); - public override int Read(object fileNode, object fileDesc, IntPtr buffer, ulong offset, uint length, - out uint bytesTransferred) - { - bytesTransferred = 0; + if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; - if(!(fileNode is FileNode node) || - node.Handle <= 0) - return STATUS_INVALID_HANDLE; + normalizedName = Path.GetFileName(fileName); - byte[] buf = new byte[length]; - - int ret = _vfs.Read(node.Handle, buf, (long)offset); - - if(ret < 0) - return STATUS_INVALID_HANDLE; - - Marshal.Copy(buf, 0, buffer, ret); - - bytesTransferred = (uint)ret; - - return STATUS_SUCCESS; - } - - public override int GetFileInfo(object fileNode, object fileDesc, out FileInfo fileInfo) - { - fileInfo = default; - - if(!(fileNode is FileNode node)) - return STATUS_INVALID_HANDLE; - - fileInfo = node.Info; - - return STATUS_SUCCESS; - } - - public override bool ReadDirectoryEntry(object fileNode, object fileDesc, string pattern, string marker, - ref object context, out string fileName, out FileInfo fileInfo) - { - fileName = default; - fileInfo = default; - - if(!(fileNode is FileNode node) || - !node.IsDirectory) - return false; - - if(!(context is IEnumerator enumerator)) + // TODO: Real allocation size + fileInfo = new FileInfo { - if(node.MachineId > 0) + ChangeTime = (ulong)disk.UpdatedOn.ToFileTimeUtc(), + AllocationSize = (disk.Size + 511) / 512, + FileSize = disk.Size, + CreationTime = (ulong)disk.CreatedOn.ToFileTimeUtc(), + FileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), + IndexNumber = disk.Id, + LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), + LastWriteTime = (ulong)disk.UpdatedOn.ToFileTimeUtc() + }; + + fileNode = new FileNode + { + FileName = normalizedName, + Info = fileInfo, + Path = fileName, + Handle = handle + }; + + return STATUS_SUCCESS; + } + + CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); + + if(media == null) return STATUS_OBJECT_NAME_NOT_FOUND; + + if(pieces.Length > 3) return STATUS_INVALID_DEVICE_REQUEST; + + if(media.Sha256 == null && media.Sha1 == null && media.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND; + + handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5); + + if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; + + normalizedName = Path.GetFileName(fileName); + + // TODO: Real allocation size + fileInfo = new FileInfo + { + ChangeTime = (ulong)media.UpdatedOn.ToFileTimeUtc(), + AllocationSize = (media.Size + 511) / 512, + FileSize = media.Size, + CreationTime = (ulong)media.CreatedOn.ToFileTimeUtc(), + FileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), + IndexNumber = media.Id, + LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), + LastWriteTime = (ulong)media.UpdatedOn.ToFileTimeUtc() + }; + + fileNode = new FileNode + { + FileName = normalizedName, + Info = fileInfo, + Path = fileName, + Handle = handle + }; + + return STATUS_SUCCESS; + } + + public override void Close(object fileNode, object fileDesc) + { + if(!(fileNode is FileNode node)) return; + + if(node.Handle <= 0) return; + + _vfs.Close(node.Handle); + } + + public override int Read(object fileNode, object fileDesc, IntPtr buffer, ulong offset, uint length, + out uint bytesTransferred) + { + bytesTransferred = 0; + + if(!(fileNode is FileNode node) || node.Handle <= 0) return STATUS_INVALID_HANDLE; + + var buf = new byte[length]; + + int ret = _vfs.Read(node.Handle, buf, (long)offset); + + if(ret < 0) return STATUS_INVALID_HANDLE; + + Marshal.Copy(buf, 0, buffer, ret); + + bytesTransferred = (uint)ret; + + return STATUS_SUCCESS; + } + + public override int GetFileInfo(object fileNode, object fileDesc, out FileInfo fileInfo) + { + fileInfo = default(FileInfo); + + if(!(fileNode is FileNode node)) return STATUS_INVALID_HANDLE; + + fileInfo = node.Info; + + return STATUS_SUCCESS; + } + + public override bool ReadDirectoryEntry(object fileNode, object fileDesc, string pattern, string marker, + ref object context, out string fileName, out FileInfo fileInfo) + { + fileName = default(string); + fileInfo = default(FileInfo); + + if(!(fileNode is FileNode node) || !node.IsDirectory) return false; + + if(!(context is IEnumerator enumerator)) + { + if(node.MachineId > 0) + { + ConcurrentDictionary cachedMachineFiles = _vfs.GetFilesFromMachine(node.MachineId); + + ConcurrentDictionary cachedMachineDisks = _vfs.GetDisksFromMachine(node.MachineId); + + ConcurrentDictionary cachedMachineMedias = + _vfs.GetMediasFromMachine(node.MachineId); + + node.Children = new List { - ConcurrentDictionary cachedMachineFiles = - _vfs.GetFilesFromMachine(node.MachineId); - - ConcurrentDictionary cachedMachineDisks = - _vfs.GetDisksFromMachine(node.MachineId); - - ConcurrentDictionary cachedMachineMedias = - _vfs.GetMediasFromMachine(node.MachineId); - - node.Children = new List + new() { - new FileEntry - { - FileName = ".", - Info = node.Info - }, - new FileEntry - { - FileName = "..", - Info = node.ParentInfo - } - }; - - node.Children.AddRange(cachedMachineFiles.Select(file => new FileEntry + FileName = ".", + Info = node.Info + }, + new() { - FileName = file.Key, - Info = new FileInfo - { - ChangeTime = (ulong)file.Value.UpdatedOn.ToFileTimeUtc(), - AllocationSize = (file.Value.Size + 511) / 512, - FileSize = file.Value.Size, - CreationTime = (ulong)file.Value.CreatedOn.ToFileTimeUtc(), - FileAttributes = - (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), - IndexNumber = file.Value.Id, - LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), - LastWriteTime = (ulong)(file.Value.FileLastModification?.ToFileTimeUtc() ?? file.Value.UpdatedOn.ToFileTimeUtc()) - } - })); + FileName = "..", + Info = node.ParentInfo + } + }; - node.Children.AddRange(cachedMachineDisks.Select(disk => new FileEntry - { - FileName = disk.Key + ".chd", - Info = new FileInfo - { - ChangeTime = (ulong)disk.Value.UpdatedOn.ToFileTimeUtc(), - AllocationSize = (disk.Value.Size + 511) / 512, - FileSize = disk.Value.Size, - CreationTime = (ulong)disk.Value.CreatedOn.ToFileTimeUtc(), - FileAttributes = - (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), - IndexNumber = disk.Value.Id, - LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), - LastWriteTime = (ulong)disk.Value.UpdatedOn.ToFileTimeUtc() - } - })); - - node.Children.AddRange(cachedMachineMedias.Select(media => new FileEntry - { - FileName = media.Key + ".aif", - Info = new FileInfo - { - ChangeTime = (ulong)media.Value.UpdatedOn.ToFileTimeUtc(), - AllocationSize = (media.Value.Size + 511) / 512, - FileSize = media.Value.Size, - CreationTime = (ulong)media.Value.CreatedOn.ToFileTimeUtc(), - FileAttributes = - (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), - IndexNumber = media.Value.Id, - LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), - LastWriteTime = (ulong)media.Value.UpdatedOn.ToFileTimeUtc() - } - })); - } - else if(node.RomSetId > 0) + node.Children.AddRange(cachedMachineFiles.Select(file => new FileEntry { - ConcurrentDictionary machines = _vfs.GetMachinesFromRomSet(node.RomSetId); - - node.Children = new List + FileName = file.Key, + Info = new FileInfo { - new FileEntry - { - FileName = ".", - Info = node.Info - }, - new FileEntry - { - FileName = "..", - Info = node.ParentInfo - } - }; + ChangeTime = (ulong)file.Value.UpdatedOn.ToFileTimeUtc(), + AllocationSize = (file.Value.Size + 511) / 512, + FileSize = file.Value.Size, + CreationTime = (ulong)file.Value.CreatedOn.ToFileTimeUtc(), + FileAttributes = + (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), + IndexNumber = file.Value.Id, + LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), + LastWriteTime = (ulong)(file.Value.FileLastModification?.ToFileTimeUtc() ?? + file.Value.UpdatedOn.ToFileTimeUtc()) + } + })); - node.Children.AddRange(machines.Select(machine => new FileEntry - { - FileName = machine.Key, - Info = new FileInfo - { - CreationTime = (ulong)machine.Value.CreationDate.ToUniversalTime().ToFileTimeUtc(), - FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), - LastWriteTime = (ulong)machine.Value.ModificationDate.ToUniversalTime().ToFileTimeUtc() - } - })); - } - else + node.Children.AddRange(cachedMachineDisks.Select(disk => new FileEntry { - node.Children = new List(); - - node.Children.AddRange(_vfs.GetRootEntries().Select(e => new FileEntry + FileName = disk.Key + ".chd", + Info = new FileInfo { - FileName = e, - IsRomSet = true - })); - } + ChangeTime = (ulong)disk.Value.UpdatedOn.ToFileTimeUtc(), + AllocationSize = (disk.Value.Size + 511) / 512, + FileSize = disk.Value.Size, + CreationTime = (ulong)disk.Value.CreatedOn.ToFileTimeUtc(), + FileAttributes = + (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), + IndexNumber = disk.Value.Id, + LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), + LastWriteTime = (ulong)disk.Value.UpdatedOn.ToFileTimeUtc() + } + })); - if(marker != null) + node.Children.AddRange(cachedMachineMedias.Select(media => new FileEntry { - int idx = node.Children.FindIndex(f => f.FileName == marker); - - if(idx >= 0) - node.Children.RemoveRange(0, idx + 1); - } - - context = enumerator = node.Children.GetEnumerator(); + FileName = media.Key + ".aif", + Info = new FileInfo + { + ChangeTime = (ulong)media.Value.UpdatedOn.ToFileTimeUtc(), + AllocationSize = (media.Value.Size + 511) / 512, + FileSize = media.Value.Size, + CreationTime = (ulong)media.Value.CreatedOn.ToFileTimeUtc(), + FileAttributes = + (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly), + IndexNumber = media.Value.Id, + LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(), + LastWriteTime = (ulong)media.Value.UpdatedOn.ToFileTimeUtc() + } + })); } - - while(enumerator.MoveNext()) + else if(node.RomSetId > 0) { - FileEntry entry = enumerator.Current; + ConcurrentDictionary machines = _vfs.GetMachinesFromRomSet(node.RomSetId); - if(entry is null) - continue; - - if(entry.IsRomSet) + node.Children = new List { - long romSetId = _vfs.GetRomSetId(entry.FileName); - - if(romSetId <= 0) - continue; - - RomSet romSet = _vfs.GetRomSet(romSetId); - - if(romSet is null) - continue; - - entry.Info = new FileInfo + new() { - CreationTime = (ulong)romSet.CreatedOn.ToUniversalTime().ToFileTimeUtc(), + FileName = ".", + Info = node.Info + }, + new() + { + FileName = "..", + Info = node.ParentInfo + } + }; + + node.Children.AddRange(machines.Select(machine => new FileEntry + { + FileName = machine.Key, + Info = new FileInfo + { + CreationTime = (ulong)machine.Value.CreationDate.ToUniversalTime().ToFileTimeUtc(), FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), - LastWriteTime = (ulong)romSet.UpdatedOn.ToUniversalTime().ToFileTimeUtc() - }; - } + LastWriteTime = (ulong)machine.Value.ModificationDate.ToUniversalTime().ToFileTimeUtc() + } + })); + } + else + { + node.Children = new List(); - fileName = entry.FileName; - fileInfo = entry.Info; - - return true; + node.Children.AddRange(_vfs.GetRootEntries() + .Select(e => new FileEntry + { + FileName = e, + IsRomSet = true + })); } - return false; + if(marker != null) + { + int idx = node.Children.FindIndex(f => f.FileName == marker); + + if(idx >= 0) node.Children.RemoveRange(0, idx + 1); + } + + context = enumerator = node.Children.GetEnumerator(); } - public override int GetSecurityByName(string fileName, out uint fileAttributes, ref byte[] securityDescriptor) + while(enumerator.MoveNext()) { - fileAttributes = 0; + FileEntry entry = enumerator.Current; - string[] pieces = _vfs.SplitPath(fileName); + if(entry is null) continue; - // Root directory - if(pieces.Length == 0) + if(entry.IsRomSet) { - fileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed); + long romSetId = _vfs.GetRomSetId(entry.FileName); - if(securityDescriptor == null) - return STATUS_SUCCESS; + if(romSetId <= 0) continue; - string rootSddl = "O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"; + RomSet romSet = _vfs.GetRomSet(romSetId); - var rootSecurityDescriptor = new RawSecurityDescriptor(rootSddl); - byte[] fileSecurity = new byte[rootSecurityDescriptor.BinaryLength]; - rootSecurityDescriptor.GetBinaryForm(fileSecurity, 0); - securityDescriptor = fileSecurity; + if(romSet is null) continue; - return STATUS_SUCCESS; + entry.Info = new FileInfo + { + CreationTime = (ulong)romSet.CreatedOn.ToUniversalTime().ToFileTimeUtc(), + FileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed), + LastWriteTime = (ulong)romSet.UpdatedOn.ToUniversalTime().ToFileTimeUtc() + }; } - long romSetId = _vfs.GetRomSetId(pieces[0]); + fileName = entry.FileName; + fileInfo = entry.Info; - if(romSetId <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; + return true; + } - RomSet romSet = _vfs.GetRomSet(romSetId); + return false; + } - if(romSet == null) - return STATUS_OBJECT_NAME_NOT_FOUND; + public override int GetSecurityByName(string fileName, out uint fileAttributes, ref byte[] securityDescriptor) + { + fileAttributes = 0; - // ROM Set - if(pieces.Length == 1) - { - fileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed); + string[] pieces = _vfs.SplitPath(fileName); - return STATUS_SUCCESS; - } + // Root directory + if(pieces.Length == 0) + { + fileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed); - CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); + if(securityDescriptor == null) return STATUS_SUCCESS; - if(machine == null) - return STATUS_OBJECT_NAME_NOT_FOUND; + var rootSddl = "O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"; - // Machine - if(pieces.Length == 2) - { - fileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed); + var rootSecurityDescriptor = new RawSecurityDescriptor(rootSddl); + var fileSecurity = new byte[rootSecurityDescriptor.BinaryLength]; + rootSecurityDescriptor.GetBinaryForm(fileSecurity, 0); + securityDescriptor = fileSecurity; - return STATUS_SUCCESS; - } + return STATUS_SUCCESS; + } - long handle = 0; - CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); + long romSetId = _vfs.GetRomSetId(pieces[0]); - if(file != null) - { - if(pieces.Length > 3) - return STATUS_INVALID_DEVICE_REQUEST; + if(romSetId <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; - if(file.Sha384 == null) - return STATUS_OBJECT_NAME_NOT_FOUND; + RomSet romSet = _vfs.GetRomSet(romSetId); - handle = _vfs.Open(file.Sha384, (long)file.Size); + if(romSet == null) return STATUS_OBJECT_NAME_NOT_FOUND; - if(handle <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; + // ROM Set + if(pieces.Length == 1) + { + fileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed); - fileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly); + return STATUS_SUCCESS; + } - return STATUS_SUCCESS; - } + CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]); - CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); + if(machine == null) return STATUS_OBJECT_NAME_NOT_FOUND; - if(disk != null) - { - if(pieces.Length > 3) - return STATUS_INVALID_DEVICE_REQUEST; + // Machine + if(pieces.Length == 2) + { + fileAttributes = (uint)(FileAttributes.Directory | FileAttributes.Compressed); - if(disk.Sha1 == null && - disk.Md5 == null) - return STATUS_OBJECT_NAME_NOT_FOUND; + return STATUS_SUCCESS; + } - handle = _vfs.OpenDisk(disk.Sha1, disk.Md5); + long handle = 0; + CachedFile file = _vfs.GetFile(machine.Id, pieces[2]); - if(handle <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; + if(file != null) + { + if(pieces.Length > 3) return STATUS_INVALID_DEVICE_REQUEST; - fileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly); + if(file.Sha384 == null) return STATUS_OBJECT_NAME_NOT_FOUND; - return STATUS_SUCCESS; - } + handle = _vfs.Open(file.Sha384, (long)file.Size); - CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); - - if(media == null) - return STATUS_OBJECT_NAME_NOT_FOUND; - - if(pieces.Length > 3) - return STATUS_INVALID_DEVICE_REQUEST; - - if(media.Sha256 == null && - media.Sha1 == null && - media.Md5 == null) - return STATUS_OBJECT_NAME_NOT_FOUND; - - handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5); - - if(handle <= 0) - return STATUS_OBJECT_NAME_NOT_FOUND; + if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; fileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly); return STATUS_SUCCESS; } - sealed class FileEntry + CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]); + + if(disk != null) { - public string FileName { get; set; } - public FileInfo Info { get; set; } - public bool IsRomSet { get; set; } + if(pieces.Length > 3) return STATUS_INVALID_DEVICE_REQUEST; + + if(disk.Sha1 == null && disk.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND; + + handle = _vfs.OpenDisk(disk.Sha1, disk.Md5); + + if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; + + fileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly); + + return STATUS_SUCCESS; } - sealed class FileNode - { - public FileInfo Info { get; set; } - public FileInfo ParentInfo { get; set; } - public string FileName { get; set; } - public string Path { get; set; } - public long Handle { get; set; } - public List Children { get; set; } - public bool IsDirectory { get; set; } - public long RomSetId { get; set; } - public ulong MachineId { get; set; } - } + CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]); + + if(media == null) return STATUS_OBJECT_NAME_NOT_FOUND; + + if(pieces.Length > 3) return STATUS_INVALID_DEVICE_REQUEST; + + if(media.Sha256 == null && media.Sha1 == null && media.Md5 == null) return STATUS_OBJECT_NAME_NOT_FOUND; + + handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5); + + if(handle <= 0) return STATUS_OBJECT_NAME_NOT_FOUND; + + fileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly); + + return STATUS_SUCCESS; + } + + sealed class FileEntry + { + public string FileName { get; set; } + public FileInfo Info { get; set; } + public bool IsRomSet { get; set; } + } + + sealed class FileNode + { + public FileInfo Info { get; set; } + public FileInfo ParentInfo { get; set; } + public string FileName { get; set; } + public string Path { get; set; } + public long Handle { get; set; } + public List Children { get; set; } + public bool IsDirectory { get; set; } + public long RomSetId { get; set; } + public ulong MachineId { get; set; } } } \ No newline at end of file diff --git a/RomRepoMgr.Core/ForcedSeekStream.cs b/RomRepoMgr.Core/ForcedSeekStream.cs index 8600001..cc4c956 100644 --- a/RomRepoMgr.Core/ForcedSeekStream.cs +++ b/RomRepoMgr.Core/ForcedSeekStream.cs @@ -34,173 +34,165 @@ using System; using System.IO; using RomRepoMgr.Core.Resources; -namespace RomRepoMgr.Core +namespace RomRepoMgr.Core; + +/// +/// +/// ForcedSeekStream allows to seek a forward-readable stream (like System.IO.Compression streams) by doing the +/// slow and known trick of rewinding and forward reading until arriving the desired position. +/// +internal sealed class ForcedSeekStream : Stream where T : Stream { + const int BUFFER_LEN = 1048576; + readonly string _backFile; + readonly FileStream _backStream; + readonly T _baseStream; + long _streamLength; + /// - /// - /// ForcedSeekStream allows to seek a forward-readable stream (like System.IO.Compression streams) by doing the - /// slow and known trick of rewinding and forward reading until arriving the desired position. - /// - internal sealed class ForcedSeekStream : Stream where T : Stream + /// Initializes a new instance of the class. + /// The real (uncompressed) length of the stream. + /// Parameters that are used to create the base stream. + public ForcedSeekStream(long length, params object[] args) { - const int BUFFER_LEN = 1048576; - readonly string _backFile; - readonly FileStream _backStream; - readonly T _baseStream; - long _streamLength; + _streamLength = length; + _baseStream = (T)Activator.CreateInstance(typeof(T), args); + _backFile = Path.GetTempFileName(); + _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); - /// - /// Initializes a new instance of the class. - /// The real (uncompressed) length of the stream. - /// Parameters that are used to create the base stream. - public ForcedSeekStream(long length, params object[] args) + if(length == 0) CalculateLength(); + } + + public override bool CanRead => _baseStream.CanRead; + + public override bool CanSeek => true; + + public override bool CanWrite => false; + + public override long Length => _streamLength; + + public override long Position + { + get => _backStream.Position; + + set => SetPosition(value); + } + + /// + /// Calculates the real (uncompressed) length of the stream. It basically reads (decompresses) the whole stream to + /// memory discarding its contents, so it should be used as a last resort. + /// + /// The length. + void CalculateLength() + { + int read; + + do { - _streamLength = length; - _baseStream = (T)Activator.CreateInstance(typeof(T), args); - _backFile = Path.GetTempFileName(); - _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); + var buffer = new byte[BUFFER_LEN]; + read = _baseStream.Read(buffer, 0, BUFFER_LEN); + _backStream.Write(buffer, 0, read); + } while(read == BUFFER_LEN); - if(length == 0) - CalculateLength(); + _streamLength = _backStream.Length; + _backStream.Position = 0; + } + + void SetPosition(long position) + { + if(position == _backStream.Position) return; + + if(position < _backStream.Length) + { + _backStream.Position = position; + + return; } - public override bool CanRead => _baseStream.CanRead; + _backStream.Position = _backStream.Length; + long toPosition = position - _backStream.Position; + var fullBufferReads = (int)(toPosition / BUFFER_LEN); + var restToRead = (int)(toPosition % BUFFER_LEN); + byte[] buffer; - public override bool CanSeek => true; - - public override bool CanWrite => false; - - public override long Length => _streamLength; - - public override long Position + for(var i = 0; i < fullBufferReads; i++) { - get => _backStream.Position; - - set => SetPosition(value); + buffer = new byte[BUFFER_LEN]; + _baseStream.Read(buffer, 0, BUFFER_LEN); + _backStream.Write(buffer, 0, BUFFER_LEN); } - /// - /// Calculates the real (uncompressed) length of the stream. It basically reads (decompresses) the whole stream to - /// memory discarding its contents, so it should be used as a last resort. - /// - /// The length. - void CalculateLength() + buffer = new byte[restToRead]; + _baseStream.Read(buffer, 0, restToRead); + _backStream.Write(buffer, 0, restToRead); + } + + public override void Flush() + { + _baseStream.Flush(); + _backStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if(_backStream.Position + count <= _backStream.Length) return _backStream.Read(buffer, offset, count); + + SetPosition(_backStream.Position + count); + SetPosition(_backStream.Position - count); + + return _backStream.Read(buffer, offset, count); + } + + public override int ReadByte() + { + if(_backStream.Position + 1 <= _backStream.Length) return _backStream.ReadByte(); + + SetPosition(_backStream.Position + 1); + SetPosition(_backStream.Position - 1); + + return _backStream.ReadByte(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch(origin) { - int read; + case SeekOrigin.Begin: + if(offset < 0) throw new IOException(Localization.Cannot_seek_before_start); - do - { - byte[] buffer = new byte[BUFFER_LEN]; - read = _baseStream.Read(buffer, 0, BUFFER_LEN); - _backStream.Write(buffer, 0, read); - } while(read == BUFFER_LEN); + SetPosition(offset); - _streamLength = _backStream.Length; - _backStream.Position = 0; + break; + case SeekOrigin.End: + if(offset > 0) throw new IOException(Localization.Cannot_seek_after_end); + + if(_streamLength == 0) CalculateLength(); + + SetPosition(_streamLength + offset); + + break; + default: + SetPosition(_backStream.Position + offset); + + break; } - void SetPosition(long position) - { - if(position == _backStream.Position) - return; + return _backStream.Position; + } - if(position < _backStream.Length) - { - _backStream.Position = position; + public override void SetLength(long value) => throw new NotSupportedException(); - return; - } + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); - _backStream.Position = _backStream.Length; - long toPosition = position - _backStream.Position; - int fullBufferReads = (int)(toPosition / BUFFER_LEN); - int restToRead = (int)(toPosition % BUFFER_LEN); - byte[] buffer; + public override void Close() + { + _backStream?.Close(); + File.Delete(_backFile); + } - for(int i = 0; i < fullBufferReads; i++) - { - buffer = new byte[BUFFER_LEN]; - _baseStream.Read(buffer, 0, BUFFER_LEN); - _backStream.Write(buffer, 0, BUFFER_LEN); - } - - buffer = new byte[restToRead]; - _baseStream.Read(buffer, 0, restToRead); - _backStream.Write(buffer, 0, restToRead); - } - - public override void Flush() - { - _baseStream.Flush(); - _backStream.Flush(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if(_backStream.Position + count <= _backStream.Length) - return _backStream.Read(buffer, offset, count); - - SetPosition(_backStream.Position + count); - SetPosition(_backStream.Position - count); - - return _backStream.Read(buffer, offset, count); - } - - public override int ReadByte() - { - if(_backStream.Position + 1 <= _backStream.Length) - return _backStream.ReadByte(); - - SetPosition(_backStream.Position + 1); - SetPosition(_backStream.Position - 1); - - return _backStream.ReadByte(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - switch(origin) - { - case SeekOrigin.Begin: - if(offset < 0) - throw new IOException(Localization.Cannot_seek_before_start); - - SetPosition(offset); - - break; - case SeekOrigin.End: - if(offset > 0) - throw new IOException(Localization.Cannot_seek_after_end); - - if(_streamLength == 0) - CalculateLength(); - - SetPosition(_streamLength + offset); - - break; - default: - SetPosition(_backStream.Position + offset); - - break; - } - - return _backStream.Position; - } - - public override void SetLength(long value) => throw new NotSupportedException(); - - public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); - - public override void Close() - { - _backStream?.Close(); - File.Delete(_backFile); - } - - ~ForcedSeekStream() - { - _backStream?.Close(); - File.Delete(_backFile); - } + ~ForcedSeekStream() + { + _backStream?.Close(); + File.Delete(_backFile); } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Models/AssemblyModel.cs b/RomRepoMgr.Core/Models/AssemblyModel.cs index 06451c5..e740695 100644 --- a/RomRepoMgr.Core/Models/AssemblyModel.cs +++ b/RomRepoMgr.Core/Models/AssemblyModel.cs @@ -23,11 +23,10 @@ // Copyright © 2020-2024 Natalia Portillo *******************************************************************************/ -namespace RomRepoMgr.Core.Models +namespace RomRepoMgr.Core.Models; + +public sealed class AssemblyModel { - public sealed class AssemblyModel - { - public string Name { get; set; } - public string Version { get; set; } - } + public string Name { get; set; } + public string Version { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Models/ImportRomItem.cs b/RomRepoMgr.Core/Models/ImportRomItem.cs index c5bcb60..63e245c 100644 --- a/RomRepoMgr.Core/Models/ImportRomItem.cs +++ b/RomRepoMgr.Core/Models/ImportRomItem.cs @@ -23,11 +23,10 @@ // Copyright © 2020-2024 Natalia Portillo *******************************************************************************/ -namespace RomRepoMgr.Core.Models +namespace RomRepoMgr.Core.Models; + +public sealed class ImportRomItem { - public sealed class ImportRomItem - { - public string Filename { get; set; } - public string Status { get; set; } - } + public string Filename { get; set; } + public string Status { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Models/RomSetModel.cs b/RomRepoMgr.Core/Models/RomSetModel.cs index e32ac82..ccaeedd 100644 --- a/RomRepoMgr.Core/Models/RomSetModel.cs +++ b/RomRepoMgr.Core/Models/RomSetModel.cs @@ -23,26 +23,25 @@ // Copyright © 2020-2024 Natalia Portillo *******************************************************************************/ -namespace RomRepoMgr.Core.Models +namespace RomRepoMgr.Core.Models; + +public class RomSetModel { - public class RomSetModel - { - public string Author { get; set; } - public string Comment { get; set; } - public string Date { get; set; } - public string Description { get; set; } - public string Homepage { get; set; } - public string Name { get; set; } - public string Version { get; set; } - public string Filename { get; set; } - public string Sha384 { get; set; } - public long TotalMachines { get; set; } - public long CompleteMachines { get; set; } - public long IncompleteMachines { get; set; } - public long TotalRoms { get; set; } - public long HaveRoms { get; set; } - public long MissRoms { get; set; } - public long Id { get; set; } - public string Category { get; set; } - } + public string Author { get; set; } + public string Comment { get; set; } + public string Date { get; set; } + public string Description { get; set; } + public string Homepage { get; set; } + public string Name { get; set; } + public string Version { get; set; } + public string Filename { get; set; } + public string Sha384 { get; set; } + public long TotalMachines { get; set; } + public long CompleteMachines { get; set; } + public long IncompleteMachines { get; set; } + public long TotalRoms { get; set; } + public long HaveRoms { get; set; } + public long MissRoms { get; set; } + public long Id { get; set; } + public string Category { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Resources/Localization.Designer.cs b/RomRepoMgr.Core/Resources/Localization.Designer.cs index 00ecd66..37c9881 100644 --- a/RomRepoMgr.Core/Resources/Localization.Designer.cs +++ b/RomRepoMgr.Core/Resources/Localization.Designer.cs @@ -11,32 +11,46 @@ namespace RomRepoMgr.Core.Resources { using System; - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Localization { - private static System.Resources.ResourceManager resourceMan; + private static global::System.Resources.ResourceManager resourceMan; - private static System.Globalization.CultureInfo resourceCulture; + private static global::System.Globalization.CultureInfo resourceCulture; - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Localization() { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Resources.ResourceManager ResourceManager { + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RomRepoMgr.Core.Resources.Localization", typeof(Localization).Assembly); + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RomRepoMgr.Core.Resources.Localization", typeof(Localization).Assembly); resourceMan = temp; } return resourceMan; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Globalization.CultureInfo Culture { + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -45,351 +59,525 @@ namespace RomRepoMgr.Core.Resources { } } - internal static string Base32_Not_enought_data { - get { - return ResourceManager.GetString("Base32_Not_enought_data", resourceCulture); - } - } - - internal static string Base32_Invalid_format { - get { - return ResourceManager.GetString("Base32_Invalid_format", resourceCulture); - } - } - - internal static string Cannot_seek_before_start { - get { - return ResourceManager.GetString("Cannot_seek_before_start", resourceCulture); - } - } - - internal static string Cannot_seek_after_end { - get { - return ResourceManager.GetString("Cannot_seek_after_end", resourceCulture); - } - } - - internal static string Spamsum_no_binary { - get { - return ResourceManager.GetString("Spamsum_no_binary", resourceCulture); - } - } - - internal static string Assertion_failed { - get { - return ResourceManager.GetString("Assertion_failed", resourceCulture); - } - } - - internal static string Spamsum_Input_exceeds_data { - get { - return ResourceManager.GetString("Spamsum_Input_exceeds_data", resourceCulture); - } - } - - internal static string Not_yet_implemented { - get { - return ResourceManager.GetString("Not_yet_implemented", resourceCulture); - } - } - - internal static string DatabaseFileExistsMsgBoxTitle { - get { - return ResourceManager.GetString("DatabaseFileExistsMsgBoxTitle", resourceCulture); - } - } - - internal static string UnArPathNotSet { - get { - return ResourceManager.GetString("UnArPathNotSet", resourceCulture); - } - } - - internal static string CannotFindUnArAtPath { - get { - return ResourceManager.GetString("CannotFindUnArAtPath", resourceCulture); - } - } - - internal static string CannotFindLsAr { - get { - return ResourceManager.GetString("CannotFindLsAr", resourceCulture); - } - } - - internal static string CannotRunUnAr { - get { - return ResourceManager.GetString("CannotRunUnAr", resourceCulture); - } - } - - internal static string CannotRunLsAr { - get { - return ResourceManager.GetString("CannotRunLsAr", resourceCulture); - } - } - - internal static string NotCorrectUnAr { - get { - return ResourceManager.GetString("NotCorrectUnAr", resourceCulture); - } - } - - internal static string NotCorrectLsAr { - get { - return ResourceManager.GetString("NotCorrectLsAr", resourceCulture); - } - } - - internal static string ParsinDatFile { - get { - return ResourceManager.GetString("ParsinDatFile", resourceCulture); - } - } - - internal static string HashingDatFile { - get { - return ResourceManager.GetString("HashingDatFile", resourceCulture); - } - } - - internal static string DatAlreadyInDatabase { - get { - return ResourceManager.GetString("DatAlreadyInDatabase", resourceCulture); - } - } - + /// + /// Looks up a localized string similar to Adding DAT to database.... + /// internal static string AddingDatToDatabase { get { return ResourceManager.GetString("AddingDatToDatabase", resourceCulture); } } - internal static string CompressingDatFile { - get { - return ResourceManager.GetString("CompressingDatFile", resourceCulture); - } - } - - internal static string GettingMachineNames { - get { - return ResourceManager.GetString("GettingMachineNames", resourceCulture); - } - } - - internal static string AddingMachines { - get { - return ResourceManager.GetString("AddingMachines", resourceCulture); - } - } - - internal static string SavingChangesToDatabase { - get { - return ResourceManager.GetString("SavingChangesToDatabase", resourceCulture); - } - } - - internal static string RetrievingRomsAndDisks { - get { - return ResourceManager.GetString("RetrievingRomsAndDisks", resourceCulture); - } - } - - internal static string AddingRoms { - get { - return ResourceManager.GetString("AddingRoms", resourceCulture); - } - } - - internal static string FoundRomWithoutMachine { - get { - return ResourceManager.GetString("FoundRomWithoutMachine", resourceCulture); - } - } - - internal static string UnhandledException { - get { - return ResourceManager.GetString("UnhandledException", resourceCulture); - } - } - - internal static string RetrievingRomSetFromDatabase { - get { - return ResourceManager.GetString("RetrievingRomSetFromDatabase", resourceCulture); - } - } - - internal static string CouldNotFindRomSetInDatabase { - get { - return ResourceManager.GetString("CouldNotFindRomSetInDatabase", resourceCulture); - } - } - - internal static string ExportingRoms { - get { - return ResourceManager.GetString("ExportingRoms", resourceCulture); - } - } - - internal static string Finished { - get { - return ResourceManager.GetString("Finished", resourceCulture); - } - } - - internal static string CannotFindZipEntryInDictionary { - get { - return ResourceManager.GetString("CannotFindZipEntryInDictionary", resourceCulture); - } - } - - internal static string CannotFindHashInRepository { - get { - return ResourceManager.GetString("CannotFindHashInRepository", resourceCulture); - } - } - - internal static string Compressing { - get { - return ResourceManager.GetString("Compressing", resourceCulture); - } - } - - internal static string EnumeratingFiles { - get { - return ResourceManager.GetString("EnumeratingFiles", resourceCulture); - } - } - - internal static string Importing { - get { - return ResourceManager.GetString("Importing", resourceCulture); - } - } - - internal static string CheckingIfFIleIsAnArchive { - get { - return ResourceManager.GetString("CheckingIfFIleIsAnArchive", resourceCulture); - } - } - - internal static string OK { - get { - return ResourceManager.GetString("OK", resourceCulture); - } - } - - internal static string ErrorWithMessage { - get { - return ResourceManager.GetString("ErrorWithMessage", resourceCulture); - } - } - - internal static string ExtractingArchive { - get { - return ResourceManager.GetString("ExtractingArchive", resourceCulture); - } - } - - internal static string RemovingTemporaryPath { - get { - return ResourceManager.GetString("RemovingTemporaryPath", resourceCulture); - } - } - - internal static string ExtractedContents { - get { - return ResourceManager.GetString("ExtractedContents", resourceCulture); - } - } - - internal static string HashingFile { - get { - return ResourceManager.GetString("HashingFile", resourceCulture); - } - } - - internal static string UnknownFile { - get { - return ResourceManager.GetString("UnknownFile", resourceCulture); - } - } - - internal static string CompressingFile { - get { - return ResourceManager.GetString("CompressingFile", resourceCulture); - } - } - - internal static string Finishing { - get { - return ResourceManager.GetString("Finishing", resourceCulture); - } - } - - internal static string UnhandledExceptionWhenImporting { - get { - return ResourceManager.GetString("UnhandledExceptionWhenImporting", resourceCulture); - } - } - + /// + /// Looks up a localized string similar to Adding disks.... + /// internal static string AddingDisks { get { return ResourceManager.GetString("AddingDisks", resourceCulture); } } - internal static string FoundDiskWithoutMachine { + /// + /// Looks up a localized string similar to Adding machines (games).... + /// + internal static string AddingMachines { get { - return ResourceManager.GetString("FoundDiskWithoutMachine", resourceCulture); - } - } - - internal static string NotAChdFile { - get { - return ResourceManager.GetString("NotAChdFile", resourceCulture); - } - } - - internal static string NoChecksumsFound { - get { - return ResourceManager.GetString("NoChecksumsFound", resourceCulture); - } - } - - internal static string Copying { - get { - return ResourceManager.GetString("Copying", resourceCulture); - } - } - - internal static string CopyingFile { - get { - return ResourceManager.GetString("CopyingFile", resourceCulture); + return ResourceManager.GetString("AddingMachines", resourceCulture); } } + /// + /// Looks up a localized string similar to Adding medias.... + /// internal static string AddingMedias { get { return ResourceManager.GetString("AddingMedias", resourceCulture); } } + /// + /// Looks up a localized string similar to Adding ROMs.... + /// + internal static string AddingRoms { + get { + return ResourceManager.GetString("AddingRoms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Assertion failed. + /// + internal static string Assertion_failed { + get { + return ResourceManager.GetString("Assertion_failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specified string is not valid Base32 format because character "{0}" does not exist in Base32 alphabet. + /// + internal static string Base32_Invalid_format { + get { + return ResourceManager.GetString("Base32_Invalid_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specified string is not valid Base32 format because it doesn't have enough data to construct a complete byte array. + /// + internal static string Base32_Not_enought_data { + get { + return ResourceManager.GetString("Base32_Not_enought_data", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot seek after stream end.. + /// + internal static string Cannot_seek_after_end { + get { + return ResourceManager.GetString("Cannot_seek_after_end", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot seek before stream start.. + /// + internal static string Cannot_seek_before_start { + get { + return ResourceManager.GetString("Cannot_seek_before_start", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot find file with hash {0} in the repository. + /// + internal static string CannotFindHashInRepository { + get { + return ResourceManager.GetString("CannotFindHashInRepository", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot find lsar executable.. + /// + internal static string CannotFindLsAr { + get { + return ResourceManager.GetString("CannotFindLsAr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot find unar executable at {0}.. + /// + internal static string CannotFindUnArAtPath { + get { + return ResourceManager.GetString("CannotFindUnArAtPath", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot find requested zip entry in hashes dictionary. + /// + internal static string CannotFindZipEntryInDictionary { + get { + return ResourceManager.GetString("CannotFindZipEntryInDictionary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot run lsar.. + /// + internal static string CannotRunLsAr { + get { + return ResourceManager.GetString("CannotRunLsAr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot run unar.. + /// + internal static string CannotRunUnAr { + get { + return ResourceManager.GetString("CannotRunUnAr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Checking if file is an archive.... + /// + internal static string CheckingIfFIleIsAnArchive { + get { + return ResourceManager.GetString("CheckingIfFIleIsAnArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Compressing {0}.... + /// + internal static string Compressing { + get { + return ResourceManager.GetString("Compressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Compressing DAT file.... + /// + internal static string CompressingDatFile { + get { + return ResourceManager.GetString("CompressingDatFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Compressing file.... + /// + internal static string CompressingFile { + get { + return ResourceManager.GetString("CompressingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copying {0}.... + /// + internal static string Copying { + get { + return ResourceManager.GetString("Copying", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copying file.... + /// + internal static string CopyingFile { + get { + return ResourceManager.GetString("CopyingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find ROM set in database.. + /// + internal static string CouldNotFindRomSetInDatabase { + get { + return ResourceManager.GetString("CouldNotFindRomSetInDatabase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File exists. + /// + internal static string DatabaseFileExistsMsgBoxTitle { + get { + return ResourceManager.GetString("DatabaseFileExistsMsgBoxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DAT file is already in database, not importing duplicates.. + /// + internal static string DatAlreadyInDatabase { + get { + return ResourceManager.GetString("DatAlreadyInDatabase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Imported {0} machines with {1} ROMs.. + /// + internal static string DatImportSuccess { + get { + return ResourceManager.GetString("DatImportSuccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enumerating files.... + /// + internal static string EnumeratingFiles { + get { + return ResourceManager.GetString("EnumeratingFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error: {0}. + /// + internal static string ErrorWithMessage { + get { + return ResourceManager.GetString("ErrorWithMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exporting ROMs.... + /// + internal static string ExportingRoms { + get { + return ResourceManager.GetString("ExportingRoms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extracted contents. + /// + internal static string ExtractedContents { + get { + return ResourceManager.GetString("ExtractedContents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extracting archive contents.... + /// + internal static string ExtractingArchive { + get { + return ResourceManager.GetString("ExtractingArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Finished. + /// + internal static string Finished { + get { + return ResourceManager.GetString("Finished", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Finishing.... + /// + internal static string Finishing { + get { + return ResourceManager.GetString("Finishing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Found a disk with an unknown machine, this should not happen.. + /// + internal static string FoundDiskWithoutMachine { + get { + return ResourceManager.GetString("FoundDiskWithoutMachine", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Found media with an unknown machine, this should not happen.. + /// internal static string FoundMediaWithoutMachine { get { return ResourceManager.GetString("FoundMediaWithoutMachine", resourceCulture); } } + /// + /// Looks up a localized string similar to Found a ROM with an unknown machine, this should not happen.. + /// + internal static string FoundRomWithoutMachine { + get { + return ResourceManager.GetString("FoundRomWithoutMachine", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Getting machine (game) names.... + /// + internal static string GettingMachineNames { + get { + return ResourceManager.GetString("GettingMachineNames", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hashing DAT file.... + /// + internal static string HashingDatFile { + get { + return ResourceManager.GetString("HashingDatFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hashing file.... + /// + internal static string HashingFile { + get { + return ResourceManager.GetString("HashingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Importing {0}.... + /// + internal static string Importing { + get { + return ResourceManager.GetString("Importing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No checksums found.. + /// + internal static string NoChecksumsFound { + get { + return ResourceManager.GetString("NoChecksumsFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not yet implemented.. + /// + internal static string Not_yet_implemented { + get { + return ResourceManager.GetString("Not_yet_implemented", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not a CHD file.. + /// + internal static string NotAChdFile { + get { + return ResourceManager.GetString("NotAChdFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not an AaruFormat file.. + /// internal static string NotAnAaruFormatFile { get { return ResourceManager.GetString("NotAnAaruFormatFile", resourceCulture); } } - internal static string DatImportSuccess { + /// + /// Looks up a localized string similar to Not the correct lsar executable. + /// + internal static string NotCorrectLsAr { get { - return ResourceManager.GetString("DatImportSuccess", resourceCulture); + return ResourceManager.GetString("NotCorrectLsAr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not the correct unar executable. + /// + internal static string NotCorrectUnAr { + get { + return ResourceManager.GetString("NotCorrectUnAr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OK. + /// + internal static string OK { + get { + return ResourceManager.GetString("OK", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parsing DAT file.... + /// + internal static string ParsinDatFile { + get { + return ResourceManager.GetString("ParsinDatFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Removing temporary path.... + /// + internal static string RemovingTemporaryPath { + get { + return ResourceManager.GetString("RemovingTemporaryPath", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Retrieving ROMs and disks.... + /// + internal static string RetrievingRomsAndDisks { + get { + return ResourceManager.GetString("RetrievingRomsAndDisks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Retrieving ROM set from database.. + /// + internal static string RetrievingRomSetFromDatabase { + get { + return ResourceManager.GetString("RetrievingRomSetFromDatabase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving changes to database.... + /// + internal static string SavingChangesToDatabase { + get { + return ResourceManager.GetString("SavingChangesToDatabase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The input exceeds data types.. + /// + internal static string Spamsum_Input_exceeds_data { + get { + return ResourceManager.GetString("Spamsum_Input_exceeds_data", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SpamSum does not have a binary representation.. + /// + internal static string Spamsum_no_binary { + get { + return ResourceManager.GetString("Spamsum_no_binary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to unar path is not set.. + /// + internal static string UnArPathNotSet { + get { + return ResourceManager.GetString("UnArPathNotSet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unhandled exception occurred.. + /// + internal static string UnhandledException { + get { + return ResourceManager.GetString("UnhandledException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unhandled exception when importing file.. + /// + internal static string UnhandledExceptionWhenImporting { + get { + return ResourceManager.GetString("UnhandledExceptionWhenImporting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown file.. + /// + internal static string UnknownFile { + get { + return ResourceManager.GetString("UnknownFile", resourceCulture); } } } diff --git a/RomRepoMgr.Core/Resources/Localization.es.resx b/RomRepoMgr.Core/Resources/Localization.es.resx index 83702f1..ebdacd0 100644 --- a/RomRepoMgr.Core/Resources/Localization.es.resx +++ b/RomRepoMgr.Core/Resources/Localization.es.resx @@ -1,185 +1,189 @@  - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Añadiendo DAT a la base de datos... - + Añadiendo máquinas (juegos)... - + Añadiendo ROMs... - + Aserción fallida - + El texto especificado no tiene un formato Base32 válido porque el caracter "{0}" no existe en el alfabeto Base32 - + El texto especificado no tiene un formato Base32 válido porque no tiene datos suficientes para completar una ristra de bytes - + No se puede encontrar el archivo con hash {0} en el repositorio - + No se puede encontrar el ejecutable lsar. - + No se puede encontrar el ejecutable unar en {0}. - + No se puede encontrar la entrada zip solicitada en el diccionari de hashes - + No se puede ejecutar lsar. - + No se puede ejecutar unar. - + No se puede posicionar antes del comienzo. - + No se puede posicionar después del comienzo. - + Comprobando si el fichero es un archivo comprimido... - + Comprimiendo {0}... - + Comprimiendo archivo DAT... - + Comprimiendo archivo... - + No se pudo encontrar el set de ROMs en la base de datos. - + El archivo existe - + El archivo DAT ya está en la base de datos, no importaré duplicados. - + Enumerando archivos... - + Error: {0} - + Exportando ROMs... - + Contenidos extraídos - + Extrayendo contenidos del archivo... - + Terminado - + Terminando... - + Se encontró una ROM sin máquina, esto no debería pasar. - + Obteniendo nombre de las máquinas (juegos)... - + Calculando hash del archivo DAT... - + Calculando hash del archivo... - + Importando {0}... - + No es el ejecutable correcto de lsar - + No es el ejecutable corrector de unar - + Aún no implementado. - + OK - + Analizando archivo DAT... - + Eliminando ruta remporal... - + Recuperando ROMs y discos... - + Recuperando set de ROMs de la base de datos... - + Guardando cambios en la base de datos... - + La entrada excede los tipos de datos. - + SpamSum no posee una representación binaria. - + La ruta a unar no está establecida. - + Ocurrió una excepción no controlada. - + Excepción no controlada al importar archivo. - + Archivo desconocido. - + Añadiendo discos... - + Se encontró un disco sin máquina, esto no debería pasar. - + No se encontraron hashes. - + No es un CHD. - + Copiando {0}... - + Copiando archivo... - + Se encontró un medio sin máquina, esto no debería pasar. - + No es un archivo AaruFormat. - + Añadiendo medios... \ No newline at end of file diff --git a/RomRepoMgr.Core/Resources/Localization.resx b/RomRepoMgr.Core/Resources/Localization.resx index 3d06f17..235219c 100644 --- a/RomRepoMgr.Core/Resources/Localization.resx +++ b/RomRepoMgr.Core/Resources/Localization.resx @@ -1,193 +1,198 @@ - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Specified string is not valid Base32 format because it doesn't have enough data to construct a complete byte array - + Specified string is not valid Base32 format because character "{0}" does not exist in Base32 alphabet - + Cannot seek before stream start. - + Cannot seek after stream end. - + SpamSum does not have a binary representation. - + Assertion failed - + The input exceeds data types. - + Not yet implemented. - + File exists - + unar path is not set. - + Cannot find unar executable at {0}. - + Cannot find lsar executable. - + Cannot run unar. - + Cannot run lsar. - + Not the correct unar executable - + Not the correct lsar executable - + Parsing DAT file... - + Hashing DAT file... - + DAT file is already in database, not importing duplicates. - + Adding DAT to database... - + Compressing DAT file... - + Getting machine (game) names... - + Adding machines (games)... - + Saving changes to database... - + Retrieving ROMs and disks... - + Adding ROMs... - + Found a ROM with an unknown machine, this should not happen. - + Unhandled exception occurred. - + Retrieving ROM set from database. - + Could not find ROM set in database. - + Exporting ROMs... - + Finished - + Cannot find requested zip entry in hashes dictionary - + Cannot find file with hash {0} in the repository - + Compressing {0}... - + Enumerating files... - + Importing {0}... - + Checking if file is an archive... - + OK - + Error: {0} - + Extracting archive contents... - + Removing temporary path... - + Extracted contents - + Hashing file... - + Unknown file. - + Compressing file... - + Finishing... - + Unhandled exception when importing file. - + Adding disks... - + Found a disk with an unknown machine, this should not happen. - + Not a CHD file. - + No checksums found. - + Copying {0}... - + Copying file... - + Adding medias... - + Found media with an unknown machine, this should not happen. - + Not an AaruFormat file. - + Imported {0} machines with {1} ROMs. \ No newline at end of file diff --git a/RomRepoMgr.Core/RomRepoMgr.Core.csproj b/RomRepoMgr.Core/RomRepoMgr.Core.csproj index 4514a16..12ba233 100644 --- a/RomRepoMgr.Core/RomRepoMgr.Core.csproj +++ b/RomRepoMgr.Core/RomRepoMgr.Core.csproj @@ -1,39 +1,39 @@ - - net9.0 - en - default - + + net9.0 + en + default + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - ResXFileCodeGenerator - Localization.Designer.cs - - + + + ResXFileCodeGenerator + Localization.Designer.cs + + - - - True - True - Language.resx - - + + + True + True + Language.resx + + diff --git a/RomRepoMgr.Core/StreamWithLength.cs b/RomRepoMgr.Core/StreamWithLength.cs index dbae417..232f9eb 100644 --- a/RomRepoMgr.Core/StreamWithLength.cs +++ b/RomRepoMgr.Core/StreamWithLength.cs @@ -1,43 +1,42 @@ using System; using System.IO; -namespace RomRepoMgr.Core +namespace RomRepoMgr.Core; + +internal sealed class StreamWithLength : Stream { - internal sealed class StreamWithLength : Stream + readonly Stream _baseStream; + + public StreamWithLength(Stream baseStream, long length) { - readonly Stream _baseStream; + _baseStream = baseStream; + Length = length; + } - public StreamWithLength(Stream baseStream, long length) - { - _baseStream = baseStream; - Length = length; - } + public override bool CanRead => _baseStream.CanRead; + public override bool CanSeek => _baseStream.CanSeek; + public override bool CanWrite => _baseStream.CanWrite; + public override long Length { get; } - public override bool CanRead => _baseStream.CanRead; - public override bool CanSeek => _baseStream.CanSeek; - public override bool CanWrite => _baseStream.CanWrite; - public override long Length { get; } + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } + public override void Flush() => _baseStream.Flush(); - public override void Flush() => _baseStream.Flush(); + public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count); - public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count); + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); - public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); - public override void SetLength(long value) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); - public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); - - public override void Close() - { - _baseStream.Close(); - base.Close(); - } + public override void Close() + { + _baseStream.Close(); + base.Close(); } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Workers/Checksum.cs b/RomRepoMgr.Core/Workers/Checksum.cs index 17469b4..c16f0df 100644 --- a/RomRepoMgr.Core/Workers/Checksum.cs +++ b/RomRepoMgr.Core/Workers/Checksum.cs @@ -34,154 +34,159 @@ using System.Collections.Generic; using System.Threading; using Aaru.Checksums; -namespace RomRepoMgr.Core.Workers +namespace RomRepoMgr.Core.Workers; + +internal enum ChecksumType { - internal enum ChecksumType + Crc32, + Md5, + Sha1, + Sha256, + Sha384, + Sha512 +} + +internal sealed class Checksum +{ + readonly Crc32Context _crc32Ctx; + readonly Md5Context _md5Ctx; + readonly Sha1Context _sha1Ctx; + readonly Sha256Context _sha256Ctx; + readonly Sha384Context _sha384Ctx; + readonly Sha512Context _sha512Ctx; + Crc32Packet _crc32Pkt; + Thread _crc32Thread; + Md5Packet _md5Pkt; + Thread _md5Thread; + Sha1Packet _sha1Pkt; + Thread _sha1Thread; + Sha256Packet _sha256Pkt; + Thread _sha256Thread; + Sha384Packet _sha384Pkt; + Thread _sha384Thread; + Sha512Packet _sha512Pkt; + Thread _sha512Thread; + + internal Checksum() { - Crc32, Md5, Sha1, - Sha256, Sha384, Sha512 + _crc32Ctx = new Crc32Context(); + _md5Ctx = new Md5Context(); + _sha1Ctx = new Sha1Context(); + _sha256Ctx = new Sha256Context(); + _sha384Ctx = new Sha384Context(); + _sha512Ctx = new Sha512Context(); + + _crc32Thread = new Thread(UpdateCrc32); + _md5Thread = new Thread(UpdateMd5); + _sha1Thread = new Thread(UpdateSha1); + _sha256Thread = new Thread(UpdateSha256); + _sha384Thread = new Thread(UpdateSha384); + _sha512Thread = new Thread(UpdateSha512); + + _crc32Pkt = new Crc32Packet(); + _md5Pkt = new Md5Packet(); + _sha1Pkt = new Sha1Packet(); + _sha256Pkt = new Sha256Packet(); + _sha384Pkt = new Sha384Packet(); + _sha512Pkt = new Sha512Packet(); + + _crc32Pkt.context = _crc32Ctx; + _md5Pkt.context = _md5Ctx; + _sha1Pkt.context = _sha1Ctx; + _sha256Pkt.context = _sha256Ctx; + _sha384Pkt.context = _sha384Ctx; + _sha512Pkt.context = _sha512Ctx; } - internal sealed class Checksum + internal void Update(byte[] data) { - readonly Crc32Context _crc32Ctx; - readonly Md5Context _md5Ctx; - readonly Sha1Context _sha1Ctx; - readonly Sha256Context _sha256Ctx; - readonly Sha384Context _sha384Ctx; - readonly Sha512Context _sha512Ctx; - Crc32Packet _crc32Pkt; - Thread _crc32Thread; - Md5Packet _md5Pkt; - Thread _md5Thread; - Sha1Packet _sha1Pkt; - Thread _sha1Thread; - Sha256Packet _sha256Pkt; - Thread _sha256Thread; - Sha384Packet _sha384Pkt; - Thread _sha384Thread; - Sha512Packet _sha512Pkt; - Thread _sha512Thread; + _crc32Pkt.data = data; + _crc32Thread.Start(_crc32Pkt); + _md5Pkt.data = data; + _md5Thread.Start(_md5Pkt); + _sha1Pkt.data = data; + _sha1Thread.Start(_sha1Pkt); + _sha256Pkt.data = data; + _sha256Thread.Start(_sha256Pkt); + _sha384Pkt.data = data; + _sha384Thread.Start(_sha384Pkt); + _sha512Pkt.data = data; + _sha512Thread.Start(_sha512Pkt); - internal Checksum() - { - _crc32Ctx = new Crc32Context(); - _md5Ctx = new Md5Context(); - _sha1Ctx = new Sha1Context(); - _sha256Ctx = new Sha256Context(); - _sha384Ctx = new Sha384Context(); - _sha512Ctx = new Sha512Context(); + while(_crc32Thread.IsAlive || + _md5Thread.IsAlive || + _sha1Thread.IsAlive || + _sha256Thread.IsAlive || + _sha384Thread.IsAlive || + _sha512Thread.IsAlive) {} - _crc32Thread = new Thread(UpdateCrc32); - _md5Thread = new Thread(UpdateMd5); - _sha1Thread = new Thread(UpdateSha1); - _sha256Thread = new Thread(UpdateSha256); - _sha384Thread = new Thread(UpdateSha384); - _sha512Thread = new Thread(UpdateSha512); - - _crc32Pkt = new Crc32Packet(); - _md5Pkt = new Md5Packet(); - _sha1Pkt = new Sha1Packet(); - _sha256Pkt = new Sha256Packet(); - _sha384Pkt = new Sha384Packet(); - _sha512Pkt = new Sha512Packet(); - - _crc32Pkt.context = _crc32Ctx; - _md5Pkt.context = _md5Ctx; - _sha1Pkt.context = _sha1Ctx; - _sha256Pkt.context = _sha256Ctx; - _sha384Pkt.context = _sha384Ctx; - _sha512Pkt.context = _sha512Ctx; - } - - internal void Update(byte[] data) - { - _crc32Pkt.data = data; - _crc32Thread.Start(_crc32Pkt); - _md5Pkt.data = data; - _md5Thread.Start(_md5Pkt); - _sha1Pkt.data = data; - _sha1Thread.Start(_sha1Pkt); - _sha256Pkt.data = data; - _sha256Thread.Start(_sha256Pkt); - _sha384Pkt.data = data; - _sha384Thread.Start(_sha384Pkt); - _sha512Pkt.data = data; - _sha512Thread.Start(_sha512Pkt); - - while(_crc32Thread.IsAlive || - _md5Thread.IsAlive || - _sha1Thread.IsAlive || - _sha256Thread.IsAlive || - _sha384Thread.IsAlive || - _sha512Thread.IsAlive) {} - - _crc32Thread = new Thread(UpdateCrc32); - _md5Thread = new Thread(UpdateMd5); - _sha1Thread = new Thread(UpdateSha1); - _sha256Thread = new Thread(UpdateSha256); - _sha384Thread = new Thread(UpdateSha384); - _sha512Thread = new Thread(UpdateSha512); - } - - internal Dictionary End() => new Dictionary - { - [ChecksumType.Crc32] = _crc32Ctx.End(), - [ChecksumType.Md5] = _md5Ctx.End(), - [ChecksumType.Sha1] = _sha1Ctx.End(), - [ChecksumType.Sha256] = _sha256Ctx.End(), - [ChecksumType.Sha384] = _sha384Ctx.End(), - [ChecksumType.Sha512] = _sha512Ctx.End() - }; - - #region Threading helpers - struct Crc32Packet - { - public Crc32Context context; - public byte[] data; - } - - struct Md5Packet - { - public Md5Context context; - public byte[] data; - } - - struct Sha1Packet - { - public Sha1Context context; - public byte[] data; - } - - struct Sha256Packet - { - public Sha256Context context; - public byte[] data; - } - - struct Sha384Packet - { - public Sha384Context context; - public byte[] data; - } - - struct Sha512Packet - { - public Sha512Context context; - public byte[] data; - } - - static void UpdateCrc32(object packet) => ((Crc32Packet)packet).context.Update(((Crc32Packet)packet).data); - - static void UpdateMd5(object packet) => ((Md5Packet)packet).context.Update(((Md5Packet)packet).data); - - static void UpdateSha1(object packet) => ((Sha1Packet)packet).context.Update(((Sha1Packet)packet).data); - - static void UpdateSha256(object packet) => ((Sha256Packet)packet).context.Update(((Sha256Packet)packet).data); - - static void UpdateSha384(object packet) => ((Sha384Packet)packet).context.Update(((Sha384Packet)packet).data); - - static void UpdateSha512(object packet) => ((Sha512Packet)packet).context.Update(((Sha512Packet)packet).data); - #endregion Threading helpers + _crc32Thread = new Thread(UpdateCrc32); + _md5Thread = new Thread(UpdateMd5); + _sha1Thread = new Thread(UpdateSha1); + _sha256Thread = new Thread(UpdateSha256); + _sha384Thread = new Thread(UpdateSha384); + _sha512Thread = new Thread(UpdateSha512); } + + internal Dictionary End() => new() + { + [ChecksumType.Crc32] = _crc32Ctx.End(), + [ChecksumType.Md5] = _md5Ctx.End(), + [ChecksumType.Sha1] = _sha1Ctx.End(), + [ChecksumType.Sha256] = _sha256Ctx.End(), + [ChecksumType.Sha384] = _sha384Ctx.End(), + [ChecksumType.Sha512] = _sha512Ctx.End() + }; + +#region Threading helpers + + struct Crc32Packet + { + public Crc32Context context; + public byte[] data; + } + + struct Md5Packet + { + public Md5Context context; + public byte[] data; + } + + struct Sha1Packet + { + public Sha1Context context; + public byte[] data; + } + + struct Sha256Packet + { + public Sha256Context context; + public byte[] data; + } + + struct Sha384Packet + { + public Sha384Context context; + public byte[] data; + } + + struct Sha512Packet + { + public Sha512Context context; + public byte[] data; + } + + static void UpdateCrc32(object packet) => ((Crc32Packet)packet).context.Update(((Crc32Packet)packet).data); + + static void UpdateMd5(object packet) => ((Md5Packet)packet).context.Update(((Md5Packet)packet).data); + + static void UpdateSha1(object packet) => ((Sha1Packet)packet).context.Update(((Sha1Packet)packet).data); + + static void UpdateSha256(object packet) => ((Sha256Packet)packet).context.Update(((Sha256Packet)packet).data); + + static void UpdateSha384(object packet) => ((Sha384Packet)packet).context.Update(((Sha384Packet)packet).data); + + static void UpdateSha512(object packet) => ((Sha512Packet)packet).context.Update(((Sha512Packet)packet).data); + +#endregion Threading helpers } \ No newline at end of file diff --git a/RomRepoMgr.Core/Workers/Compression.cs b/RomRepoMgr.Core/Workers/Compression.cs index bc55d8b..f10f8f9 100644 --- a/RomRepoMgr.Core/Workers/Compression.cs +++ b/RomRepoMgr.Core/Workers/Compression.cs @@ -32,207 +32,217 @@ using SharpCompress.Compressors; using SharpCompress.Compressors.LZMA; using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs; -namespace RomRepoMgr.Core.Workers +namespace RomRepoMgr.Core.Workers; + +public sealed class Compression { - public sealed class Compression + const long BUFFER_SIZE = 131072; + + public event EventHandler SetProgressBounds; + public event EventHandler SetProgress; + public event EventHandler FinishedWithText; + public event EventHandler FailedWithText; + + public void CompressFile(string source, string destination) { - const long BUFFER_SIZE = 131072; + var inFs = new FileStream(source, FileMode.Open, FileAccess.Read); + var outFs = new FileStream(destination, FileMode.CreateNew, FileAccess.Write); + Stream zStream = new LZipStream(outFs, CompressionMode.Compress); - public event EventHandler SetProgressBounds; - public event EventHandler SetProgress; - public event EventHandler FinishedWithText; - public event EventHandler FailedWithText; + var buffer = new byte[BUFFER_SIZE]; - public void CompressFile(string source, string destination) + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); + + while(inFs.Position + BUFFER_SIZE <= inFs.Length) { - var inFs = new FileStream(source, FileMode.Open, FileAccess.Read); - var outFs = new FileStream(destination, FileMode.CreateNew, FileAccess.Write); - Stream zStream = new LZipStream(outFs, CompressionMode.Compress); - - byte[] buffer = new byte[BUFFER_SIZE]; - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); - - while(inFs.Position + BUFFER_SIZE <= inFs.Length) - { - SetProgress?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - zStream.Write(buffer, 0, buffer.Length); - } - - buffer = new byte[inFs.Length - inFs.Position]; - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); inFs.Read(buffer, 0, buffer.Length); zStream.Write(buffer, 0, buffer.Length); - - inFs.Close(); - zStream.Close(); - outFs.Dispose(); } - public void DecompressFile(string source, string destination) + buffer = new byte[inFs.Length - inFs.Position]; + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); + + inFs.Read(buffer, 0, buffer.Length); + zStream.Write(buffer, 0, buffer.Length); + + inFs.Close(); + zStream.Close(); + outFs.Dispose(); + } + + public void DecompressFile(string source, string destination) + { + var inFs = new FileStream(source, FileMode.Open, FileAccess.Read); + var outFs = new FileStream(destination, FileMode.Create, FileAccess.Write); + Stream zStream = new LZipStream(inFs, CompressionMode.Decompress); + + zStream.CopyTo(outFs); + + outFs.Close(); + zStream.Close(); + inFs.Close(); + } + + public bool CheckUnAr(string unArPath) + { + if(string.IsNullOrWhiteSpace(unArPath)) { - var inFs = new FileStream(source, FileMode.Open, FileAccess.Read); - var outFs = new FileStream(destination, FileMode.Create, FileAccess.Write); - Stream zStream = new LZipStream(inFs, CompressionMode.Decompress); + FailedWithText?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.UnArPathNotSet + }); - zStream.CopyTo(outFs); - - outFs.Close(); - zStream.Close(); - inFs.Close(); + return false; } - public bool CheckUnAr(string unArPath) + string unarFolder = Path.GetDirectoryName(unArPath); + string extension = Path.GetExtension(unArPath); + string unarfilename = Path.GetFileNameWithoutExtension(unArPath); + string lsarfilename = unarfilename.Replace("unar", "lsar"); + string unarPath = Path.Combine(unarFolder, unarfilename + extension); + string lsarPath = Path.Combine(unarFolder, lsarfilename + extension); + + if(!File.Exists(unarPath)) { - if(string.IsNullOrWhiteSpace(unArPath)) - { - FailedWithText?.Invoke(this, new ErrorEventArgs - { - Message = Localization.UnArPathNotSet - }); + FailedWithText?.Invoke(this, + new ErrorEventArgs + { + Message = string.Format(Localization.CannotFindUnArAtPath, unarPath) + }); - return false; - } + return false; + } - string unarFolder = Path.GetDirectoryName(unArPath); - string extension = Path.GetExtension(unArPath); - string unarfilename = Path.GetFileNameWithoutExtension(unArPath); - string lsarfilename = unarfilename.Replace("unar", "lsar"); - string unarPath = Path.Combine(unarFolder, unarfilename + extension); - string lsarPath = Path.Combine(unarFolder, lsarfilename + extension); + if(!File.Exists(lsarPath)) + { + FailedWithText?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.CannotFindLsAr + }); - if(!File.Exists(unarPath)) - { - FailedWithText?.Invoke(this, new ErrorEventArgs - { - Message = string.Format(Localization.CannotFindUnArAtPath, unarPath) - }); + return false; + } - return false; - } + string unarOut, lsarOut; - if(!File.Exists(lsarPath)) - { - FailedWithText?.Invoke(this, new ErrorEventArgs - { - Message = Localization.CannotFindLsAr - }); - - return false; - } - - string unarOut, lsarOut; - - try - { - var unarProcess = new Process - { - StartInfo = - { - FileName = unarPath, - CreateNoWindow = true, - RedirectStandardOutput = true, - UseShellExecute = false - } - }; - - unarProcess.Start(); - unarProcess.WaitForExit(); - unarOut = unarProcess.StandardOutput.ReadToEnd(); - } - catch - { - FailedWithText?.Invoke(this, new ErrorEventArgs - { - Message = Localization.CannotRunUnAr - }); - - return false; - } - - try - { - var lsarProcess = new Process - { - StartInfo = - { - FileName = lsarPath, - CreateNoWindow = true, - RedirectStandardOutput = true, - UseShellExecute = false - } - }; - - lsarProcess.Start(); - lsarProcess.WaitForExit(); - lsarOut = lsarProcess.StandardOutput.ReadToEnd(); - } - catch - { - FailedWithText?.Invoke(this, new ErrorEventArgs - { - Message = Localization.CannotRunLsAr - }); - - return false; - } - - if(!unarOut.StartsWith("unar ", StringComparison.CurrentCulture)) - { - FailedWithText?.Invoke(this, new ErrorEventArgs - { - Message = Localization.NotCorrectUnAr - }); - - return false; - } - - if(!lsarOut.StartsWith("lsar ", StringComparison.CurrentCulture)) - { - FailedWithText?.Invoke(this, new ErrorEventArgs - { - Message = Localization.NotCorrectLsAr - }); - - return false; - } - - var versionProcess = new Process + try + { + var unarProcess = new Process { StartInfo = { FileName = unarPath, CreateNoWindow = true, RedirectStandardOutput = true, - UseShellExecute = false, - Arguments = "-v" + UseShellExecute = false } }; - versionProcess.Start(); - versionProcess.WaitForExit(); - - FinishedWithText?.Invoke(this, new MessageEventArgs - { - Message = versionProcess.StandardOutput.ReadToEnd().TrimEnd('\n') - }); - - return true; + unarProcess.Start(); + unarProcess.WaitForExit(); + unarOut = unarProcess.StandardOutput.ReadToEnd(); } + catch + { + FailedWithText?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.CannotRunUnAr + }); + + return false; + } + + try + { + var lsarProcess = new Process + { + StartInfo = + { + FileName = lsarPath, + CreateNoWindow = true, + RedirectStandardOutput = true, + UseShellExecute = false + } + }; + + lsarProcess.Start(); + lsarProcess.WaitForExit(); + lsarOut = lsarProcess.StandardOutput.ReadToEnd(); + } + catch + { + FailedWithText?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.CannotRunLsAr + }); + + return false; + } + + if(!unarOut.StartsWith("unar ", StringComparison.CurrentCulture)) + { + FailedWithText?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.NotCorrectUnAr + }); + + return false; + } + + if(!lsarOut.StartsWith("lsar ", StringComparison.CurrentCulture)) + { + FailedWithText?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.NotCorrectLsAr + }); + + return false; + } + + var versionProcess = new Process + { + StartInfo = + { + FileName = unarPath, + CreateNoWindow = true, + RedirectStandardOutput = true, + UseShellExecute = false, + Arguments = "-v" + } + }; + + versionProcess.Start(); + versionProcess.WaitForExit(); + + FinishedWithText?.Invoke(this, + new MessageEventArgs + { + Message = versionProcess.StandardOutput.ReadToEnd().TrimEnd('\n') + }); + + return true; } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Workers/DatImporter.cs b/RomRepoMgr.Core/Workers/DatImporter.cs index 598ae84..6e26129 100644 --- a/RomRepoMgr.Core/Workers/DatImporter.cs +++ b/RomRepoMgr.Core/Workers/DatImporter.cs @@ -40,1138 +40,1202 @@ using RomRepoMgr.Database; using RomRepoMgr.Database.Models; using SabreTools.DatFiles; using SabreTools.DatItems; -using SabreTools.IO; using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs; using Machine = RomRepoMgr.Database.Models.Machine; -namespace RomRepoMgr.Core.Workers +namespace RomRepoMgr.Core.Workers; + +public sealed class DatImporter { - public sealed class DatImporter + readonly string _category; + readonly string _datFilesPath; + readonly string _datPath; + bool _aborted; + + public DatImporter(string datPath, string category) { - readonly string _category; - readonly string _datFilesPath; - readonly string _datPath; - bool _aborted; + _datPath = datPath; + _datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles"); - public DatImporter(string datPath, string category) + if(!string.IsNullOrWhiteSpace(category)) _category = category; + } + + public void Import() + { + try { - _datPath = datPath; - _datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles"); + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - if(!string.IsNullOrWhiteSpace(category)) - _category = category; - } + SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - public void Import() - { - try + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.ParsinDatFile + }); + + var datFile = DatFile.Create(); + datFile.ParseFile(_datPath, 0, false, true); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.HashingDatFile + }); + + string datHash = Sha384Context.File(_datPath, out byte[] datHashBinary); + + string datHash32 = Base32.ToBase32String(datHashBinary); + + if(!Directory.Exists(_datFilesPath)) Directory.CreateDirectory(_datFilesPath); + + string compressedDatPath = Path.Combine(_datFilesPath, datHash32 + ".lz"); + + if(File.Exists(compressedDatPath)) { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + ErrorOccurred?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.DatAlreadyInDatabase + }); + + return; + } + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.AddingDatToDatabase + }); + + // TODO: Check if there is a hash in database but not in repo + + var romSet = new RomSet + { + Author = datFile.Header.Author, + Comment = datFile.Header.Comment, + Date = datFile.Header.Date, + Description = datFile.Header.Description, + Filename = Path.GetFileName(_datPath), + Homepage = datFile.Header.Homepage, + Name = datFile.Header.Name, + Sha384 = datHash, + Version = datFile.Header.Version, + CreatedOn = DateTime.UtcNow, + UpdatedOn = DateTime.UtcNow, + Category = _category + }; + + ctx.RomSets.Add(romSet); + ctx.SaveChanges(); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.CompressingDatFile + }); + + var datCompress = new Compression(); + datCompress.SetProgress += SetProgress; + datCompress.SetProgressBounds += SetProgressBounds; + datCompress.CompressFile(_datPath, compressedDatPath); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.GettingMachineNames + }); + + var machineNames = (from value in datFile.Items.Values from item in value select item.Machine.Name) + .Distinct() + .ToList(); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.AddingMachines + }); + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = machineNames.Count + }); + + var position = 0; + var machines = new Dictionary(); + + foreach(string name in machineNames) + { + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = position + }); + + var machine = new Machine + { + Name = name, + RomSetId = romSet.Id, + CreatedOn = DateTime.UtcNow, + UpdatedOn = DateTime.UtcNow + }; + + machines[name] = machine; + + position++; + } + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.SavingChangesToDatabase + }); + + SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); + + ctx.BulkInsert(machines.Values.ToList(), b => b.SetOutputIdentity = true); + ctx.SaveChanges(); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.RetrievingRomsAndDisks + }); + + var roms = new List(); + var disks = new List(); + var medias = new List(); + + var tmpRomCrc32Table = Guid.NewGuid().ToString(); + var tmpRomMd5Table = Guid.NewGuid().ToString(); + var tmpRomSha1Table = Guid.NewGuid().ToString(); + var tmpRomSha256Table = Guid.NewGuid().ToString(); + var tmpRomSha384Table = Guid.NewGuid().ToString(); + var tmpRomSha512Table = Guid.NewGuid().ToString(); + var tmpDiskMd5Table = Guid.NewGuid().ToString(); + var tmpDiskSha1Table = Guid.NewGuid().ToString(); + var tmpMediaMd5Table = Guid.NewGuid().ToString(); + var tmpMediaSha1Table = Guid.NewGuid().ToString(); + var tmpMediaSha256Table = Guid.NewGuid().ToString(); + + var romsHaveCrc = false; + var romsHaveMd5 = false; + var romsHaveSha1 = false; + var romsHaveSha256 = false; + var romsHaveSha384 = false; + var romsHaveSha512 = false; + var disksHaveMd5 = false; + var disksHaveSha1 = false; + var mediasHaveMd5 = false; + var mediasHaveSha1 = false; + var mediasHaveSha256 = false; + + DbConnection dbConnection = ctx.Database.GetDbConnection(); + dbConnection.Open(); + + position = 0; + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = datFile.Items.Values.Count + }); + + using(DbTransaction dbTransaction = dbConnection.BeginTransaction()) + { + DbCommand dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"CREATE TABLE \"{tmpRomCrc32Table}\" (\"Size\" INTEGER NOT NULL, \"Crc32\" TEXT NOT NULL);"; + + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"CREATE TABLE \"{tmpRomMd5Table}\" (\"Size\" INTEGER NOT NULL, \"Md5\" TEXT NOT NULL);"; + + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"CREATE TABLE \"{tmpRomSha1Table}\" (\"Size\" INTEGER NOT NULL, \"Sha1\" TEXT NOT NULL);"; + + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"CREATE TABLE \"{tmpRomSha256Table}\" (\"Size\" INTEGER NOT NULL, \"Sha256\" TEXT NOT NULL);"; + + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"CREATE TABLE \"{tmpRomSha384Table}\" (\"Size\" INTEGER NOT NULL, \"Sha384\" TEXT NOT NULL);"; + + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"CREATE TABLE \"{tmpRomSha512Table}\" (\"Size\" INTEGER NOT NULL, \"Sha512\" TEXT NOT NULL);"; + + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + dbcc.CommandText = $"CREATE TABLE \"{tmpDiskMd5Table}\" (\"Md5\" TEXT NOT NULL);"; + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + dbcc.CommandText = $"CREATE TABLE \"{tmpDiskSha1Table}\" (\"Sha1\" TEXT NOT NULL);"; + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + dbcc.CommandText = $"CREATE TABLE \"{tmpMediaMd5Table}\" (\"Md5\" TEXT NOT NULL);"; + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + dbcc.CommandText = $"CREATE TABLE \"{tmpMediaSha1Table}\" (\"Sha1\" TEXT NOT NULL);"; + dbcc.ExecuteNonQuery(); + dbcc = dbConnection.CreateCommand(); + dbcc.CommandText = $"CREATE TABLE \"{tmpMediaSha256Table}\" (\"Sha256\" TEXT NOT NULL);"; + dbcc.ExecuteNonQuery(); + + foreach(List values in datFile.Items.Values) + { + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = position + }); + + foreach(DatItem item in values) + { + switch(item) + { + case Rom rom: + if(rom.CRC != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpRomCrc32Table}\" (\"Size\", \"Crc32\") VALUES (\"{(ulong)rom.Size}\", \"{rom.CRC}\");"; + + dbcc.ExecuteNonQuery(); + + romsHaveCrc = true; + } + + if(rom.MD5 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpRomMd5Table}\" (\"Size\", \"Md5\") VALUES (\"{(ulong)rom.Size}\", \"{rom.MD5}\");"; + + dbcc.ExecuteNonQuery(); + + romsHaveMd5 = true; + } + + if(rom.SHA1 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpRomSha1Table}\" (\"Size\", \"Sha1\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA1}\");"; + + dbcc.ExecuteNonQuery(); + + romsHaveSha1 = true; + } + + if(rom.SHA256 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpRomSha256Table}\" (\"Size\", \"Sha256\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA256}\");"; + + dbcc.ExecuteNonQuery(); + + romsHaveSha256 = true; + } + + if(rom.SHA384 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpRomSha384Table}\" (\"Size\", \"Sha384\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA384}\");"; + + dbcc.ExecuteNonQuery(); + + romsHaveSha384 = true; + } + + if(rom.SHA512 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpRomSha512Table}\" (\"Size\", \"Sha512\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA512}\");"; + + dbcc.ExecuteNonQuery(); + + romsHaveSha512 = true; + } + + roms.Add(rom); + + continue; + case Disk disk: + if(disk.MD5 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpDiskMd5Table}\" (\"Md5\") VALUES (\"{disk.MD5}\");"; + + dbcc.ExecuteNonQuery(); + + disksHaveMd5 = true; + } + + if(disk.SHA1 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpDiskSha1Table}\" (\"Sha1\") VALUES (\"{disk.SHA1}\");"; + + dbcc.ExecuteNonQuery(); + + disksHaveSha1 = true; + } + + disks.Add(disk); + + continue; + case Media media: + if(media.MD5 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpMediaMd5Table}\" (\"Md5\") VALUES (\"{media.MD5}\");"; + + dbcc.ExecuteNonQuery(); + + mediasHaveMd5 = true; + } + + if(media.SHA1 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpMediaSha1Table}\" (\"Sha1\") VALUES (\"{media.SHA1}\");"; + + dbcc.ExecuteNonQuery(); + + mediasHaveSha1 = true; + } + + if(media.SHA256 != null) + { + dbcc = dbConnection.CreateCommand(); + + dbcc.CommandText = + $"INSERT INTO \"{tmpMediaSha256Table}\" (\"Sha256\") VALUES (\"{media.SHA256}\");"; + + dbcc.ExecuteNonQuery(); + + mediasHaveSha256 = true; + } + + medias.Add(media); + + continue; + } + } + + position++; + } SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - SetMessage?.Invoke(this, new MessageEventArgs + dbTransaction.Commit(); + } + + List pendingFilesByCrcList = romsHaveCrc + ? ctx.Files + .FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomCrc32Table}] AS t WHERE f.Crc32 = t.Crc32 AND f.Size = t.Size") + .ToList() + : new List(); + + List pendingFilesByMd5List = romsHaveMd5 + ? ctx.Files + .FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomMd5Table}] AS t WHERE f.Md5 = t.Md5 AND f.Size = t.Size") + .ToList() + : new List(); + + List pendingFilesBySha1List = + romsHaveSha1 + ? ctx.Files + .FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha1Table}] AS t WHERE f.Sha1 = t.Sha1 AND f.Size = t.Size") + .ToList() + : new List(); + + List pendingFilesBySha256List = + romsHaveSha256 + ? ctx.Files + .FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha256Table}] AS t WHERE f.Sha256 = t.Sha256 AND f.Size = t.Size") + .ToList() + : new List(); + + List pendingFilesBySha384List = + romsHaveSha384 + ? ctx.Files + .FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha384Table}] AS t WHERE f.Sha384 = t.Sha384 AND f.Size = t.Size") + .ToList() + : new List(); + + List pendingFilesBySha512List = + romsHaveSha512 + ? ctx.Files + .FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha512Table}] AS t WHERE f.Sha512 = t.Sha512 AND f.Size = t.Size") + .ToList() + : new List(); + + Dictionary pendingDisksByMd5 = + disksHaveMd5 + ? ctx.Disks + .FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskMd5Table}] AS t WHERE f.Md5 = t.Md5") + .ToDictionary(f => f.Md5) + : new Dictionary(); + + Dictionary pendingDisksBySha1 = + disksHaveSha1 + ? ctx.Disks + .FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskSha1Table}] AS t WHERE f.Sha1 = t.Sha1") + .ToDictionary(f => f.Sha1) + : new Dictionary(); + + Dictionary pendingMediasByMd5 = + mediasHaveMd5 + ? ctx.Medias + .FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaMd5Table}] AS t WHERE f.Md5 = t.Md5") + .ToDictionary(f => f.Md5) + : new Dictionary(); + + Dictionary pendingMediasBySha1 = + mediasHaveSha1 + ? ctx.Medias + .FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha1Table}] AS t WHERE f.Sha1 = t.Sha1") + .ToDictionary(f => f.Sha1) + : new Dictionary(); + + Dictionary pendingMediasBySha256 = + mediasHaveSha256 + ? ctx.Medias + .FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha256Table}] AS t WHERE f.Sha256 = t.Sha256") + .ToDictionary(f => f.Sha256) + : new Dictionary(); + + var pendingFilesByCrc = new Dictionary(); + var pendingFilesByMd5 = new Dictionary(); + var pendingFilesBySha1 = new Dictionary(); + var pendingFilesBySha256 = new Dictionary(); + var pendingFilesBySha384 = new Dictionary(); + var pendingFilesBySha512 = new Dictionary(); + var pendingFiles = new List(); + + // This is because of hash collisions. + foreach(DbFile item in pendingFilesByCrcList) + { + if(pendingFilesByCrc.ContainsKey(item.Crc32)) + pendingFiles.Add(item); + else + pendingFilesByCrc[item.Crc32] = item; + } + + foreach(DbFile item in pendingFilesByMd5List) + { + if(pendingFilesByMd5.ContainsKey(item.Md5)) + pendingFiles.Add(item); + else + pendingFilesByMd5[item.Md5] = item; + } + + foreach(DbFile item in pendingFilesBySha1List) + { + if(pendingFilesBySha1.ContainsKey(item.Sha1)) + pendingFiles.Add(item); + else + pendingFilesBySha1[item.Sha1] = item; + } + + foreach(DbFile item in pendingFilesBySha256List) + { + if(pendingFilesBySha256.ContainsKey(item.Sha256)) + pendingFiles.Add(item); + else + pendingFilesBySha256[item.Sha256] = item; + } + + foreach(DbFile item in pendingFilesBySha384List) + { + if(pendingFilesBySha384.ContainsKey(item.Sha384)) + pendingFiles.Add(item); + else + pendingFilesBySha384[item.Sha384] = item; + } + + foreach(DbFile item in pendingFilesBySha512List) + { + if(pendingFilesBySha512.ContainsKey(item.Sha512)) + pendingFiles.Add(item); + else + pendingFilesBySha512[item.Sha512] = item; + } + + // Clear some memory + pendingFilesByCrcList.Clear(); + pendingFilesByMd5List.Clear(); + pendingFilesBySha1List.Clear(); + pendingFilesBySha256List.Clear(); + pendingFilesBySha384List.Clear(); + pendingFilesBySha512List.Clear(); + + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomCrc32Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomMd5Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha1Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha256Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha384Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha512Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpDiskMd5Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpDiskSha1Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpMediaMd5Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpMediaSha1Table}]"); + ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpMediaSha256Table}]"); + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = roms.Count + }); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.AddingRoms + }); + + position = 0; + + var newFiles = new List(); + var newFilesByMachine = new List(); + + foreach(Rom rom in roms) + { + var hashCollision = false; + + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = position + }); + + if(!machines.TryGetValue(rom.Machine.Name, out Machine machine)) { - Message = Localization.ParsinDatFile - }); - - var datFile = DatFile.Create(); - datFile.ParseFile(_datPath, 0, false, throwOnError: true); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.HashingDatFile - }); - - string datHash = Sha384Context.File(_datPath, out byte[] datHashBinary); - - string datHash32 = Base32.ToBase32String(datHashBinary); - - if(!Directory.Exists(_datFilesPath)) - Directory.CreateDirectory(_datFilesPath); - - string compressedDatPath = Path.Combine(_datFilesPath, datHash32 + ".lz"); - - if(File.Exists(compressedDatPath)) - { - ErrorOccurred?.Invoke(this, new ErrorEventArgs - { - Message = Localization.DatAlreadyInDatabase - }); + ErrorOccurred?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.FoundRomWithoutMachine + }); return; } - SetMessage?.Invoke(this, new MessageEventArgs + var uSize = (ulong)rom.Size; + + DbFile file = null; + + if(rom.SHA512 != null) { - Message = Localization.AddingDatToDatabase - }); - - // TODO: Check if there is a hash in database but not in repo - - var romSet = new RomSet - { - Author = datFile.Header.Author, - Comment = datFile.Header.Comment, - Date = datFile.Header.Date, - Description = datFile.Header.Description, - Filename = Path.GetFileName(_datPath), - Homepage = datFile.Header.Homepage, - Name = datFile.Header.Name, - Sha384 = datHash, - Version = datFile.Header.Version, - CreatedOn = DateTime.UtcNow, - UpdatedOn = DateTime.UtcNow, - Category = _category - }; - - ctx.RomSets.Add(romSet); - ctx.SaveChanges(); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.CompressingDatFile - }); - - var datCompress = new Compression(); - datCompress.SetProgress += SetProgress; - datCompress.SetProgressBounds += SetProgressBounds; - datCompress.CompressFile(_datPath, compressedDatPath); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.GettingMachineNames - }); - - List machineNames = - (from value in datFile.Items.Values from item in value select item.Machine.Name).Distinct(). - ToList(); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.AddingMachines - }); - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = machineNames.Count - }); - - int position = 0; - Dictionary machines = new Dictionary(); - - foreach(string name in machineNames) - { - SetProgress?.Invoke(this, new ProgressEventArgs + if(pendingFilesBySha512.TryGetValue(rom.SHA512, out file)) { - Value = position - }); + if(file.Size != uSize) + { + hashCollision = true; + file = null; + } + } + } - var machine = new Machine + if(rom.SHA384 != null && file == null) + { + if(pendingFilesBySha384.TryGetValue(rom.SHA384, out file)) { - Name = name, - RomSetId = romSet.Id, + if(file.Size != uSize) + { + hashCollision = true; + file = null; + } + } + } + + if(rom.SHA256 != null && file == null) + { + if(pendingFilesBySha256.TryGetValue(rom.SHA256, out file)) + { + if(file.Size != uSize) + { + hashCollision = true; + file = null; + } + } + } + + if(rom.SHA1 != null && file == null) + { + if(pendingFilesBySha1.TryGetValue(rom.SHA1, out file)) + { + if(file.Size != uSize) + { + hashCollision = true; + file = null; + } + } + } + + if(rom.MD5 != null && file == null) + { + if(pendingFilesByMd5.TryGetValue(rom.MD5, out file)) + { + if(file.Size != uSize) + { + hashCollision = true; + file = null; + } + } + } + + if(rom.CRC != null && file == null) + { + if(pendingFilesByCrc.TryGetValue(rom.CRC, out file)) + { + if(file.Size != uSize) + { + hashCollision = true; + file = null; + } + } + } + + if(file == null && hashCollision) + { + if(rom.SHA512 != null) + file = pendingFiles.FirstOrDefault(f => f.Sha512 == rom.SHA512 && f.Size == uSize); + + if(file == null && rom.SHA384 != null) + file = pendingFiles.FirstOrDefault(f => f.Sha384 == rom.SHA384 && f.Size == uSize); + + if(file == null && rom.SHA256 != null) + file = pendingFiles.FirstOrDefault(f => f.Sha256 == rom.SHA256 && f.Size == uSize); + + if(file == null && rom.SHA1 != null) + file = pendingFiles.FirstOrDefault(f => f.Sha1 == rom.SHA1 && f.Size == uSize); + + if(file == null && rom.MD5 != null) + file = pendingFiles.FirstOrDefault(f => f.Md5 == rom.MD5 && f.Size == uSize); + + if(file == null && rom.CRC != null) + file = pendingFiles.FirstOrDefault(f => f.Crc32 == rom.CRC && f.Size == uSize); + } + + if(file == null) + { + file = new DbFile + { + Crc32 = rom.CRC, CreatedOn = DateTime.UtcNow, + Md5 = rom.MD5, + Sha1 = rom.SHA1, + Sha256 = rom.SHA256, + Sha384 = rom.SHA384, + Sha512 = rom.SHA512, + Size = uSize, UpdatedOn = DateTime.UtcNow }; - machines[name] = machine; - - position++; + newFiles.Add(file); } - SetMessage?.Invoke(this, new MessageEventArgs + if(string.IsNullOrEmpty(file.Crc32) && !string.IsNullOrEmpty(rom.CRC)) { - Message = Localization.SavingChangesToDatabase - }); - - SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - - ctx.BulkInsert(machines.Values.ToList(), b => b.SetOutputIdentity = true); - ctx.SaveChanges(); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.RetrievingRomsAndDisks - }); - - List roms = new List(); - List disks = new List(); - List medias = new List(); - - string tmpRomCrc32Table = Guid.NewGuid().ToString(); - string tmpRomMd5Table = Guid.NewGuid().ToString(); - string tmpRomSha1Table = Guid.NewGuid().ToString(); - string tmpRomSha256Table = Guid.NewGuid().ToString(); - string tmpRomSha384Table = Guid.NewGuid().ToString(); - string tmpRomSha512Table = Guid.NewGuid().ToString(); - string tmpDiskMd5Table = Guid.NewGuid().ToString(); - string tmpDiskSha1Table = Guid.NewGuid().ToString(); - string tmpMediaMd5Table = Guid.NewGuid().ToString(); - string tmpMediaSha1Table = Guid.NewGuid().ToString(); - string tmpMediaSha256Table = Guid.NewGuid().ToString(); - - bool romsHaveCrc = false; - bool romsHaveMd5 = false; - bool romsHaveSha1 = false; - bool romsHaveSha256 = false; - bool romsHaveSha384 = false; - bool romsHaveSha512 = false; - bool disksHaveMd5 = false; - bool disksHaveSha1 = false; - bool mediasHaveMd5 = false; - bool mediasHaveSha1 = false; - bool mediasHaveSha256 = false; - - DbConnection dbConnection = ctx.Database.GetDbConnection(); - dbConnection.Open(); - - position = 0; - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = datFile.Items.Values.Count - }); - - using(DbTransaction dbTransaction = dbConnection.BeginTransaction()) - { - DbCommand dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"CREATE TABLE \"{tmpRomCrc32Table}\" (\"Size\" INTEGER NOT NULL, \"Crc32\" TEXT NOT NULL);"; - - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"CREATE TABLE \"{tmpRomMd5Table}\" (\"Size\" INTEGER NOT NULL, \"Md5\" TEXT NOT NULL);"; - - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"CREATE TABLE \"{tmpRomSha1Table}\" (\"Size\" INTEGER NOT NULL, \"Sha1\" TEXT NOT NULL);"; - - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"CREATE TABLE \"{tmpRomSha256Table}\" (\"Size\" INTEGER NOT NULL, \"Sha256\" TEXT NOT NULL);"; - - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"CREATE TABLE \"{tmpRomSha384Table}\" (\"Size\" INTEGER NOT NULL, \"Sha384\" TEXT NOT NULL);"; - - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"CREATE TABLE \"{tmpRomSha512Table}\" (\"Size\" INTEGER NOT NULL, \"Sha512\" TEXT NOT NULL);"; - - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - dbcc.CommandText = $"CREATE TABLE \"{tmpDiskMd5Table}\" (\"Md5\" TEXT NOT NULL);"; - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - dbcc.CommandText = $"CREATE TABLE \"{tmpDiskSha1Table}\" (\"Sha1\" TEXT NOT NULL);"; - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - dbcc.CommandText = $"CREATE TABLE \"{tmpMediaMd5Table}\" (\"Md5\" TEXT NOT NULL);"; - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - dbcc.CommandText = $"CREATE TABLE \"{tmpMediaSha1Table}\" (\"Sha1\" TEXT NOT NULL);"; - dbcc.ExecuteNonQuery(); - dbcc = dbConnection.CreateCommand(); - dbcc.CommandText = $"CREATE TABLE \"{tmpMediaSha256Table}\" (\"Sha256\" TEXT NOT NULL);"; - dbcc.ExecuteNonQuery(); - - foreach(List values in datFile.Items.Values) - { - SetProgress?.Invoke(this, new ProgressEventArgs - { - Value = position - }); - - foreach(DatItem item in values) - { - switch(item) - { - case Rom rom: - if(rom.CRC != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpRomCrc32Table}\" (\"Size\", \"Crc32\") VALUES (\"{(ulong)rom.Size}\", \"{rom.CRC}\");"; - - dbcc.ExecuteNonQuery(); - - romsHaveCrc = true; - } - - if(rom.MD5 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpRomMd5Table}\" (\"Size\", \"Md5\") VALUES (\"{(ulong)rom.Size}\", \"{rom.MD5}\");"; - - dbcc.ExecuteNonQuery(); - - romsHaveMd5 = true; - } - - if(rom.SHA1 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpRomSha1Table}\" (\"Size\", \"Sha1\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA1}\");"; - - dbcc.ExecuteNonQuery(); - - romsHaveSha1 = true; - } - - if(rom.SHA256 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpRomSha256Table}\" (\"Size\", \"Sha256\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA256}\");"; - - dbcc.ExecuteNonQuery(); - - romsHaveSha256 = true; - } - - if(rom.SHA384 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpRomSha384Table}\" (\"Size\", \"Sha384\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA384}\");"; - - dbcc.ExecuteNonQuery(); - - romsHaveSha384 = true; - } - - if(rom.SHA512 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpRomSha512Table}\" (\"Size\", \"Sha512\") VALUES (\"{(ulong)rom.Size}\", \"{rom.SHA512}\");"; - - dbcc.ExecuteNonQuery(); - - romsHaveSha512 = true; - } - - roms.Add(rom); - - continue; - case Disk disk: - if(disk.MD5 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpDiskMd5Table}\" (\"Md5\") VALUES (\"{disk.MD5}\");"; - - dbcc.ExecuteNonQuery(); - - disksHaveMd5 = true; - } - - if(disk.SHA1 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpDiskSha1Table}\" (\"Sha1\") VALUES (\"{disk.SHA1}\");"; - - dbcc.ExecuteNonQuery(); - - disksHaveSha1 = true; - } - - disks.Add(disk); - - continue; - case Media media: - if(media.MD5 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpMediaMd5Table}\" (\"Md5\") VALUES (\"{media.MD5}\");"; - - dbcc.ExecuteNonQuery(); - - mediasHaveMd5 = true; - } - - if(media.SHA1 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpMediaSha1Table}\" (\"Sha1\") VALUES (\"{media.SHA1}\");"; - - dbcc.ExecuteNonQuery(); - - mediasHaveSha1 = true; - } - - if(media.SHA256 != null) - { - dbcc = dbConnection.CreateCommand(); - - dbcc.CommandText = - $"INSERT INTO \"{tmpMediaSha256Table}\" (\"Sha256\") VALUES (\"{media.SHA256}\");"; - - dbcc.ExecuteNonQuery(); - - mediasHaveSha256 = true; - } - - medias.Add(media); - - continue; - } - } - - position++; - } - - SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - - dbTransaction.Commit(); + file.Crc32 = rom.CRC; + file.UpdatedOn = DateTime.UtcNow; } - List pendingFilesByCrcList = romsHaveCrc ? ctx.Files. - FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomCrc32Table}] AS t WHERE f.Crc32 = t.Crc32 AND f.Size = t.Size"). - ToList() : new List(); - - List pendingFilesByMd5List = romsHaveMd5 ? ctx.Files. - FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomMd5Table}] AS t WHERE f.Md5 = t.Md5 AND f.Size = t.Size"). - ToList() : new List(); - - List pendingFilesBySha1List = romsHaveSha1 ? ctx.Files. - FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha1Table}] AS t WHERE f.Sha1 = t.Sha1 AND f.Size = t.Size"). - ToList() : new List(); - - List pendingFilesBySha256List = romsHaveSha256 ? ctx.Files. - FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha256Table}] AS t WHERE f.Sha256 = t.Sha256 AND f.Size = t.Size"). - ToList() : new List(); - - List pendingFilesBySha384List = romsHaveSha384 ? ctx.Files. - FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha384Table}] AS t WHERE f.Sha384 = t.Sha384 AND f.Size = t.Size"). - ToList() : new List(); - - List pendingFilesBySha512List = romsHaveSha512 ? ctx.Files. - FromSqlRaw($"SELECT DISTINCT f.* FROM Files AS f, [{tmpRomSha512Table}] AS t WHERE f.Sha512 = t.Sha512 AND f.Size = t.Size"). - ToList() : new List(); - - Dictionary pendingDisksByMd5 = disksHaveMd5 ? ctx.Disks. - FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskMd5Table}] AS t WHERE f.Md5 = t.Md5"). - ToDictionary(f => f.Md5) - : new Dictionary(); - - Dictionary pendingDisksBySha1 = disksHaveSha1 ? ctx.Disks. - FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskSha1Table}] AS t WHERE f.Sha1 = t.Sha1"). - ToDictionary(f => f.Sha1) - : new Dictionary(); - - Dictionary pendingMediasByMd5 = mediasHaveMd5 ? ctx.Medias. - FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaMd5Table}] AS t WHERE f.Md5 = t.Md5"). - ToDictionary(f => f.Md5) - : new Dictionary(); - - Dictionary pendingMediasBySha1 = mediasHaveSha1 ? ctx.Medias. - FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha1Table}] AS t WHERE f.Sha1 = t.Sha1"). - ToDictionary(f => f.Sha1) - : new Dictionary(); - - Dictionary pendingMediasBySha256 = mediasHaveSha256 ? ctx.Medias. - FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha256Table}] AS t WHERE f.Sha256 = t.Sha256"). - ToDictionary(f => f.Sha256) - : new Dictionary(); - - Dictionary pendingFilesByCrc = new Dictionary(); - Dictionary pendingFilesByMd5 = new Dictionary(); - Dictionary pendingFilesBySha1 = new Dictionary(); - Dictionary pendingFilesBySha256 = new Dictionary(); - Dictionary pendingFilesBySha384 = new Dictionary(); - Dictionary pendingFilesBySha512 = new Dictionary(); - List pendingFiles = new List(); - - // This is because of hash collisions. - foreach(DbFile item in pendingFilesByCrcList) - if(pendingFilesByCrc.ContainsKey(item.Crc32)) - pendingFiles.Add(item); - else - pendingFilesByCrc[item.Crc32] = item; - - foreach(DbFile item in pendingFilesByMd5List) - if(pendingFilesByMd5.ContainsKey(item.Md5)) - pendingFiles.Add(item); - else - pendingFilesByMd5[item.Md5] = item; - - foreach(DbFile item in pendingFilesBySha1List) - if(pendingFilesBySha1.ContainsKey(item.Sha1)) - pendingFiles.Add(item); - else - pendingFilesBySha1[item.Sha1] = item; - - foreach(DbFile item in pendingFilesBySha256List) - if(pendingFilesBySha256.ContainsKey(item.Sha256)) - pendingFiles.Add(item); - else - pendingFilesBySha256[item.Sha256] = item; - - foreach(DbFile item in pendingFilesBySha384List) - if(pendingFilesBySha384.ContainsKey(item.Sha384)) - pendingFiles.Add(item); - else - pendingFilesBySha384[item.Sha384] = item; - - foreach(DbFile item in pendingFilesBySha512List) - if(pendingFilesBySha512.ContainsKey(item.Sha512)) - pendingFiles.Add(item); - else - pendingFilesBySha512[item.Sha512] = item; - - // Clear some memory - pendingFilesByCrcList.Clear(); - pendingFilesByMd5List.Clear(); - pendingFilesBySha1List.Clear(); - pendingFilesBySha256List.Clear(); - pendingFilesBySha384List.Clear(); - pendingFilesBySha512List.Clear(); - - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomCrc32Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomMd5Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha1Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha256Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha384Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpRomSha512Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpDiskMd5Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpDiskSha1Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpMediaMd5Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpMediaSha1Table}]"); - ctx.Database.ExecuteSqlRaw($"DROP TABLE [{tmpMediaSha256Table}]"); - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs + if(string.IsNullOrEmpty(file.Md5) && !string.IsNullOrEmpty(rom.MD5)) { - Minimum = 0, - Maximum = roms.Count - }); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.AddingRoms - }); - - position = 0; - - List newFiles = new List(); - List newFilesByMachine = new List(); - - foreach(Rom rom in roms) - { - bool hashCollision = false; - - SetProgress?.Invoke(this, new ProgressEventArgs - { - Value = position - }); - - if(!machines.TryGetValue(rom.Machine.Name, out Machine machine)) - { - ErrorOccurred?.Invoke(this, new ErrorEventArgs - { - Message = Localization.FoundRomWithoutMachine - }); - - return; - } - - ulong uSize = (ulong)rom.Size; - - DbFile file = null; - - if(rom.SHA512 != null) - if(pendingFilesBySha512.TryGetValue(rom.SHA512, out file)) - if(file.Size != uSize) - { - hashCollision = true; - file = null; - } - - if(rom.SHA384 != null && - file == null) - if(pendingFilesBySha384.TryGetValue(rom.SHA384, out file)) - if(file.Size != uSize) - { - hashCollision = true; - file = null; - } - - if(rom.SHA256 != null && - file == null) - if(pendingFilesBySha256.TryGetValue(rom.SHA256, out file)) - if(file.Size != uSize) - { - hashCollision = true; - file = null; - } - - if(rom.SHA1 != null && - file == null) - if(pendingFilesBySha1.TryGetValue(rom.SHA1, out file)) - if(file.Size != uSize) - { - hashCollision = true; - file = null; - } - - if(rom.MD5 != null && - file == null) - if(pendingFilesByMd5.TryGetValue(rom.MD5, out file)) - if(file.Size != uSize) - { - hashCollision = true; - file = null; - } - - if(rom.CRC != null && - file == null) - if(pendingFilesByCrc.TryGetValue(rom.CRC, out file)) - if(file.Size != uSize) - { - hashCollision = true; - file = null; - } - - if(file == null && hashCollision) - { - if(rom.SHA512 != null) - file = pendingFiles.FirstOrDefault(f => f.Sha512 == rom.SHA512 && f.Size == uSize); - - if(file == null && - rom.SHA384 != null) - file = pendingFiles.FirstOrDefault(f => f.Sha384 == rom.SHA384 && f.Size == uSize); - - if(file == null && - rom.SHA256 != null) - file = pendingFiles.FirstOrDefault(f => f.Sha256 == rom.SHA256 && f.Size == uSize); - - if(file == null && - rom.SHA1 != null) - file = pendingFiles.FirstOrDefault(f => f.Sha1 == rom.SHA1 && f.Size == uSize); - - if(file == null && - rom.MD5 != null) - file = pendingFiles.FirstOrDefault(f => f.Md5 == rom.MD5 && f.Size == uSize); - - if(file == null && - rom.CRC != null) - file = pendingFiles.FirstOrDefault(f => f.Crc32 == rom.CRC && f.Size == uSize); - } - - if(file == null) - { - file = new DbFile - { - Crc32 = rom.CRC, - CreatedOn = DateTime.UtcNow, - Md5 = rom.MD5, - Sha1 = rom.SHA1, - Sha256 = rom.SHA256, - Sha384 = rom.SHA384, - Sha512 = rom.SHA512, - Size = uSize, - UpdatedOn = DateTime.UtcNow - }; - - newFiles.Add(file); - } - - if(string.IsNullOrEmpty(file.Crc32) && - !string.IsNullOrEmpty(rom.CRC)) - { - file.Crc32 = rom.CRC; - file.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(file.Md5) && - !string.IsNullOrEmpty(rom.MD5)) - { - file.Md5 = rom.MD5; - file.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(file.Sha1) && - !string.IsNullOrEmpty(rom.SHA1)) - { - file.Sha1 = rom.SHA1; - file.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(file.Sha256) && - !string.IsNullOrEmpty(rom.SHA256)) - { - file.Sha256 = rom.SHA256; - file.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(file.Sha384) && - !string.IsNullOrEmpty(rom.SHA384)) - { - file.Sha384 = rom.SHA384; - file.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(file.Sha512) && - !string.IsNullOrEmpty(rom.SHA512)) - { - file.Sha512 = rom.SHA512; - file.UpdatedOn = DateTime.UtcNow; - } - - DateTime? fileModificationDate = null; - - if(!string.IsNullOrEmpty(rom.Date)) - { - rom.Date = rom.Date.Replace("/", "\\"); - - if(DateTime.TryParseExact(rom.Date, @"yyyy\\M\\d H:mm", CultureInfo.InvariantCulture, - DateTimeStyles.AssumeUniversal, out DateTime date)) - fileModificationDate = date; - } - - string filename; - string path = null; - - if(rom.Name.Contains('\\')) - { - filename = Path.GetFileName(rom.Name.Replace('\\', '/')); - path = Path.GetDirectoryName(rom.Name.Replace('\\', '/')); - } - else if(rom.Name.Contains('/')) - { - filename = Path.GetFileName(rom.Name); - path = Path.GetDirectoryName(rom.Name); - } - else - filename = rom.Name; - - newFilesByMachine.Add(new FileByMachine - { - File = file, - Machine = machine, - Name = filename, - FileLastModification = fileModificationDate, - Path = path - }); - - if(hashCollision) - pendingFiles.Add(file); - else if(file.Sha512 != null) - pendingFilesBySha512[file.Sha512] = file; - else if(file.Sha384 != null) - pendingFilesBySha384[file.Sha384] = file; - else if(file.Sha256 != null) - pendingFilesBySha256[file.Sha256] = file; - else if(file.Sha1 != null) - pendingFilesBySha1[file.Sha1] = file; - else if(file.Md5 != null) - pendingFilesByMd5[file.Md5] = file; - else if(file.Crc32 != null) - pendingFilesByCrc[file.Crc32] = file; - - position++; + file.Md5 = rom.MD5; + file.UpdatedOn = DateTime.UtcNow; } - SetMessage?.Invoke(this, new MessageEventArgs + if(string.IsNullOrEmpty(file.Sha1) && !string.IsNullOrEmpty(rom.SHA1)) { - Message = Localization.SavingChangesToDatabase - }); - - SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - - ctx.BulkInsert(newFiles, b => b.SetOutputIdentity = true); - - foreach(FileByMachine fbm in newFilesByMachine) - { - fbm.FileId = fbm.File.Id; - fbm.MachineId = fbm.Machine.Id; + file.Sha1 = rom.SHA1; + file.UpdatedOn = DateTime.UtcNow; } - ctx.BulkInsert(newFilesByMachine); - - ctx.SaveChanges(); - - pendingFilesBySha512.Clear(); - pendingFilesBySha384.Clear(); - pendingFilesBySha256.Clear(); - pendingFilesBySha1.Clear(); - pendingFilesByMd5.Clear(); - pendingFilesByCrc.Clear(); - pendingFiles.Clear(); - newFiles.Clear(); - newFilesByMachine.Clear(); - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs + if(string.IsNullOrEmpty(file.Sha256) && !string.IsNullOrEmpty(rom.SHA256)) { - Minimum = 0, - Maximum = disks.Count - }); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.AddingDisks - }); - - position = 0; - - List newDisks = new List(); - List newDisksByMachine = new List(); - - foreach(Disk disk in disks) - { - SetProgress?.Invoke(this, new ProgressEventArgs - { - Value = position - }); - - if(!machines.TryGetValue(disk.Machine.Name, out Machine machine)) - { - ErrorOccurred?.Invoke(this, new ErrorEventArgs - { - Message = Localization.FoundDiskWithoutMachine - }); - - return; - } - - if(disk.MD5 == null && - disk.SHA1 == null) - { - position++; - - continue; - } - - DbDisk dbDisk = null; - - if(disk.SHA1 != null && - dbDisk == null) - pendingDisksBySha1.TryGetValue(disk.SHA1, out dbDisk); - - if(disk.MD5 != null && - dbDisk == null) - pendingDisksByMd5.TryGetValue(disk.MD5, out dbDisk); - - if(dbDisk == null) - { - dbDisk = new DbDisk - { - CreatedOn = DateTime.UtcNow, - Md5 = disk.MD5, - Sha1 = disk.SHA1, - UpdatedOn = DateTime.UtcNow - }; - - newDisks.Add(dbDisk); - } - - if(string.IsNullOrEmpty(dbDisk.Md5) && - !string.IsNullOrEmpty(disk.MD5)) - { - dbDisk.Md5 = disk.MD5; - dbDisk.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(dbDisk.Sha1) && - !string.IsNullOrEmpty(disk.SHA1)) - { - dbDisk.Sha1 = disk.SHA1; - dbDisk.UpdatedOn = DateTime.UtcNow; - } - - newDisksByMachine.Add(new DiskByMachine - { - Disk = dbDisk, - Machine = machine, - Name = disk.Name - }); - - if(dbDisk.Sha1 != null) - pendingDisksBySha1[dbDisk.Sha1] = dbDisk; - - if(dbDisk.Md5 != null) - pendingDisksByMd5[dbDisk.Md5] = dbDisk; - - position++; + file.Sha256 = rom.SHA256; + file.UpdatedOn = DateTime.UtcNow; } - SetMessage?.Invoke(this, new MessageEventArgs + if(string.IsNullOrEmpty(file.Sha384) && !string.IsNullOrEmpty(rom.SHA384)) { - Message = Localization.SavingChangesToDatabase - }); - - SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - - ctx.BulkInsert(newDisks, b => b.SetOutputIdentity = true); - - foreach(DiskByMachine dbm in newDisksByMachine) - { - dbm.DiskId = dbm.Disk.Id; - dbm.MachineId = dbm.Machine.Id; + file.Sha384 = rom.SHA384; + file.UpdatedOn = DateTime.UtcNow; } - ctx.BulkInsert(newDisksByMachine); - - ctx.SaveChanges(); - - pendingDisksBySha1.Clear(); - pendingDisksByMd5.Clear(); - newDisks.Clear(); - newDisksByMachine.Clear(); - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs + if(string.IsNullOrEmpty(file.Sha512) && !string.IsNullOrEmpty(rom.SHA512)) { - Minimum = 0, - Maximum = medias.Count - }); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.AddingMedias - }); - - position = 0; - - List newMedias = new List(); - List newMediasByMachine = new List(); - - foreach(Media media in medias) - { - SetProgress?.Invoke(this, new ProgressEventArgs - { - Value = position - }); - - if(!machines.TryGetValue(media.Machine.Name, out Machine machine)) - { - ErrorOccurred?.Invoke(this, new ErrorEventArgs - { - Message = Localization.FoundMediaWithoutMachine - }); - - return; - } - - if(media.MD5 == null && - media.SHA1 == null && - media.SHA256 == null) - { - position++; - - continue; - } - - DbMedia dbMedia = null; - - if(media.SHA256 != null && - dbMedia == null) - pendingMediasBySha256.TryGetValue(media.SHA256, out dbMedia); - - if(media.SHA1 != null && - dbMedia == null) - pendingMediasBySha1.TryGetValue(media.SHA1, out dbMedia); - - if(media.MD5 != null && - dbMedia == null) - pendingMediasByMd5.TryGetValue(media.MD5, out dbMedia); - - // TODO: SpamSum - if(dbMedia == null) - { - dbMedia = new DbMedia - { - CreatedOn = DateTime.UtcNow, - Md5 = media.MD5, - Sha1 = media.SHA1, - Sha256 = media.SHA256, - UpdatedOn = DateTime.UtcNow - }; - - newMedias.Add(dbMedia); - } - - if(string.IsNullOrEmpty(dbMedia.Md5) && - !string.IsNullOrEmpty(media.MD5)) - { - dbMedia.Md5 = media.MD5; - dbMedia.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(dbMedia.Sha1) && - !string.IsNullOrEmpty(media.SHA1)) - { - dbMedia.Sha1 = media.SHA1; - dbMedia.UpdatedOn = DateTime.UtcNow; - } - - if(string.IsNullOrEmpty(dbMedia.Sha256) && - !string.IsNullOrEmpty(media.SHA256)) - { - dbMedia.Sha256 = media.SHA256; - dbMedia.UpdatedOn = DateTime.UtcNow; - } - - newMediasByMachine.Add(new MediaByMachine - { - Media = dbMedia, - Machine = machine, - Name = media.Name - }); - - if(dbMedia.Sha256 != null) - pendingMediasBySha256[dbMedia.Sha256] = dbMedia; - - if(dbMedia.Sha1 != null) - pendingMediasBySha1[dbMedia.Sha1] = dbMedia; - - if(dbMedia.Md5 != null) - pendingMediasByMd5[dbMedia.Md5] = dbMedia; - - position++; + file.Sha512 = rom.SHA512; + file.UpdatedOn = DateTime.UtcNow; } - SetMessage?.Invoke(this, new MessageEventArgs + DateTime? fileModificationDate = null; + + if(!string.IsNullOrEmpty(rom.Date)) { - Message = Localization.SavingChangesToDatabase - }); + rom.Date = rom.Date.Replace("/", "\\"); - SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - - ctx.BulkInsert(newMedias, b => b.SetOutputIdentity = true); - - foreach(MediaByMachine mbm in newMediasByMachine) - { - mbm.MediaId = mbm.Media.Id; - mbm.MachineId = mbm.Machine.Id; + if(DateTime.TryParseExact(rom.Date, + @"yyyy\\M\\d H:mm", + CultureInfo.InvariantCulture, + DateTimeStyles.AssumeUniversal, + out DateTime date)) + fileModificationDate = date; } - ctx.BulkInsert(newMediasByMachine); + string filename; + string path = null; - ctx.SaveChanges(); - - pendingMediasBySha256.Clear(); - pendingMediasBySha1.Clear(); - pendingMediasByMd5.Clear(); - newMedias.Clear(); - newMediasByMachine.Clear(); - - RomSetStat stats = ctx.RomSets.Where(r => r.Id == romSet.Id).Select(r => new RomSetStat + if(rom.Name.Contains('\\')) { - RomSetId = r.Id, - TotalMachines = r.Machines.Count, - CompleteMachines = - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count == 0 && - m.Files.All(f => f.File.IsInRepo)) + - r.Machines.Count(m => m.Disks.Count > 0 && m.Files.Count == 0 && - m.Disks.All(f => f.Disk.IsInRepo)) + - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count > 0 && - m.Files.All(f => f.File.IsInRepo) && m.Disks.All(f => f.Disk.IsInRepo)), - IncompleteMachines = - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count == 0 && - m.Files.Any(f => !f.File.IsInRepo)) + - r.Machines.Count(m => m.Disks.Count > 0 && m.Files.Count == 0 && - m.Disks.Any(f => !f.Disk.IsInRepo)) + - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count > 0 && - (m.Files.Any(f => !f.File.IsInRepo) || - m.Disks.Any(f => !f.Disk.IsInRepo))), - TotalRoms = r.Machines.Sum(m => m.Files.Count) + r.Machines.Sum(m => m.Disks.Count) + - r.Machines.Sum(m => m.Medias.Count), - HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) + - r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) + - r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)), - MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) + - r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) + - r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo)) - }).FirstOrDefault(); - - RomSetStat oldStats = ctx.RomSetStats.Find(stats.RomSetId); - - if(oldStats != null) - ctx.Remove(oldStats); - - ctx.RomSetStats.Add(stats); - - ctx.SaveChanges(); - - WorkFinished?.Invoke(this, new MessageEventArgs + filename = Path.GetFileName(rom.Name.Replace('\\', '/')); + path = Path.GetDirectoryName(rom.Name.Replace('\\', '/')); + } + else if(rom.Name.Contains('/')) { - Message = string.Format(Localization.DatImportSuccess, stats.TotalMachines, stats.TotalRoms) + filename = Path.GetFileName(rom.Name); + path = Path.GetDirectoryName(rom.Name); + } + else + filename = rom.Name; + + newFilesByMachine.Add(new FileByMachine + { + File = file, + Machine = machine, + Name = filename, + FileLastModification = fileModificationDate, + Path = path }); - RomSetAdded?.Invoke(this, new RomSetEventArgs - { - RomSet = new RomSetModel - { - Id = romSet.Id, - Author = romSet.Author, - Comment = romSet.Comment, - Date = romSet.Date, - Description = romSet.Description, - Filename = romSet.Filename, - Homepage = romSet.Homepage, - Name = romSet.Name, - Sha384 = romSet.Sha384, - Version = romSet.Version, - TotalMachines = stats.TotalMachines, - CompleteMachines = stats.CompleteMachines, - IncompleteMachines = stats.IncompleteMachines, - TotalRoms = stats.TotalRoms, - HaveRoms = stats.HaveRoms, - MissRoms = stats.MissRoms, - Category = romSet.Category - } - }); + if(hashCollision) + pendingFiles.Add(file); + else if(file.Sha512 != null) + pendingFilesBySha512[file.Sha512] = file; + else if(file.Sha384 != null) + pendingFilesBySha384[file.Sha384] = file; + else if(file.Sha256 != null) + pendingFilesBySha256[file.Sha256] = file; + else if(file.Sha1 != null) + pendingFilesBySha1[file.Sha1] = file; + else if(file.Md5 != null) + pendingFilesByMd5[file.Md5] = file; + else if(file.Crc32 != null) pendingFilesByCrc[file.Crc32] = file; + + position++; } - catch(Exception e) + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.SavingChangesToDatabase + }); + + SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); + + ctx.BulkInsert(newFiles, b => b.SetOutputIdentity = true); + + foreach(FileByMachine fbm in newFilesByMachine) { - if(Debugger.IsAttached) - throw; - - ErrorOccurred?.Invoke(this, new ErrorEventArgs - { - Message = Localization.UnhandledException - }); + fbm.FileId = fbm.File.Id; + fbm.MachineId = fbm.Machine.Id; } + + ctx.BulkInsert(newFilesByMachine); + + ctx.SaveChanges(); + + pendingFilesBySha512.Clear(); + pendingFilesBySha384.Clear(); + pendingFilesBySha256.Clear(); + pendingFilesBySha1.Clear(); + pendingFilesByMd5.Clear(); + pendingFilesByCrc.Clear(); + pendingFiles.Clear(); + newFiles.Clear(); + newFilesByMachine.Clear(); + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = disks.Count + }); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.AddingDisks + }); + + position = 0; + + var newDisks = new List(); + var newDisksByMachine = new List(); + + foreach(Disk disk in disks) + { + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = position + }); + + if(!machines.TryGetValue(disk.Machine.Name, out Machine machine)) + { + ErrorOccurred?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.FoundDiskWithoutMachine + }); + + return; + } + + if(disk.MD5 == null && disk.SHA1 == null) + { + position++; + + continue; + } + + DbDisk dbDisk = null; + + if(disk.SHA1 != null && dbDisk == null) pendingDisksBySha1.TryGetValue(disk.SHA1, out dbDisk); + + if(disk.MD5 != null && dbDisk == null) pendingDisksByMd5.TryGetValue(disk.MD5, out dbDisk); + + if(dbDisk == null) + { + dbDisk = new DbDisk + { + CreatedOn = DateTime.UtcNow, + Md5 = disk.MD5, + Sha1 = disk.SHA1, + UpdatedOn = DateTime.UtcNow + }; + + newDisks.Add(dbDisk); + } + + if(string.IsNullOrEmpty(dbDisk.Md5) && !string.IsNullOrEmpty(disk.MD5)) + { + dbDisk.Md5 = disk.MD5; + dbDisk.UpdatedOn = DateTime.UtcNow; + } + + if(string.IsNullOrEmpty(dbDisk.Sha1) && !string.IsNullOrEmpty(disk.SHA1)) + { + dbDisk.Sha1 = disk.SHA1; + dbDisk.UpdatedOn = DateTime.UtcNow; + } + + newDisksByMachine.Add(new DiskByMachine + { + Disk = dbDisk, + Machine = machine, + Name = disk.Name + }); + + if(dbDisk.Sha1 != null) pendingDisksBySha1[dbDisk.Sha1] = dbDisk; + + if(dbDisk.Md5 != null) pendingDisksByMd5[dbDisk.Md5] = dbDisk; + + position++; + } + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.SavingChangesToDatabase + }); + + SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); + + ctx.BulkInsert(newDisks, b => b.SetOutputIdentity = true); + + foreach(DiskByMachine dbm in newDisksByMachine) + { + dbm.DiskId = dbm.Disk.Id; + dbm.MachineId = dbm.Machine.Id; + } + + ctx.BulkInsert(newDisksByMachine); + + ctx.SaveChanges(); + + pendingDisksBySha1.Clear(); + pendingDisksByMd5.Clear(); + newDisks.Clear(); + newDisksByMachine.Clear(); + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = medias.Count + }); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.AddingMedias + }); + + position = 0; + + var newMedias = new List(); + var newMediasByMachine = new List(); + + foreach(Media media in medias) + { + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = position + }); + + if(!machines.TryGetValue(media.Machine.Name, out Machine machine)) + { + ErrorOccurred?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.FoundMediaWithoutMachine + }); + + return; + } + + if(media.MD5 == null && media.SHA1 == null && media.SHA256 == null) + { + position++; + + continue; + } + + DbMedia dbMedia = null; + + if(media.SHA256 != null && dbMedia == null) + pendingMediasBySha256.TryGetValue(media.SHA256, out dbMedia); + + if(media.SHA1 != null && dbMedia == null) pendingMediasBySha1.TryGetValue(media.SHA1, out dbMedia); + + if(media.MD5 != null && dbMedia == null) pendingMediasByMd5.TryGetValue(media.MD5, out dbMedia); + + // TODO: SpamSum + if(dbMedia == null) + { + dbMedia = new DbMedia + { + CreatedOn = DateTime.UtcNow, + Md5 = media.MD5, + Sha1 = media.SHA1, + Sha256 = media.SHA256, + UpdatedOn = DateTime.UtcNow + }; + + newMedias.Add(dbMedia); + } + + if(string.IsNullOrEmpty(dbMedia.Md5) && !string.IsNullOrEmpty(media.MD5)) + { + dbMedia.Md5 = media.MD5; + dbMedia.UpdatedOn = DateTime.UtcNow; + } + + if(string.IsNullOrEmpty(dbMedia.Sha1) && !string.IsNullOrEmpty(media.SHA1)) + { + dbMedia.Sha1 = media.SHA1; + dbMedia.UpdatedOn = DateTime.UtcNow; + } + + if(string.IsNullOrEmpty(dbMedia.Sha256) && !string.IsNullOrEmpty(media.SHA256)) + { + dbMedia.Sha256 = media.SHA256; + dbMedia.UpdatedOn = DateTime.UtcNow; + } + + newMediasByMachine.Add(new MediaByMachine + { + Media = dbMedia, + Machine = machine, + Name = media.Name + }); + + if(dbMedia.Sha256 != null) pendingMediasBySha256[dbMedia.Sha256] = dbMedia; + + if(dbMedia.Sha1 != null) pendingMediasBySha1[dbMedia.Sha1] = dbMedia; + + if(dbMedia.Md5 != null) pendingMediasByMd5[dbMedia.Md5] = dbMedia; + + position++; + } + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.SavingChangesToDatabase + }); + + SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); + + ctx.BulkInsert(newMedias, b => b.SetOutputIdentity = true); + + foreach(MediaByMachine mbm in newMediasByMachine) + { + mbm.MediaId = mbm.Media.Id; + mbm.MachineId = mbm.Machine.Id; + } + + ctx.BulkInsert(newMediasByMachine); + + ctx.SaveChanges(); + + pendingMediasBySha256.Clear(); + pendingMediasBySha1.Clear(); + pendingMediasByMd5.Clear(); + newMedias.Clear(); + newMediasByMachine.Clear(); + + RomSetStat stats = ctx.RomSets.Where(r => r.Id == romSet.Id) + .Select(r => new RomSetStat + { + RomSetId = r.Id, + TotalMachines = r.Machines.Count, + CompleteMachines = + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count == 0 && + m.Files.All(f => f.File.IsInRepo)) + + r.Machines.Count(m => m.Disks.Count > 0 && + m.Files.Count == 0 && + m.Disks.All(f => f.Disk.IsInRepo)) + + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count > 0 && + m.Files.All(f => f.File.IsInRepo) && + m.Disks.All(f => f.Disk.IsInRepo)), + IncompleteMachines = + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count == 0 && + m.Files.Any(f => !f.File.IsInRepo)) + + r.Machines.Count(m => m.Disks.Count > 0 && + m.Files.Count == 0 && + m.Disks.Any(f => !f.Disk.IsInRepo)) + + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count > 0 && + (m.Files.Any(f => !f.File.IsInRepo) || + m.Disks.Any(f => !f.Disk.IsInRepo))), + TotalRoms = + r.Machines.Sum(m => m.Files.Count) + + r.Machines.Sum(m => m.Disks.Count) + + r.Machines.Sum(m => m.Medias.Count), + HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) + + r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) + + r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)), + MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) + + r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) + + r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo)) + }) + .FirstOrDefault(); + + RomSetStat oldStats = ctx.RomSetStats.Find(stats.RomSetId); + + if(oldStats != null) ctx.Remove(oldStats); + + ctx.RomSetStats.Add(stats); + + ctx.SaveChanges(); + + WorkFinished?.Invoke(this, + new MessageEventArgs + { + Message = string.Format(Localization.DatImportSuccess, + stats.TotalMachines, + stats.TotalRoms) + }); + + RomSetAdded?.Invoke(this, + new RomSetEventArgs + { + RomSet = new RomSetModel + { + Id = romSet.Id, + Author = romSet.Author, + Comment = romSet.Comment, + Date = romSet.Date, + Description = romSet.Description, + Filename = romSet.Filename, + Homepage = romSet.Homepage, + Name = romSet.Name, + Sha384 = romSet.Sha384, + Version = romSet.Version, + TotalMachines = stats.TotalMachines, + CompleteMachines = stats.CompleteMachines, + IncompleteMachines = stats.IncompleteMachines, + TotalRoms = stats.TotalRoms, + HaveRoms = stats.HaveRoms, + MissRoms = stats.MissRoms, + Category = romSet.Category + } + }); } + catch(Exception e) + { + if(Debugger.IsAttached) throw; - // TODO: Cancel and get back - public void Abort() => _aborted = true; - - public event EventHandler SetIndeterminateProgress; - public event EventHandler WorkFinished; - public event EventHandler ErrorOccurred; - public event EventHandler SetProgressBounds; - public event EventHandler SetProgress; - public event EventHandler SetMessage; - public event EventHandler RomSetAdded; + ErrorOccurred?.Invoke(this, + new ErrorEventArgs + { + Message = Localization.UnhandledException + }); + } } + + // TODO: Cancel and get back + public void Abort() => _aborted = true; + + public event EventHandler SetIndeterminateProgress; + public event EventHandler WorkFinished; + public event EventHandler ErrorOccurred; + public event EventHandler SetProgressBounds; + public event EventHandler SetProgress; + public event EventHandler SetMessage; + public event EventHandler RomSetAdded; } \ No newline at end of file diff --git a/RomRepoMgr.Core/Workers/FileExporter.cs b/RomRepoMgr.Core/Workers/FileExporter.cs index 31852fe..6733f79 100644 --- a/RomRepoMgr.Core/Workers/FileExporter.cs +++ b/RomRepoMgr.Core/Workers/FileExporter.cs @@ -13,637 +13,653 @@ using RomRepoMgr.Database.Models; using SharpCompress.Compressors.LZMA; using CompressionMode = SharpCompress.Compressors.CompressionMode; -namespace RomRepoMgr.Core.Workers +namespace RomRepoMgr.Core.Workers; + +public class FileExporter { - public class FileExporter + const long BUFFER_SIZE = 131072; + readonly string _outPath; + readonly long _romSetId; + long _filePosition; + Dictionary _filesByMachine; + long _machinePosition; + Machine[] _machines; + string _zipCurrentEntryName; + + public FileExporter(long romSetId, string outPath) { - const long BUFFER_SIZE = 131072; - readonly string _outPath; - readonly long _romSetId; - long _filePosition; - Dictionary _filesByMachine; - long _machinePosition; - Machine[] _machines; - string _zipCurrentEntryName; + _romSetId = romSetId; + _outPath = outPath; + } - public FileExporter(long romSetId, string outPath) + public event EventHandler WorkFinished; + public event EventHandler SetProgressBounds; + public event EventHandler SetProgress; + public event EventHandler SetMessage; + public event EventHandler SetProgress2Bounds; + public event EventHandler SetProgress2; + public event EventHandler SetMessage2; + public event EventHandler SetProgress3Bounds; + public event EventHandler SetProgress3; + public event EventHandler SetMessage3; + + public void Export() + { + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.RetrievingRomSetFromDatabase + }); + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + RomSet romSet = ctx.RomSets.Find(_romSetId); + + if(romSet == null) { - _romSetId = romSetId; - _outPath = outPath; + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.CouldNotFindRomSetInDatabase + }); + + WorkFinished?.Invoke(this, System.EventArgs.Empty); + + return; } - public event EventHandler WorkFinished; - public event EventHandler SetProgressBounds; - public event EventHandler SetProgress; - public event EventHandler SetMessage; - public event EventHandler SetProgress2Bounds; - public event EventHandler SetProgress2; - public event EventHandler SetMessage2; - public event EventHandler SetProgress3Bounds; - public event EventHandler SetProgress3; - public event EventHandler SetMessage3; + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.ExportingRoms + }); - public void Export() + _machines = ctx.Machines.Where(m => m.RomSet.Id == _romSetId).ToArray(); + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = _machines.Length + }); + + _machinePosition = 0; + CompressNextMachine(); + } + + void CompressNextMachine() + { + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = _machinePosition + }); + + if(_machinePosition >= _machines.Length) { - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.RetrievingRomSetFromDatabase - }); + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.Finished + }); - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + WorkFinished?.Invoke(this, System.EventArgs.Empty); - RomSet romSet = ctx.RomSets.Find(_romSetId); - - if(romSet == null) - { - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.CouldNotFindRomSetInDatabase - }); - - WorkFinished?.Invoke(this, System.EventArgs.Empty); - - return; - } - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.ExportingRoms - }); - - _machines = ctx.Machines.Where(m => m.RomSet.Id == _romSetId).ToArray(); - - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = _machines.Length - }); - - _machinePosition = 0; - CompressNextMachine(); + return; } - void CompressNextMachine() + Machine machine = _machines[_machinePosition]; + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = machine.Name + }); + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + string machineName = machine.Name; + + var mediasByMachine = ctx.MediasByMachines.Where(f => f.Machine.Id == machine.Id && f.Media.IsInRepo) + .ToDictionary(f => f.Name); + + if(mediasByMachine.Count > 0) { - SetProgress?.Invoke(this, new ProgressEventArgs - { - Value = _machinePosition - }); + SetProgress2Bounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = mediasByMachine.Count + }); - if(_machinePosition >= _machines.Length) + if(machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) + machineName = machineName.Substring(0, machineName.Length - 4); + + string machinePath = Path.Combine(_outPath, machineName); + + if(!Directory.Exists(machinePath)) Directory.CreateDirectory(machinePath); + + long mediaPosition = 0; + + foreach(KeyValuePair mediaByMachine in mediasByMachine) { - SetMessage?.Invoke(this, new MessageEventArgs + string outputPath = Path.Combine(machinePath, mediaByMachine.Key); + + if(!outputPath.EndsWith(".aif", StringComparison.InvariantCultureIgnoreCase)) outputPath += ".aif"; + + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = mediaPosition + }); + + string repoPath = null; + string md5Path = null; + string sha1Path = null; + string sha256Path = null; + + DbMedia media = mediaByMachine.Value.Media; + + if(media.Sha256 != null) { - Message = Localization.Finished - }); + var sha256Bytes = new byte[32]; + string sha256 = media.Sha256; - WorkFinished?.Invoke(this, System.EventArgs.Empty); + for(var i = 0; i < 32; i++) + { + if(sha256[i * 2] >= 0x30 && sha256[i * 2] <= 0x39) + sha256Bytes[i] = (byte)((sha256[i * 2] - 0x30) * 0x10); + else if(sha256[i * 2] >= 0x41 && sha256[i * 2] <= 0x46) + sha256Bytes[i] = (byte)((sha256[i * 2] - 0x37) * 0x10); + else if(sha256[i * 2] >= 0x61 && sha256[i * 2] <= 0x66) + sha256Bytes[i] = (byte)((sha256[i * 2] - 0x57) * 0x10); - return; - } + if(sha256[i * 2 + 1] >= 0x30 && sha256[i * 2 + 1] <= 0x39) + sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x30); + else if(sha256[i * 2 + 1] >= 0x41 && sha256[i * 2 + 1] <= 0x46) + sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x37); + else if(sha256[i * 2 + 1] >= 0x61 && sha256[i * 2 + 1] <= 0x66) + sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x57); + } - Machine machine = _machines[_machinePosition]; + string sha256B32 = Base32.ToBase32String(sha256Bytes); - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = machine.Name - }); + sha256Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "sha256", + sha256B32[0].ToString(), + sha256B32[1].ToString(), + sha256B32[2].ToString(), + sha256B32[3].ToString(), + sha256B32[4].ToString(), + sha256B32 + ".aif"); + } - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - string machineName = machine.Name; - - Dictionary mediasByMachine = ctx.MediasByMachines. - Where(f => f.Machine.Id == machine.Id && - f.Media.IsInRepo). - ToDictionary(f => f.Name); - - if(mediasByMachine.Count > 0) - { - SetProgress2Bounds?.Invoke(this, new ProgressBoundsEventArgs + if(media.Sha1 != null) { - Minimum = 0, - Maximum = mediasByMachine.Count - }); + var sha1Bytes = new byte[20]; + string sha1 = media.Sha1; - if(machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) - machineName = machineName.Substring(0, machineName.Length - 4); + for(var i = 0; i < 20; i++) + { + if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); + else if(sha1[i * 2] >= 0x41 && sha1[i * 2] <= 0x46) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); + else if(sha1[i * 2] >= 0x61 && sha1[i * 2] <= 0x66) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); - string machinePath = Path.Combine(_outPath, machineName); + if(sha1[i * 2 + 1] >= 0x30 && sha1[i * 2 + 1] <= 0x39) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x30); + else if(sha1[i * 2 + 1] >= 0x41 && sha1[i * 2 + 1] <= 0x46) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x37); + else if(sha1[i * 2 + 1] >= 0x61 && sha1[i * 2 + 1] <= 0x66) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x57); + } - if(!Directory.Exists(machinePath)) - Directory.CreateDirectory(machinePath); + string sha1B32 = Base32.ToBase32String(sha1Bytes); - long mediaPosition = 0; + sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "sha1", + sha1B32[0].ToString(), + sha1B32[1].ToString(), + sha1B32[2].ToString(), + sha1B32[3].ToString(), + sha1B32[4].ToString(), + sha1B32 + ".aif"); + } - foreach(KeyValuePair mediaByMachine in mediasByMachine) + if(media.Md5 != null) { - string outputPath = Path.Combine(machinePath, mediaByMachine.Key); + var md5Bytes = new byte[16]; + string md5 = media.Md5; - if(!outputPath.EndsWith(".aif", StringComparison.InvariantCultureIgnoreCase)) - outputPath += ".aif"; - - SetProgress2?.Invoke(this, new ProgressEventArgs + for(var i = 0; i < 16; i++) { - Value = mediaPosition - }); + if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39) + md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); + else if(md5[i * 2] >= 0x41 && md5[i * 2] <= 0x46) + md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); + else if(md5[i * 2] >= 0x61 && md5[i * 2] <= 0x66) + md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); - string repoPath = null; - string md5Path = null; - string sha1Path = null; - string sha256Path = null; - - DbMedia media = mediaByMachine.Value.Media; - - if(media.Sha256 != null) - { - byte[] sha256Bytes = new byte[32]; - string sha256 = media.Sha256; - - for(int i = 0; i < 32; i++) - { - if(sha256[i * 2] >= 0x30 && - sha256[i * 2] <= 0x39) - sha256Bytes[i] = (byte)((sha256[i * 2] - 0x30) * 0x10); - else if(sha256[i * 2] >= 0x41 && - sha256[i * 2] <= 0x46) - sha256Bytes[i] = (byte)((sha256[i * 2] - 0x37) * 0x10); - else if(sha256[i * 2] >= 0x61 && - sha256[i * 2] <= 0x66) - sha256Bytes[i] = (byte)((sha256[i * 2] - 0x57) * 0x10); - - if(sha256[(i * 2) + 1] >= 0x30 && - sha256[(i * 2) + 1] <= 0x39) - sha256Bytes[i] += (byte)(sha256[(i * 2) + 1] - 0x30); - else if(sha256[(i * 2) + 1] >= 0x41 && - sha256[(i * 2) + 1] <= 0x46) - sha256Bytes[i] += (byte)(sha256[(i * 2) + 1] - 0x37); - else if(sha256[(i * 2) + 1] >= 0x61 && - sha256[(i * 2) + 1] <= 0x66) - sha256Bytes[i] += (byte)(sha256[(i * 2) + 1] - 0x57); - } - - string sha256B32 = Base32.ToBase32String(sha256Bytes); - - sha256Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "sha256", - sha256B32[0].ToString(), sha256B32[1].ToString(), - sha256B32[2].ToString(), sha256B32[3].ToString(), - sha256B32[4].ToString(), sha256B32 + ".aif"); + if(md5[i * 2 + 1] >= 0x30 && md5[i * 2 + 1] <= 0x39) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x30); + else if(md5[i * 2 + 1] >= 0x41 && md5[i * 2 + 1] <= 0x46) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x37); + else if(md5[i * 2 + 1] >= 0x61 && md5[i * 2 + 1] <= 0x66) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x57); } - if(media.Sha1 != null) - { - byte[] sha1Bytes = new byte[20]; - string sha1 = media.Sha1; + string md5B32 = Base32.ToBase32String(md5Bytes); - for(int i = 0; i < 20; i++) - { - if(sha1[i * 2] >= 0x30 && - sha1[i * 2] <= 0x39) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); - else if(sha1[i * 2] >= 0x41 && - sha1[i * 2] <= 0x46) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); - else if(sha1[i * 2] >= 0x61 && - sha1[i * 2] <= 0x66) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); + md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "md5", + md5B32[0].ToString(), + md5B32[1].ToString(), + md5B32[2].ToString(), + md5B32[3].ToString(), + md5B32[4].ToString(), + md5B32 + ".aif"); + } - if(sha1[(i * 2) + 1] >= 0x30 && - sha1[(i * 2) + 1] <= 0x39) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x30); - else if(sha1[(i * 2) + 1] >= 0x41 && - sha1[(i * 2) + 1] <= 0x46) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x37); - else if(sha1[(i * 2) + 1] >= 0x61 && - sha1[(i * 2) + 1] <= 0x66) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x57); - } + if(File.Exists(sha256Path)) + repoPath = sha256Path; + else if(File.Exists(sha1Path)) + repoPath = sha1Path; + else if(File.Exists(md5Path)) repoPath = md5Path; - string sha1B32 = Base32.ToBase32String(sha1Bytes); + if(repoPath == null) + { + throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository, + media.Sha256 ?? media.Sha1 ?? media.Md5)); + } - sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "sha1", - sha1B32[0].ToString(), sha1B32[1].ToString(), sha1B32[2].ToString(), - sha1B32[3].ToString(), sha1B32[4].ToString(), sha1B32 + ".aif"); - } + var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read); + var outFs = new FileStream(outputPath, FileMode.Create, FileAccess.Write); - if(media.Md5 != null) - { - byte[] md5Bytes = new byte[16]; - string md5 = media.Md5; + SetMessage3?.Invoke(this, + new MessageEventArgs + { + Message = string.Format(Localization.Copying, Path.GetFileName(outputPath)) + }); - for(int i = 0; i < 16; i++) - { - if(md5[i * 2] >= 0x30 && - md5[i * 2] <= 0x39) - md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); - else if(md5[i * 2] >= 0x41 && - md5[i * 2] <= 0x46) - md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); - else if(md5[i * 2] >= 0x61 && - md5[i * 2] <= 0x66) - md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); + SetProgress3Bounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); - if(md5[(i * 2) + 1] >= 0x30 && - md5[(i * 2) + 1] <= 0x39) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x30); - else if(md5[(i * 2) + 1] >= 0x41 && - md5[(i * 2) + 1] <= 0x46) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x37); - else if(md5[(i * 2) + 1] >= 0x61 && - md5[(i * 2) + 1] <= 0x66) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x57); - } + var buffer = new byte[BUFFER_SIZE]; - string md5B32 = Base32.ToBase32String(md5Bytes); - - md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "md5", - md5B32[0].ToString(), md5B32[1].ToString(), md5B32[2].ToString(), - md5B32[3].ToString(), md5B32[4].ToString(), md5B32 + ".aif"); - } - - if(File.Exists(sha256Path)) - repoPath = sha256Path; - else if(File.Exists(sha1Path)) - repoPath = sha1Path; - else if(File.Exists(md5Path)) - repoPath = md5Path; - - if(repoPath == null) - throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository, - media.Sha256 ?? media.Sha1 ?? media.Md5)); - - var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read); - var outFs = new FileStream(outputPath, FileMode.Create, FileAccess.Write); - - SetMessage3?.Invoke(this, new MessageEventArgs - { - Message = string.Format(Localization.Copying, Path.GetFileName(outputPath)) - }); - - SetProgress3Bounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); - - byte[] buffer = new byte[BUFFER_SIZE]; - - while(inFs.Position + BUFFER_SIZE <= inFs.Length) - { - SetProgress3?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - outFs.Write(buffer, 0, buffer.Length); - } - - buffer = new byte[inFs.Length - inFs.Position]; - - SetProgress3?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); + while(inFs.Position + BUFFER_SIZE <= inFs.Length) + { + SetProgress3?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); inFs.Read(buffer, 0, buffer.Length); outFs.Write(buffer, 0, buffer.Length); - - inFs.Close(); - outFs.Close(); - - mediaPosition++; } + + buffer = new byte[inFs.Length - inFs.Position]; + + SetProgress3?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); + + inFs.Read(buffer, 0, buffer.Length); + outFs.Write(buffer, 0, buffer.Length); + + inFs.Close(); + outFs.Close(); + + mediaPosition++; } + } - Dictionary disksByMachine = ctx.DisksByMachines. - Where(f => f.Machine.Id == machine.Id && - f.Disk.IsInRepo). - ToDictionary(f => f.Name); + var disksByMachine = ctx.DisksByMachines.Where(f => f.Machine.Id == machine.Id && f.Disk.IsInRepo) + .ToDictionary(f => f.Name); - if(disksByMachine.Count > 0) + if(disksByMachine.Count > 0) + { + SetProgress2Bounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = disksByMachine.Count + }); + + if(machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) + machineName = machineName.Substring(0, machineName.Length - 4); + + string machinePath = Path.Combine(_outPath, machineName); + + if(!Directory.Exists(machinePath)) Directory.CreateDirectory(machinePath); + + long diskPosition = 0; + + foreach(KeyValuePair diskByMachine in disksByMachine) { - SetProgress2Bounds?.Invoke(this, new ProgressBoundsEventArgs + string outputPath = Path.Combine(machinePath, diskByMachine.Key); + + if(!outputPath.EndsWith(".chd", StringComparison.InvariantCultureIgnoreCase)) outputPath += ".chd"; + + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = diskPosition + }); + + string repoPath = null; + string md5Path = null; + string sha1Path = null; + + DbDisk disk = diskByMachine.Value.Disk; + + if(disk.Sha1 != null) { - Minimum = 0, - Maximum = disksByMachine.Count - }); + var sha1Bytes = new byte[20]; + string sha1 = disk.Sha1; - if(machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) - machineName = machineName.Substring(0, machineName.Length - 4); + for(var i = 0; i < 20; i++) + { + if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); + else if(sha1[i * 2] >= 0x41 && sha1[i * 2] <= 0x46) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); + else if(sha1[i * 2] >= 0x61 && sha1[i * 2] <= 0x66) + sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); - string machinePath = Path.Combine(_outPath, machineName); + if(sha1[i * 2 + 1] >= 0x30 && sha1[i * 2 + 1] <= 0x39) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x30); + else if(sha1[i * 2 + 1] >= 0x41 && sha1[i * 2 + 1] <= 0x46) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x37); + else if(sha1[i * 2 + 1] >= 0x61 && sha1[i * 2 + 1] <= 0x66) + sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x57); + } - if(!Directory.Exists(machinePath)) - Directory.CreateDirectory(machinePath); + string sha1B32 = Base32.ToBase32String(sha1Bytes); - long diskPosition = 0; + sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "chd", + "sha1", + sha1B32[0].ToString(), + sha1B32[1].ToString(), + sha1B32[2].ToString(), + sha1B32[3].ToString(), + sha1B32[4].ToString(), + sha1B32 + ".chd"); + } - foreach(KeyValuePair diskByMachine in disksByMachine) + if(disk.Md5 != null) { - string outputPath = Path.Combine(machinePath, diskByMachine.Key); + var md5Bytes = new byte[16]; + string md5 = disk.Md5; - if(!outputPath.EndsWith(".chd", StringComparison.InvariantCultureIgnoreCase)) - outputPath += ".chd"; - - SetProgress2?.Invoke(this, new ProgressEventArgs + for(var i = 0; i < 16; i++) { - Value = diskPosition - }); + if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39) + md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); + else if(md5[i * 2] >= 0x41 && md5[i * 2] <= 0x46) + md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); + else if(md5[i * 2] >= 0x61 && md5[i * 2] <= 0x66) + md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); - string repoPath = null; - string md5Path = null; - string sha1Path = null; - - DbDisk disk = diskByMachine.Value.Disk; - - if(disk.Sha1 != null) - { - byte[] sha1Bytes = new byte[20]; - string sha1 = disk.Sha1; - - for(int i = 0; i < 20; i++) - { - if(sha1[i * 2] >= 0x30 && - sha1[i * 2] <= 0x39) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10); - else if(sha1[i * 2] >= 0x41 && - sha1[i * 2] <= 0x46) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10); - else if(sha1[i * 2] >= 0x61 && - sha1[i * 2] <= 0x66) - sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10); - - if(sha1[(i * 2) + 1] >= 0x30 && - sha1[(i * 2) + 1] <= 0x39) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x30); - else if(sha1[(i * 2) + 1] >= 0x41 && - sha1[(i * 2) + 1] <= 0x46) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x37); - else if(sha1[(i * 2) + 1] >= 0x61 && - sha1[(i * 2) + 1] <= 0x66) - sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x57); - } - - string sha1B32 = Base32.ToBase32String(sha1Bytes); - - sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "sha1", - sha1B32[0].ToString(), sha1B32[1].ToString(), sha1B32[2].ToString(), - sha1B32[3].ToString(), sha1B32[4].ToString(), sha1B32 + ".chd"); + if(md5[i * 2 + 1] >= 0x30 && md5[i * 2 + 1] <= 0x39) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x30); + else if(md5[i * 2 + 1] >= 0x41 && md5[i * 2 + 1] <= 0x46) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x37); + else if(md5[i * 2 + 1] >= 0x61 && md5[i * 2 + 1] <= 0x66) + md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x57); } - if(disk.Md5 != null) - { - byte[] md5Bytes = new byte[16]; - string md5 = disk.Md5; + string md5B32 = Base32.ToBase32String(md5Bytes); - for(int i = 0; i < 16; i++) - { - if(md5[i * 2] >= 0x30 && - md5[i * 2] <= 0x39) - md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10); - else if(md5[i * 2] >= 0x41 && - md5[i * 2] <= 0x46) - md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10); - else if(md5[i * 2] >= 0x61 && - md5[i * 2] <= 0x66) - md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10); + md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "chd", + "md5", + md5B32[0].ToString(), + md5B32[1].ToString(), + md5B32[2].ToString(), + md5B32[3].ToString(), + md5B32[4].ToString(), + md5B32 + ".chd"); + } - if(md5[(i * 2) + 1] >= 0x30 && - md5[(i * 2) + 1] <= 0x39) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x30); - else if(md5[(i * 2) + 1] >= 0x41 && - md5[(i * 2) + 1] <= 0x46) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x37); - else if(md5[(i * 2) + 1] >= 0x61 && - md5[(i * 2) + 1] <= 0x66) - md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x57); - } + if(File.Exists(sha1Path)) + repoPath = sha1Path; + else if(File.Exists(md5Path)) repoPath = md5Path; - string md5B32 = Base32.ToBase32String(md5Bytes); + if(repoPath == null) + { + throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository, + disk.Sha1 ?? disk.Md5)); + } - md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "md5", - md5B32[0].ToString(), md5B32[1].ToString(), md5B32[2].ToString(), - md5B32[3].ToString(), md5B32[4].ToString(), md5B32 + ".chd"); - } + var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read); + var outFs = new FileStream(outputPath, FileMode.Create, FileAccess.Write); - if(File.Exists(sha1Path)) - repoPath = sha1Path; - else if(File.Exists(md5Path)) - repoPath = md5Path; + SetMessage3?.Invoke(this, + new MessageEventArgs + { + Message = string.Format(Localization.Copying, Path.GetFileName(outputPath)) + }); - if(repoPath == null) - throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository, - disk.Sha1 ?? disk.Md5)); + SetProgress3Bounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); - var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read); - var outFs = new FileStream(outputPath, FileMode.Create, FileAccess.Write); + var buffer = new byte[BUFFER_SIZE]; - SetMessage3?.Invoke(this, new MessageEventArgs - { - Message = string.Format(Localization.Copying, Path.GetFileName(outputPath)) - }); - - SetProgress3Bounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); - - byte[] buffer = new byte[BUFFER_SIZE]; - - while(inFs.Position + BUFFER_SIZE <= inFs.Length) - { - SetProgress3?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - outFs.Write(buffer, 0, buffer.Length); - } - - buffer = new byte[inFs.Length - inFs.Position]; - - SetProgress3?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); + while(inFs.Position + BUFFER_SIZE <= inFs.Length) + { + SetProgress3?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); inFs.Read(buffer, 0, buffer.Length); outFs.Write(buffer, 0, buffer.Length); - - inFs.Close(); - outFs.Close(); - - diskPosition++; - } - } - - _filesByMachine = ctx.FilesByMachines.Where(f => f.Machine.Id == machine.Id && f.File.IsInRepo). - ToDictionary(f => f.Name); - - if(_filesByMachine.Count == 0) - { - _machinePosition++; - Task.Run(CompressNextMachine); - - return; - } - - SetProgress2Bounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = _filesByMachine.Count - }); - - if(!machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) - machineName += ".zip"; - - var zf = new ZipFile(Path.Combine(_outPath, machineName), Encoding.UTF8) - { - CompressionLevel = CompressionLevel.BestCompression, - CompressionMethod = CompressionMethod.Deflate, - EmitTimesInUnixFormatWhenSaving = true, - EmitTimesInWindowsFormatWhenSaving = true, - UseZip64WhenSaving = Zip64Option.AsNecessary, - SortEntriesBeforeSaving = true - }; - - zf.SaveProgress += Zf_SaveProgress; - - foreach(KeyValuePair fileByMachine in _filesByMachine) - { - // Is a directory - if((fileByMachine.Key.EndsWith("/", StringComparison.InvariantCultureIgnoreCase) || - fileByMachine.Key.EndsWith("\\", StringComparison.InvariantCultureIgnoreCase)) && - fileByMachine.Value.File.Size == 0) - { - ZipEntry zd = zf.AddDirectoryByName(fileByMachine.Key.Replace('/', '\\')); - zd.Attributes = FileAttributes.Normal; - zd.CreationTime = DateTime.UtcNow; - zd.AccessedTime = DateTime.UtcNow; - zd.LastModified = DateTime.UtcNow; - zd.ModifiedTime = DateTime.UtcNow; - - continue; } - ZipEntry zi = zf.AddEntry(fileByMachine.Key, Zf_HandleOpen, Zf_HandleClose); - zi.Attributes = FileAttributes.Normal; - zi.CreationTime = DateTime.UtcNow; - zi.AccessedTime = DateTime.UtcNow; - zi.LastModified = DateTime.UtcNow; - zi.ModifiedTime = DateTime.UtcNow; - } + buffer = new byte[inFs.Length - inFs.Position]; - zf.Save(); + SetProgress3?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); + + inFs.Read(buffer, 0, buffer.Length); + outFs.Write(buffer, 0, buffer.Length); + + inFs.Close(); + outFs.Close(); + + diskPosition++; + } } - Stream Zf_HandleOpen(string entryName) + _filesByMachine = ctx.FilesByMachines.Where(f => f.Machine.Id == machine.Id && f.File.IsInRepo) + .ToDictionary(f => f.Name); + + if(_filesByMachine.Count == 0) { - if(!_filesByMachine.TryGetValue(entryName, out FileByMachine fileByMachine)) - if(!_filesByMachine.TryGetValue(entryName.Replace('/', '\\'), out fileByMachine)) + _machinePosition++; + Task.Run(CompressNextMachine); + + return; + } + + SetProgress2Bounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = _filesByMachine.Count + }); + + if(!machineName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) machineName += ".zip"; + + var zf = new ZipFile(Path.Combine(_outPath, machineName), Encoding.UTF8) + { + CompressionLevel = CompressionLevel.BestCompression, + CompressionMethod = CompressionMethod.Deflate, + EmitTimesInUnixFormatWhenSaving = true, + EmitTimesInWindowsFormatWhenSaving = true, + UseZip64WhenSaving = Zip64Option.AsNecessary, + SortEntriesBeforeSaving = true + }; + + zf.SaveProgress += Zf_SaveProgress; + + foreach(KeyValuePair fileByMachine in _filesByMachine) + { + // Is a directory + if((fileByMachine.Key.EndsWith("/", StringComparison.InvariantCultureIgnoreCase) || + fileByMachine.Key.EndsWith("\\", StringComparison.InvariantCultureIgnoreCase)) && + fileByMachine.Value.File.Size == 0) + { + ZipEntry zd = zf.AddDirectoryByName(fileByMachine.Key.Replace('/', '\\')); + zd.Attributes = FileAttributes.Normal; + zd.CreationTime = DateTime.UtcNow; + zd.AccessedTime = DateTime.UtcNow; + zd.LastModified = DateTime.UtcNow; + zd.ModifiedTime = DateTime.UtcNow; + + continue; + } + + ZipEntry zi = zf.AddEntry(fileByMachine.Key, Zf_HandleOpen, Zf_HandleClose); + zi.Attributes = FileAttributes.Normal; + zi.CreationTime = DateTime.UtcNow; + zi.AccessedTime = DateTime.UtcNow; + zi.LastModified = DateTime.UtcNow; + zi.ModifiedTime = DateTime.UtcNow; + } + + zf.Save(); + } + + Stream Zf_HandleOpen(string entryName) + { + if(!_filesByMachine.TryGetValue(entryName, out FileByMachine fileByMachine)) + { + if(!_filesByMachine.TryGetValue(entryName.Replace('/', '\\'), out fileByMachine)) + throw new ArgumentException(Localization.CannotFindZipEntryInDictionary); + } + + DbFile file = fileByMachine.File; + + // Special case for empty file, as it seems to crash when SharpCompress tries to unLZMA it. + if(file.Size == 0) return new MemoryStream(); + + var sha384Bytes = new byte[48]; + string sha384 = file.Sha384; + + for(var i = 0; i < 48; i++) + { + if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); + else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); + else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); + + if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30); + else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37); + else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57); + } + + string sha384B32 = Base32.ToBase32String(sha384Bytes); + + string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, + "files", + sha384B32[0].ToString(), + sha384B32[1].ToString(), + sha384B32[2].ToString(), + sha384B32[3].ToString(), + sha384B32[4].ToString(), + sha384B32 + ".lz"); + + if(!File.Exists(repoPath)) + throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository, file.Sha256)); + + var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read); + + return new StreamWithLength(new LZipStream(inFs, CompressionMode.Decompress), (long)file.Size); + } + + void Zf_HandleClose(string entryName, Stream stream) => stream.Close(); + + void Zf_SaveProgress(object sender, SaveProgressEventArgs e) + { + if(e.CurrentEntry != null && e.CurrentEntry.FileName != _zipCurrentEntryName) + { + _zipCurrentEntryName = e.CurrentEntry.FileName; + _filePosition++; + + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = _filePosition + }); + + if(!_filesByMachine.TryGetValue(e.CurrentEntry.FileName, out FileByMachine fileByMachine)) + { + if(!_filesByMachine.TryGetValue(e.CurrentEntry.FileName.Replace('/', '\\'), out fileByMachine)) throw new ArgumentException(Localization.CannotFindZipEntryInDictionary); - - DbFile file = fileByMachine.File; - - // Special case for empty file, as it seems to crash when SharpCompress tries to unLZMA it. - if(file.Size == 0) - return new MemoryStream(); - - byte[] sha384Bytes = new byte[48]; - string sha384 = file.Sha384; - - for(int i = 0; i < 48; i++) - { - if(sha384[i * 2] >= 0x30 && - sha384[i * 2] <= 0x39) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); - else if(sha384[i * 2] >= 0x41 && - sha384[i * 2] <= 0x46) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); - else if(sha384[i * 2] >= 0x61 && - sha384[i * 2] <= 0x66) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); - - if(sha384[(i * 2) + 1] >= 0x30 && - sha384[(i * 2) + 1] <= 0x39) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30); - else if(sha384[(i * 2) + 1] >= 0x41 && - sha384[(i * 2) + 1] <= 0x46) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37); - else if(sha384[(i * 2) + 1] >= 0x61 && - sha384[(i * 2) + 1] <= 0x66) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57); } - string sha384B32 = Base32.ToBase32String(sha384Bytes); + DbFile currentFile = fileByMachine.File; - string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "files", sha384B32[0].ToString(), - sha384B32[1].ToString(), sha384B32[2].ToString(), sha384B32[3].ToString(), - sha384B32[4].ToString(), sha384B32 + ".lz"); + SetMessage3?.Invoke(this, + new MessageEventArgs + { + Message = string.Format(Localization.Compressing, e.CurrentEntry.FileName) + }); - if(!File.Exists(repoPath)) - throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository, file.Sha256)); - - var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read); - - return new StreamWithLength(new LZipStream(inFs, CompressionMode.Decompress), (long)file.Size); + SetProgress3Bounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = currentFile.Size + }); } - void Zf_HandleClose(string entryName, Stream stream) => stream.Close(); + SetProgress3?.Invoke(this, + new ProgressEventArgs + { + Value = e.BytesTransferred + }); - void Zf_SaveProgress(object sender, SaveProgressEventArgs e) + switch(e.EventType) { - if(e.CurrentEntry != null && - e.CurrentEntry.FileName != _zipCurrentEntryName) - { - _zipCurrentEntryName = e.CurrentEntry.FileName; - _filePosition++; + case ZipProgressEventType.Error_Saving: +#if DEBUG + throw new Exception(); +#endif - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = _filePosition - }); + break; + case ZipProgressEventType.Saving_Completed: + _machinePosition++; + CompressNextMachine(); - if(!_filesByMachine.TryGetValue(e.CurrentEntry.FileName, out FileByMachine fileByMachine)) - if(!_filesByMachine.TryGetValue(e.CurrentEntry.FileName.Replace('/', '\\'), out fileByMachine)) - throw new ArgumentException(Localization.CannotFindZipEntryInDictionary); - - DbFile currentFile = fileByMachine.File; - - SetMessage3?.Invoke(this, new MessageEventArgs - { - Message = string.Format(Localization.Compressing, e.CurrentEntry.FileName) - }); - - SetProgress3Bounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = currentFile.Size - }); - } - - SetProgress3?.Invoke(this, new ProgressEventArgs - { - Value = e.BytesTransferred - }); - - switch(e.EventType) - { - case ZipProgressEventType.Error_Saving: - #if DEBUG - throw new Exception(); - #endif - - break; - case ZipProgressEventType.Saving_Completed: - _machinePosition++; - CompressNextMachine(); - - break; - } + break; } } } \ No newline at end of file diff --git a/RomRepoMgr.Core/Workers/FileImporter.cs b/RomRepoMgr.Core/Workers/FileImporter.cs index 2f7f965..b3680b4 100644 --- a/RomRepoMgr.Core/Workers/FileImporter.cs +++ b/RomRepoMgr.Core/Workers/FileImporter.cs @@ -16,1328 +16,1351 @@ using SabreTools.FileTypes.CHD; using SharpCompress.Compressors; using SharpCompress.Compressors.LZMA; -namespace RomRepoMgr.Core.Workers +namespace RomRepoMgr.Core.Workers; + +public class FileImporter { - public class FileImporter + const long BUFFER_SIZE = 131072; + readonly Context _ctx; + readonly bool _deleteAfterImport; + readonly List _newDisks; + readonly List _newFiles; + readonly List _newMedias; + readonly bool _onlyKnown; + readonly Dictionary _pendingDisksByMd5; + readonly Dictionary _pendingDisksBySha1; + readonly Dictionary _pendingFiles; + readonly Dictionary _pendingMediasByMd5; + readonly Dictionary _pendingMediasBySha1; + readonly Dictionary _pendingMediasBySha256; + string _lastMessage; + long _position; + long _totalFiles; + + public FileImporter(bool onlyKnown, bool deleteAfterImport) { - const long BUFFER_SIZE = 131072; - readonly Context _ctx; - readonly bool _deleteAfterImport; - readonly List _newDisks; - readonly List _newFiles; - readonly List _newMedias; - readonly bool _onlyKnown; - readonly Dictionary _pendingDisksByMd5; - readonly Dictionary _pendingDisksBySha1; - readonly Dictionary _pendingFiles; - readonly Dictionary _pendingMediasByMd5; - readonly Dictionary _pendingMediasBySha1; - readonly Dictionary _pendingMediasBySha256; - string _lastMessage; - long _position; - long _totalFiles; + _pendingFiles = new Dictionary(); + _pendingDisksByMd5 = new Dictionary(); + _pendingDisksBySha1 = new Dictionary(); + _pendingMediasBySha256 = new Dictionary(); + _pendingMediasBySha1 = new Dictionary(); + _pendingMediasByMd5 = new Dictionary(); + _newFiles = new List(); + _newDisks = new List(); + _newMedias = new List(); + _onlyKnown = onlyKnown; + _deleteAfterImport = deleteAfterImport; + _position = 0; + _ctx = Context.Create(Settings.Settings.Current.DatabasePath); + } - public FileImporter(bool onlyKnown, bool deleteAfterImport) + public event EventHandler SetIndeterminateProgress2; + public event EventHandler SetProgressBounds2; + public event EventHandler SetProgress2; + public event EventHandler SetMessage2; + public event EventHandler SetIndeterminateProgress; + public event EventHandler SetProgressBounds; + public event EventHandler SetProgress; + public event EventHandler SetMessage; + public event EventHandler Finished; + public event EventHandler ImportedRom; + + public void ProcessPath(string path, bool rootPath, bool processArchives) + { + try { - _pendingFiles = new Dictionary(); - _pendingDisksByMd5 = new Dictionary(); - _pendingDisksBySha1 = new Dictionary(); - _pendingMediasBySha256 = new Dictionary(); - _pendingMediasBySha1 = new Dictionary(); - _pendingMediasByMd5 = new Dictionary(); - _newFiles = new List(); - _newDisks = new List(); - _newMedias = new List(); - _onlyKnown = onlyKnown; - _deleteAfterImport = deleteAfterImport; - _position = 0; - _ctx = Context.Create(Settings.Settings.Current.DatabasePath); - } + SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - public event EventHandler SetIndeterminateProgress2; - public event EventHandler SetProgressBounds2; - public event EventHandler SetProgress2; - public event EventHandler SetMessage2; - public event EventHandler SetIndeterminateProgress; - public event EventHandler SetProgressBounds; - public event EventHandler SetProgress; - public event EventHandler SetMessage; - public event EventHandler Finished; - public event EventHandler ImportedRom; + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.EnumeratingFiles + }); - public void ProcessPath(string path, bool rootPath, bool processArchives) - { - try + string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories).OrderBy(p => p).ToArray(); + _totalFiles += files.LongLength; + + SetProgressBounds?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = _totalFiles + }); + + foreach(string file in files) { - SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty); - - SetMessage?.Invoke(this, new MessageEventArgs + try { - Message = Localization.EnumeratingFiles - }); + SetProgress?.Invoke(this, + new ProgressEventArgs + { + Value = _position + }); - string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories).OrderBy(p => p).ToArray(); - _totalFiles += files.LongLength; + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = string.Format(Localization.Importing, Path.GetFileName(file)) + }); - SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = _totalFiles - }); + string archiveFormat = null; + long archiveFiles = 0; - foreach(string file in files) - { - try + var fs = new FileStream(file, FileMode.Open, FileAccess.Read); + + var aif = AaruFormat.Create(fs); + + if(aif != null) { - SetProgress?.Invoke(this, new ProgressEventArgs - { - Value = _position - }); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = string.Format(Localization.Importing, Path.GetFileName(file)) - }); - - string archiveFormat = null; - long archiveFiles = 0; - - var fs = new FileStream(file, FileMode.Open, FileAccess.Read); - - var aif = AaruFormat.Create(fs); - - if(aif != null) - { - fs.Close(); - - bool ret = ImportMedia(file); - - if(ret) - { - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs - { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = Localization.OK - } - }); - } - else - { - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs - { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = string.Format(Localization.ErrorWithMessage, _lastMessage) - } - }); - } - - _position++; - - continue; - } - - fs.Position = 0; - - var chd = CHDFile.Create(fs); - - if(chd != null) - { - fs.Close(); - - bool ret = ImportDisk(file); - - if(ret) - { - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs - { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = Localization.OK - } - }); - } - else - { - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs - { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = string.Format(Localization.ErrorWithMessage, _lastMessage) - } - }); - } - - _position++; - - continue; - } - fs.Close(); - if(processArchives) + bool ret = ImportMedia(file); + + if(ret) { - SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.CheckingIfFIleIsAnArchive - }); - - archiveFormat = GetArchiveFormat(file, out archiveFiles); - - // If a floppy contains only the archive, unar will recognize it, on its skipping of SFXs. - if(archiveFormat != null && - FAT.Identify(file)) - archiveFormat = null; - } - - if(archiveFormat == null) - { - bool ret = ImportRom(file); - - if(ret) - { - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs - { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = Localization.OK - } - }); - } - else - { - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs - { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = string.Format(Localization.ErrorWithMessage, _lastMessage) - } - }); - } + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = Localization.OK + } + }); } else { - if(!Directory.Exists(Settings.Settings.Current.TemporaryFolder)) - Directory.CreateDirectory(Settings.Settings.Current.TemporaryFolder); - - string tmpFolder = - Path.Combine(Settings.Settings.Current.TemporaryFolder, Path.GetRandomFileName()); - - Directory.CreateDirectory(tmpFolder); - - SetProgressBounds2?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = archiveFiles - }); - - SetMessage?.Invoke(this, new MessageEventArgs - { - Message = Localization.ExtractingArchive - }); - - ExtractArchive(file, tmpFolder); - - ProcessPath(tmpFolder, false, true); - - SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.RemovingTemporaryPath - }); - - Directory.Delete(tmpFolder, true); - - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs - { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = Localization.ExtractedContents - } - }); + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = string.Format(Localization.ErrorWithMessage, + _lastMessage) + } + }); } _position++; + + continue; } - catch(Exception) + + fs.Position = 0; + + var chd = CHDFile.Create(fs); + + if(chd != null) { - ImportedRom?.Invoke(this, new ImportedRomItemEventArgs + fs.Close(); + + bool ret = ImportDisk(file); + + if(ret) { - Item = new ImportRomItem - { - Filename = Path.GetFileName(file), - Status = Localization.UnhandledException - } - }); + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = Localization.OK + } + }); + } + else + { + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = string.Format(Localization.ErrorWithMessage, + _lastMessage) + } + }); + } + + _position++; + + continue; } + + fs.Close(); + + if(processArchives) + { + SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.CheckingIfFIleIsAnArchive + }); + + archiveFormat = GetArchiveFormat(file, out archiveFiles); + + // If a floppy contains only the archive, unar will recognize it, on its skipping of SFXs. + if(archiveFormat != null && FAT.Identify(file)) archiveFormat = null; + } + + if(archiveFormat == null) + { + bool ret = ImportRom(file); + + if(ret) + { + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = Localization.OK + } + }); + } + else + { + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = string.Format(Localization.ErrorWithMessage, + _lastMessage) + } + }); + } + } + else + { + if(!Directory.Exists(Settings.Settings.Current.TemporaryFolder)) + Directory.CreateDirectory(Settings.Settings.Current.TemporaryFolder); + + string tmpFolder = + Path.Combine(Settings.Settings.Current.TemporaryFolder, Path.GetRandomFileName()); + + Directory.CreateDirectory(tmpFolder); + + SetProgressBounds2?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = archiveFiles + }); + + SetMessage?.Invoke(this, + new MessageEventArgs + { + Message = Localization.ExtractingArchive + }); + + ExtractArchive(file, tmpFolder); + + ProcessPath(tmpFolder, false, true); + + SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.RemovingTemporaryPath + }); + + Directory.Delete(tmpFolder, true); + + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = Localization.ExtractedContents + } + }); + } + + _position++; + } + catch(Exception) + { + ImportedRom?.Invoke(this, + new ImportedRomItemEventArgs + { + Item = new ImportRomItem + { + Filename = Path.GetFileName(file), + Status = Localization.UnhandledException + } + }); } - - if(!rootPath) - return; - - SaveChanges(); - Finished?.Invoke(this, System.EventArgs.Empty); - } - catch(Exception) - { - // TODO: Send error back - if(rootPath) - Finished?.Invoke(this, System.EventArgs.Empty); } + + if(!rootPath) return; + + SaveChanges(); + Finished?.Invoke(this, System.EventArgs.Empty); } - - bool ImportRom(string path) + catch(Exception) { - try + // TODO: Send error back + if(rootPath) Finished?.Invoke(this, System.EventArgs.Empty); + } + } + + bool ImportRom(string path) + { + try + { + var inFs = new FileStream(path, FileMode.Open, FileAccess.Read); + + byte[] buffer; + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.HashingFile + }); + + var checksumWorker = new Checksum(); + + if(inFs.Length > BUFFER_SIZE) { - var inFs = new FileStream(path, FileMode.Open, FileAccess.Read); + SetProgressBounds2?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); - byte[] buffer; + long offset; + long remainder = inFs.Length % BUFFER_SIZE; - SetMessage2?.Invoke(this, new MessageEventArgs + for(offset = 0; offset < inFs.Length - remainder; offset += (int)BUFFER_SIZE) { - Message = Localization.HashingFile - }); + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = offset + }); - var checksumWorker = new Checksum(); - - if(inFs.Length > BUFFER_SIZE) - { - SetProgressBounds2?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); - - long offset; - long remainder = inFs.Length % BUFFER_SIZE; - - for(offset = 0; offset < inFs.Length - remainder; offset += (int)BUFFER_SIZE) - { - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = offset - }); - - buffer = new byte[BUFFER_SIZE]; - inFs.Read(buffer, 0, (int)BUFFER_SIZE); - checksumWorker.Update(buffer); - } - - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = offset - }); - - buffer = new byte[remainder]; - inFs.Read(buffer, 0, (int)remainder); - checksumWorker.Update(buffer); - } - else - { - SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); - buffer = new byte[inFs.Length]; - inFs.Read(buffer, 0, (int)inFs.Length); + buffer = new byte[BUFFER_SIZE]; + inFs.Read(buffer, 0, (int)BUFFER_SIZE); checksumWorker.Update(buffer); } - Dictionary checksums = checksumWorker.End(); - - ulong uSize = (ulong)inFs.Length; - bool fileInDb = true; - - bool knownFile = _pendingFiles.TryGetValue(checksums[ChecksumType.Sha512], out DbFile dbFile); - - dbFile ??= _ctx.Files.FirstOrDefault(f => (f.Sha512 == checksums[ChecksumType.Sha512] || - f.Sha384 == checksums[ChecksumType.Sha384] || - f.Sha256 == checksums[ChecksumType.Sha256] || - f.Sha1 == checksums[ChecksumType.Sha1] || - f.Md5 == checksums[ChecksumType.Md5] || - f.Crc32 == checksums[ChecksumType.Crc32]) && - f.Size == uSize); - - if(dbFile == null) - { - if(_onlyKnown) - { - _lastMessage = Localization.UnknownFile; - - return false; - } - - dbFile = new DbFile - { - Crc32 = checksums[ChecksumType.Crc32], - Md5 = checksums[ChecksumType.Md5], - Sha1 = checksums[ChecksumType.Sha1], - Sha256 = checksums[ChecksumType.Sha256], - Sha384 = checksums[ChecksumType.Sha384], - Sha512 = checksums[ChecksumType.Sha512], - Size = uSize, - CreatedOn = DateTime.UtcNow, - UpdatedOn = DateTime.UtcNow, - OriginalFileName = Path.GetFileName(path) - }; - - fileInDb = false; - } - - if(!knownFile) - _pendingFiles[checksums[ChecksumType.Sha512]] = dbFile; - - byte[] sha384Bytes = new byte[48]; - string sha384 = checksums[ChecksumType.Sha384]; - - for(int i = 0; i < 48; i++) - { - if(sha384[i * 2] >= 0x30 && - sha384[i * 2] <= 0x39) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); - else if(sha384[i * 2] >= 0x41 && - sha384[i * 2] <= 0x46) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); - else if(sha384[i * 2] >= 0x61 && - sha384[i * 2] <= 0x66) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); - - if(sha384[(i * 2) + 1] >= 0x30 && - sha384[(i * 2) + 1] <= 0x39) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30); - else if(sha384[(i * 2) + 1] >= 0x41 && - sha384[(i * 2) + 1] <= 0x46) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37); - else if(sha384[(i * 2) + 1] >= 0x61 && - sha384[(i * 2) + 1] <= 0x66) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57); - } - - string sha384B32 = Base32.ToBase32String(sha384Bytes); - - string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "files", - sha384B32[0].ToString(), sha384B32[1].ToString(), - sha384B32[2].ToString(), sha384B32[3].ToString(), - sha384B32[4].ToString()); - - if(!Directory.Exists(repoPath)) - Directory.CreateDirectory(repoPath); - - repoPath = Path.Combine(repoPath, sha384B32 + ".lz"); - - if(dbFile.Crc32 == null) - { - dbFile.Crc32 = checksums[ChecksumType.Crc32]; - dbFile.UpdatedOn = DateTime.UtcNow; - } - - if(dbFile.Md5 == null) - { - dbFile.Md5 = checksums[ChecksumType.Md5]; - dbFile.UpdatedOn = DateTime.UtcNow; - } - - if(dbFile.Sha1 == null) - { - dbFile.Sha1 = checksums[ChecksumType.Sha1]; - dbFile.UpdatedOn = DateTime.UtcNow; - } - - if(dbFile.Sha256 == null) - { - dbFile.Sha256 = checksums[ChecksumType.Sha256]; - dbFile.UpdatedOn = DateTime.UtcNow; - } - - if(dbFile.Sha384 == null) - { - dbFile.Sha384 = checksums[ChecksumType.Sha384]; - dbFile.UpdatedOn = DateTime.UtcNow; - } - - if(dbFile.Sha512 == null) - { - dbFile.Sha512 = checksums[ChecksumType.Sha512]; - dbFile.UpdatedOn = DateTime.UtcNow; - } - - if(File.Exists(repoPath)) - { - dbFile.IsInRepo = true; - dbFile.UpdatedOn = DateTime.UtcNow; - - if(!fileInDb) - _newFiles.Add(dbFile); - - inFs.Close(); - - if(_deleteAfterImport) - File.Delete(path); - - return true; - } - - inFs.Position = 0; - - var outFs = new FileStream(repoPath, FileMode.CreateNew, FileAccess.Write); - Stream zStream = null; - zStream = new LZipStream(outFs, CompressionMode.Compress); - - SetProgressBounds2?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.CompressingFile - }); - - buffer = new byte[BUFFER_SIZE]; - - while(inFs.Position + BUFFER_SIZE <= inFs.Length) - { - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - zStream.Write(buffer, 0, buffer.Length); - } - - buffer = new byte[inFs.Length - inFs.Position]; - - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - zStream.Write(buffer, 0, buffer.Length); + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = offset + }); + buffer = new byte[remainder]; + inFs.Read(buffer, 0, (int)remainder); + checksumWorker.Update(buffer); + } + else + { SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); + buffer = new byte[inFs.Length]; + inFs.Read(buffer, 0, (int)inFs.Length); + checksumWorker.Update(buffer); + } - SetMessage2?.Invoke(this, new MessageEventArgs + Dictionary checksums = checksumWorker.End(); + + var uSize = (ulong)inFs.Length; + var fileInDb = true; + + bool knownFile = _pendingFiles.TryGetValue(checksums[ChecksumType.Sha512], out DbFile dbFile); + + dbFile ??= _ctx.Files.FirstOrDefault(f => (f.Sha512 == checksums[ChecksumType.Sha512] || + f.Sha384 == checksums[ChecksumType.Sha384] || + f.Sha256 == checksums[ChecksumType.Sha256] || + f.Sha1 == checksums[ChecksumType.Sha1] || + f.Md5 == checksums[ChecksumType.Md5] || + f.Crc32 == checksums[ChecksumType.Crc32]) && + f.Size == uSize); + + if(dbFile == null) + { + if(_onlyKnown) { - Message = Localization.Finishing - }); + _lastMessage = Localization.UnknownFile; - inFs.Close(); - zStream.Close(); - outFs.Dispose(); + return false; + } + dbFile = new DbFile + { + Crc32 = checksums[ChecksumType.Crc32], + Md5 = checksums[ChecksumType.Md5], + Sha1 = checksums[ChecksumType.Sha1], + Sha256 = checksums[ChecksumType.Sha256], + Sha384 = checksums[ChecksumType.Sha384], + Sha512 = checksums[ChecksumType.Sha512], + Size = uSize, + CreatedOn = DateTime.UtcNow, + UpdatedOn = DateTime.UtcNow, + OriginalFileName = Path.GetFileName(path) + }; + + fileInDb = false; + } + + if(!knownFile) _pendingFiles[checksums[ChecksumType.Sha512]] = dbFile; + + var sha384Bytes = new byte[48]; + string sha384 = checksums[ChecksumType.Sha384]; + + for(var i = 0; i < 48; i++) + { + if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); + else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); + else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); + + if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30); + else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37); + else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57); + } + + string sha384B32 = Base32.ToBase32String(sha384Bytes); + + string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, + "files", + sha384B32[0].ToString(), + sha384B32[1].ToString(), + sha384B32[2].ToString(), + sha384B32[3].ToString(), + sha384B32[4].ToString()); + + if(!Directory.Exists(repoPath)) Directory.CreateDirectory(repoPath); + + repoPath = Path.Combine(repoPath, sha384B32 + ".lz"); + + if(dbFile.Crc32 == null) + { + dbFile.Crc32 = checksums[ChecksumType.Crc32]; + dbFile.UpdatedOn = DateTime.UtcNow; + } + + if(dbFile.Md5 == null) + { + dbFile.Md5 = checksums[ChecksumType.Md5]; + dbFile.UpdatedOn = DateTime.UtcNow; + } + + if(dbFile.Sha1 == null) + { + dbFile.Sha1 = checksums[ChecksumType.Sha1]; + dbFile.UpdatedOn = DateTime.UtcNow; + } + + if(dbFile.Sha256 == null) + { + dbFile.Sha256 = checksums[ChecksumType.Sha256]; + dbFile.UpdatedOn = DateTime.UtcNow; + } + + if(dbFile.Sha384 == null) + { + dbFile.Sha384 = checksums[ChecksumType.Sha384]; + dbFile.UpdatedOn = DateTime.UtcNow; + } + + if(dbFile.Sha512 == null) + { + dbFile.Sha512 = checksums[ChecksumType.Sha512]; + dbFile.UpdatedOn = DateTime.UtcNow; + } + + if(File.Exists(repoPath)) + { dbFile.IsInRepo = true; dbFile.UpdatedOn = DateTime.UtcNow; - if(!fileInDb) - _newFiles.Add(dbFile); - - if(_deleteAfterImport) - File.Delete(path); - - return true; - } - catch(Exception e) - { - _lastMessage = Localization.UnhandledExceptionWhenImporting; - - return false; - } - } - - bool ImportDisk(string path) - { - try - { - var inFs = new FileStream(path, FileMode.Open, FileAccess.Read); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.HashingFile - }); - - var chd = CHDFile.Create(path); - - if(chd == null) - { - _lastMessage = Localization.NotAChdFile; - - return false; - } - - if(chd.MD5 == null && - chd.SHA1 == null) - { - _lastMessage = Localization.NoChecksumsFound; - - return false; - } - - string md5 = null; - string sha1 = null; - - if(chd.MD5 != null) - { - char[] chdArray = new char[32]; - - for(int i = 0; i < 16; i++) - { - int nibble1 = chd.MD5[i] >> 4; - int nibble2 = chd.MD5[i] & 0xF; - - nibble1 += nibble1 >= 0xA ? 0x37 : 0x30; - nibble2 += nibble2 >= 0xA ? 0x37 : 0x30; - - chdArray[i * 2] = (char)nibble1; - chdArray[(i * 2) + 1] = (char)nibble2; - } - - md5 = new string(chdArray); - } - - if(chd.SHA1 != null) - { - char[] chdArray = new char[40]; - - for(int i = 0; i < 20; i++) - { - int nibble1 = chd.SHA1[i] >> 4; - int nibble2 = chd.SHA1[i] & 0xF; - - nibble1 += nibble1 >= 0xA ? 0x57 : 0x30; - nibble2 += nibble2 >= 0xA ? 0x57 : 0x30; - - chdArray[i * 2] = (char)nibble1; - chdArray[(i * 2) + 1] = (char)nibble2; - } - - sha1 = new string(chdArray); - } - - ulong uSize = (ulong)inFs.Length; - bool diskInDb = true; - DbDisk dbDisk = null; - bool knownDisk = false; - bool knownDiskWasBigger = false; - - if(sha1 != null) - knownDisk = _pendingDisksBySha1.TryGetValue(sha1, out dbDisk); - - if(!knownDisk && - md5 != null) - knownDisk = _pendingDisksByMd5.TryGetValue(md5, out dbDisk); - - dbDisk ??= _ctx.Disks.FirstOrDefault(d => (d.Sha1 != null && d.Sha1 == sha1) || - (d.Md5 != null && d.Md5 == sha1)); - - if(dbDisk == null) - { - if(_onlyKnown) - { - _lastMessage = Localization.UnknownFile; - - return false; - } - - dbDisk = new DbDisk - { - Md5 = md5, - Sha1 = sha1, - Size = uSize, - CreatedOn = DateTime.UtcNow, - UpdatedOn = DateTime.UtcNow, - OriginalFileName = Path.GetFileName(path) - }; - - diskInDb = false; - } - - if(!knownDisk) - { - if(sha1 != null) - _pendingDisksBySha1[sha1] = dbDisk; - else if(md5 != null) - _pendingDisksByMd5[md5] = dbDisk; - } - - string sha1B32 = null; - string md5B32 = null; - - if(chd.SHA1 != null) - sha1B32 = Base32.ToBase32String(chd.SHA1); - - if(chd.MD5 != null) - md5B32 = Base32.ToBase32String(chd.SHA1); - - if(dbDisk.Md5 == null && - md5 != null) - { - dbDisk.Md5 = md5; - dbDisk.UpdatedOn = DateTime.UtcNow; - } - - if(dbDisk.Sha1 == null && - sha1 != null) - { - dbDisk.Sha1 = sha1; - dbDisk.UpdatedOn = DateTime.UtcNow; - } - - if(dbDisk.Size > uSize) - { - knownDiskWasBigger = true; - dbDisk.Size = null; - } - - if(dbDisk.Size == null) - { - dbDisk.Size = uSize; - dbDisk.UpdatedOn = DateTime.UtcNow; - } - - string md5Path = null; - string sha1Path = null; - string repoPath = null; - - if(md5 != null) - { - md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "md5", md5B32[0].ToString(), - md5B32[1].ToString(), md5B32[2].ToString(), md5B32[3].ToString(), - md5B32[4].ToString()); - - repoPath = md5Path; - - md5Path = Path.Combine(repoPath, md5B32 + ".chd"); - } - - if(sha1 != null) - { - sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "sha1", - sha1B32[0].ToString(), sha1B32[1].ToString(), sha1B32[2].ToString(), - sha1B32[3].ToString(), sha1B32[4].ToString()); - - repoPath = sha1Path; - - sha1Path = Path.Combine(repoPath, sha1B32 + ".chd"); - } - - if(!Directory.Exists(repoPath)) - Directory.CreateDirectory(repoPath); - - if(File.Exists(md5Path) && - sha1Path != null) - File.Move(md5Path, sha1Path); - - if(sha1Path != null) - repoPath = sha1Path; - else if(md5Path != null) - repoPath = md5Path; - - if(File.Exists(repoPath)) - { - if(!knownDiskWasBigger) - File.Move(repoPath, repoPath + ".bak", true); - else - { - dbDisk.IsInRepo = true; - dbDisk.UpdatedOn = DateTime.UtcNow; - - if(!diskInDb) - _newDisks.Add(dbDisk); - - inFs.Close(); - - if(_deleteAfterImport) - File.Delete(path); - - return true; - } - } - - inFs.Position = 0; - var outFs = new FileStream(repoPath, FileMode.CreateNew, FileAccess.Write); - - SetProgressBounds2?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.CopyingFile - }); - - byte[] buffer = new byte[BUFFER_SIZE]; - - while(inFs.Position + BUFFER_SIZE <= inFs.Length) - { - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - outFs.Write(buffer, 0, buffer.Length); - } - - buffer = new byte[inFs.Length - inFs.Position]; - - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - outFs.Write(buffer, 0, buffer.Length); - - SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.Finishing - }); + if(!fileInDb) _newFiles.Add(dbFile); inFs.Close(); - outFs.Close(); - dbDisk.IsInRepo = true; - dbDisk.UpdatedOn = DateTime.UtcNow; - - if(!diskInDb) - _newDisks.Add(dbDisk); - - if(_deleteAfterImport) - File.Delete(path); - - if(knownDiskWasBigger) - File.Delete(repoPath + ".bak"); + if(_deleteAfterImport) File.Delete(path); return true; } - catch(Exception e) + + inFs.Position = 0; + + var outFs = new FileStream(repoPath, FileMode.CreateNew, FileAccess.Write); + Stream zStream = null; + zStream = new LZipStream(outFs, CompressionMode.Compress); + + SetProgressBounds2?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.CompressingFile + }); + + buffer = new byte[BUFFER_SIZE]; + + while(inFs.Position + BUFFER_SIZE <= inFs.Length) { - _lastMessage = Localization.UnhandledExceptionWhenImporting; - - return false; - } - } - - bool ImportMedia(string path) - { - try - { - var inFs = new FileStream(path, FileMode.Open, FileAccess.Read); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.HashingFile - }); - - var aif = AaruFormat.Create(path); - - if(aif == null) - { - _lastMessage = Localization.NotAnAaruFormatFile; - - return false; - } - - if(aif.MD5 == null && - aif.SHA1 == null && - aif.SHA256 == null) - { - _lastMessage = Localization.NoChecksumsFound; - - return false; - } - - string md5 = null; - string sha1 = null; - string sha256 = null; - - if(aif.MD5 != null) - { - char[] chdArray = new char[32]; - - for(int i = 0; i < 16; i++) - { - int nibble1 = aif.MD5[i] >> 4; - int nibble2 = aif.MD5[i] & 0xF; - - nibble1 += nibble1 >= 0xA ? 0x37 : 0x30; - nibble2 += nibble2 >= 0xA ? 0x37 : 0x30; - - chdArray[i * 2] = (char)nibble1; - chdArray[(i * 2) + 1] = (char)nibble2; - } - - md5 = new string(chdArray); - } - - if(aif.SHA1 != null) - { - char[] chdArray = new char[40]; - - for(int i = 0; i < 20; i++) - { - int nibble1 = aif.SHA1[i] >> 4; - int nibble2 = aif.SHA1[i] & 0xF; - - nibble1 += nibble1 >= 0xA ? 0x57 : 0x30; - nibble2 += nibble2 >= 0xA ? 0x57 : 0x30; - - chdArray[i * 2] = (char)nibble1; - chdArray[(i * 2) + 1] = (char)nibble2; - } - - sha1 = new string(chdArray); - } - - if(aif.SHA256 != null) - { - char[] chdArray = new char[64]; - - for(int i = 0; i < 32; i++) - { - int nibble1 = aif.SHA256[i] >> 4; - int nibble2 = aif.SHA256[i] & 0xF; - - nibble1 += nibble1 >= 0xA ? 0x57 : 0x30; - nibble2 += nibble2 >= 0xA ? 0x57 : 0x30; - - chdArray[i * 2] = (char)nibble1; - chdArray[(i * 2) + 1] = (char)nibble2; - } - - sha256 = new string(chdArray); - } - - ulong uSize = (ulong)inFs.Length; - bool mediaInDb = true; - DbMedia dbMedia = null; - bool knownMedia = false; - bool knownMediaWasBigger = false; - - if(sha256 != null) - knownMedia = _pendingMediasBySha256.TryGetValue(sha256, out dbMedia); - - if(!knownMedia && - sha1 != null) - knownMedia = _pendingMediasBySha1.TryGetValue(sha1, out dbMedia); - - if(!knownMedia && - md5 != null) - knownMedia = _pendingMediasByMd5.TryGetValue(md5, out dbMedia); - - dbMedia ??= _ctx.Medias.FirstOrDefault(d => (d.Sha256 != null && d.Sha256 == sha256) || - (d.Sha1 != null && d.Sha1 == sha1) || - (d.Md5 != null && d.Md5 == sha1)); - - if(dbMedia == null) - { - if(_onlyKnown) - { - _lastMessage = Localization.UnknownFile; - - return false; - } - - dbMedia = new DbMedia - { - Md5 = md5, - Sha1 = sha1, - Sha256 = sha256, - Size = uSize, - CreatedOn = DateTime.UtcNow, - UpdatedOn = DateTime.UtcNow, - OriginalFileName = Path.GetFileName(path) - }; - - mediaInDb = false; - } - - if(!knownMedia) - { - if(sha256 != null) - _pendingMediasBySha256[sha256] = dbMedia; - else if(sha1 != null) - _pendingMediasBySha1[sha1] = dbMedia; - else if(md5 != null) - _pendingMediasByMd5[md5] = dbMedia; - } - - string sha256B32 = null; - string sha1B32 = null; - string md5B32 = null; - - if(aif.SHA256 != null) - sha256B32 = Base32.ToBase32String(aif.SHA256); - - if(aif.SHA1 != null) - sha1B32 = Base32.ToBase32String(aif.SHA1); - - if(aif.MD5 != null) - md5B32 = Base32.ToBase32String(aif.SHA1); - - if(dbMedia.Md5 == null && - md5 != null) - { - dbMedia.Md5 = md5; - dbMedia.UpdatedOn = DateTime.UtcNow; - } - - if(dbMedia.Sha1 == null && - sha1 != null) - { - dbMedia.Sha1 = sha1; - dbMedia.UpdatedOn = DateTime.UtcNow; - } - - if(dbMedia.Sha256 == null && - sha256 != null) - { - dbMedia.Sha256 = sha256; - dbMedia.UpdatedOn = DateTime.UtcNow; - } - - if(dbMedia.Size > uSize) - { - knownMediaWasBigger = true; - dbMedia.Size = null; - } - - if(dbMedia.Size == null) - { - dbMedia.Size = uSize; - dbMedia.UpdatedOn = DateTime.UtcNow; - } - - string md5Path = null; - string sha1Path = null; - string sha256Path = null; - string repoPath = null; - - if(md5 != null) - { - md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "md5", - md5B32[0].ToString(), md5B32[1].ToString(), md5B32[2].ToString(), - md5B32[3].ToString(), md5B32[4].ToString()); - - repoPath = md5Path; - - md5Path = Path.Combine(repoPath, md5B32 + ".aif"); - } - - if(sha1 != null) - { - sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "sha1", - sha1B32[0].ToString(), sha1B32[1].ToString(), sha1B32[2].ToString(), - sha1B32[3].ToString(), sha1B32[4].ToString()); - - repoPath = sha1Path; - - sha1Path = Path.Combine(repoPath, sha1B32 + ".aif"); - } - - if(sha256 != null) - { - sha256Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "aaru", "sha256", - sha256B32[0].ToString(), sha256B32[1].ToString(), sha256B32[2].ToString(), - sha256B32[3].ToString(), sha256B32[4].ToString()); - - repoPath = sha256Path; - - sha256Path = Path.Combine(repoPath, sha256B32 + ".aif"); - } - - if(!Directory.Exists(repoPath)) - Directory.CreateDirectory(repoPath); - - if(File.Exists(md5Path)) - { - if(sha256Path != null) - File.Move(md5Path, sha256Path); - else if(sha1Path != null) - File.Move(md5Path, sha1Path); - } - - if(File.Exists(sha1Path) && - sha256Path != null) - File.Move(sha1Path, sha256Path); - - if(sha256Path != null) - repoPath = sha256Path; - else if(sha1Path != null) - repoPath = sha1Path; - else if(md5Path != null) - repoPath = md5Path; - - if(File.Exists(repoPath)) - { - if(!knownMediaWasBigger) - File.Move(repoPath, repoPath + ".bak", true); - else - { - dbMedia.IsInRepo = true; - dbMedia.UpdatedOn = DateTime.UtcNow; - - if(!mediaInDb) - _newMedias.Add(dbMedia); - - inFs.Close(); - - if(_deleteAfterImport) - File.Delete(path); - - return true; - } - } - - inFs.Position = 0; - var outFs = new FileStream(repoPath, FileMode.CreateNew, FileAccess.Write); - - SetProgressBounds2?.Invoke(this, new ProgressBoundsEventArgs - { - Minimum = 0, - Maximum = inFs.Length - }); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.CopyingFile - }); - - byte[] buffer = new byte[BUFFER_SIZE]; - - while(inFs.Position + BUFFER_SIZE <= inFs.Length) - { - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); - - inFs.Read(buffer, 0, buffer.Length); - outFs.Write(buffer, 0, buffer.Length); - } - - buffer = new byte[inFs.Length - inFs.Position]; - - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = inFs.Position - }); + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); inFs.Read(buffer, 0, buffer.Length); - outFs.Write(buffer, 0, buffer.Length); - - SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); - - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.Finishing - }); - - inFs.Close(); - outFs.Close(); - - dbMedia.IsInRepo = true; - dbMedia.UpdatedOn = DateTime.UtcNow; - - if(!mediaInDb) - _newMedias.Add(dbMedia); - - if(_deleteAfterImport) - File.Delete(path); - - if(knownMediaWasBigger) - File.Delete(repoPath + ".bak"); - - return true; + zStream.Write(buffer, 0, buffer.Length); } - catch(Exception e) - { - _lastMessage = Localization.UnhandledExceptionWhenImporting; - return false; - } - } + buffer = new byte[inFs.Length - inFs.Position]; + + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); + + inFs.Read(buffer, 0, buffer.Length); + zStream.Write(buffer, 0, buffer.Length); - void SaveChanges() - { SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); - SetMessage2?.Invoke(this, new MessageEventArgs - { - Message = Localization.SavingChangesToDatabase - }); + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.Finishing + }); - _ctx.SaveChanges(); + inFs.Close(); + zStream.Close(); + outFs.Dispose(); - _ctx.Files.AddRange(_newFiles); - _ctx.Disks.AddRange(_newDisks); - _ctx.Medias.AddRange(_newMedias); + dbFile.IsInRepo = true; + dbFile.UpdatedOn = DateTime.UtcNow; - _ctx.SaveChanges(); + if(!fileInDb) _newFiles.Add(dbFile); - _ctx.Database.ExecuteSqlRaw("DELETE FROM \"RomSetStats\""); + if(_deleteAfterImport) File.Delete(path); - _ctx.RomSetStats.AddRange(_ctx.RomSets.OrderBy(r => r.Id).Select(r => new RomSetStat - { - RomSetId = r.Id, - TotalMachines = r.Machines.Count, - CompleteMachines = - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count == 0 && - m.Files.All(f => f.File.IsInRepo)) + - r.Machines.Count(m => m.Disks.Count > 0 && m.Files.Count == 0 && - m.Disks.All(f => f.Disk.IsInRepo)) + - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count > 0 && m.Files.All(f => f.File.IsInRepo) && - m.Disks.All(f => f.Disk.IsInRepo)), - IncompleteMachines = - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count == 0 && - m.Files.Any(f => !f.File.IsInRepo)) + - r.Machines.Count(m => m.Disks.Count > 0 && m.Files.Count == 0 && - m.Disks.Any(f => !f.Disk.IsInRepo)) + - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count > 0 && - (m.Files.Any(f => !f.File.IsInRepo) || m.Disks.Any(f => !f.Disk.IsInRepo))), - TotalRoms = r.Machines.Sum(m => m.Files.Count) + r.Machines.Sum(m => m.Disks.Count) + - r.Machines.Sum(m => m.Medias.Count), - HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) + - r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) + - r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)), - MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) + - r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) + - r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo)) - })); - - // TODO: Refresh main view - - _ctx.SaveChanges(); - - _newFiles.Clear(); - _newDisks.Clear(); - _newMedias.Clear(); + return true; } - - string GetArchiveFormat(string path, out long counter) + catch(Exception e) { - counter = 0; + _lastMessage = Localization.UnhandledExceptionWhenImporting; - if(!File.Exists(path)) - return null; + return false; + } + } - try + bool ImportDisk(string path) + { + try + { + var inFs = new FileStream(path, FileMode.Open, FileAccess.Read); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.HashingFile + }); + + var chd = CHDFile.Create(path); + + if(chd == null) { - string unarFolder = Path.GetDirectoryName(Settings.Settings.Current.UnArchiverPath); - string extension = Path.GetExtension(Settings.Settings.Current.UnArchiverPath); - string unarFilename = Path.GetFileNameWithoutExtension(Settings.Settings.Current.UnArchiverPath); - string lsarFilename = unarFilename?.Replace("unar", "lsar"); - string lsarPath = Path.Combine(unarFolder, lsarFilename + extension); + _lastMessage = Localization.NotAChdFile; - var lsarProcess = new Process + return false; + } + + if(chd.MD5 == null && chd.SHA1 == null) + { + _lastMessage = Localization.NoChecksumsFound; + + return false; + } + + string md5 = null; + string sha1 = null; + + if(chd.MD5 != null) + { + var chdArray = new char[32]; + + for(var i = 0; i < 16; i++) { - StartInfo = - { - FileName = lsarPath, - CreateNoWindow = true, - RedirectStandardOutput = true, - UseShellExecute = false, - ArgumentList = - { - "-j", - path - } - } + int nibble1 = chd.MD5[i] >> 4; + int nibble2 = chd.MD5[i] & 0xF; + + nibble1 += nibble1 >= 0xA ? 0x37 : 0x30; + nibble2 += nibble2 >= 0xA ? 0x37 : 0x30; + + chdArray[i * 2] = (char)nibble1; + chdArray[i * 2 + 1] = (char)nibble2; + } + + md5 = new string(chdArray); + } + + if(chd.SHA1 != null) + { + var chdArray = new char[40]; + + for(var i = 0; i < 20; i++) + { + int nibble1 = chd.SHA1[i] >> 4; + int nibble2 = chd.SHA1[i] & 0xF; + + nibble1 += nibble1 >= 0xA ? 0x57 : 0x30; + nibble2 += nibble2 >= 0xA ? 0x57 : 0x30; + + chdArray[i * 2] = (char)nibble1; + chdArray[i * 2 + 1] = (char)nibble2; + } + + sha1 = new string(chdArray); + } + + var uSize = (ulong)inFs.Length; + var diskInDb = true; + DbDisk dbDisk = null; + var knownDisk = false; + var knownDiskWasBigger = false; + + if(sha1 != null) knownDisk = _pendingDisksBySha1.TryGetValue(sha1, out dbDisk); + + if(!knownDisk && md5 != null) knownDisk = _pendingDisksByMd5.TryGetValue(md5, out dbDisk); + + dbDisk ??= + _ctx.Disks.FirstOrDefault(d => d.Sha1 != null && d.Sha1 == sha1 || d.Md5 != null && d.Md5 == sha1); + + if(dbDisk == null) + { + if(_onlyKnown) + { + _lastMessage = Localization.UnknownFile; + + return false; + } + + dbDisk = new DbDisk + { + Md5 = md5, + Sha1 = sha1, + Size = uSize, + CreatedOn = DateTime.UtcNow, + UpdatedOn = DateTime.UtcNow, + OriginalFileName = Path.GetFileName(path) }; - lsarProcess.Start(); - string lsarOutput = lsarProcess.StandardOutput.ReadToEnd(); - lsarProcess.WaitForExit(); - string format = null; - var jsReader = new JsonTextReader(new StringReader(lsarOutput)); - - while(jsReader.Read()) - switch(jsReader.TokenType) - { - case JsonToken.PropertyName - when jsReader.Value != null && jsReader.Value.ToString() == "XADFileName": - counter++; - - break; - case JsonToken.PropertyName - when jsReader.Value != null && jsReader.Value.ToString() == "lsarFormatName": - jsReader.Read(); - - if(jsReader.TokenType == JsonToken.String && - jsReader.Value != null) - format = jsReader.Value.ToString(); - - break; - } - - return counter == 0 ? null : format; + diskInDb = false; } - catch(Exception) + + if(!knownDisk) { - return null; + if(sha1 != null) + _pendingDisksBySha1[sha1] = dbDisk; + else if(md5 != null) _pendingDisksByMd5[md5] = dbDisk; } - } - void ExtractArchive(string archivePath, string outPath) + string sha1B32 = null; + string md5B32 = null; + + if(chd.SHA1 != null) sha1B32 = Base32.ToBase32String(chd.SHA1); + + if(chd.MD5 != null) md5B32 = Base32.ToBase32String(chd.SHA1); + + if(dbDisk.Md5 == null && md5 != null) + { + dbDisk.Md5 = md5; + dbDisk.UpdatedOn = DateTime.UtcNow; + } + + if(dbDisk.Sha1 == null && sha1 != null) + { + dbDisk.Sha1 = sha1; + dbDisk.UpdatedOn = DateTime.UtcNow; + } + + if(dbDisk.Size > uSize) + { + knownDiskWasBigger = true; + dbDisk.Size = null; + } + + if(dbDisk.Size == null) + { + dbDisk.Size = uSize; + dbDisk.UpdatedOn = DateTime.UtcNow; + } + + string md5Path = null; + string sha1Path = null; + string repoPath = null; + + if(md5 != null) + { + md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "chd", + "md5", + md5B32[0].ToString(), + md5B32[1].ToString(), + md5B32[2].ToString(), + md5B32[3].ToString(), + md5B32[4].ToString()); + + repoPath = md5Path; + + md5Path = Path.Combine(repoPath, md5B32 + ".chd"); + } + + if(sha1 != null) + { + sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "chd", + "sha1", + sha1B32[0].ToString(), + sha1B32[1].ToString(), + sha1B32[2].ToString(), + sha1B32[3].ToString(), + sha1B32[4].ToString()); + + repoPath = sha1Path; + + sha1Path = Path.Combine(repoPath, sha1B32 + ".chd"); + } + + if(!Directory.Exists(repoPath)) Directory.CreateDirectory(repoPath); + + if(File.Exists(md5Path) && sha1Path != null) File.Move(md5Path, sha1Path); + + if(sha1Path != null) + repoPath = sha1Path; + else if(md5Path != null) repoPath = md5Path; + + if(File.Exists(repoPath)) + { + if(!knownDiskWasBigger) + File.Move(repoPath, repoPath + ".bak", true); + else + { + dbDisk.IsInRepo = true; + dbDisk.UpdatedOn = DateTime.UtcNow; + + if(!diskInDb) _newDisks.Add(dbDisk); + + inFs.Close(); + + if(_deleteAfterImport) File.Delete(path); + + return true; + } + } + + inFs.Position = 0; + var outFs = new FileStream(repoPath, FileMode.CreateNew, FileAccess.Write); + + SetProgressBounds2?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.CopyingFile + }); + + var buffer = new byte[BUFFER_SIZE]; + + while(inFs.Position + BUFFER_SIZE <= inFs.Length) + { + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); + + inFs.Read(buffer, 0, buffer.Length); + outFs.Write(buffer, 0, buffer.Length); + } + + buffer = new byte[inFs.Length - inFs.Position]; + + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); + + inFs.Read(buffer, 0, buffer.Length); + outFs.Write(buffer, 0, buffer.Length); + + SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.Finishing + }); + + inFs.Close(); + outFs.Close(); + + dbDisk.IsInRepo = true; + dbDisk.UpdatedOn = DateTime.UtcNow; + + if(!diskInDb) _newDisks.Add(dbDisk); + + if(_deleteAfterImport) File.Delete(path); + + if(knownDiskWasBigger) File.Delete(repoPath + ".bak"); + + return true; + } + catch(Exception e) { - var unarProcess = new Process + _lastMessage = Localization.UnhandledExceptionWhenImporting; + + return false; + } + } + + bool ImportMedia(string path) + { + try + { + var inFs = new FileStream(path, FileMode.Open, FileAccess.Read); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.HashingFile + }); + + var aif = AaruFormat.Create(path); + + if(aif == null) + { + _lastMessage = Localization.NotAnAaruFormatFile; + + return false; + } + + if(aif.MD5 == null && aif.SHA1 == null && aif.SHA256 == null) + { + _lastMessage = Localization.NoChecksumsFound; + + return false; + } + + string md5 = null; + string sha1 = null; + string sha256 = null; + + if(aif.MD5 != null) + { + var chdArray = new char[32]; + + for(var i = 0; i < 16; i++) + { + int nibble1 = aif.MD5[i] >> 4; + int nibble2 = aif.MD5[i] & 0xF; + + nibble1 += nibble1 >= 0xA ? 0x37 : 0x30; + nibble2 += nibble2 >= 0xA ? 0x37 : 0x30; + + chdArray[i * 2] = (char)nibble1; + chdArray[i * 2 + 1] = (char)nibble2; + } + + md5 = new string(chdArray); + } + + if(aif.SHA1 != null) + { + var chdArray = new char[40]; + + for(var i = 0; i < 20; i++) + { + int nibble1 = aif.SHA1[i] >> 4; + int nibble2 = aif.SHA1[i] & 0xF; + + nibble1 += nibble1 >= 0xA ? 0x57 : 0x30; + nibble2 += nibble2 >= 0xA ? 0x57 : 0x30; + + chdArray[i * 2] = (char)nibble1; + chdArray[i * 2 + 1] = (char)nibble2; + } + + sha1 = new string(chdArray); + } + + if(aif.SHA256 != null) + { + var chdArray = new char[64]; + + for(var i = 0; i < 32; i++) + { + int nibble1 = aif.SHA256[i] >> 4; + int nibble2 = aif.SHA256[i] & 0xF; + + nibble1 += nibble1 >= 0xA ? 0x57 : 0x30; + nibble2 += nibble2 >= 0xA ? 0x57 : 0x30; + + chdArray[i * 2] = (char)nibble1; + chdArray[i * 2 + 1] = (char)nibble2; + } + + sha256 = new string(chdArray); + } + + var uSize = (ulong)inFs.Length; + var mediaInDb = true; + DbMedia dbMedia = null; + var knownMedia = false; + var knownMediaWasBigger = false; + + if(sha256 != null) knownMedia = _pendingMediasBySha256.TryGetValue(sha256, out dbMedia); + + if(!knownMedia && sha1 != null) knownMedia = _pendingMediasBySha1.TryGetValue(sha1, out dbMedia); + + if(!knownMedia && md5 != null) knownMedia = _pendingMediasByMd5.TryGetValue(md5, out dbMedia); + + dbMedia ??= _ctx.Medias.FirstOrDefault(d => d.Sha256 != null && d.Sha256 == sha256 || + d.Sha1 != null && d.Sha1 == sha1 || + d.Md5 != null && d.Md5 == sha1); + + if(dbMedia == null) + { + if(_onlyKnown) + { + _lastMessage = Localization.UnknownFile; + + return false; + } + + dbMedia = new DbMedia + { + Md5 = md5, + Sha1 = sha1, + Sha256 = sha256, + Size = uSize, + CreatedOn = DateTime.UtcNow, + UpdatedOn = DateTime.UtcNow, + OriginalFileName = Path.GetFileName(path) + }; + + mediaInDb = false; + } + + if(!knownMedia) + { + if(sha256 != null) + _pendingMediasBySha256[sha256] = dbMedia; + else if(sha1 != null) + _pendingMediasBySha1[sha1] = dbMedia; + else if(md5 != null) _pendingMediasByMd5[md5] = dbMedia; + } + + string sha256B32 = null; + string sha1B32 = null; + string md5B32 = null; + + if(aif.SHA256 != null) sha256B32 = Base32.ToBase32String(aif.SHA256); + + if(aif.SHA1 != null) sha1B32 = Base32.ToBase32String(aif.SHA1); + + if(aif.MD5 != null) md5B32 = Base32.ToBase32String(aif.SHA1); + + if(dbMedia.Md5 == null && md5 != null) + { + dbMedia.Md5 = md5; + dbMedia.UpdatedOn = DateTime.UtcNow; + } + + if(dbMedia.Sha1 == null && sha1 != null) + { + dbMedia.Sha1 = sha1; + dbMedia.UpdatedOn = DateTime.UtcNow; + } + + if(dbMedia.Sha256 == null && sha256 != null) + { + dbMedia.Sha256 = sha256; + dbMedia.UpdatedOn = DateTime.UtcNow; + } + + if(dbMedia.Size > uSize) + { + knownMediaWasBigger = true; + dbMedia.Size = null; + } + + if(dbMedia.Size == null) + { + dbMedia.Size = uSize; + dbMedia.UpdatedOn = DateTime.UtcNow; + } + + string md5Path = null; + string sha1Path = null; + string sha256Path = null; + string repoPath = null; + + if(md5 != null) + { + md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "md5", + md5B32[0].ToString(), + md5B32[1].ToString(), + md5B32[2].ToString(), + md5B32[3].ToString(), + md5B32[4].ToString()); + + repoPath = md5Path; + + md5Path = Path.Combine(repoPath, md5B32 + ".aif"); + } + + if(sha1 != null) + { + sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "sha1", + sha1B32[0].ToString(), + sha1B32[1].ToString(), + sha1B32[2].ToString(), + sha1B32[3].ToString(), + sha1B32[4].ToString()); + + repoPath = sha1Path; + + sha1Path = Path.Combine(repoPath, sha1B32 + ".aif"); + } + + if(sha256 != null) + { + sha256Path = Path.Combine(Settings.Settings.Current.RepositoryPath, + "aaru", + "sha256", + sha256B32[0].ToString(), + sha256B32[1].ToString(), + sha256B32[2].ToString(), + sha256B32[3].ToString(), + sha256B32[4].ToString()); + + repoPath = sha256Path; + + sha256Path = Path.Combine(repoPath, sha256B32 + ".aif"); + } + + if(!Directory.Exists(repoPath)) Directory.CreateDirectory(repoPath); + + if(File.Exists(md5Path)) + { + if(sha256Path != null) + File.Move(md5Path, sha256Path); + else if(sha1Path != null) File.Move(md5Path, sha1Path); + } + + if(File.Exists(sha1Path) && sha256Path != null) File.Move(sha1Path, sha256Path); + + if(sha256Path != null) + repoPath = sha256Path; + else if(sha1Path != null) + repoPath = sha1Path; + else if(md5Path != null) repoPath = md5Path; + + if(File.Exists(repoPath)) + { + if(!knownMediaWasBigger) + File.Move(repoPath, repoPath + ".bak", true); + else + { + dbMedia.IsInRepo = true; + dbMedia.UpdatedOn = DateTime.UtcNow; + + if(!mediaInDb) _newMedias.Add(dbMedia); + + inFs.Close(); + + if(_deleteAfterImport) File.Delete(path); + + return true; + } + } + + inFs.Position = 0; + var outFs = new FileStream(repoPath, FileMode.CreateNew, FileAccess.Write); + + SetProgressBounds2?.Invoke(this, + new ProgressBoundsEventArgs + { + Minimum = 0, + Maximum = inFs.Length + }); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.CopyingFile + }); + + var buffer = new byte[BUFFER_SIZE]; + + while(inFs.Position + BUFFER_SIZE <= inFs.Length) + { + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); + + inFs.Read(buffer, 0, buffer.Length); + outFs.Write(buffer, 0, buffer.Length); + } + + buffer = new byte[inFs.Length - inFs.Position]; + + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = inFs.Position + }); + + inFs.Read(buffer, 0, buffer.Length); + outFs.Write(buffer, 0, buffer.Length); + + SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.Finishing + }); + + inFs.Close(); + outFs.Close(); + + dbMedia.IsInRepo = true; + dbMedia.UpdatedOn = DateTime.UtcNow; + + if(!mediaInDb) _newMedias.Add(dbMedia); + + if(_deleteAfterImport) File.Delete(path); + + if(knownMediaWasBigger) File.Delete(repoPath + ".bak"); + + return true; + } + catch(Exception e) + { + _lastMessage = Localization.UnhandledExceptionWhenImporting; + + return false; + } + } + + void SaveChanges() + { + SetIndeterminateProgress2?.Invoke(this, System.EventArgs.Empty); + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = Localization.SavingChangesToDatabase + }); + + _ctx.SaveChanges(); + + _ctx.Files.AddRange(_newFiles); + _ctx.Disks.AddRange(_newDisks); + _ctx.Medias.AddRange(_newMedias); + + _ctx.SaveChanges(); + + _ctx.Database.ExecuteSqlRaw("DELETE FROM \"RomSetStats\""); + + _ctx.RomSetStats.AddRange(_ctx.RomSets.OrderBy(r => r.Id) + .Select(r => new RomSetStat + { + RomSetId = r.Id, + TotalMachines = r.Machines.Count, + CompleteMachines = + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count == 0 && + m.Files.All(f => f.File.IsInRepo)) + + r.Machines.Count(m => m.Disks.Count > 0 && + m.Files.Count == 0 && + m.Disks.All(f => f.Disk.IsInRepo)) + + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count > 0 && + m.Files.All(f => f.File.IsInRepo) && + m.Disks.All(f => f.Disk.IsInRepo)), + IncompleteMachines = + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count == 0 && + m.Files.Any(f => !f.File.IsInRepo)) + + r.Machines.Count(m => m.Disks.Count > 0 && + m.Files.Count == 0 && + m.Disks.Any(f => !f.Disk.IsInRepo)) + + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count > 0 && + (m.Files.Any(f => !f.File.IsInRepo) || + m.Disks.Any(f => !f.Disk.IsInRepo))), + TotalRoms = + r.Machines.Sum(m => m.Files.Count) + + r.Machines.Sum(m => m.Disks.Count) + + r.Machines.Sum(m => m.Medias.Count), + HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) + + r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) + + r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)), + MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) + + r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) + + r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo)) + })); + + // TODO: Refresh main view + + _ctx.SaveChanges(); + + _newFiles.Clear(); + _newDisks.Clear(); + _newMedias.Clear(); + } + + string GetArchiveFormat(string path, out long counter) + { + counter = 0; + + if(!File.Exists(path)) return null; + + try + { + string unarFolder = Path.GetDirectoryName(Settings.Settings.Current.UnArchiverPath); + string extension = Path.GetExtension(Settings.Settings.Current.UnArchiverPath); + string unarFilename = Path.GetFileNameWithoutExtension(Settings.Settings.Current.UnArchiverPath); + string lsarFilename = unarFilename?.Replace("unar", "lsar"); + string lsarPath = Path.Combine(unarFolder, lsarFilename + extension); + + var lsarProcess = new Process { StartInfo = { - FileName = Settings.Settings.Current.UnArchiverPath, + FileName = lsarPath, CreateNoWindow = true, RedirectStandardOutput = true, UseShellExecute = false, ArgumentList = { - "-o", - outPath, - "-r", - "-D", - "-k", - "hidden", - archivePath + "-j", + path } } }; - long counter = 0; + lsarProcess.Start(); + string lsarOutput = lsarProcess.StandardOutput.ReadToEnd(); + lsarProcess.WaitForExit(); + string format = null; + var jsReader = new JsonTextReader(new StringReader(lsarOutput)); - unarProcess.OutputDataReceived += (sender, e) => + while(jsReader.Read()) { - counter++; - - SetMessage2?.Invoke(this, new MessageEventArgs + switch(jsReader.TokenType) { - Message = e.Data - }); + case JsonToken.PropertyName + when jsReader.Value != null && jsReader.Value.ToString() == "XADFileName": + counter++; - SetProgress2?.Invoke(this, new ProgressEventArgs - { - Value = counter - }); - }; + break; + case JsonToken.PropertyName + when jsReader.Value != null && jsReader.Value.ToString() == "lsarFormatName": + jsReader.Read(); - unarProcess.Start(); - unarProcess.BeginOutputReadLine(); - unarProcess.WaitForExit(); - unarProcess.Close(); + if(jsReader.TokenType == JsonToken.String && jsReader.Value != null) + format = jsReader.Value.ToString(); + + break; + } + } + + return counter == 0 ? null : format; + } + catch(Exception) + { + return null; } } + + void ExtractArchive(string archivePath, string outPath) + { + var unarProcess = new Process + { + StartInfo = + { + FileName = Settings.Settings.Current.UnArchiverPath, + CreateNoWindow = true, + RedirectStandardOutput = true, + UseShellExecute = false, + ArgumentList = + { + "-o", + outPath, + "-r", + "-D", + "-k", + "hidden", + archivePath + } + } + }; + + long counter = 0; + + unarProcess.OutputDataReceived += (sender, e) => + { + counter++; + + SetMessage2?.Invoke(this, + new MessageEventArgs + { + Message = e.Data + }); + + SetProgress2?.Invoke(this, + new ProgressEventArgs + { + Value = counter + }); + }; + + unarProcess.Start(); + unarProcess.BeginOutputReadLine(); + unarProcess.WaitForExit(); + unarProcess.Close(); + } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Context.cs b/RomRepoMgr.Database/Context.cs index 0697d1d..e705920 100644 --- a/RomRepoMgr.Database/Context.cs +++ b/RomRepoMgr.Database/Context.cs @@ -27,181 +27,177 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using RomRepoMgr.Database.Models; -namespace RomRepoMgr.Database +namespace RomRepoMgr.Database; + +public sealed class Context : DbContext { - public sealed class Context : DbContext + public Context(DbContextOptions options) : base(options) {} + + public DbSet Files { get; set; } + public DbSet RomSets { get; set; } + public DbSet Machines { get; set; } + public DbSet FilesByMachines { get; set; } + public DbSet Disks { get; set; } + public DbSet DisksByMachines { get; set; } + public DbSet Medias { get; set; } + public DbSet MediasByMachines { get; set; } + public DbSet RomSetStats { get; set; } + + public static Context Create(string dbPath) { - public Context(DbContextOptions options) : base(options) {} + var optionsBuilder = new DbContextOptionsBuilder(); - public DbSet Files { get; set; } - public DbSet RomSets { get; set; } - public DbSet Machines { get; set; } - public DbSet FilesByMachines { get; set; } - public DbSet Disks { get; set; } - public DbSet DisksByMachines { get; set; } - public DbSet Medias { get; set; } - public DbSet MediasByMachines { get; set; } - public DbSet RomSetStats { get; set; } + optionsBuilder.UseLazyLoadingProxies() +#if DEBUG + .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole())) +#endif + .UseSqlite($"Data Source={dbPath}"); - public static Context Create(string dbPath) + return new Context(optionsBuilder.Options); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(entity => { - var optionsBuilder = new DbContextOptionsBuilder(); + entity.HasIndex(e => e.Crc32); - optionsBuilder.UseLazyLoadingProxies() - #if DEBUG - .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole())) - #endif - .UseSqlite($"Data Source={dbPath}"); + entity.HasIndex(e => e.Md5); - return new Context(optionsBuilder.Options); - } + entity.HasIndex(e => e.Sha1); - protected override void OnModelCreating(ModelBuilder modelBuilder) + entity.HasIndex(e => e.Sha256); + + entity.HasIndex(e => e.Sha384); + + entity.HasIndex(e => e.Sha512); + + entity.HasIndex(e => e.Size); + + entity.HasIndex(e => e.IsInRepo); + + entity.HasIndex(e => new + { + e.Crc32, + e.Size + }); + + entity.HasIndex(e => new + { + e.Md5, + e.Size + }); + + entity.HasIndex(e => new + { + e.Sha1, + e.Size + }); + + entity.HasIndex(e => new + { + e.Sha256, + e.Size + }); + + entity.HasIndex(e => new + { + e.Sha384, + e.Size + }); + + entity.HasIndex(e => new + { + e.Sha512, + e.Size + }); + }); + + modelBuilder.Entity(entity => { - base.OnModelCreating(modelBuilder); + entity.HasIndex(e => e.Author); - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Crc32); + entity.HasIndex(e => e.Comment); - entity.HasIndex(e => e.Md5); + entity.HasIndex(e => e.Date); - entity.HasIndex(e => e.Sha1); + entity.HasIndex(e => e.Description); - entity.HasIndex(e => e.Sha256); + entity.HasIndex(e => e.Homepage); - entity.HasIndex(e => e.Sha384); + entity.HasIndex(e => e.Name); - entity.HasIndex(e => e.Sha512); + entity.HasIndex(e => e.Version); - entity.HasIndex(e => e.Size); + entity.HasIndex(e => e.Filename); - entity.HasIndex(e => e.IsInRepo); + entity.HasIndex(e => e.Sha384); - entity.HasIndex(e => new - { - e.Crc32, - e.Size - }); + entity.HasIndex(e => e.Category); + }); - entity.HasIndex(e => new - { - e.Md5, - e.Size - }); + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.Name); - entity.HasIndex(e => new - { - e.Sha1, - e.Size - }); + entity.HasOne(e => e.RomSet).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); + }); - entity.HasIndex(e => new - { - e.Sha256, - e.Size - }); + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.Name); - entity.HasIndex(e => new - { - e.Sha384, - e.Size - }); + entity.HasOne(e => e.Machine).WithMany(e => e.Files).OnDelete(DeleteBehavior.Cascade); - entity.HasIndex(e => new - { - e.Sha512, - e.Size - }); - }); + entity.HasOne(e => e.File).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); + }); - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Author); + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.Md5); - entity.HasIndex(e => e.Comment); + entity.HasIndex(e => e.Sha1); - entity.HasIndex(e => e.Date); + entity.HasIndex(e => e.Size); - entity.HasIndex(e => e.Description); + entity.HasIndex(e => e.IsInRepo); + }); - entity.HasIndex(e => e.Homepage); + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.Name); - entity.HasIndex(e => e.Name); + entity.HasOne(e => e.Machine).WithMany(e => e.Disks).OnDelete(DeleteBehavior.Cascade); - entity.HasIndex(e => e.Version); + entity.HasOne(e => e.Disk).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); + }); - entity.HasIndex(e => e.Filename); + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.Md5); - entity.HasIndex(e => e.Sha384); + entity.HasIndex(e => e.Sha1); - entity.HasIndex(e => e.Category); - }); + entity.HasIndex(e => e.Sha256); - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Name); + entity.HasIndex(e => e.SpamSum); - entity.HasOne(e => e.RomSet).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); - }); + entity.HasIndex(e => e.Size); - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Name); + entity.HasIndex(e => e.IsInRepo); + }); - entity.HasOne(e => e.Machine).WithMany(e => e.Files).OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.Name); - entity.HasOne(e => e.File).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); - }); + entity.HasOne(e => e.Machine).WithMany(e => e.Medias).OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Md5); + entity.HasOne(e => e.Media).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); + }); - entity.HasIndex(e => e.Sha1); - - entity.HasIndex(e => e.Size); - - entity.HasIndex(e => e.IsInRepo); - }); - - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Name); - - entity.HasOne(e => e.Machine).WithMany(e => e.Disks).OnDelete(DeleteBehavior.Cascade); - - entity.HasOne(e => e.Disk).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Md5); - - entity.HasIndex(e => e.Sha1); - - entity.HasIndex(e => e.Sha256); - - entity.HasIndex(e => e.SpamSum); - - entity.HasIndex(e => e.Size); - - entity.HasIndex(e => e.IsInRepo); - }); - - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.Name); - - entity.HasOne(e => e.Machine).WithMany(e => e.Medias).OnDelete(DeleteBehavior.Cascade); - - entity.HasOne(e => e.Media).WithMany(e => e.Machines).OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity(entity => - { - entity.HasOne(e => e.RomSet).WithOne(e => e.Statistics); - }); - } + modelBuilder.Entity(entity => { entity.HasOne(e => e.RomSet).WithOne(e => e.Statistics); }); } } \ No newline at end of file diff --git a/RomRepoMgr.Database/ContextFactory.cs b/RomRepoMgr.Database/ContextFactory.cs index 5d6db18..7333159 100644 --- a/RomRepoMgr.Database/ContextFactory.cs +++ b/RomRepoMgr.Database/ContextFactory.cs @@ -25,10 +25,9 @@ using Microsoft.EntityFrameworkCore.Design; -namespace RomRepoMgr.Database +namespace RomRepoMgr.Database; + +public class ContextFactory : IDesignTimeDbContextFactory { - public class ContextFactory : IDesignTimeDbContextFactory - { - public Context CreateDbContext(string[] args) => Context.Create("romrepo.db"); - } + public Context CreateDbContext(string[] args) => Context.Create("romrepo.db"); } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/BaseModel.cs b/RomRepoMgr.Database/Models/BaseModel.cs index c1036a6..e6aabba 100644 --- a/RomRepoMgr.Database/Models/BaseModel.cs +++ b/RomRepoMgr.Database/Models/BaseModel.cs @@ -25,13 +25,12 @@ using System; -namespace RomRepoMgr.Database.Models -{ - public abstract class BaseModel - { - public TKey Id { get; set; } +namespace RomRepoMgr.Database.Models; - public DateTime CreatedOn { get; set; } - public DateTime UpdatedOn { get; set; } - } +public abstract class BaseModel +{ + public TKey Id { get; set; } + + public DateTime CreatedOn { get; set; } + public DateTime UpdatedOn { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/DbDisk.cs b/RomRepoMgr.Database/Models/DbDisk.cs index e93b042..764dc40 100644 --- a/RomRepoMgr.Database/Models/DbDisk.cs +++ b/RomRepoMgr.Database/Models/DbDisk.cs @@ -27,18 +27,17 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class DbDisk : BaseModel { - public class DbDisk : BaseModel - { - public ulong? Size { get; set; } - [StringLength(32, MinimumLength = 32)] - public string Md5 { get; set; } - [StringLength(40, MinimumLength = 40)] - public string Sha1 { get; set; } - [DefaultValue(false)] - public bool IsInRepo { get; set; } - public string OriginalFileName { get; set; } - public virtual ICollection Machines { get; set; } - } + public ulong? Size { get; set; } + [StringLength(32, MinimumLength = 32)] + public string Md5 { get; set; } + [StringLength(40, MinimumLength = 40)] + public string Sha1 { get; set; } + [DefaultValue(false)] + public bool IsInRepo { get; set; } + public string OriginalFileName { get; set; } + public virtual ICollection Machines { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/DbFile.cs b/RomRepoMgr.Database/Models/DbFile.cs index 0a59446..80130aa 100644 --- a/RomRepoMgr.Database/Models/DbFile.cs +++ b/RomRepoMgr.Database/Models/DbFile.cs @@ -27,27 +27,26 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class DbFile : BaseModel { - public class DbFile : BaseModel - { - [Required] - public ulong Size { get; set; } - [StringLength(8, MinimumLength = 8)] - public string Crc32 { get; set; } - [StringLength(32, MinimumLength = 32)] - public string Md5 { get; set; } - [StringLength(40, MinimumLength = 40)] - public string Sha1 { get; set; } - [StringLength(64, MinimumLength = 64)] - public string Sha256 { get; set; } - [StringLength(96, MinimumLength = 96)] - public string Sha384 { get; set; } - [StringLength(128, MinimumLength = 128)] - public string Sha512 { get; set; } - [DefaultValue(false)] - public bool IsInRepo { get; set; } - public string OriginalFileName { get; set; } - public virtual ICollection Machines { get; set; } - } + [Required] + public ulong Size { get; set; } + [StringLength(8, MinimumLength = 8)] + public string Crc32 { get; set; } + [StringLength(32, MinimumLength = 32)] + public string Md5 { get; set; } + [StringLength(40, MinimumLength = 40)] + public string Sha1 { get; set; } + [StringLength(64, MinimumLength = 64)] + public string Sha256 { get; set; } + [StringLength(96, MinimumLength = 96)] + public string Sha384 { get; set; } + [StringLength(128, MinimumLength = 128)] + public string Sha512 { get; set; } + [DefaultValue(false)] + public bool IsInRepo { get; set; } + public string OriginalFileName { get; set; } + public virtual ICollection Machines { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/DbMedia.cs b/RomRepoMgr.Database/Models/DbMedia.cs index bc64014..b09dd6c 100644 --- a/RomRepoMgr.Database/Models/DbMedia.cs +++ b/RomRepoMgr.Database/Models/DbMedia.cs @@ -27,21 +27,20 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class DbMedia : BaseModel { - public class DbMedia : BaseModel - { - public ulong? Size { get; set; } - [StringLength(32, MinimumLength = 32)] - public string Md5 { get; set; } - [StringLength(40, MinimumLength = 40)] - public string Sha1 { get; set; } - [StringLength(64, MinimumLength = 64)] - public string Sha256 { get; set; } - public string SpamSum { get; set; } - [DefaultValue(false)] - public bool IsInRepo { get; set; } - public string OriginalFileName { get; set; } - public virtual IEnumerable Machines { get; set; } - } + public ulong? Size { get; set; } + [StringLength(32, MinimumLength = 32)] + public string Md5 { get; set; } + [StringLength(40, MinimumLength = 40)] + public string Sha1 { get; set; } + [StringLength(64, MinimumLength = 64)] + public string Sha256 { get; set; } + public string SpamSum { get; set; } + [DefaultValue(false)] + public bool IsInRepo { get; set; } + public string OriginalFileName { get; set; } + public virtual IEnumerable Machines { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/DiskByMachine.cs b/RomRepoMgr.Database/Models/DiskByMachine.cs index a592ce1..44f03c9 100644 --- a/RomRepoMgr.Database/Models/DiskByMachine.cs +++ b/RomRepoMgr.Database/Models/DiskByMachine.cs @@ -25,19 +25,18 @@ using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class DiskByMachine { - public class DiskByMachine - { - [Key] - public ulong Id { get; set; } - [Required] - public virtual DbDisk Disk { get; set; } - [Required] - public virtual Machine Machine { get; set; } - [Required] - public string Name { get; set; } - public ulong DiskId { get; set; } - public ulong MachineId { get; set; } - } + [Key] + public ulong Id { get; set; } + [Required] + public virtual DbDisk Disk { get; set; } + [Required] + public virtual Machine Machine { get; set; } + [Required] + public string Name { get; set; } + public ulong DiskId { get; set; } + public ulong MachineId { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/FileByMachine.cs b/RomRepoMgr.Database/Models/FileByMachine.cs index 477513a..61aa2d8 100644 --- a/RomRepoMgr.Database/Models/FileByMachine.cs +++ b/RomRepoMgr.Database/Models/FileByMachine.cs @@ -26,22 +26,21 @@ using System; using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class FileByMachine { - public class FileByMachine - { - [Key] - public ulong Id { get; set; } - [Required] - public virtual DbFile File { get; set; } - [Required] - public virtual Machine Machine { get; set; } - [Required] - public string Name { get; set; } - public ulong FileId { get; set; } - public ulong MachineId { get; set; } - public DateTime? FileLastModification { get; set; } - [StringLength(4096)] - public string Path { get; set; } - } + [Key] + public ulong Id { get; set; } + [Required] + public virtual DbFile File { get; set; } + [Required] + public virtual Machine Machine { get; set; } + [Required] + public string Name { get; set; } + public ulong FileId { get; set; } + public ulong MachineId { get; set; } + public DateTime? FileLastModification { get; set; } + [StringLength(4096)] + public string Path { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/Machine.cs b/RomRepoMgr.Database/Models/Machine.cs index 8d4cac5..4eb4e9f 100644 --- a/RomRepoMgr.Database/Models/Machine.cs +++ b/RomRepoMgr.Database/Models/Machine.cs @@ -26,17 +26,16 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class Machine : BaseModel { - public class Machine : BaseModel - { - [Required] - public string Name { get; set; } - [Required] - public virtual RomSet RomSet { get; set; } - public virtual ICollection Files { get; set; } - public virtual ICollection Disks { get; set; } - public virtual ICollection Medias { get; set; } - public long RomSetId { get; set; } - } + [Required] + public string Name { get; set; } + [Required] + public virtual RomSet RomSet { get; set; } + public virtual ICollection Files { get; set; } + public virtual ICollection Disks { get; set; } + public virtual ICollection Medias { get; set; } + public long RomSetId { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/MediaByMachine.cs b/RomRepoMgr.Database/Models/MediaByMachine.cs index e77f5d9..025dde0 100644 --- a/RomRepoMgr.Database/Models/MediaByMachine.cs +++ b/RomRepoMgr.Database/Models/MediaByMachine.cs @@ -25,19 +25,18 @@ using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class MediaByMachine { - public class MediaByMachine - { - [Key] - public ulong Id { get; set; } - [Required] - public virtual DbMedia Media { get; set; } - [Required] - public virtual Machine Machine { get; set; } - [Required] - public string Name { get; set; } - public ulong MediaId { get; set; } - public ulong MachineId { get; set; } - } + [Key] + public ulong Id { get; set; } + [Required] + public virtual DbMedia Media { get; set; } + [Required] + public virtual Machine Machine { get; set; } + [Required] + public string Name { get; set; } + public ulong MediaId { get; set; } + public ulong MachineId { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/RomSet.cs b/RomRepoMgr.Database/Models/RomSet.cs index 527a875..e692702 100644 --- a/RomRepoMgr.Database/Models/RomSet.cs +++ b/RomRepoMgr.Database/Models/RomSet.cs @@ -26,24 +26,24 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models -{ - public class RomSet : BaseModel - { - public string Author { get; set; } - public string Comment { get; set; } - public string Date { get; set; } - public string Description { get; set; } - public string Homepage { get; set; } - public string Name { get; set; } - public string Version { get; set; } - [Required] - public string Filename { get; set; } - [Required, StringLength(96, MinimumLength = 96)] - public string Sha384 { get; set; } - public string Category { get; set; } - public virtual RomSetStat Statistics { get; set; } +namespace RomRepoMgr.Database.Models; - public virtual ICollection Machines { get; set; } - } +public class RomSet : BaseModel +{ + public string Author { get; set; } + public string Comment { get; set; } + public string Date { get; set; } + public string Description { get; set; } + public string Homepage { get; set; } + public string Name { get; set; } + public string Version { get; set; } + [Required] + public string Filename { get; set; } + [Required] + [StringLength(96, MinimumLength = 96)] + public string Sha384 { get; set; } + public string Category { get; set; } + public virtual RomSetStat Statistics { get; set; } + + public virtual ICollection Machines { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Models/RomSetStat.cs b/RomRepoMgr.Database/Models/RomSetStat.cs index 2b0bd82..8ab00cc 100644 --- a/RomRepoMgr.Database/Models/RomSetStat.cs +++ b/RomRepoMgr.Database/Models/RomSetStat.cs @@ -1,17 +1,16 @@ using System.ComponentModel.DataAnnotations; -namespace RomRepoMgr.Database.Models +namespace RomRepoMgr.Database.Models; + +public class RomSetStat { - public class RomSetStat - { - public long TotalMachines { get; set; } - public long CompleteMachines { get; set; } - public long IncompleteMachines { get; set; } - public long TotalRoms { get; set; } - public long HaveRoms { get; set; } - public long MissRoms { get; set; } - public virtual RomSet RomSet { get; set; } - [Key] - public long RomSetId { get; set; } - } + public long TotalMachines { get; set; } + public long CompleteMachines { get; set; } + public long IncompleteMachines { get; set; } + public long TotalRoms { get; set; } + public long HaveRoms { get; set; } + public long MissRoms { get; set; } + public virtual RomSet RomSet { get; set; } + [Key] + public long RomSetId { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Resources/Localization.Designer.cs b/RomRepoMgr.Database/Resources/Localization.Designer.cs index 64b1ec2..5d3cc1d 100644 --- a/RomRepoMgr.Database/Resources/Localization.Designer.cs +++ b/RomRepoMgr.Database/Resources/Localization.Designer.cs @@ -11,32 +11,46 @@ namespace RomRepoMgr.Database.Resources { using System; - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Localization { - private static System.Resources.ResourceManager resourceMan; + private static global::System.Resources.ResourceManager resourceMan; - private static System.Globalization.CultureInfo resourceCulture; + private static global::System.Globalization.CultureInfo resourceCulture; - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Localization() { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Resources.ResourceManager ResourceManager { + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RomRepoMgr.Database.Resources.Localization", typeof(Localization).Assembly); + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RomRepoMgr.Database.Resources.Localization", typeof(Localization).Assembly); resourceMan = temp; } return resourceMan; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Globalization.CultureInfo Culture { + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -45,6 +59,9 @@ namespace RomRepoMgr.Database.Resources { } } + /// + /// Looks up a localized string similar to Settings are not initialized!. + /// internal static string Settings_not_initialized { get { return ResourceManager.GetString("Settings_not_initialized", resourceCulture); diff --git a/RomRepoMgr.Database/Resources/Localization.es.resx b/RomRepoMgr.Database/Resources/Localization.es.resx index 21e7579..0c39815 100644 --- a/RomRepoMgr.Database/Resources/Localization.es.resx +++ b/RomRepoMgr.Database/Resources/Localization.es.resx @@ -1,17 +1,21 @@  - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + ¡Las preferencias no están inicializadas! \ No newline at end of file diff --git a/RomRepoMgr.Database/Resources/Localization.resx b/RomRepoMgr.Database/Resources/Localization.resx index 569eb36..ef9b677 100644 --- a/RomRepoMgr.Database/Resources/Localization.resx +++ b/RomRepoMgr.Database/Resources/Localization.resx @@ -1,22 +1,27 @@ - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Settings are not initialized! \ No newline at end of file diff --git a/RomRepoMgr.Database/RomRepoMgr.Database.csproj b/RomRepoMgr.Database/RomRepoMgr.Database.csproj index 91aad25..4d6a30b 100644 --- a/RomRepoMgr.Database/RomRepoMgr.Database.csproj +++ b/RomRepoMgr.Database/RomRepoMgr.Database.csproj @@ -1,42 +1,42 @@ - - net9.0 - default - + + net9.0 + default + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + - - - + + + - - - + + + - - - ResXFileCodeGenerator - Localization.Designer.cs - - + + + ResXFileCodeGenerator + Localization.Designer.cs + + - - - True - True - Language.resx - - + + + True + True + Language.resx + + diff --git a/RomRepoMgr.Settings/Interop/DetectOS.cs b/RomRepoMgr.Settings/Interop/DetectOS.cs index 9947dc7..3431d2f 100644 --- a/RomRepoMgr.Settings/Interop/DetectOS.cs +++ b/RomRepoMgr.Settings/Interop/DetectOS.cs @@ -43,344 +43,380 @@ using System.Runtime.InteropServices; using System.Security.Principal; using RomRepoMgr.Settings.Resources; -namespace Aaru.CommonTypes.Interop +namespace Aaru.CommonTypes.Interop; + +public static class DetectOS { - public static class DetectOS + public static readonly bool IsMono = + RuntimeInformation.FrameworkDescription.StartsWith("Mono", StringComparison.Ordinal); + public static readonly bool IsNetFramework = + RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.Ordinal); + public static readonly bool IsNetCore = + RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.Ordinal); + public static readonly bool IsNetNative = + RuntimeInformation.FrameworkDescription.StartsWith(".NET Native", StringComparison.Ordinal); + + /// Checks if the underlying runtime runs in 64-bit mode + public static readonly bool Is64Bit = IntPtr.Size == 8; + + /// Checks if the underlying runtime runs in 32-bit mode + public static readonly bool Is32Bit = IntPtr.Size == 4; + + public static bool IsWindows => GetRealPlatformID() == PlatformID.Win32NT || + GetRealPlatformID() == PlatformID.Win32S || + GetRealPlatformID() == PlatformID.Win32Windows || + GetRealPlatformID() == PlatformID.WinCE || + GetRealPlatformID() == PlatformID.WindowsPhone || + GetRealPlatformID() == PlatformID.Xbox; + + public static bool IsAdmin { - public static readonly bool IsMono = - RuntimeInformation.FrameworkDescription.StartsWith("Mono", StringComparison.Ordinal); - public static readonly bool IsNetFramework = - RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.Ordinal); - public static readonly bool IsNetCore = - RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.Ordinal); - public static readonly bool IsNetNative = - RuntimeInformation.FrameworkDescription.StartsWith(".NET Native", StringComparison.Ordinal); - - /// Checks if the underlying runtime runs in 64-bit mode - public static readonly bool Is64Bit = IntPtr.Size == 8; - - /// Checks if the underlying runtime runs in 32-bit mode - public static readonly bool Is32Bit = IntPtr.Size == 4; - - public static bool IsWindows => GetRealPlatformID() == PlatformID.Win32NT || - GetRealPlatformID() == PlatformID.Win32S || - GetRealPlatformID() == PlatformID.Win32Windows || - GetRealPlatformID() == PlatformID.WinCE || - GetRealPlatformID() == PlatformID.WindowsPhone || - GetRealPlatformID() == PlatformID.Xbox; - - public static bool IsAdmin + get { - get + if(!OperatingSystem.IsWindows()) return Environment.UserName == "root"; + + bool isAdmin; + WindowsIdentity user = null; + + try { - if(!OperatingSystem.IsWindows()) - return Environment.UserName == "root"; - - bool isAdmin; - WindowsIdentity user = null; - - try - { - user = WindowsIdentity.GetCurrent(); - var principal = new WindowsPrincipal(user); - isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator); - } - catch(UnauthorizedAccessException) - { - isAdmin = false; - } - catch(Exception) - { - isAdmin = false; - } - finally - { - user?.Dispose(); - } - - return isAdmin; + user = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(user); + isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator); } - } - - [DllImport("libc", SetLastError = true)] - static extern int uname(out utsname name); - - [DllImport("libc", SetLastError = true, EntryPoint = "sysctlbyname", CharSet = CharSet.Ansi)] - static extern int OSX_sysctlbyname(string name, IntPtr oldp, IntPtr oldlenp, IntPtr newp, uint newlen); - - /// Gets the real platform ID, not the incomplete .NET framework one - /// Platform ID - /// Unhandled exception - public static PlatformID GetRealPlatformID() - { - if((int)Environment.OSVersion.Platform < 4 || - (int)Environment.OSVersion.Platform == 5) - return (PlatformID)(int)Environment.OSVersion.Platform; - - int error = uname(out utsname unixname); - - if(error != 0) - throw new Exception(string.Format(Localization.Unhandled_exception_uname, Marshal.GetLastWin32Error())); - - switch(unixname.sysname) + catch(UnauthorizedAccessException) { - // TODO: Differentiate Linux, Android, Tizen - case "Linux": - { - #if __ANDROID__ + isAdmin = false; + } + catch(Exception) + { + isAdmin = false; + } + finally + { + user?.Dispose(); + } + + return isAdmin; + } + } + + [DllImport("libc", SetLastError = true)] + static extern int uname(out utsname name); + + [DllImport("libc", SetLastError = true, EntryPoint = "sysctlbyname", CharSet = CharSet.Ansi)] + static extern int OSX_sysctlbyname(string name, IntPtr oldp, IntPtr oldlenp, IntPtr newp, uint newlen); + + /// Gets the real platform ID, not the incomplete .NET framework one + /// Platform ID + /// Unhandled exception + public static PlatformID GetRealPlatformID() + { + if((int)Environment.OSVersion.Platform < 4 || (int)Environment.OSVersion.Platform == 5) + return (PlatformID)(int)Environment.OSVersion.Platform; + + int error = uname(out utsname unixname); + + if(error != 0) + throw new Exception(string.Format(Localization.Unhandled_exception_uname, Marshal.GetLastWin32Error())); + + switch(unixname.sysname) + { + // TODO: Differentiate Linux, Android, Tizen + case "Linux": + { +#if __ANDROID__ return PlatformID.Android; - #else - return PlatformID.Linux; - #endif +#else + return PlatformID.Linux; +#endif + } + + case "Darwin": + { + IntPtr pLen = Marshal.AllocHGlobal(sizeof(int)); + int osxError = OSX_sysctlbyname("hw.machine", IntPtr.Zero, pLen, IntPtr.Zero, 0); + + if(osxError != 0) + { + Marshal.FreeHGlobal(pLen); + + throw new Exception(string.Format(Localization.Unhandled_exception_uname, + Marshal.GetLastWin32Error())); } - case "Darwin": + int length = Marshal.ReadInt32(pLen); + IntPtr pStr = Marshal.AllocHGlobal(length); + osxError = OSX_sysctlbyname("hw.machine", pStr, pLen, IntPtr.Zero, 0); + + if(osxError != 0) { - IntPtr pLen = Marshal.AllocHGlobal(sizeof(int)); - int osxError = OSX_sysctlbyname("hw.machine", IntPtr.Zero, pLen, IntPtr.Zero, 0); - - if(osxError != 0) - { - Marshal.FreeHGlobal(pLen); - - throw new Exception(string.Format(Localization.Unhandled_exception_uname, - Marshal.GetLastWin32Error())); - } - - int length = Marshal.ReadInt32(pLen); - IntPtr pStr = Marshal.AllocHGlobal(length); - osxError = OSX_sysctlbyname("hw.machine", pStr, pLen, IntPtr.Zero, 0); - - if(osxError != 0) - { - Marshal.FreeHGlobal(pStr); - Marshal.FreeHGlobal(pLen); - - throw new Exception(string.Format(Localization.Unhandled_exception_uname, - Marshal.GetLastWin32Error())); - } - - string machine = Marshal.PtrToStringAnsi(pStr); - Marshal.FreeHGlobal(pStr); Marshal.FreeHGlobal(pLen); - if(machine != null && - (machine.StartsWith("iPad", StringComparison.Ordinal) || - machine.StartsWith("iPod", StringComparison.Ordinal) || - machine.StartsWith("iPhone", StringComparison.Ordinal))) - return PlatformID.iOS; - - return PlatformID.MacOSX; + throw new Exception(string.Format(Localization.Unhandled_exception_uname, + Marshal.GetLastWin32Error())); } - case "GNU": return PlatformID.Hurd; - case "FreeBSD": - case "GNU/kFreeBSD": return PlatformID.FreeBSD; - case "DragonFly": return PlatformID.DragonFly; - case "Haiku": return PlatformID.Haiku; - case "HP-UX": return PlatformID.HPUX; - case "AIX": return PlatformID.AIX; - case "OS400": return PlatformID.OS400; - case "IRIX": - case "IRIX64": return PlatformID.IRIX; - case "Minix": return PlatformID.Minix; - case "NetBSD": return PlatformID.NetBSD; - case "NONSTOP_KERNEL": return PlatformID.NonStop; - case "OpenBSD": return PlatformID.OpenBSD; - case "QNX": return PlatformID.QNX; - case "SINIX-Y": return PlatformID.SINIX; - case "SunOS": return PlatformID.Solaris; - case "OSF1": return PlatformID.Tru64; - case "ULTRIX": return PlatformID.Ultrix; - case "SCO_SV": return PlatformID.OpenServer; - case "UnixWare": return PlatformID.UnixWare; - case "Interix": - case "UWIN-W7": return PlatformID.Win32NT; - default: - { - if(unixname.sysname.StartsWith("CYGWIN_NT", StringComparison.Ordinal) || - unixname.sysname.StartsWith("MINGW32_NT", StringComparison.Ordinal) || - unixname.sysname.StartsWith("MSYS_NT", StringComparison.Ordinal) || - unixname.sysname.StartsWith("UWIN", StringComparison.Ordinal)) - return PlatformID.Win32NT; + string machine = Marshal.PtrToStringAnsi(pStr); - return PlatformID.Unknown; - } + Marshal.FreeHGlobal(pStr); + Marshal.FreeHGlobal(pLen); + + if(machine != null && + (machine.StartsWith("iPad", StringComparison.Ordinal) || + machine.StartsWith("iPod", StringComparison.Ordinal) || + machine.StartsWith("iPhone", StringComparison.Ordinal))) + return PlatformID.iOS; + + return PlatformID.MacOSX; } - } - /// Gets a string for the current operating system REAL version (handles Darwin 1.4 and Windows 10 falsifying) - /// Current operating system version - public static string GetVersion() - { - string environ = Environment.OSVersion.Version.ToString(); - - switch(GetRealPlatformID()) + case "GNU": + return PlatformID.Hurd; + case "FreeBSD": + case "GNU/kFreeBSD": + return PlatformID.FreeBSD; + case "DragonFly": + return PlatformID.DragonFly; + case "Haiku": + return PlatformID.Haiku; + case "HP-UX": + return PlatformID.HPUX; + case "AIX": + return PlatformID.AIX; + case "OS400": + return PlatformID.OS400; + case "IRIX": + case "IRIX64": + return PlatformID.IRIX; + case "Minix": + return PlatformID.Minix; + case "NetBSD": + return PlatformID.NetBSD; + case "NONSTOP_KERNEL": + return PlatformID.NonStop; + case "OpenBSD": + return PlatformID.OpenBSD; + case "QNX": + return PlatformID.QNX; + case "SINIX-Y": + return PlatformID.SINIX; + case "SunOS": + return PlatformID.Solaris; + case "OSF1": + return PlatformID.Tru64; + case "ULTRIX": + return PlatformID.Ultrix; + case "SCO_SV": + return PlatformID.OpenServer; + case "UnixWare": + return PlatformID.UnixWare; + case "Interix": + case "UWIN-W7": + return PlatformID.Win32NT; + default: { - case PlatformID.MacOSX: - if(Environment.OSVersion.Version.Major != 1) - return $"10.{Environment.OSVersion.Version.Major - 4}.{Environment.OSVersion.Version.Minor}"; + if(unixname.sysname.StartsWith("CYGWIN_NT", StringComparison.Ordinal) || + unixname.sysname.StartsWith("MINGW32_NT", StringComparison.Ordinal) || + unixname.sysname.StartsWith("MSYS_NT", StringComparison.Ordinal) || + unixname.sysname.StartsWith("UWIN", StringComparison.Ordinal)) + return PlatformID.Win32NT; - switch(Environment.OSVersion.Version.Minor) - { - case 3: return "10.0"; - case 4: return "10.1"; - } - - goto default; - case PlatformID.Win32NT: - // From Windows 8.1 the reported version is simply falsified... - if((Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Major >= 2) || - Environment.OSVersion.Version.Major > 6) - return FileVersionInfo. - GetVersionInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), - "KERNEL32.DLL")).ProductVersion; - - return environ; - default: return environ; + return PlatformID.Unknown; } } - - /// From a platform ID and version returns a human-readable version - /// Platform ID - /// Version number - /// Operating system name - public static string GetPlatformName(PlatformID id, string version = null) - { - switch(id) - { - case PlatformID.AIX: return "AIX"; - case PlatformID.Android: return "Android"; - case PlatformID.DragonFly: return "DragonFly BSD"; - case PlatformID.FreeBSD: return "FreeBSD"; - case PlatformID.Haiku: return "Haiku"; - case PlatformID.HPUX: return "HP/UX"; - case PlatformID.Hurd: return "Hurd"; - case PlatformID.iOS: return "iOS"; - case PlatformID.IRIX: return "IRIX"; - case PlatformID.Linux: - if(!File.Exists("/proc/version")) - return "Linux"; - - string s = File.ReadAllText("/proc/version"); - - return s.Contains("Microsoft") || s.Contains("WSL") ? "Windows Subsystem for Linux" : "Linux"; - - case PlatformID.MacOSX: - if(string.IsNullOrEmpty(version)) - return "macOS"; - - string[] pieces = version.Split('.'); - - if(pieces.Length < 2 || - !int.TryParse(pieces[1], out int minor)) - return "macOS"; - - if(minor >= 12) - return "macOS"; - - if(minor >= 8) - return "OS X"; - - return "Mac OS X"; - - case PlatformID.Minix: return "MINIX"; - case PlatformID.NetBSD: return "NetBSD"; - case PlatformID.NonStop: return "NonStop OS"; - case PlatformID.OpenBSD: return "OpenBSD"; - case PlatformID.OpenServer: return "SCO OpenServer"; - case PlatformID.OS400: return "OS/400"; - case PlatformID.PlayStation3: return "Sony CellOS"; - case PlatformID.PlayStation4: return "Sony Orbis OS"; - case PlatformID.QNX: return "QNX"; - case PlatformID.SINIX: return "SINIX"; - case PlatformID.Solaris: return "Sun Solaris"; - case PlatformID.Tizen: return "Samsung Tizen"; - case PlatformID.Tru64: return "Tru64 UNIX"; - case PlatformID.Ultrix: return "Ultrix"; - case PlatformID.Unix: return "UNIX"; - case PlatformID.UnixWare: return "SCO UnixWare"; - case PlatformID.Wii: return "Nintendo Wii"; - case PlatformID.WiiU: return "Nintendo Wii U"; - case PlatformID.Win32NT: - if(string.IsNullOrEmpty(version)) - return "Windows NT/2000/XP/Vista/7/10"; - - if(version.StartsWith("3.", StringComparison.Ordinal) || - version.StartsWith("4.", StringComparison.Ordinal)) - return "Windows NT"; - - if(version.StartsWith("5.0", StringComparison.Ordinal)) - return "Windows 2000"; - - if(version.StartsWith("5.1", StringComparison.Ordinal)) - return "Windows XP"; - - if(version.StartsWith("5.2", StringComparison.Ordinal)) - return "Windows 2003"; - - if(version.StartsWith("6.0", StringComparison.Ordinal)) - return "Windows Vista"; - - if(version.StartsWith("6.1", StringComparison.Ordinal)) - return "Windows 7"; - - if(version.StartsWith("6.2", StringComparison.Ordinal)) - return "Windows 8"; - - if(version.StartsWith("6.3", StringComparison.Ordinal)) - return "Windows 8.1"; - - if(version.StartsWith("10.0", StringComparison.Ordinal)) - return "Windows 10"; - - return "Windows NT/2000/XP/Vista/7/10"; - case PlatformID.Win32S: return "Windows 3.x with win32s"; - case PlatformID.Win32Windows: - if(string.IsNullOrEmpty(version)) - return "Windows 9x/Me"; - - if(version.StartsWith("4.0", StringComparison.Ordinal)) - return "Windows 95"; - - if(version.StartsWith("4.10.2222", StringComparison.Ordinal)) - return "Windows 98 SE"; - - if(version.StartsWith("4.1", StringComparison.Ordinal)) - return "Windows 98"; - - if(version.StartsWith("4.9", StringComparison.Ordinal)) - return "Windows Me"; - - return "Windows 9x/Me"; - case PlatformID.WinCE: return "Windows CE/Mobile"; - case PlatformID.WindowsPhone: return "Windows Phone"; - case PlatformID.Xbox: return "Xbox OS"; - case PlatformID.zOS: return "z/OS"; - default: return id.ToString(); - } - } - - /// POSIX uname structure, size from OSX, big enough to handle extra fields - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - struct utsname - { - /// System name - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public readonly string sysname; - /// Node name - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public readonly string nodename; - /// Release level - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public readonly string release; - /// Version level - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public readonly string version; - /// Hardware level - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public readonly string machine; - } + } + + /// Gets a string for the current operating system REAL version (handles Darwin 1.4 and Windows 10 falsifying) + /// Current operating system version + public static string GetVersion() + { + var environ = Environment.OSVersion.Version.ToString(); + + switch(GetRealPlatformID()) + { + case PlatformID.MacOSX: + if(Environment.OSVersion.Version.Major != 1) + return $"10.{Environment.OSVersion.Version.Major - 4}.{Environment.OSVersion.Version.Minor}"; + + switch(Environment.OSVersion.Version.Minor) + { + case 3: + return "10.0"; + case 4: + return "10.1"; + } + + goto default; + case PlatformID.Win32NT: + // From Windows 8.1 the reported version is simply falsified... + if(Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Major >= 2 || + Environment.OSVersion.Version.Major > 6) + { + return FileVersionInfo + .GetVersionInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), + "KERNEL32.DLL")) + .ProductVersion; + } + + return environ; + default: + return environ; + } + } + + /// From a platform ID and version returns a human-readable version + /// Platform ID + /// Version number + /// Operating system name + public static string GetPlatformName(PlatformID id, string version = null) + { + switch(id) + { + case PlatformID.AIX: + return "AIX"; + case PlatformID.Android: + return "Android"; + case PlatformID.DragonFly: + return "DragonFly BSD"; + case PlatformID.FreeBSD: + return "FreeBSD"; + case PlatformID.Haiku: + return "Haiku"; + case PlatformID.HPUX: + return "HP/UX"; + case PlatformID.Hurd: + return "Hurd"; + case PlatformID.iOS: + return "iOS"; + case PlatformID.IRIX: + return "IRIX"; + case PlatformID.Linux: + if(!File.Exists("/proc/version")) return "Linux"; + + string s = File.ReadAllText("/proc/version"); + + return s.Contains("Microsoft") || s.Contains("WSL") ? "Windows Subsystem for Linux" : "Linux"; + + case PlatformID.MacOSX: + if(string.IsNullOrEmpty(version)) return "macOS"; + + string[] pieces = version.Split('.'); + + if(pieces.Length < 2 || !int.TryParse(pieces[1], out int minor)) return "macOS"; + + if(minor >= 12) return "macOS"; + + if(minor >= 8) return "OS X"; + + return "Mac OS X"; + + case PlatformID.Minix: + return "MINIX"; + case PlatformID.NetBSD: + return "NetBSD"; + case PlatformID.NonStop: + return "NonStop OS"; + case PlatformID.OpenBSD: + return "OpenBSD"; + case PlatformID.OpenServer: + return "SCO OpenServer"; + case PlatformID.OS400: + return "OS/400"; + case PlatformID.PlayStation3: + return "Sony CellOS"; + case PlatformID.PlayStation4: + return "Sony Orbis OS"; + case PlatformID.QNX: + return "QNX"; + case PlatformID.SINIX: + return "SINIX"; + case PlatformID.Solaris: + return "Sun Solaris"; + case PlatformID.Tizen: + return "Samsung Tizen"; + case PlatformID.Tru64: + return "Tru64 UNIX"; + case PlatformID.Ultrix: + return "Ultrix"; + case PlatformID.Unix: + return "UNIX"; + case PlatformID.UnixWare: + return "SCO UnixWare"; + case PlatformID.Wii: + return "Nintendo Wii"; + case PlatformID.WiiU: + return "Nintendo Wii U"; + case PlatformID.Win32NT: + if(string.IsNullOrEmpty(version)) return "Windows NT/2000/XP/Vista/7/10"; + + if(version.StartsWith("3.", StringComparison.Ordinal) || + version.StartsWith("4.", StringComparison.Ordinal)) + return "Windows NT"; + + if(version.StartsWith("5.0", StringComparison.Ordinal)) return "Windows 2000"; + + if(version.StartsWith("5.1", StringComparison.Ordinal)) return "Windows XP"; + + if(version.StartsWith("5.2", StringComparison.Ordinal)) return "Windows 2003"; + + if(version.StartsWith("6.0", StringComparison.Ordinal)) return "Windows Vista"; + + if(version.StartsWith("6.1", StringComparison.Ordinal)) return "Windows 7"; + + if(version.StartsWith("6.2", StringComparison.Ordinal)) return "Windows 8"; + + if(version.StartsWith("6.3", StringComparison.Ordinal)) return "Windows 8.1"; + + if(version.StartsWith("10.0", StringComparison.Ordinal)) return "Windows 10"; + + return "Windows NT/2000/XP/Vista/7/10"; + case PlatformID.Win32S: + return "Windows 3.x with win32s"; + case PlatformID.Win32Windows: + if(string.IsNullOrEmpty(version)) return "Windows 9x/Me"; + + if(version.StartsWith("4.0", StringComparison.Ordinal)) return "Windows 95"; + + if(version.StartsWith("4.10.2222", StringComparison.Ordinal)) return "Windows 98 SE"; + + if(version.StartsWith("4.1", StringComparison.Ordinal)) return "Windows 98"; + + if(version.StartsWith("4.9", StringComparison.Ordinal)) return "Windows Me"; + + return "Windows 9x/Me"; + case PlatformID.WinCE: + return "Windows CE/Mobile"; + case PlatformID.WindowsPhone: + return "Windows Phone"; + case PlatformID.Xbox: + return "Xbox OS"; + case PlatformID.zOS: + return "z/OS"; + default: + return id.ToString(); + } + } + + /// POSIX uname structure, size from OSX, big enough to handle extra fields + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + struct utsname + { + /// System name + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public readonly string sysname; + /// Node name + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public readonly string nodename; + /// Release level + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public readonly string release; + /// Version level + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public readonly string version; + /// Hardware level + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public readonly string machine; } } \ No newline at end of file diff --git a/RomRepoMgr.Settings/Interop/PlatformID.cs b/RomRepoMgr.Settings/Interop/PlatformID.cs index 788e784..43cb7b3 100644 --- a/RomRepoMgr.Settings/Interop/PlatformID.cs +++ b/RomRepoMgr.Settings/Interop/PlatformID.cs @@ -38,83 +38,83 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.CommonTypes.Interop +namespace Aaru.CommonTypes.Interop; + +/// Contains an arbitrary list of OSes, even if .NET does not run on them +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum PlatformID { - /// Contains an arbitrary list of OSes, even if .NET does not run on them - [SuppressMessage("ReSharper", "InconsistentNaming")] - public enum PlatformID - { - /// Win32s - Win32S = 0, - /// Win32 (Windows 9x) - Win32Windows = 1, - /// Windows NT - Win32NT = 2, - /// Windows Mobile - WinCE = 3, - /// UNIX (do not use, too generic) - Unix = 4, - /// Xbox 360 - Xbox = 5, - /// OS X - MacOSX = 6, - /// iOS is not OS X - iOS = 7, - /// Linux - Linux = 8, - /// Sun Solaris - Solaris = 9, - /// NetBSD - NetBSD = 10, - /// OpenBSD - OpenBSD = 11, - /// FreeBSD - FreeBSD = 12, - /// DragonFly BSD - DragonFly = 13, - /// Nintendo Wii - Wii = 14, - /// Nintendo Wii U - WiiU = 15, - /// Sony PlayStation 3 - PlayStation3 = 16, - /// Sony Playstation 4 - PlayStation4 = 17, - /// Google Android - Android = 18, - /// Samsung Tizen - Tizen = 19, - /// Windows Phone - WindowsPhone = 20, - /// GNU/Hurd - Hurd = 21, - /// Haiku - Haiku = 22, - /// HP-UX - HPUX = 23, - /// AIX - AIX = 24, - /// OS/400 - OS400 = 25, - /// IRIX - IRIX = 26, - /// Minix - Minix = 27, - /// NonStop - NonStop = 28, - /// QNX - QNX = 29, - /// SINIX - SINIX = 30, - /// Tru64 UNIX - Tru64 = 31, - /// Ultrix - Ultrix = 32, - /// SCO OpenServer / SCO UNIX - OpenServer = 33, - /// SCO UnixWare - UnixWare = 34, - /// IBM z/OS - zOS = 35, Unknown = -1 - } + /// Win32s + Win32S = 0, + /// Win32 (Windows 9x) + Win32Windows = 1, + /// Windows NT + Win32NT = 2, + /// Windows Mobile + WinCE = 3, + /// UNIX (do not use, too generic) + Unix = 4, + /// Xbox 360 + Xbox = 5, + /// OS X + MacOSX = 6, + /// iOS is not OS X + iOS = 7, + /// Linux + Linux = 8, + /// Sun Solaris + Solaris = 9, + /// NetBSD + NetBSD = 10, + /// OpenBSD + OpenBSD = 11, + /// FreeBSD + FreeBSD = 12, + /// DragonFly BSD + DragonFly = 13, + /// Nintendo Wii + Wii = 14, + /// Nintendo Wii U + WiiU = 15, + /// Sony PlayStation 3 + PlayStation3 = 16, + /// Sony Playstation 4 + PlayStation4 = 17, + /// Google Android + Android = 18, + /// Samsung Tizen + Tizen = 19, + /// Windows Phone + WindowsPhone = 20, + /// GNU/Hurd + Hurd = 21, + /// Haiku + Haiku = 22, + /// HP-UX + HPUX = 23, + /// AIX + AIX = 24, + /// OS/400 + OS400 = 25, + /// IRIX + IRIX = 26, + /// Minix + Minix = 27, + /// NonStop + NonStop = 28, + /// QNX + QNX = 29, + /// SINIX + SINIX = 30, + /// Tru64 UNIX + Tru64 = 31, + /// Ultrix + Ultrix = 32, + /// SCO OpenServer / SCO UNIX + OpenServer = 33, + /// SCO UnixWare + UnixWare = 34, + /// IBM z/OS + zOS = 35, + Unknown = -1 } \ No newline at end of file diff --git a/RomRepoMgr.Settings/Interop/Version.cs b/RomRepoMgr.Settings/Interop/Version.cs index e1ecf31..18b7999 100644 --- a/RomRepoMgr.Settings/Interop/Version.cs +++ b/RomRepoMgr.Settings/Interop/Version.cs @@ -40,44 +40,40 @@ using System; using System.Reflection; using System.Runtime; -namespace Aaru.CommonTypes.Interop +namespace Aaru.CommonTypes.Interop; + +public static class Version { - public static class Version + /// Gets version string + /// Version + public static string GetVersion() => typeof(Version).Assembly.GetName().Version?.ToString(); + + public static string GetNetCoreVersion() { - /// Gets version string - /// Version - public static string GetVersion() => typeof(Version).Assembly.GetName().Version?.ToString(); + Assembly assembly = typeof(GCSettings).Assembly; - public static string GetNetCoreVersion() - { - Assembly assembly = typeof(GCSettings).Assembly; + string[] assemblyPath = assembly.Location.Split(new[] + { + '/', '\\' + }, + StringSplitOptions.RemoveEmptyEntries); - string[] assemblyPath = assembly.Location.Split(new[] - { - '/', '\\' - }, StringSplitOptions.RemoveEmptyEntries); + int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); - int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); + if(netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) return assemblyPath[netCoreAppIndex + 1]; - if(netCoreAppIndex > 0 && - netCoreAppIndex < assemblyPath.Length - 2) - return assemblyPath[netCoreAppIndex + 1]; + return null; + } - return null; - } + public static string GetMonoVersion() + { + if(!DetectOS.IsMono) return null; - public static string GetMonoVersion() - { - if(!DetectOS.IsMono) - return null; + MethodInfo monoDisplayName = Type.GetType("Mono.Runtime") + ?.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); - MethodInfo monoDisplayName = Type.GetType("Mono.Runtime")?. - GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); + if(monoDisplayName != null) return (string)monoDisplayName.Invoke(null, null); - if(monoDisplayName != null) - return (string)monoDisplayName.Invoke(null, null); - - return null; - } + return null; } } \ No newline at end of file diff --git a/RomRepoMgr.Settings/Resources/Localization.Designer.cs b/RomRepoMgr.Settings/Resources/Localization.Designer.cs index a050660..6aca388 100644 --- a/RomRepoMgr.Settings/Resources/Localization.Designer.cs +++ b/RomRepoMgr.Settings/Resources/Localization.Designer.cs @@ -11,32 +11,46 @@ namespace RomRepoMgr.Settings.Resources { using System; - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Localization { - private static System.Resources.ResourceManager resourceMan; + private static global::System.Resources.ResourceManager resourceMan; - private static System.Globalization.CultureInfo resourceCulture; + private static global::System.Globalization.CultureInfo resourceCulture; - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Localization() { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Resources.ResourceManager ResourceManager { + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RomRepoMgr.Settings.Resources.Localization", typeof(Localization).Assembly); + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RomRepoMgr.Settings.Resources.Localization", typeof(Localization).Assembly); resourceMan = temp; } return resourceMan; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Globalization.CultureInfo Culture { + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -45,6 +59,9 @@ namespace RomRepoMgr.Settings.Resources { } } + /// + /// Looks up a localized string similar to Unhandled exception calling uname: {0}. + /// internal static string Unhandled_exception_uname { get { return ResourceManager.GetString("Unhandled_exception_uname", resourceCulture); diff --git a/RomRepoMgr.Settings/Resources/Localization.es.resx b/RomRepoMgr.Settings/Resources/Localization.es.resx index 125cef8..d87ba1b 100644 --- a/RomRepoMgr.Settings/Resources/Localization.es.resx +++ b/RomRepoMgr.Settings/Resources/Localization.es.resx @@ -1,17 +1,21 @@  - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Excepción no controlada llamando a uname: {0} \ No newline at end of file diff --git a/RomRepoMgr.Settings/Resources/Localization.resx b/RomRepoMgr.Settings/Resources/Localization.resx index 41339ec..d9a3f10 100644 --- a/RomRepoMgr.Settings/Resources/Localization.resx +++ b/RomRepoMgr.Settings/Resources/Localization.resx @@ -1,22 +1,27 @@ - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Unhandled exception calling uname: {0} \ No newline at end of file diff --git a/RomRepoMgr.Settings/RomRepoMgr.Settings.csproj b/RomRepoMgr.Settings/RomRepoMgr.Settings.csproj index 29f0423..343732d 100644 --- a/RomRepoMgr.Settings/RomRepoMgr.Settings.csproj +++ b/RomRepoMgr.Settings/RomRepoMgr.Settings.csproj @@ -1,28 +1,28 @@ - - net9.0 - default - + + net9.0 + default + - - - - + + + + - - - ResXFileCodeGenerator - Localization.Designer.cs - - + + + ResXFileCodeGenerator + Localization.Designer.cs + + - - - True - True - Localization.resx - - + + + True + True + Localization.resx + + diff --git a/RomRepoMgr.Settings/Settings.cs b/RomRepoMgr.Settings/Settings.cs index a8504fa..b37460b 100644 --- a/RomRepoMgr.Settings/Settings.cs +++ b/RomRepoMgr.Settings/Settings.cs @@ -31,280 +31,285 @@ using Claunia.PropertyList; using Microsoft.Win32; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; -namespace RomRepoMgr.Settings +namespace RomRepoMgr.Settings; + +public sealed class SetSettings { - public sealed class SetSettings + public string DatabasePath { get; set; } + public string RepositoryPath { get; set; } + public string TemporaryFolder { get; set; } + public string UnArchiverPath { get; set; } +} + +/// Manages statistics +public static class Settings +{ + const string XDG_CONFIG_HOME = "XDG_CONFIG_HOME"; + const string XDG_CONFIG_HOME_RESOLVED = ".config"; + /// Current statistics + public static SetSettings Current; + public static bool UnArUsable { get; set; } + + /// Loads settings + public static void LoadSettings() { - public string DatabasePath { get; set; } - public string RepositoryPath { get; set; } - public string TemporaryFolder { get; set; } - public string UnArchiverPath { get; set; } + Current = new SetSettings(); + PlatformID ptId = DetectOS.GetRealPlatformID(); + string homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + + FileStream prefsFs = null; + StreamReader prefsSr = null; + + try + { + switch(ptId) + { + // In case of macOS or iOS settings will be saved in ~/Library/Preferences/com.claunia.romrepomgr.plist + case PlatformID.MacOSX: + case PlatformID.iOS: + { + string preferencesPath = + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + "Library", + "Preferences"); + + string preferencesFilePath = Path.Combine(preferencesPath, "com.claunia.romrepomgr.plist"); + + if(!File.Exists(preferencesFilePath)) + { + SetDefaultSettings(); + SaveSettings(); + } + + prefsFs = new FileStream(preferencesFilePath, FileMode.Open, FileAccess.Read); + + var parsedPreferences = (NSDictionary)BinaryPropertyListParser.Parse(prefsFs); + + if(parsedPreferences != null) + { + NSObject obj; + + Current.DatabasePath = parsedPreferences.TryGetValue("DatabasePath", out obj) + ? ((NSString)obj).ToString() + : null; + + Current.RepositoryPath = parsedPreferences.TryGetValue("RepositoryPath", out obj) + ? ((NSString)obj).ToString() + : null; + + Current.TemporaryFolder = parsedPreferences.TryGetValue("TemporaryFolder", out obj) + ? ((NSString)obj).ToString() + : null; + + Current.UnArchiverPath = parsedPreferences.TryGetValue("UnArchiverPath", out obj) + ? ((NSString)obj).ToString() + : null; + + prefsFs.Close(); + } + else + { + prefsFs.Close(); + + SetDefaultSettings(); + SaveSettings(); + } + } + + break; +#if !NETSTANDARD2_0 + + // In case of Windows settings will be saved in the registry: HKLM/SOFTWARE/Claunia.com/RomRepoMgr + case PlatformID.Win32NT when OperatingSystem.IsWindows(): + case PlatformID.Win32S when OperatingSystem.IsWindows(): + case PlatformID.Win32Windows when OperatingSystem.IsWindows(): + case PlatformID.WinCE when OperatingSystem.IsWindows(): + case PlatformID.WindowsPhone when OperatingSystem.IsWindows(): + { + RegistryKey parentKey = Registry.CurrentUser.OpenSubKey("SOFTWARE")?.OpenSubKey("Claunia.com"); + + if(parentKey == null) + { + SetDefaultSettings(); + SaveSettings(); + + return; + } + + RegistryKey key = parentKey.OpenSubKey("RomRepoMgr"); + + if(key == null) + { + SetDefaultSettings(); + SaveSettings(); + + return; + } + + Current.DatabasePath = key.GetValue("DatabasePath") as string; + Current.RepositoryPath = key.GetValue("RepositoryPath") as string; + Current.TemporaryFolder = key.GetValue("TemporaryFolder") as string; + Current.UnArchiverPath = key.GetValue("UnArchiverPath") as string; + } + + break; +#endif + + // Otherwise, settings will be saved in ~/.config/RomRepoMgr.json + default: + { + string xdgConfigPath = Path.Combine(homePath, + Environment.GetEnvironmentVariable(XDG_CONFIG_HOME) ?? + XDG_CONFIG_HOME_RESOLVED); + + string settingsPath = Path.Combine(xdgConfigPath, "RomRepoMgr.json"); + + if(!File.Exists(settingsPath)) + { + SetDefaultSettings(); + SaveSettings(); + + return; + } + + prefsSr = new StreamReader(settingsPath); + + Current = JsonSerializer.Deserialize(prefsSr.ReadToEnd(), + new JsonSerializerOptions + { + AllowTrailingCommas = true, + PropertyNameCaseInsensitive = true, + ReadCommentHandling = + JsonCommentHandling.Skip, + WriteIndented = true + }); + } + + break; + } + } + catch + { + prefsFs?.Close(); + prefsSr?.Close(); + SetDefaultSettings(); + SaveSettings(); + } } - /// Manages statistics - public static class Settings + public static void SaveSettings() { - const string XDG_CONFIG_HOME = "XDG_CONFIG_HOME"; - const string XDG_CONFIG_HOME_RESOLVED = ".config"; - /// Current statistics - public static SetSettings Current; - public static bool UnArUsable { get; set; } - - /// Loads settings - public static void LoadSettings() + try { - Current = new SetSettings(); - PlatformID ptId = DetectOS.GetRealPlatformID(); - string homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + PlatformID ptId = DetectOS.GetRealPlatformID(); - FileStream prefsFs = null; - StreamReader prefsSr = null; - - try + switch(ptId) { - switch(ptId) + // In case of macOS or iOS settings will be saved in ~/Library/Preferences/com.claunia.romrepomgr.plist + case PlatformID.MacOSX: + case PlatformID.iOS: { - // In case of macOS or iOS settings will be saved in ~/Library/Preferences/com.claunia.romrepomgr.plist - case PlatformID.MacOSX: - case PlatformID.iOS: + var root = new NSDictionary { - string preferencesPath = - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", - "Preferences"); - - string preferencesFilePath = Path.Combine(preferencesPath, "com.claunia.romrepomgr.plist"); - - if(!File.Exists(preferencesFilePath)) { - SetDefaultSettings(); - SaveSettings(); + "DatabasePath", Current.DatabasePath + }, + { + "RepositoryPath", Current.RepositoryPath + }, + { + "TemporaryFolder", Current.TemporaryFolder + }, + { + "UnArchiverPath", Current.UnArchiverPath } + }; - prefsFs = new FileStream(preferencesFilePath, FileMode.Open, FileAccess.Read); + string preferencesPath = + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + "Library", + "Preferences"); - var parsedPreferences = (NSDictionary)BinaryPropertyListParser.Parse(prefsFs); + string preferencesFilePath = Path.Combine(preferencesPath, "com.claunia.romrepomgr.plist"); - if(parsedPreferences != null) - { - NSObject obj; - - Current.DatabasePath = parsedPreferences.TryGetValue("DatabasePath", out obj) - ? ((NSString)obj).ToString() : null; - - Current.RepositoryPath = parsedPreferences.TryGetValue("RepositoryPath", out obj) - ? ((NSString)obj).ToString() : null; - - Current.TemporaryFolder = parsedPreferences.TryGetValue("TemporaryFolder", out obj) - ? ((NSString)obj).ToString() : null; - - Current.UnArchiverPath = parsedPreferences.TryGetValue("UnArchiverPath", out obj) - ? ((NSString)obj).ToString() : null; - - prefsFs.Close(); - } - else - { - prefsFs.Close(); - - SetDefaultSettings(); - SaveSettings(); - } - } - - break; - #if !NETSTANDARD2_0 - - // In case of Windows settings will be saved in the registry: HKLM/SOFTWARE/Claunia.com/RomRepoMgr - case PlatformID.Win32NT when OperatingSystem.IsWindows(): - case PlatformID.Win32S when OperatingSystem.IsWindows(): - case PlatformID.Win32Windows when OperatingSystem.IsWindows(): - case PlatformID.WinCE when OperatingSystem.IsWindows(): - case PlatformID.WindowsPhone when OperatingSystem.IsWindows(): - { - RegistryKey parentKey = Registry.CurrentUser.OpenSubKey("SOFTWARE")?.OpenSubKey("Claunia.com"); - - if(parentKey == null) - { - SetDefaultSettings(); - SaveSettings(); - - return; - } - - RegistryKey key = parentKey.OpenSubKey("RomRepoMgr"); - - if(key == null) - { - SetDefaultSettings(); - SaveSettings(); - - return; - } - - Current.DatabasePath = key.GetValue("DatabasePath") as string; - Current.RepositoryPath = key.GetValue("RepositoryPath") as string; - Current.TemporaryFolder = key.GetValue("TemporaryFolder") as string; - Current.UnArchiverPath = key.GetValue("UnArchiverPath") as string; - } - - break; - #endif - - // Otherwise, settings will be saved in ~/.config/RomRepoMgr.json - default: - { - string xdgConfigPath = - Path.Combine(homePath, - Environment.GetEnvironmentVariable(XDG_CONFIG_HOME) ?? - XDG_CONFIG_HOME_RESOLVED); - - string settingsPath = Path.Combine(xdgConfigPath, "RomRepoMgr.json"); - - if(!File.Exists(settingsPath)) - { - SetDefaultSettings(); - SaveSettings(); - - return; - } - - prefsSr = new StreamReader(settingsPath); - - Current = JsonSerializer.Deserialize(prefsSr.ReadToEnd(), new JsonSerializerOptions - { - AllowTrailingCommas = true, - PropertyNameCaseInsensitive = true, - ReadCommentHandling = JsonCommentHandling.Skip, - WriteIndented = true - }); - } - - break; + var fs = new FileStream(preferencesFilePath, FileMode.Create); + BinaryPropertyListWriter.Write(fs, root); + fs.Close(); } - } - catch - { - prefsFs?.Close(); - prefsSr?.Close(); - SetDefaultSettings(); - SaveSettings(); - } - } - public static void SaveSettings() - { - try - { - PlatformID ptId = DetectOS.GetRealPlatformID(); + break; +#if !NETSTANDARD2_0 - switch(ptId) + // In case of Windows settings will be saved in the registry: HKLM/SOFTWARE/Claunia.com/RomRepoMgr + case PlatformID.Win32NT when OperatingSystem.IsWindows(): + case PlatformID.Win32S when OperatingSystem.IsWindows(): + case PlatformID.Win32Windows when OperatingSystem.IsWindows(): + case PlatformID.WinCE when OperatingSystem.IsWindows(): + case PlatformID.WindowsPhone when OperatingSystem.IsWindows(): { - // In case of macOS or iOS settings will be saved in ~/Library/Preferences/com.claunia.romrepomgr.plist - case PlatformID.MacOSX: - case PlatformID.iOS: - { - var root = new NSDictionary - { - { - "DatabasePath", Current.DatabasePath - }, - { - "RepositoryPath", Current.RepositoryPath - }, - { - "TemporaryFolder", Current.TemporaryFolder - }, - { - "UnArchiverPath", Current.UnArchiverPath - } - }; + RegistryKey parentKey = + Registry.CurrentUser.OpenSubKey("SOFTWARE", true)?.CreateSubKey("Claunia.com"); - string preferencesPath = - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", - "Preferences"); + RegistryKey key = parentKey?.CreateSubKey("RomRepoMgr"); - string preferencesFilePath = Path.Combine(preferencesPath, "com.claunia.romrepomgr.plist"); - - var fs = new FileStream(preferencesFilePath, FileMode.Create); - BinaryPropertyListWriter.Write(fs, root); - fs.Close(); - } - - break; - #if !NETSTANDARD2_0 - - // In case of Windows settings will be saved in the registry: HKLM/SOFTWARE/Claunia.com/RomRepoMgr - case PlatformID.Win32NT when OperatingSystem.IsWindows(): - case PlatformID.Win32S when OperatingSystem.IsWindows(): - case PlatformID.Win32Windows when OperatingSystem.IsWindows(): - case PlatformID.WinCE when OperatingSystem.IsWindows(): - case PlatformID.WindowsPhone when OperatingSystem.IsWindows(): - { - RegistryKey parentKey = Registry.CurrentUser.OpenSubKey("SOFTWARE", true)?. - CreateSubKey("Claunia.com"); - - RegistryKey key = parentKey?.CreateSubKey("RomRepoMgr"); - - key?.SetValue("DatabasePath", Current.DatabasePath); - key?.SetValue("RepositoryPath", Current.RepositoryPath); - key?.SetValue("TemporaryFolder", Current.TemporaryFolder); - key?.SetValue("UnArchiverPath", Current.UnArchiverPath); - } - - break; - #endif - - // Otherwise, settings will be saved in ~/.config/RomRepoMgr.json - default: - { - string homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - - string xdgConfigPath = - Path.Combine(homePath, - Environment.GetEnvironmentVariable(XDG_CONFIG_HOME) ?? - XDG_CONFIG_HOME_RESOLVED); - - string settingsPath = Path.Combine(xdgConfigPath, "RomRepoMgr.json"); - - if(!Directory.Exists(xdgConfigPath)) - Directory.CreateDirectory(xdgConfigPath); - - var prefsSr = new StreamWriter(settingsPath); - - prefsSr.Write(JsonSerializer.Serialize(Current, new JsonSerializerOptions - { - AllowTrailingCommas = true, - PropertyNameCaseInsensitive = true, - ReadCommentHandling = JsonCommentHandling.Skip, - WriteIndented = true - })); - - prefsSr.Close(); - } - - break; + key?.SetValue("DatabasePath", Current.DatabasePath); + key?.SetValue("RepositoryPath", Current.RepositoryPath); + key?.SetValue("TemporaryFolder", Current.TemporaryFolder); + key?.SetValue("UnArchiverPath", Current.UnArchiverPath); } - } - #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body - catch - { - // ignored - } - #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body - } - /// Sets default settings as all statistics, share everything - static void SetDefaultSettings() + break; +#endif + + // Otherwise, settings will be saved in ~/.config/RomRepoMgr.json + default: + { + string homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + + string xdgConfigPath = Path.Combine(homePath, + Environment.GetEnvironmentVariable(XDG_CONFIG_HOME) ?? + XDG_CONFIG_HOME_RESOLVED); + + string settingsPath = Path.Combine(xdgConfigPath, "RomRepoMgr.json"); + + if(!Directory.Exists(xdgConfigPath)) Directory.CreateDirectory(xdgConfigPath); + + var prefsSr = new StreamWriter(settingsPath); + + prefsSr.Write(JsonSerializer.Serialize(Current, + new JsonSerializerOptions + { + AllowTrailingCommas = true, + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip, + WriteIndented = true + })); + + prefsSr.Close(); + } + + break; + } + } +#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body + catch { - string docsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - string dataPath = Path.Combine(docsPath, "RomRepoMgr"); - - Current = new SetSettings - { - DatabasePath = Path.Combine(dataPath, "romrepo.db"), - RepositoryPath = Path.Combine(dataPath, "repo"), - TemporaryFolder = Path.GetTempPath() - }; + // ignored } +#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + } + + /// Sets default settings as all statistics, share everything + static void SetDefaultSettings() + { + string docsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + string dataPath = Path.Combine(docsPath, "RomRepoMgr"); + + Current = new SetSettings + { + DatabasePath = Path.Combine(dataPath, "romrepo.db"), + RepositoryPath = Path.Combine(dataPath, "repo"), + TemporaryFolder = Path.GetTempPath() + }; } } \ No newline at end of file diff --git a/RomRepoMgr/App.xaml b/RomRepoMgr/App.xaml index 513836e..38dae85 100644 --- a/RomRepoMgr/App.xaml +++ b/RomRepoMgr/App.xaml @@ -1,5 +1,7 @@ - + @@ -10,9 +12,12 @@ - - - + + + \ No newline at end of file diff --git a/RomRepoMgr/App.xaml.cs b/RomRepoMgr/App.xaml.cs index 18bc8bb..4c08f1c 100644 --- a/RomRepoMgr/App.xaml.cs +++ b/RomRepoMgr/App.xaml.cs @@ -34,79 +34,77 @@ using RomRepoMgr.Core.Models; using RomRepoMgr.ViewModels; using RomRepoMgr.Views; -namespace RomRepoMgr +namespace RomRepoMgr; + +public class App : Application { - public class App : Application + List _romSets; + + public override void Initialize() => AvaloniaXamlLoader.Load(this); + + public override void OnFrameworkInitializationCompleted() { - List _romSets; - - public override void Initialize() => AvaloniaXamlLoader.Load(this); - - public override void OnFrameworkInitializationCompleted() + if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - var splashWindow = new SplashWindow(); - var swvm = new SplashWindowViewModel(); - swvm.WorkFinished += OnSplashFinished; - swvm.GotRomSets += OnGotRomSets; - splashWindow.DataContext = swvm; - desktop.MainWindow = splashWindow; - } - - base.OnFrameworkInitializationCompleted(); + var splashWindow = new SplashWindow(); + var swvm = new SplashWindowViewModel(); + swvm.WorkFinished += OnSplashFinished; + swvm.GotRomSets += OnGotRomSets; + splashWindow.DataContext = swvm; + desktop.MainWindow = splashWindow; } - void OnGotRomSets(object sender, RomSetsEventArgs e) => _romSets = e.RomSets; + base.OnFrameworkInitializationCompleted(); + } - void OnSplashFinished(object sender, EventArgs e) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)) - return; + void OnGotRomSets(object sender, RomSetsEventArgs e) => _romSets = e.RomSets; - // Ensure not exit - desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown; + void OnSplashFinished(object sender, EventArgs e) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)) return; - // Close splash window - desktop.MainWindow.Close(); + // Ensure not exit + desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown; - // Create and show main window - desktop.MainWindow = new MainWindow(); - desktop.MainWindow.DataContext = new MainWindowViewModel(desktop.MainWindow as MainWindow, _romSets); - desktop.MainWindow.Show(); + // Close splash window + desktop.MainWindow.Close(); - // Now can close when all windows are closed - desktop.ShutdownMode = ShutdownMode.OnLastWindowClose; - } + // Create and show main window + desktop.MainWindow = new MainWindow(); + desktop.MainWindow.DataContext = new MainWindowViewModel(desktop.MainWindow as MainWindow, _romSets); + desktop.MainWindow.Show(); - void OnAboutClicked(object sender, EventArgs args) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) || - !(desktop.MainWindow is MainWindow mainWindow) || - !(mainWindow.DataContext is MainWindowViewModel mainWindowViewModel)) - return; + // Now can close when all windows are closed + desktop.ShutdownMode = ShutdownMode.OnLastWindowClose; + } - mainWindowViewModel.ExecuteAboutCommand(); - } + void OnAboutClicked(object sender, EventArgs args) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) || + !(desktop.MainWindow is MainWindow mainWindow) || + !(mainWindow.DataContext is MainWindowViewModel mainWindowViewModel)) + return; - void OnQuitClicked(object sender, EventArgs args) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) || - !(desktop.MainWindow is MainWindow mainWindow) || - !(mainWindow.DataContext is MainWindowViewModel mainWindowViewModel)) - return; + mainWindowViewModel.ExecuteAboutCommand(); + } - mainWindowViewModel.ExecuteExitCommand(); - } + void OnQuitClicked(object sender, EventArgs args) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) || + !(desktop.MainWindow is MainWindow mainWindow) || + !(mainWindow.DataContext is MainWindowViewModel mainWindowViewModel)) + return; - void OnPreferencesClicked(object sender, EventArgs args) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) || - !(desktop.MainWindow is MainWindow mainWindow) || - !(mainWindow.DataContext is MainWindowViewModel mainWindowViewModel)) - return; + mainWindowViewModel.ExecuteExitCommand(); + } - mainWindowViewModel.ExecuteSettingsCommand(); - } + void OnPreferencesClicked(object sender, EventArgs args) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) || + !(desktop.MainWindow is MainWindow mainWindow) || + !(mainWindow.DataContext is MainWindowViewModel mainWindowViewModel)) + return; + + mainWindowViewModel.ExecuteSettingsCommand(); } } \ No newline at end of file diff --git a/RomRepoMgr/Assets/emblem-checked.svg b/RomRepoMgr/Assets/emblem-checked.svg index f9eb31f..2a4350b 100644 --- a/RomRepoMgr/Assets/emblem-checked.svg +++ b/RomRepoMgr/Assets/emblem-checked.svg @@ -1,11 +1,12 @@ - - - + + + diff --git a/RomRepoMgr/Assets/emblem-error.svg b/RomRepoMgr/Assets/emblem-error.svg index 2b1ea34..9a9b739 100644 --- a/RomRepoMgr/Assets/emblem-error.svg +++ b/RomRepoMgr/Assets/emblem-error.svg @@ -1,11 +1,12 @@ - - - + + + diff --git a/RomRepoMgr/Assets/emblem-question.svg b/RomRepoMgr/Assets/emblem-question.svg index 1f9212e..70cf8a0 100644 --- a/RomRepoMgr/Assets/emblem-question.svg +++ b/RomRepoMgr/Assets/emblem-question.svg @@ -1,4 +1,6 @@ - - + + diff --git a/RomRepoMgr/Program.cs b/RomRepoMgr/Program.cs index 9f62ef5..d2cae93 100644 --- a/RomRepoMgr/Program.cs +++ b/RomRepoMgr/Program.cs @@ -26,17 +26,16 @@ using Avalonia; using Avalonia.ReactiveUI; -namespace RomRepoMgr -{ - internal class Program - { - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); +namespace RomRepoMgr; - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() => - AppBuilder.Configure().UsePlatformDetect().LogToDebug().UseReactiveUI(); - } +internal class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() => + AppBuilder.Configure().UsePlatformDetect().LogToDebug().UseReactiveUI(); } \ No newline at end of file diff --git a/RomRepoMgr/Resources/Localization.es.resx b/RomRepoMgr/Resources/Localization.es.resx index 1861a13..8df4a0b 100644 --- a/RomRepoMgr/Resources/Localization.es.resx +++ b/RomRepoMgr/Resources/Localization.es.resx @@ -1,371 +1,375 @@  - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Acerca de - + Acerda de ROM Repository Manager - + Todos los archivos - + Comprobar todos los archivos. - + Librería - + Versión - + Autores - + Desarrolladores: Natalia Portillo - + Cancelar - + Comprobando The Unarchiver... - + Elegir base de datos a abrir o crear - + Elegir... - + Elegir carpeta del repositorio - + Elegir carpeta temporal - + Elegir ejecutable de UnArchiver - + Cerrar - + Juegos completos - + Ocurrió un error al eliminar el archivo elegido. - + No se pudo eliminar el archivo. - + ¿Quieres eliminar el archivo? - + El archivo existe - + Archivo de base de datos - + ¿Quieres intentar abrir el archivo existente como una base de datos? - + ¿Quieres intentar abrir el archivo existente como una base de datos? - + Ocurrió un error al tratar de usar el archivo seleccionado como una base de datos. ¿Quieres eliminarlo? - + Ocurrió un error al tratar de usar el archivo seleccionado como una base de datos. - + No se puede usar la base de datos. - + Archivos DAT - + Descomprimiendo archivo DAT... - + ¿Estás seguro de eliminar el set de ROMs {0}? - + Eliminar set de ROMs - + Editar DAT - + Error - + Salir - + Exportando archivo DAT... - + Exportar ROMs a carpeta... - + Exportando ROMs a carpeta... - + _Salir - + Importar _archivo DAT - + Importar _carpeta de DATs - + _Preferencias - + _Archivo - + _Montar - + _Sistema de ficheros - + Terminado - + Encontrados {0} archivos... - + ROMs presentes - + _Acerca de - + _Ayuda - + Página web - + Importar archivo DAT... - + Importar DATs en carpeta... - + Importar archivos DAT de carpeta... - + Importando archivo DAT... - + Importando {0}... - + Importar ROMs de carpeta... - + IImportar ROMs de carpeta... - + Juegos incompletos - + Importar solo archivos conocidos. - + Librerías - + Licencia: Licencia Pública General GNU Versión 3 - + Cargando base de datos... - + Cargando sets de ROMs... - + Cargando preferencias... - + Migrando base de datos... - + ROMs faltantes - + OK - + Ruta: - + Intentar detectar archivos comprimiedos e importar su contenido. - + Recorrer subcarpetas. - + Eliminando set de ROM... - + Eliminar archivos después de importar satisfactoriamente. - + Eliminando archivo DAT del repositorio... - + Elimiando set de ROMs de la base de datos... - + Carpeta del repositorio - + Nombre de archivo - + Estado - + Recuperando set de ROMs de la base de datos... - + Autor - + Comentario - + Completos - + Fecha - + Descripción - + Presentes - + Incompletos - + Faltantes - + Nombre - + Sets de ROMs - + Elimina_r - + _Editar - + _Guardar archivo DAT - + Guardar ROMs en _carpeta - + Sets de _ROMs - + Juegos - + ROMs - + Versión - + Importar _carpeta - + _ROMs - + Guardar - + Guardando cambios en la base de datos... - + Buscando archivos... - + Elegir punto de montaje... - + Preferencias - + Comenzar - + Carpeta temporal - + The Unarchiver versión {0} - + Total de juegos - + Total de ROMs - + Ruta a UnAr - + _Desmontar - + Categoría - + Recuperando sets de ROMs de la base de datos... - + Eliminando estadísticas antiguas - + Calculando estadísticas para {0} - {1} ({2}) - + Base de datos - + Actualizar estadísticas - + ¿Quieres actualizar las estadísticas de los sets de ROMs en la base de datos? Tardará mucho tiempo... - + Actualizando estadísticas de sets de ROMs \ No newline at end of file diff --git a/RomRepoMgr/Resources/Localization.resx b/RomRepoMgr/Resources/Localization.resx index 451cc70..8b3e00b 100644 --- a/RomRepoMgr/Resources/Localization.resx +++ b/RomRepoMgr/Resources/Localization.resx @@ -1,377 +1,382 @@ - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + _File - + ROM sets - + Name - + Version - + Author - + Date - + Description - + Comment - + Games - + Complete - + Incomplete - + ROMs - + Have - + Miss - + Import DAT _file - + Import DAT f_older - + _Settings - + E_xit - + File_system - + _Mount - + _ROMs - + _Import folder - + ROM _sets - + _Save ROMs to folder - + Save DAT _file - + _Edit - + _Delete - + _Help - + _About - + About - + Libraries - + Authors - + About ROM Repository Manager - + License: GNU General Public License Version 3 - + Close - + Library - + Version - + Developers: Natalia Portillo - + Homepage - + Total games - + Complete games - + Incomplete games - + Total ROMs - + Have ROMs - + Missing ROMs - + Edit DAT - + Save - + Cancel - + Exporting DAT file... - + Decompressing DAT file... - + Finished - + Exporting ROM files to folder... - + Path: - + Check all files. - + Recurse subfolders. - + Import DAT files from folder... - + Filename - + Status - + Start - + Searching for files... - + Found {0} files... - + Importing {0}... - + OK - + Importing DAT file... - + Remove files after import successful. - + Only import known files. - + Try to detect archives and import their contents. - + Import ROM files from folder... - + Removing ROM set... - + Retrieving ROM set from database... - + Removing ROM set from database... - + Saving changes to database... - + Removing DAT file from repo... - + Choose... - + Settings - + Database file - + Repository folder - + Temporary folder - + Path to UnAr - + Error - + The Unarchiver version {0} - + Choose UnArchiver executable - + Choose temporary folder - + Choose repository folder - + Choose database to open / create - + Do you want to try to open the existing file as a database? - + Do you want to try to open the existing file as a database? - + File exists - + Could not use database - + An error occurred trying to use the chosen file as a database. Do you want to delete the file? - + An error occurred trying to use the chosen file as a database. - + Do you want to delete the file? - + Could not delete file - + An error occurred trying to delete the chosen file. - + Import DAT file... - + DAT files - + All files - + Import DATs from folder... - + Import ROMs from folder... - + Delete ROM set - + Are you sure you want to delete the ROM set {0}? - + Export ROMs to folder... - + Select mount point... - + Loading settings... - + Checking The Unarchiver... - + Loading database... - + Migrating database... - + Loading ROM sets... - + Exit - + _Umount - + Category - + Database - + Update statistics - + Do you want to update ROM set statistics in database? This will take a long time... - + Updating ROM sets statistics - + Calculating statistics for {0} - {1} ({2}) - + Removing old statistics - + Retrieving ROM sets from database... \ No newline at end of file diff --git a/RomRepoMgr/RomRepoMgr.csproj b/RomRepoMgr/RomRepoMgr.csproj index af4a9eb..35da12b 100644 --- a/RomRepoMgr/RomRepoMgr.csproj +++ b/RomRepoMgr/RomRepoMgr.csproj @@ -1,44 +1,44 @@  - - WinExe - net9.0 - default - - - - %(Filename) - - - Designer - - - - True - True - Localization.resx - - - - - - - - - - - - - - - - - - - - - - ResXFileCodeGenerator - Localization.Designer.cs - - + + WinExe + net9.0 + default + + + + %(Filename) + + + Designer + + + + True + True + Localization.resx + + + + + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Localization.Designer.cs + + diff --git a/RomRepoMgr/ViewLocator.cs b/RomRepoMgr/ViewLocator.cs index 3246f34..cc53e0e 100644 --- a/RomRepoMgr/ViewLocator.cs +++ b/RomRepoMgr/ViewLocator.cs @@ -6,23 +6,24 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using RomRepoMgr.ViewModels; -namespace RomRepoMgr +namespace RomRepoMgr; + +public class ViewLocator : IDataTemplate { - public class ViewLocator : IDataTemplate + public bool SupportsRecycling => false; + + public IControl Build(object data) { - public bool SupportsRecycling => false; + string name = data.GetType().FullName?.Replace("ViewModel", "View"); + Type type = name is null ? null : Type.GetType(name); - public IControl Build(object data) - { - string name = data.GetType().FullName?.Replace("ViewModel", "View"); - Type type = name is null ? null : Type.GetType(name); - - return type is null ? new TextBlock - { - Text = "Not Found: " + name - } : (Control)Activator.CreateInstance(type); - } - - public bool Match(object data) => data is ViewModelBase; + return type is null + ? new TextBlock + { + Text = "Not Found: " + name + } + : (Control)Activator.CreateInstance(type); } + + public bool Match(object data) => data is ViewModelBase; } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/AboutViewModel.cs b/RomRepoMgr/ViewModels/AboutViewModel.cs index e983f08..43bea88 100644 --- a/RomRepoMgr/ViewModels/AboutViewModel.cs +++ b/RomRepoMgr/ViewModels/AboutViewModel.cs @@ -31,136 +31,130 @@ using System.Reactive; using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Avalonia.Platform; using JetBrains.Annotations; -using Microsoft.CodeAnalysis; -using Microsoft.DotNet.PlatformAbstractions; using ReactiveUI; using RomRepoMgr.Core.Models; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class AboutViewModel : ViewModelBase { - public sealed class AboutViewModel : ViewModelBase + readonly About _view; + string _versionText; + + public AboutViewModel(About view) { - readonly About _view; - string _versionText; + _view = view; - public AboutViewModel(About view) + VersionText = + (Attribute.GetCustomAttribute(typeof(App).Assembly, typeof(AssemblyInformationalVersionAttribute)) as + AssemblyInformationalVersionAttribute)?.InformationalVersion; + + WebsiteCommand = ReactiveCommand.Create(ExecuteWebsiteCommand); + LicenseCommand = ReactiveCommand.Create(ExecuteLicenseCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + + Assemblies = new ObservableCollection(); + + // TODO: They do not load in time + Task.Run(() => { - _view = view; - - VersionText = - (Attribute.GetCustomAttribute(typeof(App).Assembly, typeof(AssemblyInformationalVersionAttribute)) as - AssemblyInformationalVersionAttribute)?.InformationalVersion; - - WebsiteCommand = ReactiveCommand.Create(ExecuteWebsiteCommand); - LicenseCommand = ReactiveCommand.Create(ExecuteLicenseCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - - Assemblies = new ObservableCollection(); - - // TODO: They do not load in time - Task.Run(() => + foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(a => a.FullName)) { - foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(a => a.FullName)) + string name = assembly.GetName().Name; + + string version = + (Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as + AssemblyInformationalVersionAttribute)?.InformationalVersion; + + if(name is null || version is null) continue; + + Assemblies.Add(new AssemblyModel { - string name = assembly.GetName().Name; - - string version = - (Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as - AssemblyInformationalVersionAttribute)?.InformationalVersion; - - if(name is null || - version is null) - continue; - - Assemblies.Add(new AssemblyModel - { - Name = name, - Version = version - }); - } - }); - } - - [NotNull] - public string AboutLabel => Localization.AboutLabel; - [NotNull] - public string LibrariesLabel => Localization.LibrariesLabel; - [NotNull] - public string AuthorsLabel => Localization.AuthorsLabel; - [NotNull] - public string Title => Localization.AboutTitle; - [NotNull] - public string SoftwareName => "RomRepoMgr"; - [NotNull] - public string SuiteName => "ROM Repository Manager"; - [NotNull] - public string Copyright => "© 2020-2024 Natalia Portillo"; - [NotNull] - public string Website => "https://www.claunia.com"; - [NotNull] - public string License => Localization.LicenseLabel; - [NotNull] - public string CloseLabel => Localization.CloseLabel; - [NotNull] - public string AssembliesLibraryText => Localization.AssembliesLibraryText; - [NotNull] - public string AssembliesVersionText => Localization.AssembliesVersionText; - [NotNull] - public string Authors => Localization.AuthorsText; - public ReactiveCommand WebsiteCommand { get; } - public ReactiveCommand LicenseCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ObservableCollection Assemblies { get; } - - public string VersionText - { - get => _versionText; - set => this.RaiseAndSetIfChanged(ref _versionText, value); - } - - void ExecuteWebsiteCommand() - { - var process = new Process - { - StartInfo = - { - UseShellExecute = false, - CreateNoWindow = true, - Arguments = "https://www.claunia.com" - } - }; - - if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - process.StartInfo.FileName = "cmd"; - process.StartInfo.Arguments = $"/c start {process.StartInfo.Arguments.Replace("&", "^&")}"; + Name = name, + Version = version + }); } - else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || - RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - process.StartInfo.FileName = "xdg-open"; - else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - process.StartInfo.FileName = "open"; - else - { - if(Debugger.IsAttached) throw new ArgumentOutOfRangeException(); - - return; - } - - process.Start(); - } - - void ExecuteLicenseCommand() - { - /* var dialog = new LicenseDialog(); - dialog.DataContext = new LicenseViewModel(dialog); - dialog.ShowDialog(_view);*/ - } - - void ExecuteCloseCommand() => _view.Close(); + }); } + + [NotNull] + public string AboutLabel => Localization.AboutLabel; + [NotNull] + public string LibrariesLabel => Localization.LibrariesLabel; + [NotNull] + public string AuthorsLabel => Localization.AuthorsLabel; + [NotNull] + public string Title => Localization.AboutTitle; + [NotNull] + public string SoftwareName => "RomRepoMgr"; + [NotNull] + public string SuiteName => "ROM Repository Manager"; + [NotNull] + public string Copyright => "© 2020-2024 Natalia Portillo"; + [NotNull] + public string Website => "https://www.claunia.com"; + [NotNull] + public string License => Localization.LicenseLabel; + [NotNull] + public string CloseLabel => Localization.CloseLabel; + [NotNull] + public string AssembliesLibraryText => Localization.AssembliesLibraryText; + [NotNull] + public string AssembliesVersionText => Localization.AssembliesVersionText; + [NotNull] + public string Authors => Localization.AuthorsText; + public ReactiveCommand WebsiteCommand { get; } + public ReactiveCommand LicenseCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ObservableCollection Assemblies { get; } + + public string VersionText + { + get => _versionText; + set => this.RaiseAndSetIfChanged(ref _versionText, value); + } + + void ExecuteWebsiteCommand() + { + var process = new Process + { + StartInfo = + { + UseShellExecute = false, + CreateNoWindow = true, + Arguments = "https://www.claunia.com" + } + }; + + if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + process.StartInfo.FileName = "cmd"; + process.StartInfo.Arguments = $"/c start {process.StartInfo.Arguments.Replace("&", "^&")}"; + } + else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || + RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + process.StartInfo.FileName = "xdg-open"; + else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + process.StartInfo.FileName = "open"; + else + { + if(Debugger.IsAttached) throw new ArgumentOutOfRangeException(); + + return; + } + + process.Start(); + } + + void ExecuteLicenseCommand() + { + /* var dialog = new LicenseDialog(); + dialog.DataContext = new LicenseViewModel(dialog); + dialog.ShowDialog(_view);*/ + } + + void ExecuteCloseCommand() => _view.Close(); } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/EditDatViewModel.cs b/RomRepoMgr/ViewModels/EditDatViewModel.cs index 5843dd1..66333a4 100644 --- a/RomRepoMgr/ViewModels/EditDatViewModel.cs +++ b/RomRepoMgr/ViewModels/EditDatViewModel.cs @@ -33,220 +33,211 @@ using RomRepoMgr.Database.Models; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public class EditDatViewModel : ViewModelBase { - public class EditDatViewModel : ViewModelBase + readonly RomSetModel _romSet; + readonly EditDat _view; + string _author; + string _category; + string _comment; + string _date; + string _description; + string _homepage; + bool _modified; + string _name; + string _version; + + public EditDatViewModel(EditDat view, RomSetModel romSet) { - readonly RomSetModel _romSet; - readonly EditDat _view; - string _author; - string _category; - string _comment; - string _date; - string _description; - string _homepage; - bool _modified; - string _name; - string _version; + _view = view; + _romSet = romSet; + _name = romSet.Name; + _version = romSet.Version; + _author = romSet.Author; + _comment = romSet.Comment; + _category = romSet.Category; + _date = romSet.Date; + _description = romSet.Description; + _homepage = romSet.Homepage; + SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); + CancelCommand = ReactiveCommand.Create(ExecuteCloseCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + } - public EditDatViewModel(EditDat view, RomSetModel romSet) + public string NameLabel => Localization.RomSetNameLabel; + public string VersionLabel => Localization.RomSetVersionLabel; + public string AuthorLabel => Localization.RomSetAuthorLabel; + public string CategoryLabel => Localization.RomSetCategoryLabel; + public string CommentLabel => Localization.RomSetCommentLabel; + public string DateLabel => Localization.RomSetDateLabel; + public string DescriptionLabel => Localization.RomSetDescriptionLabel; + public string HomepageLabel => Localization.HomepageLabel; + public string TotalMachinesLabel => Localization.TotalMachinesLabel; + public string CompleteMachinesLabel => Localization.CompleteMachinesLabel; + public string IncompleteMachinesLabel => Localization.IncompleteMachinesLabel; + public string TotalRomsLabel => Localization.TotalRomsLabel; + public string HaveRomsLabel => Localization.HaveRomsLabel; + public string MissRomsLabel => Localization.MissRomsLabel; + public string Title => Localization.EditDatTitle; + public string SaveLabel => Localization.SaveLabel; + public string CancelLabel => Localization.CancelLabel; + public string CloseLabel => Localization.CloseLabel; + + public ReactiveCommand SaveCommand { get; } + public ReactiveCommand CancelCommand { get; } + public ReactiveCommand CloseCommand { get; } + public long TotalMachines => _romSet.TotalMachines; + public long CompleteMachines => _romSet.CompleteMachines; + public long IncompleteMachines => _romSet.IncompleteMachines; + public long TotalRoms => _romSet.TotalRoms; + public long HaveRoms => _romSet.HaveRoms; + public long MissRoms => _romSet.MissRoms; + + public bool Modified + { + get => _modified; + set => this.RaiseAndSetIfChanged(ref _modified, value); + } + + public string Name + { + get => _name; + set { - _view = view; - _romSet = romSet; - _name = romSet.Name; - _version = romSet.Version; - _author = romSet.Author; - _comment = romSet.Comment; - _category = romSet.Category; - _date = romSet.Date; - _description = romSet.Description; - _homepage = romSet.Homepage; - SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); - CancelCommand = ReactiveCommand.Create(ExecuteCloseCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - } + if(value != _name) Modified = true; - public string NameLabel => Localization.RomSetNameLabel; - public string VersionLabel => Localization.RomSetVersionLabel; - public string AuthorLabel => Localization.RomSetAuthorLabel; - public string CategoryLabel => Localization.RomSetCategoryLabel; - public string CommentLabel => Localization.RomSetCommentLabel; - public string DateLabel => Localization.RomSetDateLabel; - public string DescriptionLabel => Localization.RomSetDescriptionLabel; - public string HomepageLabel => Localization.HomepageLabel; - public string TotalMachinesLabel => Localization.TotalMachinesLabel; - public string CompleteMachinesLabel => Localization.CompleteMachinesLabel; - public string IncompleteMachinesLabel => Localization.IncompleteMachinesLabel; - public string TotalRomsLabel => Localization.TotalRomsLabel; - public string HaveRomsLabel => Localization.HaveRomsLabel; - public string MissRomsLabel => Localization.MissRomsLabel; - public string Title => Localization.EditDatTitle; - public string SaveLabel => Localization.SaveLabel; - public string CancelLabel => Localization.CancelLabel; - public string CloseLabel => Localization.CloseLabel; - - public ReactiveCommand SaveCommand { get; } - public ReactiveCommand CancelCommand { get; } - public ReactiveCommand CloseCommand { get; } - public long TotalMachines => _romSet.TotalMachines; - public long CompleteMachines => _romSet.CompleteMachines; - public long IncompleteMachines => _romSet.IncompleteMachines; - public long TotalRoms => _romSet.TotalRoms; - public long HaveRoms => _romSet.HaveRoms; - public long MissRoms => _romSet.MissRoms; - - public bool Modified - { - get => _modified; - set => this.RaiseAndSetIfChanged(ref _modified, value); - } - - public string Name - { - get => _name; - set - { - if(value != _name) - Modified = true; - - this.RaiseAndSetIfChanged(ref _name, value); - } - } - - public string Version - { - get => _version; - set - { - if(value != _version) - Modified = true; - - this.RaiseAndSetIfChanged(ref _version, value); - } - } - - public string Author - { - get => _author; - set - { - if(value != _author) - Modified = true; - - this.RaiseAndSetIfChanged(ref _author, value); - } - } - - public string Comment - { - get => _comment; - set - { - if(value != _comment) - Modified = true; - - this.RaiseAndSetIfChanged(ref _comment, value); - } - } - - public string Category - { - get => _category; - set - { - if(value != _category) - Modified = true; - - this.RaiseAndSetIfChanged(ref _category, value); - } - } - - public string Date - { - get => _date; - set - { - if(value != _date) - Modified = true; - - this.RaiseAndSetIfChanged(ref _date, value); - } - } - - public string Description - { - get => _description; - set - { - if(value != _description) - Modified = true; - - this.RaiseAndSetIfChanged(ref _description, value); - } - } - - public string Homepage - { - get => _homepage; - set - { - if(value != _homepage) - Modified = true; - - this.RaiseAndSetIfChanged(ref _homepage, value); - } - } - - public EventHandler RomSetModified { get; set; } - - void ExecuteCloseCommand() => _view.Close(); - - async void ExecuteSaveCommand() - { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - RomSet romSetDb = await ctx.RomSets.FindAsync(_romSet.Id); - - if(romSetDb == null) - return; - - romSetDb.Author = Author; - romSetDb.Comment = Comment; - romSetDb.Category = Category; - romSetDb.Date = Date; - romSetDb.Description = Description; - romSetDb.Homepage = Homepage; - romSetDb.Name = Name; - romSetDb.Version = Version; - romSetDb.UpdatedOn = DateTime.UtcNow; - - await ctx.SaveChangesAsync(); - - RomSetModified?.Invoke(this, new RomSetEventArgs - { - RomSet = new RomSetModel - { - Author = Author, - Comment = Comment, - Category = Category, - Date = Date, - Description = Description, - Homepage = Homepage, - Name = Name, - Version = Version, - Filename = _romSet.Filename, - Sha384 = _romSet.Sha384, - TotalMachines = _romSet.TotalMachines, - CompleteMachines = _romSet.CompleteMachines, - IncompleteMachines = _romSet.IncompleteMachines, - TotalRoms = _romSet.TotalRoms, - HaveRoms = _romSet.HaveRoms, - MissRoms = _romSet.MissRoms, - Id = _romSet.Id - } - }); - - Modified = false; + this.RaiseAndSetIfChanged(ref _name, value); } } + + public string Version + { + get => _version; + set + { + if(value != _version) Modified = true; + + this.RaiseAndSetIfChanged(ref _version, value); + } + } + + public string Author + { + get => _author; + set + { + if(value != _author) Modified = true; + + this.RaiseAndSetIfChanged(ref _author, value); + } + } + + public string Comment + { + get => _comment; + set + { + if(value != _comment) Modified = true; + + this.RaiseAndSetIfChanged(ref _comment, value); + } + } + + public string Category + { + get => _category; + set + { + if(value != _category) Modified = true; + + this.RaiseAndSetIfChanged(ref _category, value); + } + } + + public string Date + { + get => _date; + set + { + if(value != _date) Modified = true; + + this.RaiseAndSetIfChanged(ref _date, value); + } + } + + public string Description + { + get => _description; + set + { + if(value != _description) Modified = true; + + this.RaiseAndSetIfChanged(ref _description, value); + } + } + + public string Homepage + { + get => _homepage; + set + { + if(value != _homepage) Modified = true; + + this.RaiseAndSetIfChanged(ref _homepage, value); + } + } + + public EventHandler RomSetModified { get; set; } + + void ExecuteCloseCommand() => _view.Close(); + + async void ExecuteSaveCommand() + { + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + RomSet romSetDb = await ctx.RomSets.FindAsync(_romSet.Id); + + if(romSetDb == null) return; + + romSetDb.Author = Author; + romSetDb.Comment = Comment; + romSetDb.Category = Category; + romSetDb.Date = Date; + romSetDb.Description = Description; + romSetDb.Homepage = Homepage; + romSetDb.Name = Name; + romSetDb.Version = Version; + romSetDb.UpdatedOn = DateTime.UtcNow; + + await ctx.SaveChangesAsync(); + + RomSetModified?.Invoke(this, + new RomSetEventArgs + { + RomSet = new RomSetModel + { + Author = Author, + Comment = Comment, + Category = Category, + Date = Date, + Description = Description, + Homepage = Homepage, + Name = Name, + Version = Version, + Filename = _romSet.Filename, + Sha384 = _romSet.Sha384, + TotalMachines = _romSet.TotalMachines, + CompleteMachines = _romSet.CompleteMachines, + IncompleteMachines = _romSet.IncompleteMachines, + TotalRoms = _romSet.TotalRoms, + HaveRoms = _romSet.HaveRoms, + MissRoms = _romSet.MissRoms, + Id = _romSet.Id + } + }); + + Modified = false; + } } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/ExportDatViewModel.cs b/RomRepoMgr/ViewModels/ExportDatViewModel.cs index 5634a15..fc1b397 100644 --- a/RomRepoMgr/ViewModels/ExportDatViewModel.cs +++ b/RomRepoMgr/ViewModels/ExportDatViewModel.cs @@ -36,125 +36,117 @@ using RomRepoMgr.Resources; using RomRepoMgr.Views; using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class ExportDatViewModel : ViewModelBase { - public sealed class ExportDatViewModel : ViewModelBase + readonly string _datHash; + readonly string _outPath; + readonly ExportDat _view; + readonly Compression _worker; + bool _canClose; + string _errorMessage; + bool _errorVisible; + bool _progressVisible; + string _statusMessage; + + public ExportDatViewModel(ExportDat view, string datHash, string outPath) { - readonly string _datHash; - readonly string _outPath; - readonly ExportDat _view; - readonly Compression _worker; - bool _canClose; - string _errorMessage; - bool _errorVisible; - bool _progressVisible; - string _statusMessage; + _view = view; + _datHash = datHash; + _outPath = outPath; + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + ProgressVisible = false; + ErrorVisible = false; + _worker = new Compression(); + _worker.FinishedWithText += OnWorkerOnFinishedWithText; + _worker.FailedWithText += OnWorkerOnFailedWithText; + } - public ExportDatViewModel(ExportDat view, string datHash, string outPath) + [NotNull] + public string Title => Localization.ExportDatTitle; + + public string StatusMessage + { + get => _statusMessage; + set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool ErrorVisible + { + get => _errorVisible; + set => this.RaiseAndSetIfChanged(ref _errorVisible, value); + } + + public string ErrorMessage + { + get => _errorMessage; + set => this.RaiseAndSetIfChanged(ref _errorMessage, value); + } + + public bool CanClose + { + get => _canClose; + set => this.RaiseAndSetIfChanged(ref _canClose, value); + } + + public string CloseLabel => Localization.CloseLabel; + public ReactiveCommand CloseCommand { get; } + + void OnWorkerOnFinishedWithText(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => + { + StatusMessage = Localization.Finished; + ProgressVisible = false; + CanClose = true; + }); + + void OnWorkerOnFailedWithText(object sender, ErrorEventArgs args) => Dispatcher.UIThread.Post(() => + { + ErrorMessage = args.Message; + ProgressVisible = false; + ErrorVisible = true; + CanClose = true; + }); + + void ExecuteCloseCommand() => _view.Close(); + + internal void OnOpened() + { + ProgressVisible = true; + StatusMessage = Localization.DecompressingDat; + + var sha384Bytes = new byte[48]; + string sha384 = _datHash; + + for(var i = 0; i < 48; i++) { - _view = view; - _datHash = datHash; - _outPath = outPath; - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - ProgressVisible = false; - ErrorVisible = false; - _worker = new Compression(); - _worker.FinishedWithText += OnWorkerOnFinishedWithText; - _worker.FailedWithText += OnWorkerOnFailedWithText; + if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); + else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); + else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); + + if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30); + else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37); + else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57); } - [NotNull] - public string Title => Localization.ExportDatTitle; + string datHash32 = Base32.ToBase32String(sha384Bytes); + string datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles"); + string compressedDatPath = Path.Combine(datFilesPath, datHash32 + ".lz"); - public string StatusMessage - { - get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); - } + if(!File.Exists(compressedDatPath)) _view.Close(); - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool ErrorVisible - { - get => _errorVisible; - set => this.RaiseAndSetIfChanged(ref _errorVisible, value); - } - - public string ErrorMessage - { - get => _errorMessage; - set => this.RaiseAndSetIfChanged(ref _errorMessage, value); - } - - public bool CanClose - { - get => _canClose; - set => this.RaiseAndSetIfChanged(ref _canClose, value); - } - - public string CloseLabel => Localization.CloseLabel; - public ReactiveCommand CloseCommand { get; } - - void OnWorkerOnFinishedWithText(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => - { - StatusMessage = Localization.Finished; - ProgressVisible = false; - CanClose = true; - }); - - void OnWorkerOnFailedWithText(object sender, ErrorEventArgs args) => Dispatcher.UIThread.Post(() => - { - ErrorMessage = args.Message; - ProgressVisible = false; - ErrorVisible = true; - CanClose = true; - }); - - void ExecuteCloseCommand() => _view.Close(); - - internal void OnOpened() - { - ProgressVisible = true; - StatusMessage = Localization.DecompressingDat; - - byte[] sha384Bytes = new byte[48]; - string sha384 = _datHash; - - for(int i = 0; i < 48; i++) - { - if(sha384[i * 2] >= 0x30 && - sha384[i * 2] <= 0x39) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); - else if(sha384[i * 2] >= 0x41 && - sha384[i * 2] <= 0x46) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); - else if(sha384[i * 2] >= 0x61 && - sha384[i * 2] <= 0x66) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); - - if(sha384[(i * 2) + 1] >= 0x30 && - sha384[(i * 2) + 1] <= 0x39) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30); - else if(sha384[(i * 2) + 1] >= 0x41 && - sha384[(i * 2) + 1] <= 0x46) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37); - else if(sha384[(i * 2) + 1] >= 0x61 && - sha384[(i * 2) + 1] <= 0x66) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57); - } - - string datHash32 = Base32.ToBase32String(sha384Bytes); - string datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles"); - string compressedDatPath = Path.Combine(datFilesPath, datHash32 + ".lz"); - - if(!File.Exists(compressedDatPath)) - _view.Close(); - - Task.Run(() => _worker.DecompressFile(compressedDatPath, _outPath)); - } + Task.Run(() => _worker.DecompressFile(compressedDatPath, _outPath)); } } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/ExportRomsViewModel.cs b/RomRepoMgr/ViewModels/ExportRomsViewModel.cs index 1977d99..007a594 100644 --- a/RomRepoMgr/ViewModels/ExportRomsViewModel.cs +++ b/RomRepoMgr/ViewModels/ExportRomsViewModel.cs @@ -34,237 +34,236 @@ using RomRepoMgr.Core.Workers; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class ExportRomsViewModel : ViewModelBase { - public sealed class ExportRomsViewModel : ViewModelBase + readonly long _romSetId; + readonly ExportRoms _view; + bool _canClose; + bool _progress2IsIndeterminate; + double _progress2Maximum; + double _progress2Minimum; + double _progress2Value; + bool _progress2Visible; + bool _progress3IsIndeterminate; + double _progress3Maximum; + double _progress3Minimum; + double _progress3Value; + bool _progress3Visible; + bool _progressIsIndeterminate; + double _progressMaximum; + double _progressMinimum; + double _progressValue; + bool _progressVisible; + string _status2Message; + string _status3Message; + string _statusMessage; + + public ExportRomsViewModel(ExportRoms view, string folderPath, long romSetId) { - readonly long _romSetId; - readonly ExportRoms _view; - bool _canClose; - bool _progress2IsIndeterminate; - double _progress2Maximum; - double _progress2Minimum; - double _progress2Value; - bool _progress2Visible; - bool _progress3IsIndeterminate; - double _progress3Maximum; - double _progress3Minimum; - double _progress3Value; - bool _progress3Visible; - bool _progressIsIndeterminate; - double _progressMaximum; - double _progressMinimum; - double _progressValue; - bool _progressVisible; - string _status2Message; - string _status3Message; - string _statusMessage; + _view = view; + _romSetId = romSetId; + FolderPath = folderPath; + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + CanClose = false; + } - public ExportRomsViewModel(ExportRoms view, string folderPath, long romSetId) - { - _view = view; - _romSetId = romSetId; - FolderPath = folderPath; - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - CanClose = false; - } + [NotNull] + public string PathLabel => Localization.PathLabel; + public string FolderPath { get; } - [NotNull] - public string PathLabel => Localization.PathLabel; - public string FolderPath { get; } + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } + public string StatusMessage + { + get => _statusMessage; + set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + } - public string StatusMessage - { - get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); - } + public double ProgressMinimum + { + get => _progressMinimum; + set => this.RaiseAndSetIfChanged(ref _progressMinimum, value); + } - public double ProgressMinimum - { - get => _progressMinimum; - set => this.RaiseAndSetIfChanged(ref _progressMinimum, value); - } + public double ProgressMaximum + { + get => _progressMaximum; + set => this.RaiseAndSetIfChanged(ref _progressMaximum, value); + } - public double ProgressMaximum - { - get => _progressMaximum; - set => this.RaiseAndSetIfChanged(ref _progressMaximum, value); - } + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } + public bool ProgressIsIndeterminate + { + get => _progressIsIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIsIndeterminate, value); + } - public bool ProgressIsIndeterminate - { - get => _progressIsIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIsIndeterminate, value); - } + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } + public string Status2Message + { + get => _status2Message; + set => this.RaiseAndSetIfChanged(ref _status2Message, value); + } - public string Status2Message - { - get => _status2Message; - set => this.RaiseAndSetIfChanged(ref _status2Message, value); - } + public double Progress2Minimum + { + get => _progress2Minimum; + set => this.RaiseAndSetIfChanged(ref _progress2Minimum, value); + } - public double Progress2Minimum - { - get => _progress2Minimum; - set => this.RaiseAndSetIfChanged(ref _progress2Minimum, value); - } + public double Progress2Maximum + { + get => _progress2Maximum; + set => this.RaiseAndSetIfChanged(ref _progress2Maximum, value); + } - public double Progress2Maximum - { - get => _progress2Maximum; - set => this.RaiseAndSetIfChanged(ref _progress2Maximum, value); - } + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } + public bool Progress2IsIndeterminate + { + get => _progress2IsIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2IsIndeterminate, value); + } - public bool Progress2IsIndeterminate - { - get => _progress2IsIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2IsIndeterminate, value); - } + public bool Progress3Visible + { + get => _progress3Visible; + set => this.RaiseAndSetIfChanged(ref _progress3Visible, value); + } - public bool Progress3Visible - { - get => _progress3Visible; - set => this.RaiseAndSetIfChanged(ref _progress3Visible, value); - } + public string Status3Message + { + get => _status3Message; + set => this.RaiseAndSetIfChanged(ref _status3Message, value); + } - public string Status3Message - { - get => _status3Message; - set => this.RaiseAndSetIfChanged(ref _status3Message, value); - } + public double Progress3Minimum + { + get => _progress3Minimum; + set => this.RaiseAndSetIfChanged(ref _progress3Minimum, value); + } - public double Progress3Minimum - { - get => _progress3Minimum; - set => this.RaiseAndSetIfChanged(ref _progress3Minimum, value); - } + public double Progress3Maximum + { + get => _progress3Maximum; + set => this.RaiseAndSetIfChanged(ref _progress3Maximum, value); + } - public double Progress3Maximum - { - get => _progress3Maximum; - set => this.RaiseAndSetIfChanged(ref _progress3Maximum, value); - } + public double Progress3Value + { + get => _progress3Value; + set => this.RaiseAndSetIfChanged(ref _progress3Value, value); + } - public double Progress3Value - { - get => _progress3Value; - set => this.RaiseAndSetIfChanged(ref _progress3Value, value); - } + public bool Progress3IsIndeterminate + { + get => _progress3IsIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progress3IsIndeterminate, value); + } - public bool Progress3IsIndeterminate - { - get => _progress3IsIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progress3IsIndeterminate, value); - } + [NotNull] + public string Title => Localization.ExportRomsTitle; + [NotNull] + public string CloseLabel => Localization.CloseLabel; - [NotNull] - public string Title => Localization.ExportRomsTitle; - [NotNull] - public string CloseLabel => Localization.CloseLabel; + public bool CanClose + { + get => _canClose; + set => this.RaiseAndSetIfChanged(ref _canClose, value); + } - public bool CanClose - { - get => _canClose; - set => this.RaiseAndSetIfChanged(ref _canClose, value); - } + public ReactiveCommand CloseCommand { get; } - public ReactiveCommand CloseCommand { get; } + void ExecuteCloseCommand() => _view.Close(); - void ExecuteCloseCommand() => _view.Close(); + void OnWorkerOnFinished(object sender, EventArgs args) => Dispatcher.UIThread.Post(() => + { + ProgressVisible = false; + CanClose = true; + Progress2Visible = false; + Progress3Visible = false; + }); - void OnWorkerOnFinished(object sender, EventArgs args) => Dispatcher.UIThread.Post(() => - { - ProgressVisible = false; - CanClose = true; - Progress2Visible = false; - Progress3Visible = false; - }); + void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => + { + ProgressIsIndeterminate = false; + ProgressMaximum = args.Maximum; + ProgressMinimum = args.Minimum; + }); - void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => - { - ProgressIsIndeterminate = false; - ProgressMaximum = args.Maximum; - ProgressMinimum = args.Minimum; - }); + void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => + Dispatcher.UIThread.Post(() => ProgressValue = args.Value); - void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => - Dispatcher.UIThread.Post(() => ProgressValue = args.Value); + void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => + Dispatcher.UIThread.Post(() => StatusMessage = args.Message); - void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => - Dispatcher.UIThread.Post(() => StatusMessage = args.Message); + void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => + Dispatcher.UIThread.Post(() => ProgressIsIndeterminate = true); - void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => - Dispatcher.UIThread.Post(() => ProgressIsIndeterminate = true); + void OnWorkerOnSetProgressBounds2(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => + { + Progress2Visible = true; + Progress2IsIndeterminate = false; + Progress2Maximum = args.Maximum; + Progress2Minimum = args.Minimum; + }); - void OnWorkerOnSetProgressBounds2(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => - { - Progress2Visible = true; - Progress2IsIndeterminate = false; - Progress2Maximum = args.Maximum; - Progress2Minimum = args.Minimum; - }); + void OnWorkerOnSetProgress2(object sender, ProgressEventArgs args) => + Dispatcher.UIThread.Post(() => Progress2Value = args.Value); - void OnWorkerOnSetProgress2(object sender, ProgressEventArgs args) => - Dispatcher.UIThread.Post(() => Progress2Value = args.Value); + void OnWorkerOnSetMessage2(object sender, MessageEventArgs args) => + Dispatcher.UIThread.Post(() => Status2Message = args.Message); - void OnWorkerOnSetMessage2(object sender, MessageEventArgs args) => - Dispatcher.UIThread.Post(() => Status2Message = args.Message); + void OnWorkerOnSetProgressBounds3(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => + { + Progress3Visible = true; + Progress3IsIndeterminate = false; + Progress3Maximum = args.Maximum; + Progress3Minimum = args.Minimum; + }); - void OnWorkerOnSetProgressBounds3(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => - { - Progress3Visible = true; - Progress3IsIndeterminate = false; - Progress3Maximum = args.Maximum; - Progress3Minimum = args.Minimum; - }); + void OnWorkerOnSetProgress3(object sender, ProgressEventArgs args) => + Dispatcher.UIThread.Post(() => Progress3Value = args.Value); - void OnWorkerOnSetProgress3(object sender, ProgressEventArgs args) => - Dispatcher.UIThread.Post(() => Progress3Value = args.Value); + void OnWorkerOnSetMessage3(object sender, MessageEventArgs args) => + Dispatcher.UIThread.Post(() => Status3Message = args.Message); - void OnWorkerOnSetMessage3(object sender, MessageEventArgs args) => - Dispatcher.UIThread.Post(() => Status3Message = args.Message); + public void OnOpened() + { + var worker = new FileExporter(_romSetId, FolderPath); + worker.SetMessage += OnWorkerOnSetMessage; + worker.SetProgress += OnWorkerOnSetProgress; + worker.SetProgressBounds += OnWorkerOnSetProgressBounds; + worker.SetMessage2 += OnWorkerOnSetMessage2; + worker.SetProgress2 += OnWorkerOnSetProgress2; + worker.SetProgress2Bounds += OnWorkerOnSetProgressBounds2; + worker.SetMessage3 += OnWorkerOnSetMessage3; + worker.SetProgress3 += OnWorkerOnSetProgress3; + worker.SetProgress3Bounds += OnWorkerOnSetProgressBounds3; + worker.WorkFinished += OnWorkerOnFinished; - public void OnOpened() - { - var worker = new FileExporter(_romSetId, FolderPath); - worker.SetMessage += OnWorkerOnSetMessage; - worker.SetProgress += OnWorkerOnSetProgress; - worker.SetProgressBounds += OnWorkerOnSetProgressBounds; - worker.SetMessage2 += OnWorkerOnSetMessage2; - worker.SetProgress2 += OnWorkerOnSetProgress2; - worker.SetProgress2Bounds += OnWorkerOnSetProgressBounds2; - worker.SetMessage3 += OnWorkerOnSetMessage3; - worker.SetProgress3 += OnWorkerOnSetProgress3; - worker.SetProgress3Bounds += OnWorkerOnSetProgressBounds3; - worker.WorkFinished += OnWorkerOnFinished; + ProgressVisible = true; - ProgressVisible = true; - - Task.Run(worker.Export); - } + Task.Run(worker.Export); } } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/ImportDatFolderViewModel.cs b/RomRepoMgr/ViewModels/ImportDatFolderViewModel.cs index 5337234..4959bef 100644 --- a/RomRepoMgr/ViewModels/ImportDatFolderViewModel.cs +++ b/RomRepoMgr/ViewModels/ImportDatFolderViewModel.cs @@ -38,319 +38,325 @@ using RomRepoMgr.Resources; using RomRepoMgr.Views; using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class ImportDatFolderViewModel : ViewModelBase { - public sealed class ImportDatFolderViewModel : ViewModelBase + readonly ImportDatFolder _view; + bool _allFilesChecked; + bool _canClose; + bool _canStart; + string _category; + string[] _datFiles; + bool _isImporting; + bool _isReady; + int _listPosition; + bool _progress2IsIndeterminate; + double _progress2Maximum; + double _progress2Minimum; + double _progress2Value; + bool _progress2Visible; + bool _progressIsIndeterminate; + double _progressMaximum; + double _progressMinimum; + double _progressValue; + bool _progressVisible; + bool _recursiveChecked; + string _status2Message; + string _statusMessage; + + public ImportDatFolderViewModel(ImportDatFolder view, string folderPath) { - readonly ImportDatFolder _view; - bool _allFilesChecked; - bool _canClose; - bool _canStart; - string _category; - string[] _datFiles; - bool _isImporting; - bool _isReady; - int _listPosition; - bool _progress2IsIndeterminate; - double _progress2Maximum; - double _progress2Minimum; - double _progress2Value; - bool _progress2Visible; - bool _progressIsIndeterminate; - double _progressMaximum; - double _progressMinimum; - double _progressValue; - bool _progressVisible; - bool _recursiveChecked; - string _status2Message; - string _statusMessage; + _view = view; + FolderPath = folderPath; + _allFilesChecked = false; + _recursiveChecked = true; + ImportResults = new ObservableCollection(); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + } - public ImportDatFolderViewModel(ImportDatFolder view, string folderPath) + public string PathLabel => Localization.PathLabel; + public string CategoryLabel => Localization.RomSetCategoryLabel; + public string FolderPath { get; } + public string AllFilesLabel => Localization.AllFilesLabel; + public string RecursiveLabel => Localization.RecursiveLabel; + + public bool AllFilesChecked + { + get => _allFilesChecked; + set { - _view = view; - FolderPath = folderPath; - _allFilesChecked = false; - _recursiveChecked = true; - ImportResults = new ObservableCollection(); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + this.RaiseAndSetIfChanged(ref _allFilesChecked, value); + RefreshFiles(); } + } - public string PathLabel => Localization.PathLabel; - public string CategoryLabel => Localization.RomSetCategoryLabel; - public string FolderPath { get; } - public string AllFilesLabel => Localization.AllFilesLabel; - public string RecursiveLabel => Localization.RecursiveLabel; - - public bool AllFilesChecked + public bool RecursiveChecked + { + get => _recursiveChecked; + set { - get => _allFilesChecked; - set - { - this.RaiseAndSetIfChanged(ref _allFilesChecked, value); - RefreshFiles(); - } + this.RaiseAndSetIfChanged(ref _recursiveChecked, value); + RefreshFiles(); } + } - public bool RecursiveChecked + public bool IsReady + { + get => _isReady; + set => this.RaiseAndSetIfChanged(ref _isReady, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public string StatusMessage + { + get => _statusMessage; + set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + } + + public double ProgressMinimum + { + get => _progressMinimum; + set => this.RaiseAndSetIfChanged(ref _progressMinimum, value); + } + + public double ProgressMaximum + { + get => _progressMaximum; + set => this.RaiseAndSetIfChanged(ref _progressMaximum, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public bool ProgressIsIndeterminate + { + get => _progressIsIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIsIndeterminate, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public string Status2Message + { + get => _status2Message; + set => this.RaiseAndSetIfChanged(ref _status2Message, value); + } + + public double Progress2Minimum + { + get => _progress2Minimum; + set => this.RaiseAndSetIfChanged(ref _progress2Minimum, value); + } + + public double Progress2Maximum + { + get => _progress2Maximum; + set => this.RaiseAndSetIfChanged(ref _progress2Maximum, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + public bool Progress2IsIndeterminate + { + get => _progress2IsIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2IsIndeterminate, value); + } + + public bool IsImporting + { + get => _isImporting; + set => this.RaiseAndSetIfChanged(ref _isImporting, value); + } + + public string Category + { + get => _category; + set => this.RaiseAndSetIfChanged(ref _category, value); + } + + [NotNull] + public string Title => Localization.ImportDatFolderTitle; + + public ObservableCollection ImportResults { get; } + public string ResultFilenameLabel => Localization.ResultFilenameLabel; + public string ResultStatusLabel => Localization.ResultStatusLabel; + public string CloseLabel => Localization.CloseLabel; + public string StartLabel => Localization.StartLabel; + + public bool CanClose + { + get => _canClose; + set => this.RaiseAndSetIfChanged(ref _canClose, value); + } + + public bool CanStart + { + get => _canStart; + set => this.RaiseAndSetIfChanged(ref _canStart, value); + } + + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StartCommand { get; } + + internal void OnOpened() => RefreshFiles(); + + void RefreshFiles() => Task.Run(() => + { + Dispatcher.UIThread.Post(() => { - get => _recursiveChecked; - set - { - this.RaiseAndSetIfChanged(ref _recursiveChecked, value); - RefreshFiles(); - } - } - - public bool IsReady - { - get => _isReady; - set => this.RaiseAndSetIfChanged(ref _isReady, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public string StatusMessage - { - get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); - } - - public double ProgressMinimum - { - get => _progressMinimum; - set => this.RaiseAndSetIfChanged(ref _progressMinimum, value); - } - - public double ProgressMaximum - { - get => _progressMaximum; - set => this.RaiseAndSetIfChanged(ref _progressMaximum, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public bool ProgressIsIndeterminate - { - get => _progressIsIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIsIndeterminate, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public string Status2Message - { - get => _status2Message; - set => this.RaiseAndSetIfChanged(ref _status2Message, value); - } - - public double Progress2Minimum - { - get => _progress2Minimum; - set => this.RaiseAndSetIfChanged(ref _progress2Minimum, value); - } - - public double Progress2Maximum - { - get => _progress2Maximum; - set => this.RaiseAndSetIfChanged(ref _progress2Maximum, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - public bool Progress2IsIndeterminate - { - get => _progress2IsIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2IsIndeterminate, value); - } - - public bool IsImporting - { - get => _isImporting; - set => this.RaiseAndSetIfChanged(ref _isImporting, value); - } - - public string Category - { - get => _category; - set => this.RaiseAndSetIfChanged(ref _category, value); - } - - [NotNull] - public string Title => Localization.ImportDatFolderTitle; - - public ObservableCollection ImportResults { get; } - public string ResultFilenameLabel => Localization.ResultFilenameLabel; - public string ResultStatusLabel => Localization.ResultStatusLabel; - public string CloseLabel => Localization.CloseLabel; - public string StartLabel => Localization.StartLabel; - - public bool CanClose - { - get => _canClose; - set => this.RaiseAndSetIfChanged(ref _canClose, value); - } - - public bool CanStart - { - get => _canStart; - set => this.RaiseAndSetIfChanged(ref _canStart, value); - } - - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StartCommand { get; } - - internal void OnOpened() => RefreshFiles(); - - void RefreshFiles() => Task.Run(() => - { - Dispatcher.UIThread.Post(() => - { - IsReady = false; - ProgressVisible = true; - Progress2Visible = false; - ProgressIsIndeterminate = true; - StatusMessage = Localization.SearchingForFiles; - }); - - if(_allFilesChecked) - { - _datFiles = Directory.GetFiles(FolderPath, "*.*", - _recursiveChecked ? SearchOption.AllDirectories - : SearchOption.TopDirectoryOnly).OrderBy(f => f).ToArray(); - } - else - { - string[] dats = Directory.GetFiles(FolderPath, "*.dat", - _recursiveChecked ? SearchOption.AllDirectories - : SearchOption.TopDirectoryOnly); - - string[] xmls = Directory.GetFiles(FolderPath, "*.xml", - _recursiveChecked ? SearchOption.AllDirectories - : SearchOption.TopDirectoryOnly); - - _datFiles = dats.Concat(xmls).OrderBy(f => f).ToArray(); - } - - Dispatcher.UIThread.Post(() => - { - IsReady = true; - ProgressVisible = false; - StatusMessage = string.Format(Localization.FoundFiles, _datFiles.Length); - CanClose = true; - CanStart = true; - }); - }); - - void ExecuteCloseCommand() => _view.Close(); - - void ExecuteStartCommand() - { - _listPosition = 0; - ProgressMinimum = 0; - ProgressMaximum = _datFiles.Length; - ProgressValue = 0; - ProgressIsIndeterminate = false; - ProgressVisible = true; - Progress2Visible = true; - CanClose = false; - CanStart = false; IsReady = false; - IsImporting = true; + ProgressVisible = true; + Progress2Visible = false; + ProgressIsIndeterminate = true; + StatusMessage = Localization.SearchingForFiles; + }); - Import(); + if(_allFilesChecked) + { + _datFiles = Directory + .GetFiles(FolderPath, + "*.*", + _recursiveChecked ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) + .OrderBy(f => f) + .ToArray(); + } + else + { + string[] dats = Directory.GetFiles(FolderPath, + "*.dat", + _recursiveChecked + ? SearchOption.AllDirectories + : SearchOption.TopDirectoryOnly); + + string[] xmls = Directory.GetFiles(FolderPath, + "*.xml", + _recursiveChecked + ? SearchOption.AllDirectories + : SearchOption.TopDirectoryOnly); + + _datFiles = dats.Concat(xmls).OrderBy(f => f).ToArray(); } - void Import() + Dispatcher.UIThread.Post(() => { - if(_listPosition >= _datFiles.Length) - { - Progress2Visible = false; - ProgressVisible = false; - StatusMessage = Localization.Finished; - CanClose = true; - CanStart = false; - IsReady = true; - - return; - } - - StatusMessage = string.Format(Localization.ImportingItem, Path.GetFileName(_datFiles[_listPosition])); - ProgressValue = _listPosition; - - var _worker = new DatImporter(_datFiles[_listPosition], Category); - _worker.ErrorOccurred += OnWorkerOnErrorOccurred; - _worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress; - _worker.SetMessage += OnWorkerOnSetMessage; - _worker.SetProgress += OnWorkerOnSetProgress; - _worker.SetProgressBounds += OnWorkerOnSetProgressBounds; - _worker.WorkFinished += OnWorkerOnWorkFinished; - _worker.RomSetAdded += RomSetAdded; - Task.Run(_worker.Import); - } - - void OnWorkerOnWorkFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => - { - ImportResults.Add(new ImportDatFolderItem - { - Filename = Path.GetFileName(_datFiles[_listPosition]), - Status = args.Message - }); - - _listPosition++; - Import(); + IsReady = true; + ProgressVisible = false; + StatusMessage = string.Format(Localization.FoundFiles, _datFiles.Length); + CanClose = true; + CanStart = true; }); + }); - void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => - { - Progress2IsIndeterminate = false; - Progress2Maximum = args.Maximum; - Progress2Minimum = args.Minimum; - }); + void ExecuteCloseCommand() => _view.Close(); - void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => - Dispatcher.UIThread.Post(() => Progress2Value = args.Value); - - void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => - Dispatcher.UIThread.Post(() => Status2Message = args.Message); - - void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => - Dispatcher.UIThread.Post(() => Progress2IsIndeterminate = true); - - void OnWorkerOnErrorOccurred(object sender, ErrorEventArgs args) => Dispatcher.UIThread.Post(() => - { - ImportResults.Add(new ImportDatFolderItem - { - Filename = Path.GetFileName(_datFiles[_listPosition]), - Status = args.Message - }); - - _listPosition++; - Import(); - }); - - public event EventHandler RomSetAdded; - } - - public sealed class ImportDatFolderItem + void ExecuteStartCommand() { - public string Filename { get; set; } - public string Status { get; set; } + _listPosition = 0; + ProgressMinimum = 0; + ProgressMaximum = _datFiles.Length; + ProgressValue = 0; + ProgressIsIndeterminate = false; + ProgressVisible = true; + Progress2Visible = true; + CanClose = false; + CanStart = false; + IsReady = false; + IsImporting = true; + + Import(); } + + void Import() + { + if(_listPosition >= _datFiles.Length) + { + Progress2Visible = false; + ProgressVisible = false; + StatusMessage = Localization.Finished; + CanClose = true; + CanStart = false; + IsReady = true; + + return; + } + + StatusMessage = string.Format(Localization.ImportingItem, Path.GetFileName(_datFiles[_listPosition])); + ProgressValue = _listPosition; + + var _worker = new DatImporter(_datFiles[_listPosition], Category); + _worker.ErrorOccurred += OnWorkerOnErrorOccurred; + _worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress; + _worker.SetMessage += OnWorkerOnSetMessage; + _worker.SetProgress += OnWorkerOnSetProgress; + _worker.SetProgressBounds += OnWorkerOnSetProgressBounds; + _worker.WorkFinished += OnWorkerOnWorkFinished; + _worker.RomSetAdded += RomSetAdded; + Task.Run(_worker.Import); + } + + void OnWorkerOnWorkFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => + { + ImportResults.Add(new ImportDatFolderItem + { + Filename = Path.GetFileName(_datFiles[_listPosition]), + Status = args.Message + }); + + _listPosition++; + Import(); + }); + + void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => + { + Progress2IsIndeterminate = false; + Progress2Maximum = args.Maximum; + Progress2Minimum = args.Minimum; + }); + + void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => + Dispatcher.UIThread.Post(() => Progress2Value = args.Value); + + void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => + Dispatcher.UIThread.Post(() => Status2Message = args.Message); + + void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => + Dispatcher.UIThread.Post(() => Progress2IsIndeterminate = true); + + void OnWorkerOnErrorOccurred(object sender, ErrorEventArgs args) => Dispatcher.UIThread.Post(() => + { + ImportResults.Add(new ImportDatFolderItem + { + Filename = Path.GetFileName(_datFiles[_listPosition]), + Status = args.Message + }); + + _listPosition++; + Import(); + }); + + public event EventHandler RomSetAdded; +} + +public sealed class ImportDatFolderItem +{ + public string Filename { get; set; } + public string Status { get; set; } } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/ImportDatViewModel.cs b/RomRepoMgr/ViewModels/ImportDatViewModel.cs index 55f6783..bbf8190 100644 --- a/RomRepoMgr/ViewModels/ImportDatViewModel.cs +++ b/RomRepoMgr/ViewModels/ImportDatViewModel.cs @@ -34,138 +34,137 @@ using RomRepoMgr.Core.Workers; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class ImportDatViewModel : ViewModelBase { - public sealed class ImportDatViewModel : ViewModelBase + readonly ImportDat _view; + readonly DatImporter _worker; + bool _canClose; + double _currentValue; + string _errorMessage; + bool _errorVisible; + bool _indeterminateProgress; + double _maximumValue; + double _minimumValue; + bool _progressVisible; + string _statusMessage; + + public ImportDatViewModel(ImportDat view, string datPath) { - readonly ImportDat _view; - readonly DatImporter _worker; - bool _canClose; - double _currentValue; - string _errorMessage; - bool _errorVisible; - bool _indeterminateProgress; - double _maximumValue; - double _minimumValue; - bool _progressVisible; - string _statusMessage; - - public ImportDatViewModel(ImportDat view, string datPath) - { - _view = view; - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - IndeterminateProgress = true; - ProgressVisible = false; - ErrorVisible = false; - _worker = new DatImporter(datPath, null); - _worker.ErrorOccurred += OnWorkerOnErrorOccurred; - _worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress; - _worker.SetMessage += OnWorkerOnSetMessage; - _worker.SetProgress += OnWorkerOnSetProgress; - _worker.SetProgressBounds += OnWorkerOnSetProgressBounds; - _worker.WorkFinished += OnWorkerOnWorkFinished; - } - - [NotNull] - public string Title => Localization.ImportDatTitle; - - public string StatusMessage - { - get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); - } - - public bool IndeterminateProgress - { - get => _indeterminateProgress; - set => this.RaiseAndSetIfChanged(ref _indeterminateProgress, value); - } - - public double MaximumValue - { - get => _maximumValue; - set => this.RaiseAndSetIfChanged(ref _maximumValue, value); - } - - public double MinimumValue - { - get => _minimumValue; - set => this.RaiseAndSetIfChanged(ref _minimumValue, value); - } - - public double CurrentValue - { - get => _currentValue; - set => this.RaiseAndSetIfChanged(ref _currentValue, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool ErrorVisible - { - get => _errorVisible; - set => this.RaiseAndSetIfChanged(ref _errorVisible, value); - } - - public string ErrorMessage - { - get => _errorMessage; - set => this.RaiseAndSetIfChanged(ref _errorMessage, value); - } - - public bool CanClose - { - get => _canClose; - set => this.RaiseAndSetIfChanged(ref _canClose, value); - } - - public string CloseLabel => Localization.CloseLabel; - public ReactiveCommand CloseCommand { get; } - - void OnWorkerOnWorkFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => - { - StatusMessage = args.Message; - ProgressVisible = false; - CanClose = true; - }); - - void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => - { - IndeterminateProgress = false; - MaximumValue = args.Maximum; - MinimumValue = args.Minimum; - }); - - void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => - Dispatcher.UIThread.Post(() => CurrentValue = args.Value); - - void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => - Dispatcher.UIThread.Post(() => StatusMessage = args.Message); - - void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => - Dispatcher.UIThread.Post(() => IndeterminateProgress = true); - - void OnWorkerOnErrorOccurred(object sender, ErrorEventArgs args) => Dispatcher.UIThread.Post(() => - { - ErrorMessage = args.Message; - ProgressVisible = false; - ErrorVisible = true; - CanClose = true; - }); - - void ExecuteCloseCommand() => _view.Close(); - - internal void OnOpened() - { - ProgressVisible = true; - _worker.RomSetAdded += RomSetAdded; - Task.Run(_worker.Import); - } - - public event EventHandler RomSetAdded; + _view = view; + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + IndeterminateProgress = true; + ProgressVisible = false; + ErrorVisible = false; + _worker = new DatImporter(datPath, null); + _worker.ErrorOccurred += OnWorkerOnErrorOccurred; + _worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress; + _worker.SetMessage += OnWorkerOnSetMessage; + _worker.SetProgress += OnWorkerOnSetProgress; + _worker.SetProgressBounds += OnWorkerOnSetProgressBounds; + _worker.WorkFinished += OnWorkerOnWorkFinished; } + + [NotNull] + public string Title => Localization.ImportDatTitle; + + public string StatusMessage + { + get => _statusMessage; + set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + } + + public bool IndeterminateProgress + { + get => _indeterminateProgress; + set => this.RaiseAndSetIfChanged(ref _indeterminateProgress, value); + } + + public double MaximumValue + { + get => _maximumValue; + set => this.RaiseAndSetIfChanged(ref _maximumValue, value); + } + + public double MinimumValue + { + get => _minimumValue; + set => this.RaiseAndSetIfChanged(ref _minimumValue, value); + } + + public double CurrentValue + { + get => _currentValue; + set => this.RaiseAndSetIfChanged(ref _currentValue, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool ErrorVisible + { + get => _errorVisible; + set => this.RaiseAndSetIfChanged(ref _errorVisible, value); + } + + public string ErrorMessage + { + get => _errorMessage; + set => this.RaiseAndSetIfChanged(ref _errorMessage, value); + } + + public bool CanClose + { + get => _canClose; + set => this.RaiseAndSetIfChanged(ref _canClose, value); + } + + public string CloseLabel => Localization.CloseLabel; + public ReactiveCommand CloseCommand { get; } + + void OnWorkerOnWorkFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => + { + StatusMessage = args.Message; + ProgressVisible = false; + CanClose = true; + }); + + void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => + { + IndeterminateProgress = false; + MaximumValue = args.Maximum; + MinimumValue = args.Minimum; + }); + + void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => + Dispatcher.UIThread.Post(() => CurrentValue = args.Value); + + void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => + Dispatcher.UIThread.Post(() => StatusMessage = args.Message); + + void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => + Dispatcher.UIThread.Post(() => IndeterminateProgress = true); + + void OnWorkerOnErrorOccurred(object sender, ErrorEventArgs args) => Dispatcher.UIThread.Post(() => + { + ErrorMessage = args.Message; + ProgressVisible = false; + ErrorVisible = true; + CanClose = true; + }); + + void ExecuteCloseCommand() => _view.Close(); + + internal void OnOpened() + { + ProgressVisible = true; + _worker.RomSetAdded += RomSetAdded; + Task.Run(_worker.Import); + } + + public event EventHandler RomSetAdded; } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/ImportRomFolderViewModel.cs b/RomRepoMgr/ViewModels/ImportRomFolderViewModel.cs index 23a3563..1153e51 100644 --- a/RomRepoMgr/ViewModels/ImportRomFolderViewModel.cs +++ b/RomRepoMgr/ViewModels/ImportRomFolderViewModel.cs @@ -36,261 +36,259 @@ using RomRepoMgr.Core.Workers; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class ImportRomFolderViewModel : ViewModelBase { - public sealed class ImportRomFolderViewModel : ViewModelBase + readonly ImportRomFolder _view; + bool _canClose; + bool _canStart; + bool _isImporting; + bool _isReady; + bool _knownOnlyChecked; + bool _progress2IsIndeterminate; + double _progress2Maximum; + double _progress2Minimum; + double _progress2Value; + bool _progress2Visible; + bool _progressIsIndeterminate; + double _progressMaximum; + double _progressMinimum; + double _progressValue; + bool _progressVisible; + bool _recurseArchivesChecked; + bool _removeFilesChecked; + bool _removeFilesEnabled; + string _status2Message; + string _statusMessage; + + public ImportRomFolderViewModel(ImportRomFolder view, string folderPath) { - readonly ImportRomFolder _view; - bool _canClose; - bool _canStart; - bool _isImporting; - bool _isReady; - bool _knownOnlyChecked; - bool _progress2IsIndeterminate; - double _progress2Maximum; - double _progress2Minimum; - double _progress2Value; - bool _progress2Visible; - bool _progressIsIndeterminate; - double _progressMaximum; - double _progressMinimum; - double _progressValue; - bool _progressVisible; - bool _recurseArchivesChecked; - bool _removeFilesChecked; - bool _removeFilesEnabled; - string _status2Message; - string _statusMessage; - - public ImportRomFolderViewModel(ImportRomFolder view, string folderPath) - { - _view = view; - FolderPath = folderPath; - _removeFilesChecked = false; - _knownOnlyChecked = true; - _recurseArchivesChecked = Settings.Settings.UnArUsable; - ImportResults = new ObservableCollection(); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - IsReady = true; - CanStart = true; - CanClose = true; - _removeFilesEnabled = false; - } - - public string PathLabel => Localization.PathLabel; - public string FolderPath { get; } - public string RemoveFilesLabel => Localization.RemoveFilesLabel; - public string KnownOnlyLabel => Localization.KnownOnlyLabel; - public string RecurseArchivesLabel => Localization.RecurseArchivesLabel; - public bool RecurseArchivesEnabled => Settings.Settings.UnArUsable; - - public bool RemoveFilesChecked - { - get => _removeFilesChecked; - set => this.RaiseAndSetIfChanged(ref _removeFilesChecked, value); - } - - public bool KnownOnlyChecked - { - get => _knownOnlyChecked; - set => this.RaiseAndSetIfChanged(ref _knownOnlyChecked, value); - } - - public bool RemoveFilesEnabled - { - get => _removeFilesEnabled; - set => this.RaiseAndSetIfChanged(ref _removeFilesEnabled, value); - } - - public bool RecurseArchivesChecked - { - get => _recurseArchivesChecked; - set - { - if(value) - RemoveFilesChecked = false; - - RemoveFilesEnabled = !value; - this.RaiseAndSetIfChanged(ref _recurseArchivesChecked, value); - } - } - - public bool IsReady - { - get => _isReady; - set => this.RaiseAndSetIfChanged(ref _isReady, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public string StatusMessage - { - get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); - } - - public double ProgressMinimum - { - get => _progressMinimum; - set => this.RaiseAndSetIfChanged(ref _progressMinimum, value); - } - - public double ProgressMaximum - { - get => _progressMaximum; - set => this.RaiseAndSetIfChanged(ref _progressMaximum, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public bool ProgressIsIndeterminate - { - get => _progressIsIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIsIndeterminate, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public string Status2Message - { - get => _status2Message; - set => this.RaiseAndSetIfChanged(ref _status2Message, value); - } - - public double Progress2Minimum - { - get => _progress2Minimum; - set => this.RaiseAndSetIfChanged(ref _progress2Minimum, value); - } - - public double Progress2Maximum - { - get => _progress2Maximum; - set => this.RaiseAndSetIfChanged(ref _progress2Maximum, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - public bool Progress2IsIndeterminate - { - get => _progress2IsIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2IsIndeterminate, value); - } - - public bool IsImporting - { - get => _isImporting; - set => this.RaiseAndSetIfChanged(ref _isImporting, value); - } - - [NotNull] - public string Title => Localization.ImportRomFolderTitle; - - public ObservableCollection ImportResults { get; } - public string ResultFilenameLabel => Localization.ResultFilenameLabel; - public string ResultStatusLabel => Localization.ResultStatusLabel; - public string CloseLabel => Localization.CloseLabel; - public string StartLabel => Localization.StartLabel; - - public bool CanClose - { - get => _canClose; - set => this.RaiseAndSetIfChanged(ref _canClose, value); - } - - public bool CanStart - { - get => _canStart; - set => this.RaiseAndSetIfChanged(ref _canStart, value); - } - - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StartCommand { get; } - - void ExecuteCloseCommand() => _view.Close(); - - void ExecuteStartCommand() - { - IsReady = false; - ProgressVisible = true; - IsImporting = true; - CanStart = false; - CanClose = false; - Progress2Visible = true; - - var worker = new FileImporter(KnownOnlyChecked, RemoveFilesChecked); - worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress; - worker.SetMessage += OnWorkerOnSetMessage; - worker.SetProgress += OnWorkerOnSetProgress; - worker.SetProgressBounds += OnWorkerOnSetProgressBounds; - worker.SetIndeterminateProgress2 += OnWorkerOnSetIndeterminateProgress2; - worker.SetMessage2 += OnWorkerOnSetMessage2; - worker.SetProgress2 += OnWorkerOnSetProgress2; - worker.SetProgressBounds2 += OnWorkerOnSetProgressBounds2; - worker.Finished += OnWorkerOnFinished; - worker.ImportedRom += OnWorkerOnImportedRom; - - Task.Run(() => worker.ProcessPath(FolderPath, true, RecurseArchivesChecked)); - } - - void OnWorkerOnImportedRom(object sender, ImportedRomItemEventArgs args) => - Dispatcher.UIThread.Post(() => ImportResults.Add(args.Item)); - - void OnWorkerOnFinished(object sender, EventArgs args) => Dispatcher.UIThread.Post(() => - { - ProgressVisible = false; - StatusMessage = Localization.Finished; - CanClose = true; - Progress2Visible = false; - }); - - void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => - { - ProgressIsIndeterminate = false; - ProgressMaximum = args.Maximum; - ProgressMinimum = args.Minimum; - }); - - void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => - Dispatcher.UIThread.Post(() => ProgressValue = args.Value); - - void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => - Dispatcher.UIThread.Post(() => StatusMessage = args.Message); - - void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => - Dispatcher.UIThread.Post(() => ProgressIsIndeterminate = true); - - void OnWorkerOnSetProgressBounds2(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => - { - Progress2IsIndeterminate = false; - Progress2Maximum = args.Maximum; - Progress2Minimum = args.Minimum; - }); - - void OnWorkerOnSetProgress2(object sender, ProgressEventArgs args) => - Dispatcher.UIThread.Post(() => Progress2Value = args.Value); - - void OnWorkerOnSetMessage2(object sender, MessageEventArgs args) => - Dispatcher.UIThread.Post(() => Status2Message = args.Message); - - void OnWorkerOnSetIndeterminateProgress2(object sender, EventArgs args) => - Dispatcher.UIThread.Post(() => Progress2IsIndeterminate = true); + _view = view; + FolderPath = folderPath; + _removeFilesChecked = false; + _knownOnlyChecked = true; + _recurseArchivesChecked = Settings.Settings.UnArUsable; + ImportResults = new ObservableCollection(); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + IsReady = true; + CanStart = true; + CanClose = true; + _removeFilesEnabled = false; } + + public string PathLabel => Localization.PathLabel; + public string FolderPath { get; } + public string RemoveFilesLabel => Localization.RemoveFilesLabel; + public string KnownOnlyLabel => Localization.KnownOnlyLabel; + public string RecurseArchivesLabel => Localization.RecurseArchivesLabel; + public bool RecurseArchivesEnabled => Settings.Settings.UnArUsable; + + public bool RemoveFilesChecked + { + get => _removeFilesChecked; + set => this.RaiseAndSetIfChanged(ref _removeFilesChecked, value); + } + + public bool KnownOnlyChecked + { + get => _knownOnlyChecked; + set => this.RaiseAndSetIfChanged(ref _knownOnlyChecked, value); + } + + public bool RemoveFilesEnabled + { + get => _removeFilesEnabled; + set => this.RaiseAndSetIfChanged(ref _removeFilesEnabled, value); + } + + public bool RecurseArchivesChecked + { + get => _recurseArchivesChecked; + set + { + if(value) RemoveFilesChecked = false; + + RemoveFilesEnabled = !value; + this.RaiseAndSetIfChanged(ref _recurseArchivesChecked, value); + } + } + + public bool IsReady + { + get => _isReady; + set => this.RaiseAndSetIfChanged(ref _isReady, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public string StatusMessage + { + get => _statusMessage; + set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + } + + public double ProgressMinimum + { + get => _progressMinimum; + set => this.RaiseAndSetIfChanged(ref _progressMinimum, value); + } + + public double ProgressMaximum + { + get => _progressMaximum; + set => this.RaiseAndSetIfChanged(ref _progressMaximum, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public bool ProgressIsIndeterminate + { + get => _progressIsIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIsIndeterminate, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public string Status2Message + { + get => _status2Message; + set => this.RaiseAndSetIfChanged(ref _status2Message, value); + } + + public double Progress2Minimum + { + get => _progress2Minimum; + set => this.RaiseAndSetIfChanged(ref _progress2Minimum, value); + } + + public double Progress2Maximum + { + get => _progress2Maximum; + set => this.RaiseAndSetIfChanged(ref _progress2Maximum, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + public bool Progress2IsIndeterminate + { + get => _progress2IsIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2IsIndeterminate, value); + } + + public bool IsImporting + { + get => _isImporting; + set => this.RaiseAndSetIfChanged(ref _isImporting, value); + } + + [NotNull] + public string Title => Localization.ImportRomFolderTitle; + + public ObservableCollection ImportResults { get; } + public string ResultFilenameLabel => Localization.ResultFilenameLabel; + public string ResultStatusLabel => Localization.ResultStatusLabel; + public string CloseLabel => Localization.CloseLabel; + public string StartLabel => Localization.StartLabel; + + public bool CanClose + { + get => _canClose; + set => this.RaiseAndSetIfChanged(ref _canClose, value); + } + + public bool CanStart + { + get => _canStart; + set => this.RaiseAndSetIfChanged(ref _canStart, value); + } + + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StartCommand { get; } + + void ExecuteCloseCommand() => _view.Close(); + + void ExecuteStartCommand() + { + IsReady = false; + ProgressVisible = true; + IsImporting = true; + CanStart = false; + CanClose = false; + Progress2Visible = true; + + var worker = new FileImporter(KnownOnlyChecked, RemoveFilesChecked); + worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress; + worker.SetMessage += OnWorkerOnSetMessage; + worker.SetProgress += OnWorkerOnSetProgress; + worker.SetProgressBounds += OnWorkerOnSetProgressBounds; + worker.SetIndeterminateProgress2 += OnWorkerOnSetIndeterminateProgress2; + worker.SetMessage2 += OnWorkerOnSetMessage2; + worker.SetProgress2 += OnWorkerOnSetProgress2; + worker.SetProgressBounds2 += OnWorkerOnSetProgressBounds2; + worker.Finished += OnWorkerOnFinished; + worker.ImportedRom += OnWorkerOnImportedRom; + + Task.Run(() => worker.ProcessPath(FolderPath, true, RecurseArchivesChecked)); + } + + void OnWorkerOnImportedRom(object sender, ImportedRomItemEventArgs args) => + Dispatcher.UIThread.Post(() => ImportResults.Add(args.Item)); + + void OnWorkerOnFinished(object sender, EventArgs args) => Dispatcher.UIThread.Post(() => + { + ProgressVisible = false; + StatusMessage = Localization.Finished; + CanClose = true; + Progress2Visible = false; + }); + + void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => + { + ProgressIsIndeterminate = false; + ProgressMaximum = args.Maximum; + ProgressMinimum = args.Minimum; + }); + + void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) => + Dispatcher.UIThread.Post(() => ProgressValue = args.Value); + + void OnWorkerOnSetMessage(object sender, MessageEventArgs args) => + Dispatcher.UIThread.Post(() => StatusMessage = args.Message); + + void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) => + Dispatcher.UIThread.Post(() => ProgressIsIndeterminate = true); + + void OnWorkerOnSetProgressBounds2(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() => + { + Progress2IsIndeterminate = false; + Progress2Maximum = args.Maximum; + Progress2Minimum = args.Minimum; + }); + + void OnWorkerOnSetProgress2(object sender, ProgressEventArgs args) => + Dispatcher.UIThread.Post(() => Progress2Value = args.Value); + + void OnWorkerOnSetMessage2(object sender, MessageEventArgs args) => + Dispatcher.UIThread.Post(() => Status2Message = args.Message); + + void OnWorkerOnSetIndeterminateProgress2(object sender, EventArgs args) => + Dispatcher.UIThread.Post(() => Progress2IsIndeterminate = true); } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/MainWindowViewModel.cs b/RomRepoMgr/ViewModels/MainWindowViewModel.cs index f5190c8..f8a5cad 100644 --- a/RomRepoMgr/ViewModels/MainWindowViewModel.cs +++ b/RomRepoMgr/ViewModels/MainWindowViewModel.cs @@ -42,330 +42,318 @@ using RomRepoMgr.Core.Models; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public class MainWindowViewModel : ViewModelBase { - public class MainWindowViewModel : ViewModelBase + readonly MainWindow _view; + RomSetModel _selectedRomSet; + Vfs _vfs; + + public MainWindowViewModel(MainWindow view, List romSets) { - readonly MainWindow _view; - RomSetModel _selectedRomSet; - Vfs _vfs; + _view = view; + ExitCommand = ReactiveCommand.Create(ExecuteExitCommand); + SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand); + AboutCommand = ReactiveCommand.Create(ExecuteAboutCommand); + ImportDatCommand = ReactiveCommand.Create(ExecuteImportDatCommand); + ImportDatFolderCommand = ReactiveCommand.Create(ExecuteImportDatFolderCommand); + ImportRomFolderCommand = ReactiveCommand.Create(ExecuteImportRomFolderCommand); + DeleteRomSetCommand = ReactiveCommand.Create(ExecuteDeleteRomSetCommand); + EditRomSetCommand = ReactiveCommand.Create(ExecuteEditRomSetCommand); + ExportDatCommand = ReactiveCommand.Create(ExecuteExportDatCommand); + ExportRomsCommand = ReactiveCommand.Create(ExecuteExportRomsCommand); + MountCommand = ReactiveCommand.Create(ExecuteMountCommand); + UmountCommand = ReactiveCommand.Create(ExecuteUmountCommand); + UpdateStatsCommand = ReactiveCommand.Create(ExecuteUpdateStatsCommand); + RomSets = new ObservableCollection(romSets); + } - public MainWindowViewModel(MainWindow view, List romSets) + public ObservableCollection RomSets { get; } + public string RomSetLabel => Localization.RomSets; + public string RomSetNameLabel => Localization.RomSetNameLabel; + public string RomSetVersionLabel => Localization.RomSetVersionLabel; + public string RomSetAuthorLabel => Localization.RomSetAuthorLabel; + public string RomSetCategoryLabel => Localization.RomSetCategoryLabel; + public string RomSetDateLabel => Localization.RomSetDateLabel; + public string RomSetDescriptionLabel => Localization.RomSetDescriptionLabel; + public string RomSetCommentLabel => Localization.RomSetCommentLabel; + public string RomSetTotalMachinesLabel => Localization.RomSetTotalMachinesLabel; + public string RomSetCompleteMachinesLabel => Localization.RomSetCompleteMachinesLabel; + public string RomSetIncompleteMachinesLabel => Localization.RomSetIncompleteMachinesLabel; + public string RomSetTotalRomsLabel => Localization.RomSetTotalRomsLabel; + public string RomSetHaveRomsLabel => Localization.RomSetHaveRomsLabel; + public string RomSetMissRomsLabel => Localization.RomSetMissRomsLabel; + public bool IsVfsAvailable => Vfs.IsAvailable; + public string FileMenuText => Localization.FileMenuText; + public string FileMenuImportDatFileText => Localization.FileMenuImportDatFileText; + public string FileMenuImportDatFolderText => Localization.FileMenuImportDatFolderText; + public string FileMenuSettingsText => Localization.FileMenuSettingsText; + public string FileMenuExitText => Localization.FileMenuExitText; + public string FilesystemMenuText => Localization.FilesystemMenuText; + public string FilesystemMenuMountText => Localization.FilesystemMenuMountText; + public string RomsMenuText => Localization.RomsMenuText; + public string RomsMenuImportText => Localization.RomsMenuImportText; + public string RomSetsMenuText => Localization.RomSetsMenuText; + public string RomSetsMenuSaveRomsText => Localization.RomSetsMenuSaveRomsText; + public string RomSetsMenuSaveDatText => Localization.RomSetsMenuSaveDatText; + public string RomSetsMenuEditText => Localization.RomSetsMenuEditText; + public string RomSetsMenuDeleteText => Localization.RomSetsMenuDeleteText; + public string HelpMenuText => Localization.HelpMenuText; + public string HelpMenuAboutText => Localization.HelpMenuAboutText; + public string FilesystemMenuUmountText => Localization.FilesystemMenuUmountText; + public string DatabaseMenuText => Localization.DatabaseMenuText; + public string DatabaseMenuUpdateStatsText => Localization.DatabaseMenuUpdateStatsText; + + public bool NativeMenuSupported => + NativeMenu.GetIsNativeMenuExported((Application.Current.ApplicationLifetime as + IClassicDesktopStyleApplicationLifetime)?.MainWindow); + + public ReactiveCommand AboutCommand { get; } + public ReactiveCommand ExitCommand { get; } + public ReactiveCommand SettingsCommand { get; } + public ReactiveCommand ImportDatCommand { get; } + public ReactiveCommand ImportDatFolderCommand { get; } + public ReactiveCommand ImportRomFolderCommand { get; } + public ReactiveCommand DeleteRomSetCommand { get; } + public ReactiveCommand EditRomSetCommand { get; } + public ReactiveCommand ExportDatCommand { get; } + public ReactiveCommand ExportRomsCommand { get; } + public ReactiveCommand MountCommand { get; } + public ReactiveCommand UmountCommand { get; } + public ReactiveCommand UpdateStatsCommand { get; } + + public Vfs Vfs + { + get => _vfs; + set => this.RaiseAndSetIfChanged(ref _vfs, value); + } + + public RomSetModel SelectedRomSet + { + get => _selectedRomSet; + set => this.RaiseAndSetIfChanged(ref _selectedRomSet, value); + } + + internal async void ExecuteSettingsCommand() + { + var dialog = new SettingsDialog(); + dialog.DataContext = new SettingsViewModel(dialog); + await dialog.ShowDialog(_view); + } + + internal void ExecuteExitCommand() => + (Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown(); + + internal void ExecuteAboutCommand() + { + var dialog = new About(); + dialog.DataContext = new AboutViewModel(dialog); + dialog.ShowDialog(_view); + } + + async void ExecuteImportDatCommand() + { + var dlgOpen = new OpenFileDialog { - _view = view; - ExitCommand = ReactiveCommand.Create(ExecuteExitCommand); - SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand); - AboutCommand = ReactiveCommand.Create(ExecuteAboutCommand); - ImportDatCommand = ReactiveCommand.Create(ExecuteImportDatCommand); - ImportDatFolderCommand = ReactiveCommand.Create(ExecuteImportDatFolderCommand); - ImportRomFolderCommand = ReactiveCommand.Create(ExecuteImportRomFolderCommand); - DeleteRomSetCommand = ReactiveCommand.Create(ExecuteDeleteRomSetCommand); - EditRomSetCommand = ReactiveCommand.Create(ExecuteEditRomSetCommand); - ExportDatCommand = ReactiveCommand.Create(ExecuteExportDatCommand); - ExportRomsCommand = ReactiveCommand.Create(ExecuteExportRomsCommand); - MountCommand = ReactiveCommand.Create(ExecuteMountCommand); - UmountCommand = ReactiveCommand.Create(ExecuteUmountCommand); - UpdateStatsCommand = ReactiveCommand.Create(ExecuteUpdateStatsCommand); - RomSets = new ObservableCollection(romSets); - } + AllowMultiple = false, + Title = Localization.ImportDatFileDialogTitle + }; - public ObservableCollection RomSets { get; } - public string RomSetLabel => Localization.RomSets; - public string RomSetNameLabel => Localization.RomSetNameLabel; - public string RomSetVersionLabel => Localization.RomSetVersionLabel; - public string RomSetAuthorLabel => Localization.RomSetAuthorLabel; - public string RomSetCategoryLabel => Localization.RomSetCategoryLabel; - public string RomSetDateLabel => Localization.RomSetDateLabel; - public string RomSetDescriptionLabel => Localization.RomSetDescriptionLabel; - public string RomSetCommentLabel => Localization.RomSetCommentLabel; - public string RomSetTotalMachinesLabel => Localization.RomSetTotalMachinesLabel; - public string RomSetCompleteMachinesLabel => Localization.RomSetCompleteMachinesLabel; - public string RomSetIncompleteMachinesLabel => Localization.RomSetIncompleteMachinesLabel; - public string RomSetTotalRomsLabel => Localization.RomSetTotalRomsLabel; - public string RomSetHaveRomsLabel => Localization.RomSetHaveRomsLabel; - public string RomSetMissRomsLabel => Localization.RomSetMissRomsLabel; - public bool IsVfsAvailable => Vfs.IsAvailable; - public string FileMenuText => Localization.FileMenuText; - public string FileMenuImportDatFileText => Localization.FileMenuImportDatFileText; - public string FileMenuImportDatFolderText => Localization.FileMenuImportDatFolderText; - public string FileMenuSettingsText => Localization.FileMenuSettingsText; - public string FileMenuExitText => Localization.FileMenuExitText; - public string FilesystemMenuText => Localization.FilesystemMenuText; - public string FilesystemMenuMountText => Localization.FilesystemMenuMountText; - public string RomsMenuText => Localization.RomsMenuText; - public string RomsMenuImportText => Localization.RomsMenuImportText; - public string RomSetsMenuText => Localization.RomSetsMenuText; - public string RomSetsMenuSaveRomsText => Localization.RomSetsMenuSaveRomsText; - public string RomSetsMenuSaveDatText => Localization.RomSetsMenuSaveDatText; - public string RomSetsMenuEditText => Localization.RomSetsMenuEditText; - public string RomSetsMenuDeleteText => Localization.RomSetsMenuDeleteText; - public string HelpMenuText => Localization.HelpMenuText; - public string HelpMenuAboutText => Localization.HelpMenuAboutText; - public string FilesystemMenuUmountText => Localization.FilesystemMenuUmountText; - public string DatabaseMenuText => Localization.DatabaseMenuText; - public string DatabaseMenuUpdateStatsText => Localization.DatabaseMenuUpdateStatsText; - - public bool NativeMenuSupported => - NativeMenu.GetIsNativeMenuExported((Application.Current.ApplicationLifetime as - IClassicDesktopStyleApplicationLifetime)?.MainWindow); - - public ReactiveCommand AboutCommand { get; } - public ReactiveCommand ExitCommand { get; } - public ReactiveCommand SettingsCommand { get; } - public ReactiveCommand ImportDatCommand { get; } - public ReactiveCommand ImportDatFolderCommand { get; } - public ReactiveCommand ImportRomFolderCommand { get; } - public ReactiveCommand DeleteRomSetCommand { get; } - public ReactiveCommand EditRomSetCommand { get; } - public ReactiveCommand ExportDatCommand { get; } - public ReactiveCommand ExportRomsCommand { get; } - public ReactiveCommand MountCommand { get; } - public ReactiveCommand UmountCommand { get; } - public ReactiveCommand UpdateStatsCommand { get; } - - public Vfs Vfs + dlgOpen.Filters.Add(new FileDialogFilter { - get => _vfs; - set => this.RaiseAndSetIfChanged(ref _vfs, value); - } - - public RomSetModel SelectedRomSet - { - get => _selectedRomSet; - set => this.RaiseAndSetIfChanged(ref _selectedRomSet, value); - } - - internal async void ExecuteSettingsCommand() - { - var dialog = new SettingsDialog(); - dialog.DataContext = new SettingsViewModel(dialog); - await dialog.ShowDialog(_view); - } - - internal void ExecuteExitCommand() => - (Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown(); - - internal void ExecuteAboutCommand() - { - var dialog = new About(); - dialog.DataContext = new AboutViewModel(dialog); - dialog.ShowDialog(_view); - } - - async void ExecuteImportDatCommand() - { - var dlgOpen = new OpenFileDialog + Extensions = new List { - AllowMultiple = false, - Title = Localization.ImportDatFileDialogTitle - }; + "dat", + "xml" + }, + Name = Localization.DatFilesDialogLabel + }); - dlgOpen.Filters.Add(new FileDialogFilter - { - Extensions = new List - { - "dat", - "xml" - }, - Name = Localization.DatFilesDialogLabel - }); - - dlgOpen.Filters.Add(new FileDialogFilter - { - Extensions = new List - { - "*" - }, - Name = Localization.AllFilesDialogLabel - }); - - string[] result = await dlgOpen.ShowAsync(_view); - - if(result?.Length != 1) - return; - - var dialog = new ImportDat(); - var importDatViewModel = new ImportDatViewModel(dialog, result[0]); - importDatViewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded; - dialog.DataContext = importDatViewModel; - await dialog.ShowDialog(_view); - } - - void ImportDatViewModelOnRomSetAdded(object sender, RomSetEventArgs e) => - Dispatcher.UIThread.Post(() => RomSets.Add(e.RomSet)); - - async void ExecuteImportDatFolderCommand() + dlgOpen.Filters.Add(new FileDialogFilter { - var dlgOpen = new OpenFolderDialog + Extensions = new List { - Title = Localization.ImportDatFolderDialogTitle - }; + "*" + }, + Name = Localization.AllFilesDialogLabel + }); - string result = await dlgOpen.ShowAsync(_view); + string[] result = await dlgOpen.ShowAsync(_view); - if(result == null) - return; + if(result?.Length != 1) return; - var dialog = new ImportDatFolder(); - var importDatFolderViewModel = new ImportDatFolderViewModel(dialog, result); - importDatFolderViewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded; - dialog.DataContext = importDatFolderViewModel; - await dialog.ShowDialog(_view); - } + var dialog = new ImportDat(); + var importDatViewModel = new ImportDatViewModel(dialog, result[0]); + importDatViewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded; + dialog.DataContext = importDatViewModel; + await dialog.ShowDialog(_view); + } - async void ExecuteImportRomFolderCommand() + void ImportDatViewModelOnRomSetAdded(object sender, RomSetEventArgs e) => + Dispatcher.UIThread.Post(() => RomSets.Add(e.RomSet)); + + async void ExecuteImportDatFolderCommand() + { + var dlgOpen = new OpenFolderDialog { - var dlgOpen = new OpenFolderDialog - { - Title = Localization.ImportRomsFolderDialogTitle - }; + Title = Localization.ImportDatFolderDialogTitle + }; - string result = await dlgOpen.ShowAsync(_view); + string result = await dlgOpen.ShowAsync(_view); - if(result == null) - return; + if(result == null) return; - var dialog = new ImportRomFolder(); - var importRomFolderViewModel = new ImportRomFolderViewModel(dialog, result); - dialog.DataContext = importRomFolderViewModel; - await dialog.ShowDialog(_view); - } + var dialog = new ImportDatFolder(); + var importDatFolderViewModel = new ImportDatFolderViewModel(dialog, result); + importDatFolderViewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded; + dialog.DataContext = importDatFolderViewModel; + await dialog.ShowDialog(_view); + } - async void ExecuteDeleteRomSetCommand() + async void ExecuteImportRomFolderCommand() + { + var dlgOpen = new OpenFolderDialog { - if(SelectedRomSet == null) - return; + Title = Localization.ImportRomsFolderDialogTitle + }; - ButtonResult result = await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DeleteRomSetMsgBoxTitle, - string. - Format(Localization.DeleteRomSetMsgBoxCaption, - SelectedRomSet.Name), ButtonEnum.YesNo, - Icon.Database).ShowDialog(_view); + string result = await dlgOpen.ShowAsync(_view); - if(result == ButtonResult.No) - return; + if(result == null) return; - var dialog = new RemoveDat(); - var removeDatViewModel = new RemoveDatViewModel(dialog, SelectedRomSet.Id); - dialog.DataContext = removeDatViewModel; - await dialog.ShowDialog(_view); + var dialog = new ImportRomFolder(); + var importRomFolderViewModel = new ImportRomFolderViewModel(dialog, result); + dialog.DataContext = importRomFolderViewModel; + await dialog.ShowDialog(_view); + } - RomSets.Remove(SelectedRomSet); - SelectedRomSet = null; - } + async void ExecuteDeleteRomSetCommand() + { + if(SelectedRomSet == null) return; - void ExecuteEditRomSetCommand() + ButtonResult result = await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DeleteRomSetMsgBoxTitle, + string.Format(Localization.DeleteRomSetMsgBoxCaption, + SelectedRomSet.Name), + ButtonEnum.YesNo, + Icon.Database) + .ShowDialog(_view); + + if(result == ButtonResult.No) return; + + var dialog = new RemoveDat(); + var removeDatViewModel = new RemoveDatViewModel(dialog, SelectedRomSet.Id); + dialog.DataContext = removeDatViewModel; + await dialog.ShowDialog(_view); + + RomSets.Remove(SelectedRomSet); + SelectedRomSet = null; + } + + void ExecuteEditRomSetCommand() + { + if(SelectedRomSet == null) return; + + var window = new EditDat(); + var viewModel = new EditDatViewModel(window, SelectedRomSet); + + viewModel.RomSetModified += (sender, args) => { - if(SelectedRomSet == null) - return; + RomSetModel old = RomSets.FirstOrDefault(r => r.Id == args.RomSet.Id); - var window = new EditDat(); - var viewModel = new EditDatViewModel(window, SelectedRomSet); + if(old == null) return; - viewModel.RomSetModified += (sender, args) => - { - RomSetModel old = RomSets.FirstOrDefault(r => r.Id == args.RomSet.Id); + RomSets.Remove(old); + RomSets.Add(args.RomSet); + }; - if(old == null) - return; + window.DataContext = viewModel; - RomSets.Remove(old); - RomSets.Add(args.RomSet); - }; + window.Show(); + } - window.DataContext = viewModel; + async void ExecuteExportDatCommand() + { + if(SelectedRomSet == null) return; - window.Show(); - } - - async void ExecuteExportDatCommand() + var dlgSave = new SaveFileDialog { - if(SelectedRomSet == null) - return; + InitialFileName = SelectedRomSet.Filename + }; - var dlgSave = new SaveFileDialog - { - InitialFileName = SelectedRomSet.Filename - }; + string result = await dlgSave.ShowAsync(_view); - string result = await dlgSave.ShowAsync(_view); + if(result == null) return; - if(result == null) - return; + var dialog = new ExportDat(); + var viewModel = new ExportDatViewModel(dialog, SelectedRomSet.Sha384, result); + dialog.DataContext = viewModel; + await dialog.ShowDialog(_view); + } - var dialog = new ExportDat(); - var viewModel = new ExportDatViewModel(dialog, SelectedRomSet.Sha384, result); - dialog.DataContext = viewModel; - await dialog.ShowDialog(_view); - } - - async void ExecuteExportRomsCommand() + async void ExecuteExportRomsCommand() + { + var dlgOpen = new OpenFolderDialog { - var dlgOpen = new OpenFolderDialog - { - Title = Localization.ExportRomsDialogTitle - }; + Title = Localization.ExportRomsDialogTitle + }; - string result = await dlgOpen.ShowAsync(_view); + string result = await dlgOpen.ShowAsync(_view); - if(result == null) - return; + if(result == null) return; - var dialog = new ExportRoms(); - var viewModel = new ExportRomsViewModel(dialog, result, SelectedRomSet.Id); - dialog.DataContext = viewModel; - await dialog.ShowDialog(_view); - } + var dialog = new ExportRoms(); + var viewModel = new ExportRomsViewModel(dialog, result, SelectedRomSet.Id); + dialog.DataContext = viewModel; + await dialog.ShowDialog(_view); + } - async void ExecuteMountCommand() + async void ExecuteMountCommand() + { + if(Vfs != null) return; + + var dlgOpen = new OpenFolderDialog { - if(Vfs != null) - return; + Title = Localization.SelectMountPointDialogTitle + }; - var dlgOpen = new OpenFolderDialog - { - Title = Localization.SelectMountPointDialogTitle - }; + string result = await dlgOpen.ShowAsync(_view); - string result = await dlgOpen.ShowAsync(_view); + if(result == null) return; - if(result == null) - return; - - try - { - Vfs = new Vfs(); - Vfs.Umounted += VfsOnUmounted; - Vfs.MountTo(result); - } - catch(Exception) - { - if(Debugger.IsAttached) - throw; - - Vfs = null; - } - } - - void VfsOnUmounted(object sender, EventArgs e) => Vfs = null; - - void ExecuteUmountCommand() => Vfs?.Umount(); - - async void ExecuteUpdateStatsCommand() + try { - ButtonResult result = await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DatabaseMenuUpdateStatsText, - Localization.UpdateStatsConfirmationDialogText, - ButtonEnum.YesNo, Icon.Database).ShowDialog(_view); + Vfs = new Vfs(); + Vfs.Umounted += VfsOnUmounted; + Vfs.MountTo(result); + } + catch(Exception) + { + if(Debugger.IsAttached) throw; - if(result == ButtonResult.No) - return; - - var view = new UpdateStats(); - var viewModel = new UpdateStatsViewModel(view); - view.DataContext = viewModel; - await view.ShowDialog(_view); + Vfs = null; } } + + void VfsOnUmounted(object sender, EventArgs e) => Vfs = null; + + void ExecuteUmountCommand() => Vfs?.Umount(); + + async void ExecuteUpdateStatsCommand() + { + ButtonResult result = await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DatabaseMenuUpdateStatsText, + Localization.UpdateStatsConfirmationDialogText, + ButtonEnum.YesNo, + Icon.Database) + .ShowDialog(_view); + + if(result == ButtonResult.No) return; + + var view = new UpdateStats(); + var viewModel = new UpdateStatsViewModel(view); + view.DataContext = viewModel; + await view.ShowDialog(_view); + } } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/RemoveDatViewModel.cs b/RomRepoMgr/ViewModels/RemoveDatViewModel.cs index 8c4abe1..770bbef 100644 --- a/RomRepoMgr/ViewModels/RemoveDatViewModel.cs +++ b/RomRepoMgr/ViewModels/RemoveDatViewModel.cs @@ -34,84 +34,75 @@ using RomRepoMgr.Database.Models; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class RemoveDatViewModel : ViewModelBase { - public sealed class RemoveDatViewModel : ViewModelBase + readonly long _romSetId; + readonly RemoveDat _view; + string _statusMessage; + + public RemoveDatViewModel(RemoveDat view, long romSetId) { - readonly long _romSetId; - readonly RemoveDat _view; - string _statusMessage; - - public RemoveDatViewModel(RemoveDat view, long romSetId) - { - _view = view; - _romSetId = romSetId; - } - - [NotNull] - public string Title => Localization.RemoveDatTitle; - - public string StatusMessage - { - get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); - } - - internal void OnOpened() => Task.Run(() => - { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - Dispatcher.UIThread.Post(() => StatusMessage = Localization.RetrievingRomSetFromDatabase); - - RomSet romSet = ctx.RomSets.Find(_romSetId); - - if(romSet == null) - return; - - Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingRomSetFromDatabase); - - ctx.RomSets.Remove(romSet); - - Dispatcher.UIThread.Post(() => StatusMessage = Localization.SavingChangesToDatabase); - - ctx.SaveChanges(); - - Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingDatFileFromRepo); - - byte[] sha384Bytes = new byte[48]; - string sha384 = romSet.Sha384; - - for(int i = 0; i < 48; i++) - { - if(sha384[i * 2] >= 0x30 && - sha384[i * 2] <= 0x39) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); - else if(sha384[i * 2] >= 0x41 && - sha384[i * 2] <= 0x46) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); - else if(sha384[i * 2] >= 0x61 && - sha384[i * 2] <= 0x66) - sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); - - if(sha384[(i * 2) + 1] >= 0x30 && - sha384[(i * 2) + 1] <= 0x39) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30); - else if(sha384[(i * 2) + 1] >= 0x41 && - sha384[(i * 2) + 1] <= 0x46) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37); - else if(sha384[(i * 2) + 1] >= 0x61 && - sha384[(i * 2) + 1] <= 0x66) - sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57); - } - - string datHash32 = Base32.ToBase32String(sha384Bytes); - string datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles"); - string compressedDatPath = Path.Combine(datFilesPath, datHash32 + ".lz"); - - if(File.Exists(compressedDatPath)) - File.Delete(compressedDatPath); - - Dispatcher.UIThread.Post(_view.Close); - }); + _view = view; + _romSetId = romSetId; } + + [NotNull] + public string Title => Localization.RemoveDatTitle; + + public string StatusMessage + { + get => _statusMessage; + set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + } + + internal void OnOpened() => Task.Run(() => + { + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + Dispatcher.UIThread.Post(() => StatusMessage = Localization.RetrievingRomSetFromDatabase); + + RomSet romSet = ctx.RomSets.Find(_romSetId); + + if(romSet == null) return; + + Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingRomSetFromDatabase); + + ctx.RomSets.Remove(romSet); + + Dispatcher.UIThread.Post(() => StatusMessage = Localization.SavingChangesToDatabase); + + ctx.SaveChanges(); + + Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingDatFileFromRepo); + + var sha384Bytes = new byte[48]; + string sha384 = romSet.Sha384; + + for(var i = 0; i < 48; i++) + { + if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10); + else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10); + else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66) + sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10); + + if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30); + else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37); + else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66) + sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57); + } + + string datHash32 = Base32.ToBase32String(sha384Bytes); + string datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles"); + string compressedDatPath = Path.Combine(datFilesPath, datHash32 + ".lz"); + + if(File.Exists(compressedDatPath)) File.Delete(compressedDatPath); + + Dispatcher.UIThread.Post(_view.Close); + }); } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/SettingsViewModel.cs b/RomRepoMgr/ViewModels/SettingsViewModel.cs index db64f7d..ba7439c 100644 --- a/RomRepoMgr/ViewModels/SettingsViewModel.cs +++ b/RomRepoMgr/ViewModels/SettingsViewModel.cs @@ -41,248 +41,219 @@ using RomRepoMgr.Resources; using RomRepoMgr.Views; using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class SettingsViewModel : ViewModelBase { - public sealed class SettingsViewModel : ViewModelBase + readonly SettingsDialog _view; + bool _databaseChanged; + string _databasePath; + bool _repositoryChanged; + string _repositoryPath; + bool _temporaryChanged; + string _temporaryPath; + bool _unArChanged; + string _unArPath; + string _unArVersion; + + public SettingsViewModel(SettingsDialog view) { - readonly SettingsDialog _view; - bool _databaseChanged; - string _databasePath; - bool _repositoryChanged; - string _repositoryPath; - bool _temporaryChanged; - string _temporaryPath; - bool _unArChanged; - string _unArPath; - string _unArVersion; + _view = view; + _databaseChanged = false; + _repositoryChanged = false; + _temporaryChanged = false; + _unArChanged = false; - public SettingsViewModel(SettingsDialog view) + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + UnArCommand = ReactiveCommand.Create(ExecuteUnArCommand); + TemporaryCommand = ReactiveCommand.Create(ExecuteTemporaryCommand); + RepositoryCommand = ReactiveCommand.Create(ExecuteRepositoryCommand); + DatabaseCommand = ReactiveCommand.Create(ExecuteDatabaseCommand); + SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); + + DatabasePath = Settings.Settings.Current.DatabasePath; + RepositoryPath = Settings.Settings.Current.RepositoryPath; + TemporaryPath = Settings.Settings.Current.TemporaryFolder; + UnArPath = Settings.Settings.Current.UnArchiverPath; + + if(!string.IsNullOrWhiteSpace(UnArPath)) CheckUnAr(); + } + + public string ChooseLabel => Localization.ChooseLabel; + public string Title => Localization.SettingsTitle; + public string CloseLabel => Localization.CloseLabel; + public string DatabaseLabel => Localization.DatabaseFileLabel; + public string RepositoryLabel => Localization.RepositoryFolderLabel; + public string TemporaryLabel => Localization.TemporaryFolderLabel; + public string UnArPathLabel => Localization.UnArPathLabel; + + public ReactiveCommand UnArCommand { get; } + public ReactiveCommand TemporaryCommand { get; } + public ReactiveCommand RepositoryCommand { get; } + public ReactiveCommand DatabaseCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand SaveCommand { get; } + + public string DatabasePath + { + get => _databasePath; + set { - _view = view; - _databaseChanged = false; - _repositoryChanged = false; - _temporaryChanged = false; - _unArChanged = false; - - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - UnArCommand = ReactiveCommand.Create(ExecuteUnArCommand); - TemporaryCommand = ReactiveCommand.Create(ExecuteTemporaryCommand); - RepositoryCommand = ReactiveCommand.Create(ExecuteRepositoryCommand); - DatabaseCommand = ReactiveCommand.Create(ExecuteDatabaseCommand); - SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); - - DatabasePath = Settings.Settings.Current.DatabasePath; - RepositoryPath = Settings.Settings.Current.RepositoryPath; - TemporaryPath = Settings.Settings.Current.TemporaryFolder; - UnArPath = Settings.Settings.Current.UnArchiverPath; - - if(!string.IsNullOrWhiteSpace(UnArPath)) - CheckUnAr(); + this.RaiseAndSetIfChanged(ref _databasePath, value); + _databaseChanged = true; } + } - public string ChooseLabel => Localization.ChooseLabel; - public string Title => Localization.SettingsTitle; - public string CloseLabel => Localization.CloseLabel; - public string DatabaseLabel => Localization.DatabaseFileLabel; - public string RepositoryLabel => Localization.RepositoryFolderLabel; - public string TemporaryLabel => Localization.TemporaryFolderLabel; - public string UnArPathLabel => Localization.UnArPathLabel; - - public ReactiveCommand UnArCommand { get; } - public ReactiveCommand TemporaryCommand { get; } - public ReactiveCommand RepositoryCommand { get; } - public ReactiveCommand DatabaseCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand SaveCommand { get; } - - public string DatabasePath + public string RepositoryPath + { + get => _repositoryPath; + set { - get => _databasePath; - set + this.RaiseAndSetIfChanged(ref _repositoryPath, value); + + // TODO: Refresh repository existing files + _repositoryChanged = true; + } + } + + public string TemporaryPath + { + get => _temporaryPath; + set + { + this.RaiseAndSetIfChanged(ref _temporaryPath, value); + _temporaryChanged = true; + } + } + + public string UnArPath + { + get => _unArPath; + set => this.RaiseAndSetIfChanged(ref _unArPath, value); + } + + public string UnArVersion + { + get => _unArVersion; + set => this.RaiseAndSetIfChanged(ref _unArVersion, value); + } + + public string SaveLabel => Localization.SaveLabel; + + void CheckUnAr() + { + var worker = new Compression(); + + worker.FinishedWithText += CheckUnArFinished; + worker.FailedWithText += CheckUnArFailed; + + Task.Run(() => worker.CheckUnAr(UnArPath)); + } + + async void CheckUnArFailed(object sender, [NotNull] ErrorEventArgs args) + { + UnArVersion = ""; + UnArPath = ""; + + await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.Error, $"{args.Message}", ButtonEnum.Ok, Icon.Error) + .ShowDialog(_view); + } + + void CheckUnArFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => + { + UnArVersion = string.Format(Localization.TheUnarchiverVersionLabel, args.Message); + _unArChanged = true; + }); + + void ExecuteCloseCommand() => _view.Close(); + + async void ExecuteUnArCommand() + { + var dlgFile = new OpenFileDialog + { + Title = Localization.ChooseUnArExecutable, + AllowMultiple = false + }; + + if(!string.IsNullOrWhiteSpace(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles))) + dlgFile.Directory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + + string[] result = await dlgFile.ShowAsync(_view); + + if(result?.Length != 1) return; + + UnArPath = result[0]; + CheckUnAr(); + } + + async void ExecuteTemporaryCommand() + { + var dlgFolder = new OpenFolderDialog + { + Title = Localization.ChooseTemporaryFolder + }; + + string result = await dlgFolder.ShowAsync(_view); + + if(result == null) return; + + TemporaryPath = result; + } + + async void ExecuteRepositoryCommand() + { + var dlgFolder = new OpenFolderDialog + { + Title = Localization.ChooseRepositoryFolder + }; + + string result = await dlgFolder.ShowAsync(_view); + + if(result == null) return; + + RepositoryPath = result; + } + + async void ExecuteDatabaseCommand() + { + var dlgFile = new SaveFileDialog + { + InitialFileName = "romrepo.db", + Directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), + Title = Localization.ChooseDatabaseFile + }; + + string result = await dlgFile.ShowAsync(_view); + + if(result == null) return; + + if(File.Exists(result)) + { + ButtonResult btnResult = await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DatabaseFileExistsMsgBoxTitle, + Localization.DatabaseFileTryOpenCaption, + ButtonEnum.YesNo, + Icon.Database) + .ShowDialog(_view); + + if(btnResult == ButtonResult.Yes) { - this.RaiseAndSetIfChanged(ref _databasePath, value); - _databaseChanged = true; - } - } - - public string RepositoryPath - { - get => _repositoryPath; - set - { - this.RaiseAndSetIfChanged(ref _repositoryPath, value); - - // TODO: Refresh repository existing files - _repositoryChanged = true; - } - } - - public string TemporaryPath - { - get => _temporaryPath; - set - { - this.RaiseAndSetIfChanged(ref _temporaryPath, value); - _temporaryChanged = true; - } - } - - public string UnArPath - { - get => _unArPath; - set => this.RaiseAndSetIfChanged(ref _unArPath, value); - } - - public string UnArVersion - { - get => _unArVersion; - set => this.RaiseAndSetIfChanged(ref _unArVersion, value); - } - - public string SaveLabel => Localization.SaveLabel; - - void CheckUnAr() - { - var worker = new Compression(); - - worker.FinishedWithText += CheckUnArFinished; - worker.FailedWithText += CheckUnArFailed; - - Task.Run(() => worker.CheckUnAr(UnArPath)); - } - - async void CheckUnArFailed(object sender, [NotNull] ErrorEventArgs args) - { - UnArVersion = ""; - UnArPath = ""; - - await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.Error, $"{args.Message}", ButtonEnum.Ok, Icon.Error). - ShowDialog(_view); - } - - void CheckUnArFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => - { - UnArVersion = string.Format(Localization.TheUnarchiverVersionLabel, args.Message); - _unArChanged = true; - }); - - void ExecuteCloseCommand() => _view.Close(); - - async void ExecuteUnArCommand() - { - var dlgFile = new OpenFileDialog - { - Title = Localization.ChooseUnArExecutable, - AllowMultiple = false - }; - - if(!string.IsNullOrWhiteSpace(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles))) - dlgFile.Directory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); - - string[] result = await dlgFile.ShowAsync(_view); - - if(result?.Length != 1) - return; - - UnArPath = result[0]; - CheckUnAr(); - } - - async void ExecuteTemporaryCommand() - { - var dlgFolder = new OpenFolderDialog - { - Title = Localization.ChooseTemporaryFolder - }; - - string result = await dlgFolder.ShowAsync(_view); - - if(result == null) - return; - - TemporaryPath = result; - } - - async void ExecuteRepositoryCommand() - { - var dlgFolder = new OpenFolderDialog - { - Title = Localization.ChooseRepositoryFolder - }; - - string result = await dlgFolder.ShowAsync(_view); - - if(result == null) - return; - - RepositoryPath = result; - } - - async void ExecuteDatabaseCommand() - { - var dlgFile = new SaveFileDialog - { - InitialFileName = "romrepo.db", - Directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), - Title = Localization.ChooseDatabaseFile - }; - - string result = await dlgFile.ShowAsync(_view); - - if(result == null) - return; - - if(File.Exists(result)) - { - ButtonResult btnResult = await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DatabaseFileExistsMsgBoxTitle, - Localization.DatabaseFileTryOpenCaption, - ButtonEnum.YesNo, Icon.Database). - ShowDialog(_view); - - if(btnResult == ButtonResult.Yes) + try { - try - { - var ctx = Context.Create(result); - await ctx.Database.MigrateAsync(); - } - catch(Exception) - { - btnResult = await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DatabaseFileUnusableMsgBoxTitle, - Localization. - DatabaseFileUnusableDeleteMsgBoxCaption, - ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); - - if(btnResult == ButtonResult.No) - return; - - try - { - File.Delete(result); - } - catch(Exception) - { - await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DatabaseFileCannotDeleteTitle, - Localization.DatabaseFileCannotDeleteCaption, - ButtonEnum.Ok, Icon.Error).ShowDialog(_view); - - return; - } - } + var ctx = Context.Create(result); + await ctx.Database.MigrateAsync(); } - else + catch(Exception) { - btnResult = await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DatabaseFileExistsMsgBoxTitle, - Localization.DatabaseFileDeleteCaption, - ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); + btnResult = await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DatabaseFileUnusableMsgBoxTitle, + Localization.DatabaseFileUnusableDeleteMsgBoxCaption, + ButtonEnum.YesNo, + Icon.Error) + .ShowDialog(_view); - if(btnResult == ButtonResult.No) - return; + if(btnResult == ButtonResult.No) return; try { @@ -290,58 +261,83 @@ namespace RomRepoMgr.ViewModels } catch(Exception) { - await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DatabaseFileCannotDeleteTitle, - Localization.DatabaseFileCannotDeleteCaption, ButtonEnum.Ok, - Icon.Error).ShowDialog(_view); + await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DatabaseFileCannotDeleteTitle, + Localization.DatabaseFileCannotDeleteCaption, + ButtonEnum.Ok, + Icon.Error) + .ShowDialog(_view); return; } } } - - try + else { - var ctx = Context.Create(result); - await ctx.Database.MigrateAsync(); - } - catch(Exception) - { - await MessageBoxManager. - GetMessageBoxStandardWindow(Localization.DatabaseFileUnusableMsgBoxTitle, - Localization.DatabaseFileUnusableMsgBoxCaption, ButtonEnum.Ok, - Icon.Error).ShowDialog(_view); + btnResult = await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DatabaseFileExistsMsgBoxTitle, + Localization.DatabaseFileDeleteCaption, + ButtonEnum.YesNo, + Icon.Error) + .ShowDialog(_view); - return; - } + if(btnResult == ButtonResult.No) return; - DatabasePath = result; + try + { + File.Delete(result); + } + catch(Exception) + { + await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DatabaseFileCannotDeleteTitle, + Localization.DatabaseFileCannotDeleteCaption, + ButtonEnum.Ok, + Icon.Error) + .ShowDialog(_view); + + return; + } + } } - void ExecuteSaveCommand() + try { - if(_databaseChanged) - Settings.Settings.Current.DatabasePath = DatabasePath; - - if(_repositoryChanged) - Settings.Settings.Current.RepositoryPath = RepositoryPath; - - if(_temporaryChanged) - Settings.Settings.Current.TemporaryFolder = TemporaryPath; - - if(_unArChanged) - { - Settings.Settings.Current.UnArchiverPath = UnArPath; - Settings.Settings.UnArUsable = true; - } - - if(_databaseChanged || - _repositoryChanged || - _temporaryChanged || - _unArChanged) - Settings.Settings.SaveSettings(); - - _view.Close(); + var ctx = Context.Create(result); + await ctx.Database.MigrateAsync(); } + catch(Exception) + { + await MessageBoxManager + .GetMessageBoxStandardWindow(Localization.DatabaseFileUnusableMsgBoxTitle, + Localization.DatabaseFileUnusableMsgBoxCaption, + ButtonEnum.Ok, + Icon.Error) + .ShowDialog(_view); + + return; + } + + DatabasePath = result; + } + + void ExecuteSaveCommand() + { + if(_databaseChanged) Settings.Settings.Current.DatabasePath = DatabasePath; + + if(_repositoryChanged) Settings.Settings.Current.RepositoryPath = RepositoryPath; + + if(_temporaryChanged) Settings.Settings.Current.TemporaryFolder = TemporaryPath; + + if(_unArChanged) + { + Settings.Settings.Current.UnArchiverPath = UnArPath; + Settings.Settings.UnArUsable = true; + } + + if(_databaseChanged || _repositoryChanged || _temporaryChanged || _unArChanged) + Settings.Settings.SaveSettings(); + + _view.Close(); } } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/SplashWindowViewModel.cs b/RomRepoMgr/ViewModels/SplashWindowViewModel.cs index 864ed9b..0ceac99 100644 --- a/RomRepoMgr/ViewModels/SplashWindowViewModel.cs +++ b/RomRepoMgr/ViewModels/SplashWindowViewModel.cs @@ -39,335 +39,339 @@ using RomRepoMgr.Core.Workers; using RomRepoMgr.Database; using RomRepoMgr.Resources; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class SplashWindowViewModel : ViewModelBase { - public sealed class SplashWindowViewModel : ViewModelBase + bool _checkingUnArError; + bool _checkingUnArOk; + bool _checkingUnArUnknown; + bool _exitVisible; + bool _loadingDatabaseError; + bool _loadingDatabaseOk; + bool _loadingDatabaseUnknown; + bool _loadingRomSetsError; + bool _loadingRomSetsOk; + bool _loadingRomSetsUnknown; + bool _loadingSettingsError; + bool _loadingSettingsOk; + bool _loadingSettingsUnknown; + bool _migratingDatabaseError; + bool _migratingDatabaseOk; + bool _migratingDatabaseUnknown; + + public SplashWindowViewModel() { - bool _checkingUnArError; - bool _checkingUnArOk; - bool _checkingUnArUnknown; - bool _exitVisible; - bool _loadingDatabaseError; - bool _loadingDatabaseOk; - bool _loadingDatabaseUnknown; - bool _loadingRomSetsError; - bool _loadingRomSetsOk; - bool _loadingRomSetsUnknown; - bool _loadingSettingsError; - bool _loadingSettingsOk; - bool _loadingSettingsUnknown; - bool _migratingDatabaseError; - bool _migratingDatabaseOk; - bool _migratingDatabaseUnknown; + ExitCommand = ReactiveCommand.Create(ExecuteExitCommand); - public SplashWindowViewModel() - { - ExitCommand = ReactiveCommand.Create(ExecuteExitCommand); - - LoadingSettingsOk = false; - LoadingSettingsError = false; - LoadingSettingsUnknown = true; - CheckingUnArOk = false; - CheckingUnArError = false; - CheckingUnArUnknown = true; - LoadingDatabaseOk = false; - LoadingDatabaseError = false; - LoadingDatabaseUnknown = true; - MigratingDatabaseOk = false; - MigratingDatabaseError = false; - MigratingDatabaseUnknown = true; - LoadingRomSetsOk = false; - LoadingRomSetsError = false; - LoadingRomSetsUnknown = true; - ExitVisible = false; - } - - public ReactiveCommand ExitCommand { get; } - - public bool LoadingSettingsOk - { - get => _loadingSettingsOk; - set => this.RaiseAndSetIfChanged(ref _loadingSettingsOk, value); - } - - public bool LoadingSettingsError - { - get => _loadingSettingsError; - set => this.RaiseAndSetIfChanged(ref _loadingSettingsError, value); - } - - public bool LoadingSettingsUnknown - { - get => _loadingSettingsUnknown; - set => this.RaiseAndSetIfChanged(ref _loadingSettingsUnknown, value); - } - - public bool CheckingUnArOk - { - get => _checkingUnArOk; - set => this.RaiseAndSetIfChanged(ref _checkingUnArOk, value); - } - - public bool CheckingUnArError - { - get => _checkingUnArError; - set => this.RaiseAndSetIfChanged(ref _checkingUnArError, value); - } - - public bool CheckingUnArUnknown - { - get => _checkingUnArUnknown; - set => this.RaiseAndSetIfChanged(ref _checkingUnArUnknown, value); - } - - public bool LoadingDatabaseOk - { - get => _loadingDatabaseOk; - set => this.RaiseAndSetIfChanged(ref _loadingDatabaseOk, value); - } - - public bool LoadingDatabaseError - { - get => _loadingDatabaseError; - set => this.RaiseAndSetIfChanged(ref _loadingDatabaseError, value); - } - - public bool LoadingDatabaseUnknown - { - get => _loadingDatabaseUnknown; - set => this.RaiseAndSetIfChanged(ref _loadingDatabaseUnknown, value); - } - - public bool MigratingDatabaseOk - { - get => _migratingDatabaseOk; - set => this.RaiseAndSetIfChanged(ref _migratingDatabaseOk, value); - } - - public bool MigratingDatabaseError - { - get => _migratingDatabaseError; - set => this.RaiseAndSetIfChanged(ref _migratingDatabaseError, value); - } - - public bool MigratingDatabaseUnknown - { - get => _migratingDatabaseUnknown; - set => this.RaiseAndSetIfChanged(ref _migratingDatabaseUnknown, value); - } - - public bool ExitVisible - { - get => _exitVisible; - set => this.RaiseAndSetIfChanged(ref _exitVisible, value); - } - - public bool LoadingRomSetsOk - { - get => _loadingRomSetsOk; - set => this.RaiseAndSetIfChanged(ref _loadingRomSetsOk, value); - } - - public bool LoadingRomSetsError - { - get => _loadingRomSetsError; - set => this.RaiseAndSetIfChanged(ref _loadingRomSetsError, value); - } - - public bool LoadingRomSetsUnknown - { - get => _loadingRomSetsUnknown; - set => this.RaiseAndSetIfChanged(ref _loadingRomSetsUnknown, value); - } - - public string LoadingText => "ROM Repository Manager"; - public string LoadingSettingsText => Localization.LoadingSettingsText; - public string CheckingUnArText => Localization.CheckingUnArText; - public string LoadingDatabaseText => Localization.LoadingDatabaseText; - public string MigratingDatabaseText => Localization.MigratingDatabaseText; - public string LoadingRomSetsText => Localization.LoadingRomSetsText; - public string ExitButtonText => Localization.ExitButtonText; - - void ExecuteExitCommand() => - (Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown(); - - internal void OnOpened() => Dispatcher.UIThread.Post(LoadSettings); - - void LoadSettings() => Task.Run(() => - { - try - { - Settings.Settings.LoadSettings(); - - Dispatcher.UIThread.Post(CheckUnAr); - } - catch(Exception e) - { - // TODO: Log error - Dispatcher.UIThread.Post(FailedLoadingSettings); - } - }); - - void FailedLoadingSettings() - { - LoadingSettingsUnknown = false; - LoadingSettingsError = true; - ExitVisible = true; - } - - void CheckUnAr() => Task.Run(() => - { - LoadingSettingsUnknown = false; - LoadingSettingsOk = true; - - try - { - var worker = new Compression(); - Settings.Settings.UnArUsable = worker.CheckUnAr(Settings.Settings.Current.UnArchiverPath); - - Dispatcher.UIThread.Post(LoadDatabase); - } - catch(Exception e) - { - // TODO: Log error - Dispatcher.UIThread.Post(FailedCheckUnAr); - } - }); - - void FailedCheckUnAr() - { - CheckingUnArUnknown = false; - CheckingUnArError = true; - ExitVisible = true; - } - - void LoadDatabase() - { - CheckingUnArUnknown = false; - CheckingUnArOk = true; - - Task.Run(() => - { - try - { - string dbPathFolder = Path.GetDirectoryName(Settings.Settings.Current.DatabasePath); - - if(!Directory.Exists(dbPathFolder)) - Directory.CreateDirectory(dbPathFolder); - - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - Dispatcher.UIThread.Post(MigrateDatabase); - } - catch(Exception e) - { - // TODO: Log error - Dispatcher.UIThread.Post(FailedLoadingDatabase); - } - }); - } - - void FailedLoadingDatabase() - { - LoadingDatabaseUnknown = false; - LoadingDatabaseError = true; - ExitVisible = true; - } - - void MigrateDatabase() - { - LoadingDatabaseUnknown = false; - LoadingDatabaseOk = true; - - Task.Run(() => - { - try - { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - ctx.Database.Migrate(); - - Dispatcher.UIThread.Post(LoadRomSets); - } - catch(Exception e) - { - // TODO: Log error - Dispatcher.UIThread.Post(FailedMigratingDatabase); - } - }); - } - - void FailedMigratingDatabase() - { - MigratingDatabaseUnknown = false; - MigratingDatabaseError = true; - ExitVisible = true; - } - - void LoadRomSets() - { - MigratingDatabaseUnknown = false; - MigratingDatabaseOk = true; - - Task.Run(() => - { - try - { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); - - GotRomSets?.Invoke(this, new RomSetsEventArgs - { - RomSets = ctx.RomSets.OrderBy(r => r.Name).ThenBy(r => r.Version).ThenBy(r => r.Date). - ThenBy(r => r.Description).ThenBy(r => r.Comment).ThenBy(r => r.Filename). - Select(r => new RomSetModel - { - Id = r.Id, - Author = r.Author, - Comment = r.Comment, - Date = r.Date, - Description = r.Description, - Filename = r.Filename, - Homepage = r.Homepage, - Name = r.Name, - Sha384 = r.Sha384, - Version = r.Version, - TotalMachines = r.Statistics.TotalMachines, - CompleteMachines = r.Statistics.CompleteMachines, - IncompleteMachines = r.Statistics.IncompleteMachines, - TotalRoms = r.Statistics.TotalRoms, - HaveRoms = r.Statistics.HaveRoms, - MissRoms = r.Statistics.MissRoms, - Category = r.Category - }).ToList() - }); - - Dispatcher.UIThread.Post(LoadMainWindow); - } - catch(Exception e) - { - // TODO: Log error - Dispatcher.UIThread.Post(FailedLoadingRomSets); - } - }); - } - - void FailedLoadingRomSets() - { - LoadingRomSetsUnknown = false; - LoadingRomSetsError = true; - ExitVisible = true; - } - - void LoadMainWindow() - { - LoadingRomSetsUnknown = false; - LoadingRomSetsOk = true; - - WorkFinished?.Invoke(this, EventArgs.Empty); - } - - internal event EventHandler WorkFinished; - - internal event EventHandler GotRomSets; + LoadingSettingsOk = false; + LoadingSettingsError = false; + LoadingSettingsUnknown = true; + CheckingUnArOk = false; + CheckingUnArError = false; + CheckingUnArUnknown = true; + LoadingDatabaseOk = false; + LoadingDatabaseError = false; + LoadingDatabaseUnknown = true; + MigratingDatabaseOk = false; + MigratingDatabaseError = false; + MigratingDatabaseUnknown = true; + LoadingRomSetsOk = false; + LoadingRomSetsError = false; + LoadingRomSetsUnknown = true; + ExitVisible = false; } + + public ReactiveCommand ExitCommand { get; } + + public bool LoadingSettingsOk + { + get => _loadingSettingsOk; + set => this.RaiseAndSetIfChanged(ref _loadingSettingsOk, value); + } + + public bool LoadingSettingsError + { + get => _loadingSettingsError; + set => this.RaiseAndSetIfChanged(ref _loadingSettingsError, value); + } + + public bool LoadingSettingsUnknown + { + get => _loadingSettingsUnknown; + set => this.RaiseAndSetIfChanged(ref _loadingSettingsUnknown, value); + } + + public bool CheckingUnArOk + { + get => _checkingUnArOk; + set => this.RaiseAndSetIfChanged(ref _checkingUnArOk, value); + } + + public bool CheckingUnArError + { + get => _checkingUnArError; + set => this.RaiseAndSetIfChanged(ref _checkingUnArError, value); + } + + public bool CheckingUnArUnknown + { + get => _checkingUnArUnknown; + set => this.RaiseAndSetIfChanged(ref _checkingUnArUnknown, value); + } + + public bool LoadingDatabaseOk + { + get => _loadingDatabaseOk; + set => this.RaiseAndSetIfChanged(ref _loadingDatabaseOk, value); + } + + public bool LoadingDatabaseError + { + get => _loadingDatabaseError; + set => this.RaiseAndSetIfChanged(ref _loadingDatabaseError, value); + } + + public bool LoadingDatabaseUnknown + { + get => _loadingDatabaseUnknown; + set => this.RaiseAndSetIfChanged(ref _loadingDatabaseUnknown, value); + } + + public bool MigratingDatabaseOk + { + get => _migratingDatabaseOk; + set => this.RaiseAndSetIfChanged(ref _migratingDatabaseOk, value); + } + + public bool MigratingDatabaseError + { + get => _migratingDatabaseError; + set => this.RaiseAndSetIfChanged(ref _migratingDatabaseError, value); + } + + public bool MigratingDatabaseUnknown + { + get => _migratingDatabaseUnknown; + set => this.RaiseAndSetIfChanged(ref _migratingDatabaseUnknown, value); + } + + public bool ExitVisible + { + get => _exitVisible; + set => this.RaiseAndSetIfChanged(ref _exitVisible, value); + } + + public bool LoadingRomSetsOk + { + get => _loadingRomSetsOk; + set => this.RaiseAndSetIfChanged(ref _loadingRomSetsOk, value); + } + + public bool LoadingRomSetsError + { + get => _loadingRomSetsError; + set => this.RaiseAndSetIfChanged(ref _loadingRomSetsError, value); + } + + public bool LoadingRomSetsUnknown + { + get => _loadingRomSetsUnknown; + set => this.RaiseAndSetIfChanged(ref _loadingRomSetsUnknown, value); + } + + public string LoadingText => "ROM Repository Manager"; + public string LoadingSettingsText => Localization.LoadingSettingsText; + public string CheckingUnArText => Localization.CheckingUnArText; + public string LoadingDatabaseText => Localization.LoadingDatabaseText; + public string MigratingDatabaseText => Localization.MigratingDatabaseText; + public string LoadingRomSetsText => Localization.LoadingRomSetsText; + public string ExitButtonText => Localization.ExitButtonText; + + void ExecuteExitCommand() => + (Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown(); + + internal void OnOpened() => Dispatcher.UIThread.Post(LoadSettings); + + void LoadSettings() => Task.Run(() => + { + try + { + Settings.Settings.LoadSettings(); + + Dispatcher.UIThread.Post(CheckUnAr); + } + catch(Exception e) + { + // TODO: Log error + Dispatcher.UIThread.Post(FailedLoadingSettings); + } + }); + + void FailedLoadingSettings() + { + LoadingSettingsUnknown = false; + LoadingSettingsError = true; + ExitVisible = true; + } + + void CheckUnAr() => Task.Run(() => + { + LoadingSettingsUnknown = false; + LoadingSettingsOk = true; + + try + { + var worker = new Compression(); + Settings.Settings.UnArUsable = worker.CheckUnAr(Settings.Settings.Current.UnArchiverPath); + + Dispatcher.UIThread.Post(LoadDatabase); + } + catch(Exception e) + { + // TODO: Log error + Dispatcher.UIThread.Post(FailedCheckUnAr); + } + }); + + void FailedCheckUnAr() + { + CheckingUnArUnknown = false; + CheckingUnArError = true; + ExitVisible = true; + } + + void LoadDatabase() + { + CheckingUnArUnknown = false; + CheckingUnArOk = true; + + Task.Run(() => + { + try + { + string dbPathFolder = Path.GetDirectoryName(Settings.Settings.Current.DatabasePath); + + if(!Directory.Exists(dbPathFolder)) Directory.CreateDirectory(dbPathFolder); + + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + Dispatcher.UIThread.Post(MigrateDatabase); + } + catch(Exception e) + { + // TODO: Log error + Dispatcher.UIThread.Post(FailedLoadingDatabase); + } + }); + } + + void FailedLoadingDatabase() + { + LoadingDatabaseUnknown = false; + LoadingDatabaseError = true; + ExitVisible = true; + } + + void MigrateDatabase() + { + LoadingDatabaseUnknown = false; + LoadingDatabaseOk = true; + + Task.Run(() => + { + try + { + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + ctx.Database.Migrate(); + + Dispatcher.UIThread.Post(LoadRomSets); + } + catch(Exception e) + { + // TODO: Log error + Dispatcher.UIThread.Post(FailedMigratingDatabase); + } + }); + } + + void FailedMigratingDatabase() + { + MigratingDatabaseUnknown = false; + MigratingDatabaseError = true; + ExitVisible = true; + } + + void LoadRomSets() + { + MigratingDatabaseUnknown = false; + MigratingDatabaseOk = true; + + Task.Run(() => + { + try + { + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + GotRomSets?.Invoke(this, + new RomSetsEventArgs + { + RomSets = ctx.RomSets.OrderBy(r => r.Name) + .ThenBy(r => r.Version) + .ThenBy(r => r.Date) + .ThenBy(r => r.Description) + .ThenBy(r => r.Comment) + .ThenBy(r => r.Filename) + .Select(r => new RomSetModel + { + Id = r.Id, + Author = r.Author, + Comment = r.Comment, + Date = r.Date, + Description = r.Description, + Filename = r.Filename, + Homepage = r.Homepage, + Name = r.Name, + Sha384 = r.Sha384, + Version = r.Version, + TotalMachines = r.Statistics.TotalMachines, + CompleteMachines = r.Statistics.CompleteMachines, + IncompleteMachines = r.Statistics.IncompleteMachines, + TotalRoms = r.Statistics.TotalRoms, + HaveRoms = r.Statistics.HaveRoms, + MissRoms = r.Statistics.MissRoms, + Category = r.Category + }) + .ToList() + }); + + Dispatcher.UIThread.Post(LoadMainWindow); + } + catch(Exception e) + { + // TODO: Log error + Dispatcher.UIThread.Post(FailedLoadingRomSets); + } + }); + } + + void FailedLoadingRomSets() + { + LoadingRomSetsUnknown = false; + LoadingRomSetsError = true; + ExitVisible = true; + } + + void LoadMainWindow() + { + LoadingRomSetsUnknown = false; + LoadingRomSetsOk = true; + + WorkFinished?.Invoke(this, EventArgs.Empty); + } + + internal event EventHandler WorkFinished; + + internal event EventHandler GotRomSets; } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/UpdateStatsViewModel.cs b/RomRepoMgr/ViewModels/UpdateStatsViewModel.cs index 21d3c0d..e08c73a 100644 --- a/RomRepoMgr/ViewModels/UpdateStatsViewModel.cs +++ b/RomRepoMgr/ViewModels/UpdateStatsViewModel.cs @@ -38,223 +38,231 @@ using RomRepoMgr.Database.Models; using RomRepoMgr.Resources; using RomRepoMgr.Views; -namespace RomRepoMgr.ViewModels +namespace RomRepoMgr.ViewModels; + +public sealed class UpdateStatsViewModel : ViewModelBase { - public sealed class UpdateStatsViewModel : ViewModelBase + readonly UpdateStats _view; + bool _canClose; + double _currentValue; + bool _indeterminateProgress; + double _maximumValue; + double _minimumValue; + bool _progressVisible; + RomSetModel _selectedRomSet; + string _statusMessage; + + public UpdateStatsViewModel(UpdateStats view) { - readonly UpdateStats _view; - bool _canClose; - double _currentValue; - bool _indeterminateProgress; - double _maximumValue; - double _minimumValue; - bool _progressVisible; - RomSetModel _selectedRomSet; - string _statusMessage; + _view = view; + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + IndeterminateProgress = true; + ProgressVisible = false; + RomSets = new ObservableCollection(); + } - public UpdateStatsViewModel(UpdateStats view) + [NotNull] + public string Title => Localization.UpdateStatsTitle; + public string RomSetNameLabel => Localization.RomSetNameLabel; + public string RomSetVersionLabel => Localization.RomSetVersionLabel; + public string RomSetAuthorLabel => Localization.RomSetAuthorLabel; + public string RomSetCategoryLabel => Localization.RomSetCategoryLabel; + public string RomSetDateLabel => Localization.RomSetDateLabel; + public string RomSetDescriptionLabel => Localization.RomSetDescriptionLabel; + public string RomSetCommentLabel => Localization.RomSetCommentLabel; + public string RomSetTotalMachinesLabel => Localization.RomSetTotalMachinesLabel; + public string RomSetCompleteMachinesLabel => Localization.RomSetCompleteMachinesLabel; + public string RomSetIncompleteMachinesLabel => Localization.RomSetIncompleteMachinesLabel; + public string RomSetTotalRomsLabel => Localization.RomSetTotalRomsLabel; + public string RomSetHaveRomsLabel => Localization.RomSetHaveRomsLabel; + public string RomSetMissRomsLabel => Localization.RomSetMissRomsLabel; + + public string StatusMessage + { + get => _statusMessage; + set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + } + + public bool IndeterminateProgress + { + get => _indeterminateProgress; + set => this.RaiseAndSetIfChanged(ref _indeterminateProgress, value); + } + + public double MaximumValue + { + get => _maximumValue; + set => this.RaiseAndSetIfChanged(ref _maximumValue, value); + } + + public double MinimumValue + { + get => _minimumValue; + set => this.RaiseAndSetIfChanged(ref _minimumValue, value); + } + + public double CurrentValue + { + get => _currentValue; + set => this.RaiseAndSetIfChanged(ref _currentValue, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public RomSetModel SelectedRomSet + { + get => _selectedRomSet; + set => this.RaiseAndSetIfChanged(ref _selectedRomSet, value); + } + + public bool CanClose + { + get => _canClose; + set => this.RaiseAndSetIfChanged(ref _canClose, value); + } + + public ObservableCollection RomSets { get; } + + public string CloseLabel => Localization.CloseLabel; + public ReactiveCommand CloseCommand { get; } + + internal void OnOpened() => Task.Run(() => + { + using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + + Dispatcher.UIThread.Post(() => { - _view = view; - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StatusMessage = Localization.RetrievingRomSetsFromDatabase; + ProgressVisible = true; IndeterminateProgress = true; - ProgressVisible = false; - RomSets = new ObservableCollection(); - } + }); - [NotNull] - public string Title => Localization.UpdateStatsTitle; - public string RomSetNameLabel => Localization.RomSetNameLabel; - public string RomSetVersionLabel => Localization.RomSetVersionLabel; - public string RomSetAuthorLabel => Localization.RomSetAuthorLabel; - public string RomSetCategoryLabel => Localization.RomSetCategoryLabel; - public string RomSetDateLabel => Localization.RomSetDateLabel; - public string RomSetDescriptionLabel => Localization.RomSetDescriptionLabel; - public string RomSetCommentLabel => Localization.RomSetCommentLabel; - public string RomSetTotalMachinesLabel => Localization.RomSetTotalMachinesLabel; - public string RomSetCompleteMachinesLabel => Localization.RomSetCompleteMachinesLabel; - public string RomSetIncompleteMachinesLabel => Localization.RomSetIncompleteMachinesLabel; - public string RomSetTotalRomsLabel => Localization.RomSetTotalRomsLabel; - public string RomSetHaveRomsLabel => Localization.RomSetHaveRomsLabel; - public string RomSetMissRomsLabel => Localization.RomSetMissRomsLabel; + long romSetCount = ctx.RomSets.LongCount(); - public string StatusMessage + Dispatcher.UIThread.Post(() => { StatusMessage = Localization.RemovingOldStatistics; }); + + ctx.Database.ExecuteSqlRaw("DELETE FROM \"RomSetStats\""); + + Dispatcher.UIThread.Post(() => { - get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); - } + IndeterminateProgress = false; + MinimumValue = 0; + MaximumValue = romSetCount; + CurrentValue = 0; + }); - public bool IndeterminateProgress + long pos = 0; + + foreach(RomSet romSet in ctx.RomSets) { - get => _indeterminateProgress; - set => this.RaiseAndSetIfChanged(ref _indeterminateProgress, value); - } - - public double MaximumValue - { - get => _maximumValue; - set => this.RaiseAndSetIfChanged(ref _maximumValue, value); - } - - public double MinimumValue - { - get => _minimumValue; - set => this.RaiseAndSetIfChanged(ref _minimumValue, value); - } - - public double CurrentValue - { - get => _currentValue; - set => this.RaiseAndSetIfChanged(ref _currentValue, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public RomSetModel SelectedRomSet - { - get => _selectedRomSet; - set => this.RaiseAndSetIfChanged(ref _selectedRomSet, value); - } - - public bool CanClose - { - get => _canClose; - set => this.RaiseAndSetIfChanged(ref _canClose, value); - } - - public ObservableCollection RomSets { get; } - - public string CloseLabel => Localization.CloseLabel; - public ReactiveCommand CloseCommand { get; } - - internal void OnOpened() => Task.Run(() => - { - using var ctx = Context.Create(Settings.Settings.Current.DatabasePath); + long currentPos = pos; Dispatcher.UIThread.Post(() => { - StatusMessage = Localization.RetrievingRomSetsFromDatabase; - ProgressVisible = true; - IndeterminateProgress = true; + StatusMessage = string.Format(Localization.CalculatingStatisticsForRomSet, + romSet.Name, + romSet.Version, + romSet.Description); + + CurrentValue = currentPos; }); - long romSetCount = ctx.RomSets.LongCount(); - - Dispatcher.UIThread.Post(() => + try { - StatusMessage = Localization.RemovingOldStatistics; - }); + RomSetStat stats = ctx.RomSets.Where(r => r.Id == romSet.Id) + .Select(r => new RomSetStat + { + RomSetId = r.Id, + TotalMachines = r.Machines.Count, + CompleteMachines = + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count == 0 && + m.Files.All(f => f.File.IsInRepo)) + + r.Machines.Count(m => m.Disks.Count > 0 && + m.Files.Count == 0 && + m.Disks.All(f => f.Disk.IsInRepo)) + + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count > 0 && + m.Files.All(f => f.File.IsInRepo) && + m.Disks.All(f => f.Disk.IsInRepo)), + IncompleteMachines = + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count == 0 && + m.Files.Any(f => !f.File.IsInRepo)) + + r.Machines.Count(m => m.Disks.Count > 0 && + m.Files.Count == 0 && + m.Disks.Any(f => !f.Disk.IsInRepo)) + + r.Machines.Count(m => m.Files.Count > 0 && + m.Disks.Count > 0 && + (m.Files.Any(f => !f.File.IsInRepo) || + m.Disks.Any(f => !f.Disk.IsInRepo))), + TotalRoms = + r.Machines.Sum(m => m.Files.Count) + + r.Machines.Sum(m => m.Disks.Count) + + r.Machines.Sum(m => m.Medias.Count), + HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) + + r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) + + r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)), + MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) + + r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) + + r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo)) + }) + .FirstOrDefault(); - ctx.Database.ExecuteSqlRaw("DELETE FROM \"RomSetStats\""); - - Dispatcher.UIThread.Post(() => - { - IndeterminateProgress = false; - MinimumValue = 0; - MaximumValue = romSetCount; - CurrentValue = 0; - }); - - long pos = 0; - - foreach(RomSet romSet in ctx.RomSets) - { - long currentPos = pos; + ctx.RomSetStats.Add(stats); Dispatcher.UIThread.Post(() => { - StatusMessage = string.Format(Localization.CalculatingStatisticsForRomSet, romSet.Name, - romSet.Version, romSet.Description); - - CurrentValue = currentPos; - }); - - try - { - RomSetStat stats = ctx.RomSets.Where(r => r.Id == romSet.Id).Select(r => new RomSetStat + RomSets.Add(new RomSetModel { - RomSetId = r.Id, - TotalMachines = r.Machines.Count, - CompleteMachines = - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count == 0 && - m.Files.All(f => f.File.IsInRepo)) + - r.Machines.Count(m => m.Disks.Count > 0 && m.Files.Count == 0 && - m.Disks.All(f => f.Disk.IsInRepo)) + - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count > 0 && - m.Files.All(f => f.File.IsInRepo) && - m.Disks.All(f => f.Disk.IsInRepo)), - IncompleteMachines = - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count == 0 && - m.Files.Any(f => !f.File.IsInRepo)) + - r.Machines.Count(m => m.Disks.Count > 0 && m.Files.Count == 0 && - m.Disks.Any(f => !f.Disk.IsInRepo)) + - r.Machines.Count(m => m.Files.Count > 0 && m.Disks.Count > 0 && - (m.Files.Any(f => !f.File.IsInRepo) || - m.Disks.Any(f => !f.Disk.IsInRepo))), - TotalRoms = r.Machines.Sum(m => m.Files.Count) + r.Machines.Sum(m => m.Disks.Count) + - r.Machines.Sum(m => m.Medias.Count), - HaveRoms = r.Machines.Sum(m => m.Files.Count(f => f.File.IsInRepo)) + - r.Machines.Sum(m => m.Disks.Count(f => f.Disk.IsInRepo)) + - r.Machines.Sum(m => m.Medias.Count(f => f.Media.IsInRepo)), - MissRoms = r.Machines.Sum(m => m.Files.Count(f => !f.File.IsInRepo)) + - r.Machines.Sum(m => m.Disks.Count(f => !f.Disk.IsInRepo)) + - r.Machines.Sum(m => m.Medias.Count(f => !f.Media.IsInRepo)) - }).FirstOrDefault(); - - ctx.RomSetStats.Add(stats); - - Dispatcher.UIThread.Post(() => - { - RomSets.Add(new RomSetModel - { - Id = romSet.Id, - Author = romSet.Author, - Comment = romSet.Comment, - Date = romSet.Date, - Description = romSet.Description, - Filename = romSet.Filename, - Homepage = romSet.Homepage, - Name = romSet.Name, - Sha384 = romSet.Sha384, - Version = romSet.Version, - TotalMachines = stats.TotalMachines, - CompleteMachines = stats.CompleteMachines, - IncompleteMachines = stats.IncompleteMachines, - TotalRoms = stats.TotalRoms, - HaveRoms = stats.HaveRoms, - MissRoms = stats.MissRoms, - Category = romSet.Category - }); + Id = romSet.Id, + Author = romSet.Author, + Comment = romSet.Comment, + Date = romSet.Date, + Description = romSet.Description, + Filename = romSet.Filename, + Homepage = romSet.Homepage, + Name = romSet.Name, + Sha384 = romSet.Sha384, + Version = romSet.Version, + TotalMachines = stats.TotalMachines, + CompleteMachines = stats.CompleteMachines, + IncompleteMachines = stats.IncompleteMachines, + TotalRoms = stats.TotalRoms, + HaveRoms = stats.HaveRoms, + MissRoms = stats.MissRoms, + Category = romSet.Category }); - } - catch(Exception) - { - // Ignored - } - - pos++; + }); + } + catch(Exception) + { + // Ignored } - Dispatcher.UIThread.Post(() => - { - StatusMessage = Localization.SavingChangesToDatabase; - ProgressVisible = true; - IndeterminateProgress = true; - }); + pos++; + } - ctx.SaveChanges(); - - Dispatcher.UIThread.Post(() => - { - StatusMessage = Localization.Finished; - ProgressVisible = false; - CanClose = true; - }); + Dispatcher.UIThread.Post(() => + { + StatusMessage = Localization.SavingChangesToDatabase; + ProgressVisible = true; + IndeterminateProgress = true; }); - void ExecuteCloseCommand() => _view.Close(); - } + ctx.SaveChanges(); + + Dispatcher.UIThread.Post(() => + { + StatusMessage = Localization.Finished; + ProgressVisible = false; + CanClose = true; + }); + }); + + void ExecuteCloseCommand() => _view.Close(); } \ No newline at end of file diff --git a/RomRepoMgr/ViewModels/ViewModelBase.cs b/RomRepoMgr/ViewModels/ViewModelBase.cs index 27e116d..7c00218 100644 --- a/RomRepoMgr/ViewModels/ViewModelBase.cs +++ b/RomRepoMgr/ViewModels/ViewModelBase.cs @@ -25,7 +25,6 @@ using ReactiveUI; -namespace RomRepoMgr.ViewModels -{ - public class ViewModelBase : ReactiveObject {} -} \ No newline at end of file +namespace RomRepoMgr.ViewModels; + +public class ViewModelBase : ReactiveObject {} \ No newline at end of file diff --git a/RomRepoMgr/Views/About.xaml b/RomRepoMgr/Views/About.xaml index 38bd4f2..5690516 100644 --- a/RomRepoMgr/Views/About.xaml +++ b/RomRepoMgr/Views/About.xaml @@ -24,12 +24,20 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="320" + x:Class="RomRepoMgr.Views.About" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}"> @@ -42,20 +50,33 @@ - - + + - + - - + - + @@ -67,17 +88,31 @@ - - - - @@ -85,14 +120,19 @@ - + - + - + @@ -104,10 +144,13 @@ - + - diff --git a/RomRepoMgr/Views/About.xaml.cs b/RomRepoMgr/Views/About.xaml.cs index ddc62fa..eaea234 100644 --- a/RomRepoMgr/Views/About.xaml.cs +++ b/RomRepoMgr/Views/About.xaml.cs @@ -26,12 +26,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace RomRepoMgr.Views -{ - public sealed class About : Window - { - public About() => InitializeComponent(); +namespace RomRepoMgr.Views; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class About : Window +{ + public About() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/RomRepoMgr/Views/EditDat.xaml b/RomRepoMgr/Views/EditDat.xaml index 5ad9cc8..add32b0 100644 --- a/RomRepoMgr/Views/EditDat.xaml +++ b/RomRepoMgr/Views/EditDat.xaml @@ -24,12 +24,21 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="720" + Height="480" + x:Class="RomRepoMgr.Views.EditDat" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterScreen"> @@ -46,138 +55,244 @@ - - + - - + + - - + + - - + + - - + + - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - diff --git a/RomRepoMgr/Views/EditDat.xaml.cs b/RomRepoMgr/Views/EditDat.xaml.cs index 2e5fc0e..fad4797 100644 --- a/RomRepoMgr/Views/EditDat.xaml.cs +++ b/RomRepoMgr/Views/EditDat.xaml.cs @@ -26,12 +26,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace RomRepoMgr.Views -{ - public sealed class EditDat : Window - { - public EditDat() => InitializeComponent(); +namespace RomRepoMgr.Views; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class EditDat : Window +{ + public EditDat() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/RomRepoMgr/Views/ExportDat.xaml b/RomRepoMgr/Views/ExportDat.xaml index ee60bfa..1b74555 100644 --- a/RomRepoMgr/Views/ExportDat.xaml +++ b/RomRepoMgr/Views/ExportDat.xaml @@ -24,12 +24,21 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="90" + x:Class="RomRepoMgr.Views.ExportDat" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterOwner"> @@ -39,12 +48,22 @@ - - + - - diff --git a/RomRepoMgr/Views/ExportDat.xaml.cs b/RomRepoMgr/Views/ExportDat.xaml.cs index b170549..6d957c0 100644 --- a/RomRepoMgr/Views/ExportDat.xaml.cs +++ b/RomRepoMgr/Views/ExportDat.xaml.cs @@ -28,18 +28,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RomRepoMgr.ViewModels; -namespace RomRepoMgr.Views +namespace RomRepoMgr.Views; + +public sealed class ExportDat : Window { - public sealed class ExportDat : Window + public ExportDat() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + protected override void OnOpened(EventArgs e) { - public ExportDat() => InitializeComponent(); - - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as ExportDatViewModel)?.OnOpened(); - } + base.OnOpened(e); + (DataContext as ExportDatViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/RomRepoMgr/Views/ExportRoms.xaml b/RomRepoMgr/Views/ExportRoms.xaml index 866b054..e5ad32d 100644 --- a/RomRepoMgr/Views/ExportRoms.xaml +++ b/RomRepoMgr/Views/ExportRoms.xaml @@ -24,12 +24,21 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="150" + x:Class="RomRepoMgr.Views.ExportRoms" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterOwner"> @@ -40,24 +49,43 @@ - - + + + - - + - + - + - + - + - diff --git a/RomRepoMgr/Views/ExportRoms.xaml.cs b/RomRepoMgr/Views/ExportRoms.xaml.cs index 1e835d8..e99c762 100644 --- a/RomRepoMgr/Views/ExportRoms.xaml.cs +++ b/RomRepoMgr/Views/ExportRoms.xaml.cs @@ -28,18 +28,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RomRepoMgr.ViewModels; -namespace RomRepoMgr.Views +namespace RomRepoMgr.Views; + +public sealed class ExportRoms : Window { - public sealed class ExportRoms : Window + public ExportRoms() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + protected override void OnOpened(EventArgs e) { - public ExportRoms() => InitializeComponent(); - - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as ExportRomsViewModel)?.OnOpened(); - } + base.OnOpened(e); + (DataContext as ExportRomsViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/RomRepoMgr/Views/ImportDat.xaml b/RomRepoMgr/Views/ImportDat.xaml index f3e44de..9aefd66 100644 --- a/RomRepoMgr/Views/ImportDat.xaml +++ b/RomRepoMgr/Views/ImportDat.xaml @@ -24,12 +24,21 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="90" + x:Class="RomRepoMgr.Views.ImportDat" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterOwner"> @@ -39,14 +48,25 @@ - - + - - diff --git a/RomRepoMgr/Views/ImportDat.xaml.cs b/RomRepoMgr/Views/ImportDat.xaml.cs index 8699f3d..46c749f 100644 --- a/RomRepoMgr/Views/ImportDat.xaml.cs +++ b/RomRepoMgr/Views/ImportDat.xaml.cs @@ -28,18 +28,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RomRepoMgr.ViewModels; -namespace RomRepoMgr.Views +namespace RomRepoMgr.Views; + +public sealed class ImportDat : Window { - public sealed class ImportDat : Window + public ImportDat() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + protected override void OnOpened(EventArgs e) { - public ImportDat() => InitializeComponent(); - - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as ImportDatViewModel)?.OnOpened(); - } + base.OnOpened(e); + (DataContext as ImportDatViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/RomRepoMgr/Views/ImportDatFolder.xaml b/RomRepoMgr/Views/ImportDatFolder.xaml index 9d6d82d..b220c96 100644 --- a/RomRepoMgr/Views/ImportDatFolder.xaml +++ b/RomRepoMgr/Views/ImportDatFolder.xaml @@ -24,12 +24,21 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="360" + x:Class="RomRepoMgr.Views.ImportDatFolder" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterOwner"> @@ -40,58 +49,95 @@ - - + + + - - + + - + - + - - + - + - + - - + - + - - - diff --git a/RomRepoMgr/Views/ImportDatFolder.xaml.cs b/RomRepoMgr/Views/ImportDatFolder.xaml.cs index c72d921..3ba591e 100644 --- a/RomRepoMgr/Views/ImportDatFolder.xaml.cs +++ b/RomRepoMgr/Views/ImportDatFolder.xaml.cs @@ -28,18 +28,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RomRepoMgr.ViewModels; -namespace RomRepoMgr.Views +namespace RomRepoMgr.Views; + +public sealed class ImportDatFolder : Window { - public sealed class ImportDatFolder : Window + public ImportDatFolder() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + protected override void OnOpened(EventArgs e) { - public ImportDatFolder() => InitializeComponent(); - - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as ImportDatFolderViewModel)?.OnOpened(); - } + base.OnOpened(e); + (DataContext as ImportDatFolderViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/RomRepoMgr/Views/ImportRomFolder.xaml b/RomRepoMgr/Views/ImportRomFolder.xaml index c85d7e3..971eac1 100644 --- a/RomRepoMgr/Views/ImportRomFolder.xaml +++ b/RomRepoMgr/Views/ImportRomFolder.xaml @@ -24,12 +24,21 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="360" + x:Class="RomRepoMgr.Views.ImportRomFolder" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterOwner"> @@ -40,56 +49,87 @@ - - + + + - - + - + - - + - + - + - - + - + - - - diff --git a/RomRepoMgr/Views/ImportRomFolder.xaml.cs b/RomRepoMgr/Views/ImportRomFolder.xaml.cs index d90c64a..466777f 100644 --- a/RomRepoMgr/Views/ImportRomFolder.xaml.cs +++ b/RomRepoMgr/Views/ImportRomFolder.xaml.cs @@ -26,12 +26,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace RomRepoMgr.Views -{ - public sealed class ImportRomFolder : Window - { - public ImportRomFolder() => InitializeComponent(); +namespace RomRepoMgr.Views; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class ImportRomFolder : Window +{ + public ImportRomFolder() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/RomRepoMgr/Views/MainWindow.xaml b/RomRepoMgr/Views/MainWindow.xaml index 7872ed0..7c6ea0f 100644 --- a/RomRepoMgr/Views/MainWindow.xaml +++ b/RomRepoMgr/Views/MainWindow.xaml @@ -1,130 +1,183 @@ - + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + x:Class="RomRepoMgr.Views.MainWindow" + Icon="/Assets/avalonia-logo.ico" + Title="RomRepoMgr"> - + - + - - - - + - - + - + - - + - + + + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/RomRepoMgr/Views/MainWindow.xaml.cs b/RomRepoMgr/Views/MainWindow.xaml.cs index b4bf633..39a3d08 100644 --- a/RomRepoMgr/Views/MainWindow.xaml.cs +++ b/RomRepoMgr/Views/MainWindow.xaml.cs @@ -27,18 +27,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace RomRepoMgr.Views -{ - public class MainWindow : Window - { - public MainWindow() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace RomRepoMgr.Views; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/RomRepoMgr/Views/RemoveDat.xaml b/RomRepoMgr/Views/RemoveDat.xaml index 5ad1d0b..7379393 100644 --- a/RomRepoMgr/Views/RemoveDat.xaml +++ b/RomRepoMgr/Views/RemoveDat.xaml @@ -24,12 +24,21 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="90" + x:Class="RomRepoMgr.Views.RemoveDat" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterOwner"> @@ -38,8 +47,12 @@ - - + + \ No newline at end of file diff --git a/RomRepoMgr/Views/RemoveDat.xaml.cs b/RomRepoMgr/Views/RemoveDat.xaml.cs index 95ccac9..d4655d7 100644 --- a/RomRepoMgr/Views/RemoveDat.xaml.cs +++ b/RomRepoMgr/Views/RemoveDat.xaml.cs @@ -28,18 +28,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RomRepoMgr.ViewModels; -namespace RomRepoMgr.Views +namespace RomRepoMgr.Views; + +public sealed class RemoveDat : Window { - public sealed class RemoveDat : Window + public RemoveDat() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + protected override void OnOpened(EventArgs e) { - public RemoveDat() => InitializeComponent(); - - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as RemoveDatViewModel)?.OnOpened(); - } + base.OnOpened(e); + (DataContext as RemoveDatViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/RomRepoMgr/Views/SettingsDialog.xaml b/RomRepoMgr/Views/SettingsDialog.xaml index 614a782..c9d8320 100644 --- a/RomRepoMgr/Views/SettingsDialog.xaml +++ b/RomRepoMgr/Views/SettingsDialog.xaml @@ -24,12 +24,20 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + Width="480" + Height="320" + x:Class="RomRepoMgr.Views.SettingsDialog" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}"> @@ -43,12 +51,23 @@ - - - @@ -56,12 +75,23 @@ - - - @@ -69,12 +99,23 @@ - - - @@ -82,22 +123,42 @@ - - - - - - - diff --git a/RomRepoMgr/Views/SettingsDialog.xaml.cs b/RomRepoMgr/Views/SettingsDialog.xaml.cs index b3557a6..b9039e9 100644 --- a/RomRepoMgr/Views/SettingsDialog.xaml.cs +++ b/RomRepoMgr/Views/SettingsDialog.xaml.cs @@ -26,12 +26,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace RomRepoMgr.Views -{ - public sealed class SettingsDialog : Window - { - public SettingsDialog() => InitializeComponent(); +namespace RomRepoMgr.Views; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class SettingsDialog : Window +{ + public SettingsDialog() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/RomRepoMgr/Views/SplashWindow.xaml b/RomRepoMgr/Views/SplashWindow.xaml index 1ff9df1..a6f1fec 100644 --- a/RomRepoMgr/Views/SplashWindow.xaml +++ b/RomRepoMgr/Views/SplashWindow.xaml @@ -1,108 +1,164 @@ - - - - - + + + + - + - + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - + - + - diff --git a/RomRepoMgr/Views/SplashWindow.xaml.cs b/RomRepoMgr/Views/SplashWindow.xaml.cs index 5f61962..0025c58 100644 --- a/RomRepoMgr/Views/SplashWindow.xaml.cs +++ b/RomRepoMgr/Views/SplashWindow.xaml.cs @@ -28,18 +28,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RomRepoMgr.ViewModels; -namespace RomRepoMgr.Views +namespace RomRepoMgr.Views; + +public sealed class SplashWindow : Window { - public sealed class SplashWindow : Window + public SplashWindow() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + protected override void OnOpened(EventArgs e) { - public SplashWindow() => InitializeComponent(); - - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as SplashWindowViewModel)?.OnOpened(); - } + base.OnOpened(e); + (DataContext as SplashWindowViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/RomRepoMgr/Views/UpdateStats.xaml b/RomRepoMgr/Views/UpdateStats.xaml index 43d385b..f45fd35 100644 --- a/RomRepoMgr/Views/UpdateStats.xaml +++ b/RomRepoMgr/Views/UpdateStats.xaml @@ -24,12 +24,19 @@ // Copyright © 2020-2024 Natalia Portillo // ****************************************************************************/ --> - + xmlns:vm="clr-namespace:RomRepoMgr.ViewModels;assembly=RomRepoMgr" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" + x:Class="RomRepoMgr.Views.UpdateStats" + Icon="/Assets/avalonia-logo.ico" + CanResize="False" + Title="{Binding Title}" + WindowStartupLocation="CenterOwner"> @@ -39,88 +46,127 @@ - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - diff --git a/RomRepoMgr/Views/UpdateStats.xaml.cs b/RomRepoMgr/Views/UpdateStats.xaml.cs index 5a6a3d7..f87379a 100644 --- a/RomRepoMgr/Views/UpdateStats.xaml.cs +++ b/RomRepoMgr/Views/UpdateStats.xaml.cs @@ -28,18 +28,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RomRepoMgr.ViewModels; -namespace RomRepoMgr.Views +namespace RomRepoMgr.Views; + +public sealed class UpdateStats : Window { - public sealed class UpdateStats : Window + public UpdateStats() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + protected override void OnOpened(EventArgs e) { - public UpdateStats() => InitializeComponent(); - - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as UpdateStatsViewModel)?.OnOpened(); - } + base.OnOpened(e); + (DataContext as UpdateStatsViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/RomRepoMgr/nuget.config b/RomRepoMgr/nuget.config index 7c07e22..f1deb81 100644 --- a/RomRepoMgr/nuget.config +++ b/RomRepoMgr/nuget.config @@ -1,6 +1,6 @@ - + \ No newline at end of file