mirror of
https://github.com/claunia/romrepomgr.git
synced 2025-12-16 19:24:51 +00:00
Compare commits
69 Commits
ab9abd56f5
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
475da761a5
|
|||
|
5d1ef578a1
|
|||
|
0903d2b5cc
|
|||
|
6f54ed2e80
|
|||
|
244bf4c7a1
|
|||
|
4eef291174
|
|||
|
627c59bcd0
|
|||
|
0c5760e745
|
|||
|
34c8edbd91
|
|||
|
bdcb5e72f7
|
|||
|
8c5e0ef99e
|
|||
|
ab804239bb
|
|||
|
b62f495824
|
|||
|
7e7660bf6c
|
|||
|
cb4d1706f8
|
|||
|
2928ac3e1f
|
|||
|
8c116535c5
|
|||
|
a551e2474d
|
|||
|
396ee08030
|
|||
|
058b8e4b92
|
|||
|
a290996037
|
|||
|
e1ced26bc5
|
|||
|
9c91d76561
|
|||
|
cf7186adbb
|
|||
|
122e397d0a
|
|||
|
a2fc47cc5b
|
|||
|
80f1c0e28e
|
|||
|
f4b87f68ec
|
|||
|
dc4646512a
|
|||
|
00d005ba98
|
|||
|
9877f3886e
|
|||
|
5bb39929b4
|
|||
|
72b2eed55c
|
|||
|
cca4af8d6d
|
|||
|
8df9bb7211
|
|||
|
52946eca2e
|
|||
|
de9ad80eef
|
|||
|
a7985955b4
|
|||
|
054b9ffd0d
|
|||
|
a8921f640d
|
|||
|
3e731115f2
|
|||
|
dc630f3e78
|
|||
|
5937e0d83e
|
|||
|
8f789484ca
|
|||
|
0bda03afee
|
|||
|
bf19439e49
|
|||
|
1435aa10ee
|
|||
|
55a6af0c74
|
|||
|
69fb3c768b
|
|||
|
b0e0ba4502
|
|||
|
c5da48fa47
|
|||
|
03c69d3671
|
|||
|
2d54cbdf23
|
|||
|
134f8c7183
|
|||
|
368e243bca
|
|||
|
23efc69abc
|
|||
|
cf0f79338a
|
|||
|
743b49c4f0
|
|||
|
8f9bf7036b
|
|||
|
ed7d61d926
|
|||
|
38cc8da72f
|
|||
|
7e009c7d66
|
|||
|
dcade0fcc0
|
|||
|
d92707d691
|
|||
|
adb597b455
|
|||
|
b9adceed95
|
|||
|
8eaca3556a
|
|||
|
32bbb55e61
|
|||
|
640c40d7f7
|
@@ -311,6 +311,7 @@ resharper_bad_switch_braces_indent_highlighting
|
||||
resharper_bad_symbol_spaces_highlighting = warning
|
||||
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
|
||||
resharper_built_in_type_reference_style_highlighting = hint
|
||||
resharper_can_simplify_dictionary_lookup_with_try_add_highlighting = warning
|
||||
resharper_check_for_reference_equality_instead_1_highlighting = warning
|
||||
resharper_check_for_reference_equality_instead_2_highlighting = warning
|
||||
resharper_check_for_reference_equality_instead_3_highlighting = warning
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
<LangVersion>default</LangVersion>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
@@ -31,10 +32,6 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Philips.CodeAnalysis.MaintainabilityAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -1,50 +1,56 @@
|
||||
<Project ToolsVersion="15.0">
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Aaru.Checksums.Native" Version="6.0.0-alpha.10"/>
|
||||
<PackageVersion Include="AsyncFixer" Version="1.6.0"/>
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.2"/>
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.2"/>
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.2"/>
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.2"/>
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.3.0"/>
|
||||
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.3.2"/>
|
||||
<PackageVersion Include="Avalonia" Version="11.3.2"/>
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
|
||||
<PackageVersion Include="DotNetZip" Version="1.16.0"/>
|
||||
<PackageVersion Include="EFCore.BulkExtensions" Version="9.0.1"/>
|
||||
<PackageVersion Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2"/>
|
||||
<PackageVersion Include="ErrorProne.NET.Structs" Version="0.1.2"/>
|
||||
<PackageVersion Include="InclusivenessAnalyzer" Version="1.3.0"/>
|
||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="2.0.0-preview1-final"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.7"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.7"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.7"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.7"/>
|
||||
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.DataGrid.EntityFrameworkAdapter" Version="4.12.1"/>
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15"/>
|
||||
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0"/>
|
||||
<PackageVersion Include="Mono.Fuse.NETStandard" Version="1.1.0"/>
|
||||
<PackageVersion Include="Philips.CodeAnalysis.MaintainabilityAnalyzers" Version="1.6.3"/>
|
||||
<PackageVersion Include="plist-cil" Version="2.2.0"/>
|
||||
<PackageVersion Include="Roslynator.Analyzers" Version="4.13.1"/>
|
||||
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="4.13.1"/>
|
||||
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.13.1"/>
|
||||
<PackageVersion Include="Roslynator.Analyzers" Version="4.14.0"/>
|
||||
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.0"/>
|
||||
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.14.0"/>
|
||||
<PackageVersion Include="SabreTools.Models" Version="1.5.8"/>
|
||||
<PackageVersion Include="Serilog" Version="4.3.0"/>
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0"/>
|
||||
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.2"/>
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0"/>
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0"/>
|
||||
<PackageVersion Include="SharpCompress" Version="0.39.0"/>
|
||||
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31"/>
|
||||
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0"/>
|
||||
<PackageVersion Include="Text.Analyzers" Version="4.14.0"/>
|
||||
<PackageVersion Include="winfsp.net" Version="2.1.25156"/>
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.116.1" Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'" />
|
||||
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||
<PackageVersion Include="SabreTools.Hashing" Version="1.2.3"/>
|
||||
<PackageVersion Include="SabreTools.IO" Version="1.4.13"/>
|
||||
<PackageVersion Include="SabreTools.Matching" Version="1.3.4"/>
|
||||
<PackageVersion Include="SabreTools.Skippers" Version="1.1.3"/>
|
||||
|
||||
<PackageVersion Include="System.IO.Compression" Version="4.3.0"/>
|
||||
<PackageVersion Include="SharpCompress" Version="0.38.0"/>
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0"/>
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.1"/>
|
||||
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.6"/>
|
||||
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.12.1"/>
|
||||
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.12.1"/>
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.116.1" Condition="$(RuntimeIdentifier.StartsWith('linux'))" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
88
README.md
Normal file
88
README.md
Normal file
@@ -0,0 +1,88 @@
|
||||
<div style="text-align: center;">
|
||||
<img src="logo.png" alt="logo"/>
|
||||
</div>
|
||||
|
||||
# ROM Repository Manager
|
||||
|
||||
ROM Repository Manager is a versatile tool designed to manage your cold storage of ROM sets effortlessly. Whether you're a retro gaming enthusiast or a dedicated collector, ROM Repository Manager streamlines the process of organizing, compressing, and deduplicating your ROM sets.
|
||||
|
||||
## Features
|
||||
|
||||
* **Organize ROMs**: Automatically sort and categorize ROM files based on the metadata found in widely available DAT files.
|
||||
* **Deduplicate**: Optimize your storage by ensuring each ROM is stored only once, even if it appears in multiple sets or duplicates within the same set.
|
||||
* **Compress:** Reduce the size of your ROMs with advanced compression options. Choose LZIP for maximum space savings or ZSTD for faster performance, both surpassing the typical ZIP files used in ROM sets.
|
||||
* **Cross-Platform**: Compatible with Windows, macOS, and Linux.
|
||||
* **User-Friendly Interface**: Intuitive and easy-to-use interface for seamless management. Choose between the desktop application for direct access or deploy it on a server to use through your browser (e.g., in a NAS). The choice is yours!
|
||||
* **Virtual filesystem**: A standout feature of our desktop application is its ability to provide direct access to the ROM set from our repository. This is achieved through a virtual filesystem, allowing you to view and interact with the ROM set in its original folder structure without the need for extraction.
|
||||
|
||||
## Applications
|
||||
|
||||
ROM Repository Manager contains two different applications:
|
||||
|
||||
* **Desktop Application**: Built using .NET, this application provides a robust and feature-rich interface for managing ROM repositories on your computer.
|
||||
* **Blazor Application**: A web-based application built with .NET Blazor, offering seamless access to ROM management features through your browser.
|
||||
|
||||
Both applications are available as pre-compiled binaries in the releases section. Users do not need to compile anything themselves.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Launch the application.
|
||||
2. Import the DAT files to get started.
|
||||
3. Import your ROM sets.
|
||||
4. Let ROM Repository Manager store your ROMs deduplicated and compressed.
|
||||
|
||||
## How does it compare?
|
||||
|
||||
When comparing with the ROM set from MAME 0.278, which occupies 78.2Gb compressed, 169Gb uncompressed:
|
||||
|
||||
* Using LZIP compression, ROM Repository Manager reduces the size to **68.5Gb**.
|
||||
* Using ZSTD compression, the size is reduced to **71.4Gb**.
|
||||
|
||||
### Let the filesystem do it
|
||||
|
||||
A filesystem can improve our deduplication, adding block-level optimization on top of our file-level strategy, and achieving slightly better results. For example:
|
||||
|
||||
* **ZFS**: Reduces the repository to **71.3Gb** with ZSTD compression.
|
||||
* **btrfs**: Reduces the repository to **85.6Gb**.
|
||||
|
||||
While these options deliver marginal gains in space and speed, they come at the cost of significantly higher RAM usage. Consider them only if your system has the resources to support it.
|
||||
|
||||
## Blazor Application Configuration
|
||||
|
||||
To configure the Blazor application, you need to modify the `appsettings.json` file. Below are the valid fields you can change, along with their purposes:
|
||||
|
||||
- **LogLevel**: Adjusts the level of detail written to the logs. Valid values are`Information` and `Debug`
|
||||
|
||||
- **LogFile**: Specifies the location where the log is written.
|
||||
|
||||
- **Repository**: Defines the location where the repository resides.
|
||||
|
||||
- **ImportRoms**: Sets the location where the application will look for ROMs to import.
|
||||
|
||||
- **ImportDats**: Sets the location where the application will look for DATs to import.
|
||||
|
||||
- **ExportRoms**: Specifies the location where the application will export ROMs.
|
||||
|
||||
- **ExportDats**: Specifies the location where the application will export DATs.
|
||||
|
||||
- **Database**: Defines the location where the repository database will reside.
|
||||
|
||||
- **Temporary**: Specifies the location where the application will store temporary files.
|
||||
|
||||
- **CompressionType**: Determines the repository compression algorithm used. This setting only applies to newly added ROMs. Valid values are `Lzip`, `Zstd` and `None`
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! If you have ideas for new features or improvements, feel free to open an issue or submit a pull request.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the GPL version 3. See the LICENSE file for details.
|
||||
|
||||
---
|
||||
|
||||
For more information, visit the [GitHub repository](https://github.com/claunia/RomRepoMgr).
|
||||
|
||||
© 2020-2025 [Nat Portillo](https://www.natportillo.es)
|
||||
|
||||
Happy ROM managing!
|
||||
20
RomRepoMgr.Blazor/Components/App.razor
Normal file
20
RomRepoMgr.Blazor/Components/App.razor
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<base href="/"/>
|
||||
<link href="@Assets["app.css"]" rel="stylesheet"/>
|
||||
<link href="@Assets["RomRepoMgr.Blazor.styles.css"]" rel="stylesheet"/>
|
||||
<ImportMap/>
|
||||
<link href="favicon.ico" rel="icon" type="image/x-icon"/>
|
||||
<HeadOutlet @rendermode="InteractiveServer"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes @rendermode="InteractiveServer"/>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
29
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor
Normal file
29
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor
Normal file
@@ -0,0 +1,29 @@
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using RomRepoMgr.Blazor.Resources
|
||||
@implements IDialogContentComponent
|
||||
@inject ILogger<ImportDats> Logger
|
||||
@inject IStringLocalizer<Localization> Localizer
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
<FluentDialog Width="800px" Height="400px" Title="Import DATs" Modal="true" TrapFocus="true">
|
||||
<FluentDialogBody>
|
||||
<div>
|
||||
<p hidden="@IsBusy">@string.Format(Localizer["DATs_will_be_imported_from"], path)</p>
|
||||
<FluentLabel Color="@StatusColor">@StatusMessage</FluentLabel>
|
||||
<FluentProgress Max="@ProgressMax" Min="@ProgressMin" Value="@ProgressValue" Visible="@ProgressVisible"/>
|
||||
</div>
|
||||
@if(Progress2Visible)
|
||||
{
|
||||
<div>
|
||||
<p>@StatusMessage2</p>
|
||||
<FluentProgress Max="@Progress2Max" Min="@Progress2Min" Value="@Progress2Value"/>
|
||||
</div>
|
||||
}
|
||||
</FluentDialogBody>
|
||||
<FluentDialogFooter>
|
||||
<FluentStack Orientation="Orientation.Horizontal">
|
||||
<FluentButton OnClick="@Start" Disabled="@IsBusy">@Localizer["Start"]</FluentButton>
|
||||
<FluentButton OnClick="@CloseAsync" Disabled="@CannotClose">@Localizer["Close"]</FluentButton>
|
||||
</FluentStack>
|
||||
</FluentDialogFooter>
|
||||
</FluentDialog>
|
||||
181
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor.cs
Normal file
181
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs;
|
||||
|
||||
namespace RomRepoMgr.Blazor.Components.Dialogs;
|
||||
|
||||
public partial class ImportDats : ComponentBase
|
||||
{
|
||||
readonly Stopwatch _stopwatch = new();
|
||||
string[] _datFiles;
|
||||
int _listPosition;
|
||||
int _workers;
|
||||
string path;
|
||||
public string StatusMessage { get; set; }
|
||||
public bool IsBusy { get; set; }
|
||||
[CascadingParameter]
|
||||
public FluentDialog Dialog { get; set; }
|
||||
public int? ProgressMax { get; set; }
|
||||
public int? ProgressMin { get; set; }
|
||||
public int? ProgressValue { get; set; }
|
||||
public bool CannotClose { get; set; }
|
||||
public bool ProgressVisible { get; set; }
|
||||
public string StatusMessage2 { get; set; }
|
||||
public bool Progress2Visible { get; set; }
|
||||
public int? Progress2Max { get; set; }
|
||||
public int? Progress2Min { get; set; }
|
||||
public int? Progress2Value { get; set; }
|
||||
public Color? StatusColor { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
path = Configuration["DataFolders:ImportDats"] ?? "incoming-dats";
|
||||
StatusMessage = "";
|
||||
StatusMessage2 = "";
|
||||
IsBusy = false;
|
||||
CannotClose = false;
|
||||
ProgressVisible = false;
|
||||
Progress2Visible = false;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
IsBusy = true;
|
||||
CannotClose = true;
|
||||
ProgressVisible = true;
|
||||
ProgressValue = null;
|
||||
StatusMessage = Localizer["SearchingForFiles"];
|
||||
|
||||
_stopwatch.Restart();
|
||||
string[] dats = Directory.GetFiles(path, "*.dat", SearchOption.AllDirectories);
|
||||
|
||||
string[] xmls = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);
|
||||
|
||||
_datFiles = dats.Concat(xmls).Order().ToArray();
|
||||
_stopwatch.Stop();
|
||||
|
||||
Logger.LogDebug("Took {TotalSeconds} to find {Length} DAT files",
|
||||
_stopwatch.Elapsed.TotalSeconds,
|
||||
_datFiles.Length);
|
||||
|
||||
StatusMessage = string.Format(Localizer["FoundFiles"], _datFiles.Length);
|
||||
|
||||
ProgressMin = 0;
|
||||
ProgressMax = _datFiles.Length;
|
||||
ProgressValue = 0;
|
||||
Progress2Visible = true;
|
||||
_listPosition = 0;
|
||||
_workers = 0;
|
||||
StateHasChanged();
|
||||
|
||||
_stopwatch.Restart();
|
||||
Logger.LogDebug("Starting to import DAT files...");
|
||||
Import();
|
||||
}
|
||||
|
||||
void Import()
|
||||
{
|
||||
if(_listPosition >= _datFiles.Length)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
ProgressVisible = false;
|
||||
Progress2Visible = false;
|
||||
StatusMessage = Localizer["Finished"];
|
||||
CannotClose = false;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
_stopwatch.Stop();
|
||||
|
||||
Logger.LogDebug("Took {TotalSeconds} seconds to import {Length} DAT files",
|
||||
_stopwatch.Elapsed.TotalSeconds,
|
||||
_datFiles.Length);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
StatusMessage = string.Format(Localizer["ImportingItem"], Path.GetFileName(_datFiles[_listPosition]));
|
||||
ProgressValue = _listPosition;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
var worker = new DatImporter(_datFiles[_listPosition], null, new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
worker.ErrorOccurred += OnWorkerOnErrorOccurred;
|
||||
worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress;
|
||||
worker.SetMessage += OnWorkerOnSetMessage;
|
||||
worker.SetProgress += OnWorkerOnSetProgress;
|
||||
worker.SetProgressBounds += OnWorkerOnSetProgressBounds;
|
||||
worker.WorkFinished += OnWorkerOnWorkFinished;
|
||||
_ = Task.Run(worker.Import);
|
||||
}
|
||||
|
||||
void OnWorkerOnWorkFinished(object? sender, MessageEventArgs args)
|
||||
{
|
||||
_listPosition++;
|
||||
Import();
|
||||
}
|
||||
|
||||
void OnWorkerOnSetProgressBounds(object? sender, ProgressBoundsEventArgs args)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Value = 0;
|
||||
Progress2Max = (int?)args.Maximum;
|
||||
Progress2Min = (int?)args.Minimum;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void OnWorkerOnSetProgress(object? sender, ProgressEventArgs args)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Value = (int?)args.Value;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void OnWorkerOnSetMessage(object? sender, MessageEventArgs args)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
StatusMessage2 = args.Message;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void OnWorkerOnSetIndeterminateProgress(object? sender, EventArgs args)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Value = null;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void OnWorkerOnErrorOccurred(object? sender, ErrorEventArgs args)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
_listPosition++;
|
||||
Import();
|
||||
});
|
||||
}
|
||||
|
||||
Task CloseAsync() => Dialog.CloseAsync();
|
||||
}
|
||||
43
RomRepoMgr.Blazor/Components/Dialogs/ImportRoms.razor
Normal file
43
RomRepoMgr.Blazor/Components/Dialogs/ImportRoms.razor
Normal file
@@ -0,0 +1,43 @@
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using RomRepoMgr.Blazor.Resources
|
||||
@using RomRepoMgr.Database
|
||||
@implements IDialogContentComponent
|
||||
@inject ILogger<ImportRoms> Logger
|
||||
@inject Context _ctx
|
||||
@inject IStringLocalizer<Localization> Localizer
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
<FluentDialog Width="1600px" Height="800px" Title="Import DATs" Modal="true" TrapFocus="true">
|
||||
<FluentDialogBody>
|
||||
<p hidden="@IsBusy">@string.Format(Localizer["ROMs_will_be_imported_from"], FolderPath)</p>
|
||||
@if(NotYetStarted)
|
||||
{
|
||||
<FluentCheckbox @bind-Value="@RemoveFilesChecked" Label="@Localizer["RemoveFilesLabel"]"/>
|
||||
|
||||
<FluentCheckbox @bind-Value="@KnownOnlyChecked" Label="@Localizer["KnownOnlyLabel"]"/>
|
||||
|
||||
<FluentCheckbox @bind-Value="@RecurseArchivesChecked"
|
||||
Label=@Localizer["RecurseArchivesLabel"]/>
|
||||
}
|
||||
@if(Importing)
|
||||
{
|
||||
<div>
|
||||
<FluentLabel Color="@StatusMessageColor">@StatusMessage</FluentLabel>
|
||||
<FluentProgress Max="@ProgressMax" Min="@ProgressMin" Value="@ProgressValue"/>
|
||||
</div>
|
||||
}
|
||||
@if(Progress2Visible)
|
||||
{
|
||||
<div>
|
||||
<FluentLabel>@StatusMessage2</FluentLabel>
|
||||
<FluentProgress Max="@Progress2Max" Min="@Progress2Min" Value="@Progress2Value"/>
|
||||
</div>
|
||||
}
|
||||
</FluentDialogBody>
|
||||
<FluentDialogFooter>
|
||||
<FluentStack Orientation="Orientation.Horizontal">
|
||||
<FluentButton OnClick="@Start" Disabled="@IsBusy">@Localizer["Start"]</FluentButton>
|
||||
<FluentButton OnClick="@CloseAsync" Disabled="@CannotClose">@Localizer["Close"]</FluentButton>
|
||||
</FluentStack>
|
||||
</FluentDialogFooter>
|
||||
</FluentDialog>
|
||||
355
RomRepoMgr.Blazor/Components/Dialogs/ImportRoms.razor.cs
Normal file
355
RomRepoMgr.Blazor/Components/Dialogs/ImportRoms.razor.cs
Normal file
@@ -0,0 +1,355 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using Serilog;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace RomRepoMgr.Blazor.Components.Dialogs;
|
||||
|
||||
public partial class ImportRoms : ComponentBase
|
||||
{
|
||||
readonly Stopwatch _mainStopwatch = new();
|
||||
readonly ConcurrentBag<DbDisk> _newDisks = [];
|
||||
readonly ConcurrentBag<DbFile> _newFiles = [];
|
||||
readonly ConcurrentBag<DbMedia> _newMedias = [];
|
||||
readonly Stopwatch _stopwatch = new();
|
||||
int _listPosition;
|
||||
FileImporter _rootImporter;
|
||||
PaginationState? pagination;
|
||||
[CascadingParameter]
|
||||
public FluentDialog Dialog { get; set; }
|
||||
public bool IsBusy { get; set; }
|
||||
public bool NotYetStarted { get; set; }
|
||||
public bool RemoveFilesChecked { get; set; }
|
||||
public bool KnownOnlyChecked { get; set; }
|
||||
public bool RecurseArchivesChecked { get; set; }
|
||||
public Color? StatusMessageColor { get; set; }
|
||||
public string? StatusMessage { get; set; }
|
||||
public int? ProgressMax { get; set; }
|
||||
public int? ProgressMin { get; set; }
|
||||
public int? ProgressValue { get; set; }
|
||||
public string? StatusMessage2 { get; set; }
|
||||
public int? Progress2Max { get; set; }
|
||||
public int? Progress2Min { get; set; }
|
||||
public int? Progress2Value { get; set; }
|
||||
public string? FolderPath { get; set; }
|
||||
public bool Importing { get; set; }
|
||||
public bool Progress2Visible { get; set; }
|
||||
public bool DataGridVisible { get; set; }
|
||||
public bool CannotClose { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
FolderPath = Configuration["DataFolders:ImportRoms"] ?? "incoming";
|
||||
IsBusy = false;
|
||||
NotYetStarted = true;
|
||||
RemoveFilesChecked = false;
|
||||
KnownOnlyChecked = true;
|
||||
RecurseArchivesChecked = true;
|
||||
StatusMessage = "";
|
||||
Importing = false;
|
||||
Progress2Visible = false;
|
||||
DataGridVisible = false;
|
||||
|
||||
Logger.LogDebug("ImportRoms dialog initialized with Path: {Path}", FolderPath);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
_rootImporter = new FileImporter(_ctx, _newFiles, _newDisks, _newMedias, KnownOnlyChecked, RemoveFilesChecked);
|
||||
_rootImporter.SetMessage += SetMessage;
|
||||
_rootImporter.SetIndeterminateProgress += SetIndeterminateProgress;
|
||||
_rootImporter.SetProgress += SetProgress;
|
||||
_rootImporter.SetProgressBounds += SetProgressBounds;
|
||||
_rootImporter.Finished += EnumeratingFilesFinished;
|
||||
ProgressValue = null;
|
||||
Importing = true;
|
||||
CannotClose = true;
|
||||
IsBusy = true;
|
||||
NotYetStarted = false;
|
||||
_mainStopwatch.Start();
|
||||
|
||||
_ = Task.Run(() => _rootImporter.FindFiles(FolderPath));
|
||||
}
|
||||
|
||||
void SetProgressBounds(object? sender, ProgressBoundsEventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
ProgressValue = 0;
|
||||
ProgressMax = (int?)e.Maximum;
|
||||
ProgressMin = (int?)e.Minimum;
|
||||
});
|
||||
}
|
||||
|
||||
void SetProgress(object? sender, ProgressEventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
ProgressValue = (int?)e.Value;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void SetIndeterminateProgress(object? sender, EventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
ProgressValue = null;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void SetMessage(object? sender, MessageEventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
StatusMessage = e.Message;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
Task CloseAsync() => Dialog.CloseAsync();
|
||||
|
||||
void EnumeratingFilesFinished(object? sender, EventArgs e)
|
||||
{
|
||||
_rootImporter.Finished -= EnumeratingFilesFinished;
|
||||
|
||||
if(RecurseArchivesChecked)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Visible = true;
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
_rootImporter.SetMessage2 += SetMessage2;
|
||||
_rootImporter.SetIndeterminateProgress2 += SetIndeterminateProgress2;
|
||||
_rootImporter.SetProgress2 += SetProgress2;
|
||||
_rootImporter.SetProgressBounds2 += SetProgress2Bounds;
|
||||
|
||||
_rootImporter.Finished += CheckArchivesFinished;
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
_rootImporter.SeparateFilesAndArchivesManaged();
|
||||
});
|
||||
}
|
||||
else
|
||||
ProcessFiles();
|
||||
}
|
||||
|
||||
void SetProgress2Bounds(object? sender, ProgressBoundsEventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Value = 0;
|
||||
Progress2Max = (int?)e.Maximum;
|
||||
Progress2Min = (int?)e.Minimum;
|
||||
});
|
||||
}
|
||||
|
||||
void SetProgress2(object? sender, ProgressEventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Value = (int?)e.Value;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void SetIndeterminateProgress2(object? sender, EventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Value = null;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void SetMessage2(object? sender, MessageEventArgs e)
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
StatusMessage2 = e.Message;
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void ProcessFiles()
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
ProgressMin = 0;
|
||||
ProgressMax = _rootImporter.Files.Count;
|
||||
ProgressValue = 0;
|
||||
NotYetStarted = false;
|
||||
CannotClose = true;
|
||||
IsBusy = true;
|
||||
Importing = true;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
_listPosition = 0;
|
||||
_stopwatch.Restart();
|
||||
|
||||
Parallel.ForEach(_rootImporter.Files,
|
||||
file =>
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
StatusMessage = string.Format(Localizer["ImportingItem"], Path.GetFileName(file));
|
||||
ProgressValue = _listPosition;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
var worker = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
worker.ImportFile(file);
|
||||
|
||||
Interlocked.Increment(ref _listPosition);
|
||||
});
|
||||
|
||||
_stopwatch.Stop();
|
||||
Log.Debug("Took {TotalSeconds} seconds to process files", _stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
_rootImporter.SaveChanges();
|
||||
|
||||
_rootImporter.UpdateRomStats();
|
||||
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
ProgressMin = 0;
|
||||
ProgressMax = 1;
|
||||
ProgressValue = 0;
|
||||
CannotClose = false;
|
||||
IsBusy = false;
|
||||
Importing = false;
|
||||
StatusMessage = Localizer["Finished"];
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
_listPosition = 0;
|
||||
_mainStopwatch.Stop();
|
||||
|
||||
Log.Debug("Took {TotalSeconds} seconds to import ROMs", _mainStopwatch.Elapsed.TotalSeconds);
|
||||
}
|
||||
|
||||
void CheckArchivesFinished(object? sender, EventArgs e)
|
||||
{
|
||||
_stopwatch.Stop();
|
||||
Log.Debug("Took {TotalSeconds} seconds to check archives", _stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Visible = false;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
_rootImporter.Finished -= CheckArchivesFinished;
|
||||
|
||||
ProcessArchivesManaged();
|
||||
}
|
||||
|
||||
void ProcessArchivesManaged()
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
ProgressMax = _rootImporter.Archives.Count;
|
||||
ProgressMin = 0;
|
||||
ProgressValue = 0;
|
||||
Progress2Visible = false;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
_listPosition = 0;
|
||||
_stopwatch.Restart();
|
||||
|
||||
// For each archive
|
||||
Parallel.ForEach(_rootImporter.Archives,
|
||||
archive =>
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
StatusMessage =
|
||||
string.Format(Localizer["ProcessingArchive"], Path.GetFileName(archive));
|
||||
|
||||
ProgressValue = _listPosition;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
// Create FileImporter
|
||||
var archiveImporter = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
// Open archive
|
||||
try
|
||||
{
|
||||
using var fs = new FileStream(archive, FileMode.Open, FileAccess.Read);
|
||||
using IReader reader = ReaderFactory.Open(fs);
|
||||
|
||||
// Process files in archive
|
||||
while(reader.MoveToNextEntry())
|
||||
{
|
||||
if(reader.Entry.IsDirectory) continue;
|
||||
|
||||
if(reader.Entry.Crc == 0 && KnownOnlyChecked) continue;
|
||||
|
||||
if(!archiveImporter.IsCrcInDb(reader.Entry.Crc) && KnownOnlyChecked) continue;
|
||||
|
||||
var worker = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
worker.ImportAndHashRom(reader.OpenEntryStream(),
|
||||
reader.Entry.Key,
|
||||
Path.Combine(Settings.Settings.Current.RepositoryPath,
|
||||
Path.GetFileName(Path.GetTempFileName())),
|
||||
reader.Entry.Size);
|
||||
}
|
||||
}
|
||||
catch(InvalidOperationException) {}
|
||||
finally
|
||||
{
|
||||
Interlocked.Increment(ref _listPosition);
|
||||
}
|
||||
});
|
||||
|
||||
_stopwatch.Stop();
|
||||
Log.Debug("Took {TotalSeconds} seconds to process archives", _stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
Progress2Visible = false;
|
||||
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
ProcessFiles();
|
||||
}
|
||||
}
|
||||
24
RomRepoMgr.Blazor/Components/Layout/MainLayout.razor
Normal file
24
RomRepoMgr.Blazor/Components/Layout/MainLayout.razor
Normal file
@@ -0,0 +1,24 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<FluentLayout>
|
||||
<FluentHeader>
|
||||
ROM Repository Manager
|
||||
</FluentHeader>
|
||||
<FluentStack Class="main" Orientation="Orientation.Horizontal" Width="100%">
|
||||
<FluentBodyContent Class="body-content">
|
||||
<div class="content">
|
||||
@Body
|
||||
</div>
|
||||
</FluentBodyContent>
|
||||
</FluentStack>
|
||||
<FluentFooter>
|
||||
<a href="https://www.natportillo.es" target="_blank">Copyright © 2020-2025 Natalia Portillo</a>
|
||||
</FluentFooter>
|
||||
<FluentDialogProvider/>
|
||||
</FluentLayout>
|
||||
|
||||
<div data-nosnippet id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a class="reload" href=".">Reload</a>
|
||||
<span class="dismiss">🗙</span>
|
||||
</div>
|
||||
37
RomRepoMgr.Blazor/Components/Pages/Error.razor
Normal file
37
RomRepoMgr.Blazor/Components/Pages/Error.razor
Normal file
@@ -0,0 +1,37 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if(ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that
|
||||
occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong>
|
||||
environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() => RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
40
RomRepoMgr.Blazor/Components/Pages/Home.razor
Normal file
40
RomRepoMgr.Blazor/Components/Pages/Home.razor
Normal file
@@ -0,0 +1,40 @@
|
||||
@page "/"
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using RomRepoMgr.Blazor.Resources
|
||||
@using RomRepoMgr.Database
|
||||
@rendermode InteractiveServer
|
||||
@inject IDialogService DialogService
|
||||
@inject Context ctx
|
||||
@inject IStringLocalizer<Localization> Localizer
|
||||
|
||||
|
||||
<PageTitle>ROM Repository Manager</PageTitle>
|
||||
|
||||
<FluentToolbar>
|
||||
<FluentButton OnClick="@ImportDatsAsync">@Localizer["ImportDats"]</FluentButton>
|
||||
<FluentButton Disabled="true">@Localizer["ExportDat"]</FluentButton>
|
||||
<FluentButton Disabled="true">@Localizer["RemoveDat"]</FluentButton>
|
||||
<FluentButton OnClick="@ImportRomsAsync">@Localizer["ImportRoms"]</FluentButton>
|
||||
<FluentButton Disabled="true">@Localizer["ExportRoms"]</FluentButton>
|
||||
</FluentToolbar>
|
||||
|
||||
<FluentDataGrid @ref="romSetsGrid" Items="@RomSets" Style="width: 100%;" AutoFit="true" Pagination="@pagination"
|
||||
AutoItemsPerPage="true" ResizableColumns="true">
|
||||
<PropertyColumn Property="@(p => p.Name)" Title="@Localizer["RomSetNameLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.Version)" Title="@Localizer["RomSetVersionLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.Author)" Title="@Localizer["RomSetAuthorLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.Category)" Title="@Localizer["RomSetCategoryLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.Date)" Title="@Localizer["RomSetDateLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.Description)" Title="@Localizer["RomSetDescriptionLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.Comment)" Title="@Localizer["RomSetCommentLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.Homepage)" Title="@Localizer["HomepageLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.TotalMachines)" Title="@Localizer["RomSetTotalMachinesLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.CompleteMachines)" Title="@Localizer["RomSetCompleteMachinesLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.IncompleteMachines)" Title="@Localizer["RomSetIncompleteMachinesLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.TotalRoms)" Title="@Localizer["RomSetTotalRomsLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.HaveRoms)" Title="@Localizer["RomSetHaveRomsLabel"]"/>
|
||||
<PropertyColumn Property="@(p => p.MissRoms)" Title="@Localizer["RomSetMissRomsLabel"]"/>
|
||||
|
||||
</FluentDataGrid>
|
||||
|
||||
<FluentPaginator State="@pagination"/>
|
||||
64
RomRepoMgr.Blazor/Components/Pages/Home.razor.cs
Normal file
64
RomRepoMgr.Blazor/Components/Pages/Home.razor.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using RomRepoMgr.Blazor.Components.Dialogs;
|
||||
using RomRepoMgr.Core.Models;
|
||||
|
||||
namespace RomRepoMgr.Blazor.Components.Pages;
|
||||
|
||||
public partial class Home : ComponentBase
|
||||
{
|
||||
readonly PaginationState pagination = new()
|
||||
{
|
||||
ItemsPerPage = 10
|
||||
};
|
||||
FluentDataGrid<RomSetModel>? romSetsGrid;
|
||||
|
||||
public IQueryable<RomSetModel>? RomSets { get; set; }
|
||||
|
||||
async Task ImportDatsAsync()
|
||||
{
|
||||
IDialogReference dialog = await DialogService.ShowDialogAsync<ImportDats>(new DialogParameters());
|
||||
}
|
||||
|
||||
async Task ImportRomsAsync()
|
||||
{
|
||||
IDialogReference dialog = await DialogService.ShowDialogAsync<ImportRoms>(new DialogParameters());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
romSetsGrid?.SetLoadingState(true);
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
romSetsGrid?.SetLoadingState(false);
|
||||
}
|
||||
}
|
||||
9
RomRepoMgr.Blazor/Components/Routes.razor
Normal file
9
RomRepoMgr.Blazor/Components/Routes.razor
Normal file
@@ -0,0 +1,9 @@
|
||||
@using RomRepoMgr.Blazor.Components.Layout
|
||||
<FluentDesignTheme StorageName="theme" Mode="DesignThemeModes.Dark">
|
||||
<Router AppAssembly="typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView DefaultLayout="typeof(MainLayout)" RouteData="routeData"/>
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
|
||||
</Found>
|
||||
</Router>
|
||||
</FluentDesignTheme>
|
||||
12
RomRepoMgr.Blazor/Components/_Imports.razor
Normal file
12
RomRepoMgr.Blazor/Components/_Imports.razor
Normal file
@@ -0,0 +1,12 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.FluentUI.AspNetCore.Components
|
||||
@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons
|
||||
@using Microsoft.JSInterop
|
||||
@using RomRepoMgr.Blazor
|
||||
@using RomRepoMgr.Blazor.Components
|
||||
172
RomRepoMgr.Blazor/Program.cs
Normal file
172
RomRepoMgr.Blazor/Program.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using RomRepoMgr.Blazor.Components;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Settings;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
// Start the application
|
||||
Log.Logger = new LoggerConfiguration().MinimumLevel.Debug().WriteTo.Console().Enrich.FromLogContext().CreateLogger();
|
||||
|
||||
Log.Information("Welcome to ROM Repository Manager!");
|
||||
Log.Information("Copyright © 2020-2025 Natalia Portillo");
|
||||
|
||||
// Configuration and settings
|
||||
// We need the builder now
|
||||
Log.Debug("Creating the builder...");
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Access the full configuration
|
||||
Log.Debug("Creating the configuration reader...");
|
||||
ConfigurationManager config = builder.Configuration;
|
||||
|
||||
string logFile = config["LogFile"] ?? "logs/rom-repo-mgr.log";
|
||||
string defaultLogLevel = config["Logging:LogLevel:Default"] ?? "Information";
|
||||
|
||||
// Parse to LogEventLevel
|
||||
if(!Enum.TryParse(defaultLogLevel, true, out LogEventLevel level))
|
||||
{
|
||||
// Fallback if parsing fails
|
||||
#if DEBUG
|
||||
level = LogEventLevel.Debug;
|
||||
#else
|
||||
level = LogEventLevel.Information;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Now create a logger with the specified log level and log file
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
#if DEBUG
|
||||
.MinimumLevel.Debug()
|
||||
#else
|
||||
.MinimumLevel.Information()
|
||||
#endif
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File(logFile, rollingInterval: RollingInterval.Day, fileSizeLimitBytes: 10 * 1048576)
|
||||
.Enrich.FromLogContext()
|
||||
.CreateLogger();
|
||||
|
||||
// Read the rest of the configuration and settings
|
||||
Log.Debug("Reading configuration settings...");
|
||||
string repoFolder = config["DataFolders:Repository"] ?? "repo";
|
||||
string importRoms = config["DataFolders:ImportRoms"] ?? "incoming";
|
||||
string importDats = config["DataFolders:ImportDats"] ?? "incoming-dats";
|
||||
string exportRoms = config["DataFolders:ExportRoms"] ?? "export";
|
||||
string exportDats = config["DataFolders:ExportDats"] ?? "export-dats";
|
||||
string databaseFolder = config["DataFolders:Database"] ?? "db";
|
||||
string temporaryFolder = config["DataFolders:Temporary"] ?? "tmp";
|
||||
string compressionTypeString = config["CompressionType"] ?? "Zstd";
|
||||
|
||||
// Parse the compression type
|
||||
if(!Enum.TryParse(compressionTypeString, true, out CompressionType compressionType))
|
||||
{
|
||||
// Fallback if parsing fails
|
||||
compressionType = CompressionType.Zstd;
|
||||
}
|
||||
|
||||
// Ensure the folders exist
|
||||
Log.Information("Ensuring folders exist...");
|
||||
|
||||
string[] folders = [repoFolder, importRoms, importDats, databaseFolder, exportRoms, exportDats, temporaryFolder];
|
||||
|
||||
foreach(string folder in folders)
|
||||
{
|
||||
// Check File.Exists for symlinks or junctions
|
||||
if(!Directory.Exists(folder) && !File.Exists(folder))
|
||||
{
|
||||
Log.Debug("Creating folder: {Folder}", folder);
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
else
|
||||
Log.Debug("Folder already exists: {Folder}", folder);
|
||||
}
|
||||
|
||||
// ✅ Plug Serilog into the host
|
||||
builder.Host.UseSerilog();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
||||
builder.Services.AddFluentUIComponents();
|
||||
|
||||
// Localization
|
||||
builder.Services.AddLocalization();
|
||||
|
||||
Log.Debug("Creating database context...");
|
||||
|
||||
builder.Services.AddDbContextFactory<Context>(options =>
|
||||
{
|
||||
options.UseSqlite($"Data Source={databaseFolder}/database.db");
|
||||
#if DEBUG
|
||||
options.EnableSensitiveDataLogging();
|
||||
options.LogTo(Log.Debug);
|
||||
#else
|
||||
options.LogTo(Log.Information, LogLevel.Information);
|
||||
#endif
|
||||
});
|
||||
|
||||
builder.Services.AddDataGridEntityFrameworkAdapter();
|
||||
|
||||
Log.Debug("Setting the settings...");
|
||||
|
||||
Settings.Current = new SetSettings
|
||||
{
|
||||
DatabasePath = Path.Combine(Environment.CurrentDirectory, databaseFolder, "database.db"),
|
||||
TemporaryFolder = Path.Combine(Environment.CurrentDirectory, temporaryFolder),
|
||||
RepositoryPath = Path.Combine(Environment.CurrentDirectory, repoFolder),
|
||||
UseInternalDecompressor = true,
|
||||
Compression = compressionType
|
||||
};
|
||||
|
||||
Log.Debug("Building the application...");
|
||||
WebApplication app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if(!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error", true);
|
||||
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.MapStaticAssets();
|
||||
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
|
||||
|
||||
// Localization
|
||||
string[] supportedCultures = ["en", "es"];
|
||||
|
||||
RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions().SetDefaultCulture("en")
|
||||
.AddSupportedCultures(supportedCultures)
|
||||
.AddSupportedUICultures(supportedCultures);
|
||||
|
||||
app.UseRequestLocalization(localizationOptions);
|
||||
|
||||
Stopwatch stopwatch = new();
|
||||
|
||||
using(IServiceScope scope = app.Services.CreateScope())
|
||||
{
|
||||
IServiceProvider services = scope.ServiceProvider;
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Updating the database...");
|
||||
stopwatch.Start();
|
||||
Context dbContext = services.GetRequiredService<Context>();
|
||||
await dbContext.Database.MigrateAsync();
|
||||
stopwatch.Stop();
|
||||
Log.Debug("Database migration: {Elapsed} seconds", stopwatch.Elapsed.TotalSeconds);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Error(ex, "An error occurred while updating the database");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.Debug("Running the application...");
|
||||
app.Run();
|
||||
23
RomRepoMgr.Blazor/Properties/launchSettings.json
Normal file
23
RomRepoMgr.Blazor/Properties/launchSettings.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5079",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7057;http://localhost:5079",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
228
RomRepoMgr.Blazor/Resources/Localization.Designer.cs
generated
Normal file
228
RomRepoMgr.Blazor/Resources/Localization.Designer.cs
generated
Normal file
@@ -0,0 +1,228 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace RomRepoMgr.Blazor.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Localization {
|
||||
|
||||
private static System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Localization() {
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.Equals(null, resourceMan)) {
|
||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RomRepoMgr.Blazor.Resources.Localization", typeof(Localization).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ROMs_will_be_imported_from {
|
||||
get {
|
||||
return ResourceManager.GetString("ROMs_will_be_imported_from", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RemoveFilesLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RemoveFilesLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string KnownOnlyLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("KnownOnlyLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RecurseArchivesLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RecurseArchivesLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string Start {
|
||||
get {
|
||||
return ResourceManager.GetString("Start", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string Close {
|
||||
get {
|
||||
return ResourceManager.GetString("Close", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ImportingItem {
|
||||
get {
|
||||
return ResourceManager.GetString("ImportingItem", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ProcessingArchive {
|
||||
get {
|
||||
return ResourceManager.GetString("ProcessingArchive", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string Finished {
|
||||
get {
|
||||
return ResourceManager.GetString("Finished", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string DATs_will_be_imported_from {
|
||||
get {
|
||||
return ResourceManager.GetString("DATs_will_be_imported_from", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string SearchingForFiles {
|
||||
get {
|
||||
return ResourceManager.GetString("SearchingForFiles", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetNameLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetNameLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetVersionLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetVersionLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetAuthorLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetAuthorLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetCategoryLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetCategoryLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetDateLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetDateLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetDescriptionLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetDescriptionLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetCommentLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetCommentLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string HomepageLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("HomepageLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetTotalMachinesLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetTotalMachinesLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetCompleteMachinesLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetCompleteMachinesLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetIncompleteMachinesLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetIncompleteMachinesLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetTotalRomsLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetTotalRomsLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetHaveRomsLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetHaveRomsLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RomSetMissRomsLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("RomSetMissRomsLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ImportDats {
|
||||
get {
|
||||
return ResourceManager.GetString("ImportDats", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ImportRoms {
|
||||
get {
|
||||
return ResourceManager.GetString("ImportRoms", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ExportDat {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportDat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RemoveDat {
|
||||
get {
|
||||
return ResourceManager.GetString("RemoveDat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ExportRoms {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportRoms", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
108
RomRepoMgr.Blazor/Resources/Localization.es.resx
Normal file
108
RomRepoMgr.Blazor/Resources/Localization.es.resx
Normal file
@@ -0,0 +1,108 @@
|
||||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<data name="ROMs_will_be_imported_from" xml:space="preserve">
|
||||
<value>Las ROMs se importarán de {0}.</value>
|
||||
</data>
|
||||
<data name="DATs_will_be_imported_from" xml:space="preserve">
|
||||
<value>Los ficheros DAT se importarán de {0}.</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Cerrar</value>
|
||||
</data>
|
||||
<data name="ExportDat" xml:space="preserve">
|
||||
<value>Exportar DAT</value>
|
||||
</data>
|
||||
<data name="ExportRoms" xml:space="preserve">
|
||||
<value>Exportar ROMs</value>
|
||||
</data>
|
||||
<data name="Finished" xml:space="preserve">
|
||||
<value>Terminado</value>
|
||||
</data>
|
||||
<data name="HomepageLabel" xml:space="preserve">
|
||||
<value>Página web</value>
|
||||
</data>
|
||||
<data name="ImportingItem" xml:space="preserve">
|
||||
<value>Importando {0}...</value>
|
||||
</data>
|
||||
<data name="ImportRoms" xml:space="preserve">
|
||||
<value>Importar ROMs</value>
|
||||
</data>
|
||||
<data name="KnownOnlyLabel" xml:space="preserve">
|
||||
<value>Importar solo archivos conocidos.</value>
|
||||
</data>
|
||||
<data name="ProcessingArchive" xml:space="preserve">
|
||||
<value>Procesando archivo: {0}</value>
|
||||
</data>
|
||||
<data name="RecurseArchivesLabel" xml:space="preserve">
|
||||
<value>Intentar detectar archivos comprimiedos e importar su contenido.</value>
|
||||
</data>
|
||||
<data name="RemoveDat" xml:space="preserve">
|
||||
<value>Eliminar DAT</value>
|
||||
</data>
|
||||
<data name="RemoveFilesLabel" xml:space="preserve">
|
||||
<value>Eliminar archivos después de importar satisfactoriamente.</value>
|
||||
</data>
|
||||
<data name="RomSetNameLabel" xml:space="preserve">
|
||||
<value>Nombre</value>
|
||||
</data>
|
||||
<data name="RomSetMissRomsLabel" xml:space="preserve">
|
||||
<value>Faltantes</value>
|
||||
</data>
|
||||
<data name="RomSetIncompleteMachinesLabel" xml:space="preserve">
|
||||
<value>Incompletos</value>
|
||||
</data>
|
||||
<data name="RomSetHaveRomsLabel" xml:space="preserve">
|
||||
<value>Presentes</value>
|
||||
</data>
|
||||
<data name="RomSetDescriptionLabel" xml:space="preserve">
|
||||
<value>Descripción</value>
|
||||
</data>
|
||||
<data name="RomSetDateLabel" xml:space="preserve">
|
||||
<value>Incompletos</value>
|
||||
</data>
|
||||
<data name="RomSetCompleteMachinesLabel" xml:space="preserve">
|
||||
<value>Completos</value>
|
||||
</data>
|
||||
<data name="RomSetCommentLabel" xml:space="preserve">
|
||||
<value>Comentario</value>
|
||||
</data>
|
||||
<data name="RomSetCategoryLabel" xml:space="preserve">
|
||||
<value>Categoría</value>
|
||||
</data>
|
||||
<data name="RomSetAuthorLabel" xml:space="preserve">
|
||||
<value>Categoría</value>
|
||||
</data>
|
||||
<data name="RomSetTotalMachinesLabel" xml:space="preserve">
|
||||
<value>Juegos</value>
|
||||
</data>
|
||||
<data name="RomSetTotalRomsLabel" xml:space="preserve">
|
||||
<value>ROMs</value>
|
||||
</data>
|
||||
<data name="RomSetVersionLabel" xml:space="preserve">
|
||||
<value>Versión</value>
|
||||
</data>
|
||||
<data name="SearchingForFiles" xml:space="preserve">
|
||||
<value>Buscando archivos...</value>
|
||||
</data>
|
||||
<data name="Start" xml:space="preserve">
|
||||
<value>Comenzar</value>
|
||||
</data>
|
||||
<data name="ImportDats" xml:space="preserve">
|
||||
<value>Importar DATs</value>
|
||||
</data>
|
||||
</root>
|
||||
117
RomRepoMgr.Blazor/Resources/Localization.resx
Normal file
117
RomRepoMgr.Blazor/Resources/Localization.resx
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<root>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
id="root"
|
||||
xmlns="">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<data name="ROMs_will_be_imported_from" xml:space="preserve">
|
||||
<value>ROM files will be imported from {0}.</value>
|
||||
</data>
|
||||
<data name="RemoveFilesLabel" xml:space="preserve">
|
||||
<value>Remove files after import successful.</value>
|
||||
</data>
|
||||
<data name="KnownOnlyLabel" xml:space="preserve">
|
||||
<value>Only import known files.</value>
|
||||
</data>
|
||||
<data name="RecurseArchivesLabel" xml:space="preserve">
|
||||
<value>Try to detect archives and import their contents.</value>
|
||||
</data>
|
||||
<data name="Start" xml:space="preserve">
|
||||
<value>Start</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
<data name="ImportingItem" xml:space="preserve">
|
||||
<value>Importing {0}...</value>
|
||||
</data>
|
||||
<data name="ProcessingArchive" xml:space="preserve">
|
||||
<value>Processing archive: {0}</value>
|
||||
</data>
|
||||
<data name="Finished" xml:space="preserve">
|
||||
<value>Finished</value>
|
||||
</data>
|
||||
<data name="DATs_will_be_imported_from" xml:space="preserve">
|
||||
<value>DAT files will be imported from {0}.</value>
|
||||
</data>
|
||||
<data name="SearchingForFiles" xml:space="preserve">
|
||||
<value>Searching for files...</value>
|
||||
</data>
|
||||
<data name="RomSetNameLabel" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="RomSetVersionLabel" xml:space="preserve">
|
||||
<value>Version</value>
|
||||
</data>
|
||||
<data name="RomSetAuthorLabel" xml:space="preserve">
|
||||
<value>Author</value>
|
||||
</data>
|
||||
<data name="RomSetCategoryLabel" xml:space="preserve">
|
||||
<value>Category</value>
|
||||
</data>
|
||||
<data name="RomSetDateLabel" xml:space="preserve">
|
||||
<value>Date</value>
|
||||
</data>
|
||||
<data name="RomSetDescriptionLabel" xml:space="preserve">
|
||||
<value>Description</value>
|
||||
</data>
|
||||
<data name="RomSetCommentLabel" xml:space="preserve">
|
||||
<value>Comment</value>
|
||||
</data>
|
||||
<data name="HomepageLabel" xml:space="preserve">
|
||||
<value>Homepage</value>
|
||||
</data>
|
||||
<data name="RomSetTotalMachinesLabel" xml:space="preserve">
|
||||
<value>Games</value>
|
||||
</data>
|
||||
<data name="RomSetCompleteMachinesLabel" xml:space="preserve">
|
||||
<value>Complete</value>
|
||||
</data>
|
||||
<data name="RomSetIncompleteMachinesLabel" xml:space="preserve">
|
||||
<value>Incomplete</value>
|
||||
</data>
|
||||
<data name="RomSetTotalRomsLabel" xml:space="preserve">
|
||||
<value>ROMs</value>
|
||||
</data>
|
||||
<data name="RomSetHaveRomsLabel" xml:space="preserve">
|
||||
<value>Have</value>
|
||||
</data>
|
||||
<data name="RomSetMissRomsLabel" xml:space="preserve">
|
||||
<value>Miss</value>
|
||||
</data>
|
||||
<data name="ImportDats" xml:space="preserve">
|
||||
<value>Import DATs</value>
|
||||
</data>
|
||||
<data name="ImportRoms" xml:space="preserve">
|
||||
<value>Import ROMs</value>
|
||||
</data>
|
||||
<data name="ExportDat" xml:space="preserve">
|
||||
<value>Export DAT</value>
|
||||
</data>
|
||||
<data name="RemoveDat" xml:space="preserve">
|
||||
<value>Remove DAT</value>
|
||||
</data>
|
||||
<data name="ExportRoms" xml:space="preserve">
|
||||
<value>Export ROMs</value>
|
||||
</data>
|
||||
</root>
|
||||
54
RomRepoMgr.Blazor/RomRepoMgr.Blazor.csproj
Normal file
54
RomRepoMgr.Blazor/RomRepoMgr.Blazor.csproj
Normal file
@@ -0,0 +1,54 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.0.0-beta.1</Version>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<InformationalVersion>1.0.0-beta.1</InformationalVersion>
|
||||
<Copyright>© $([System.DateTime]::Now.Year) Natalia Portillo</Copyright>
|
||||
<ApplicationIcon>romrepomgr.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Localization"/>
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components"/>
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.DataGrid.EntityFrameworkAdapter"/>
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons"/>
|
||||
<PackageReference Include="Serilog"/>
|
||||
<PackageReference Include="Serilog.AspNetCore"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console"/>
|
||||
<PackageReference Include="Serilog.Sinks.File"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RomRepoMgr.Core\RomRepoMgr.Core.csproj"/>
|
||||
<ProjectReference Include="..\RomRepoMgr.Database\RomRepoMgr.Database.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\Localization.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Localization.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\Localization.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Localization.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
13
RomRepoMgr.Blazor/appsettings.Development.json
Normal file
13
RomRepoMgr.Blazor/appsettings.Development.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft": "Debug",
|
||||
"Microsoft.Extensions.Localization": "Debug"
|
||||
}
|
||||
},
|
||||
"CircuitOptions": {
|
||||
"DetailedErrors": true
|
||||
}
|
||||
}
|
||||
20
RomRepoMgr.Blazor/appsettings.json
Normal file
20
RomRepoMgr.Blazor/appsettings.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"LogFile": "logs/romrepomgr.log",
|
||||
"AllowedHosts": "*",
|
||||
"DataFolders": {
|
||||
"Repository": "repo",
|
||||
"ImportRoms": "incoming",
|
||||
"ImportDats": "incoming-dats",
|
||||
"ExportRoms": "export",
|
||||
"ExportDats": "export-dats",
|
||||
"Database": "db",
|
||||
"Temporary": "tmp"
|
||||
},
|
||||
"CompressionType": "Zstd"
|
||||
}
|
||||
BIN
RomRepoMgr.Blazor/romrepomgr.ico
Normal file
BIN
RomRepoMgr.Blazor/romrepomgr.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 317 KiB |
191
RomRepoMgr.Blazor/wwwroot/app.css
Normal file
191
RomRepoMgr.Blazor/wwwroot/app.css
Normal file
@@ -0,0 +1,191 @@
|
||||
@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';
|
||||
|
||||
body {
|
||||
--body-font: "Segoe UI Variable", "Segoe UI", sans-serif;
|
||||
font-family: var(--body-font);
|
||||
font-size: var(--type-ramp-base-font-size);
|
||||
line-height: var(--type-ramp-base-line-height);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navmenu-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: calc(100dvh - 86px);
|
||||
color: var(--neutral-foreground-rest);
|
||||
align-items: stretch !important;
|
||||
}
|
||||
|
||||
.body-content {
|
||||
align-self: stretch;
|
||||
height: calc(100dvh - 86px) !important;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0.5rem 1.5rem;
|
||||
align-self: stretch !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.manage {
|
||||
width: 100dvw;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--neutral-layer-4);
|
||||
color: var(--neutral-foreground-rest);
|
||||
align-items: center;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--neutral-foreground-rest);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:focus {
|
||||
outline: 1px dashed;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.alert {
|
||||
border: 1px dashed var(--accent-fill-rest);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
.blazor-error-boundary {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||
padding: 1rem 1rem 1rem 3.7rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blazor-error-boundary::before {
|
||||
content: "An error has occurred. "
|
||||
}
|
||||
|
||||
.loading-progress {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
margin: 20vh auto 1rem auto;
|
||||
}
|
||||
|
||||
.loading-progress circle {
|
||||
fill: none;
|
||||
stroke: #e0e0e0;
|
||||
stroke-width: 0.6rem;
|
||||
transform-origin: 50% 50%;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.loading-progress circle:last-child {
|
||||
stroke: #1b6ec2;
|
||||
stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
|
||||
transition: stroke-dasharray 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.loading-progress-text {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
|
||||
}
|
||||
|
||||
.loading-progress-text:after {
|
||||
content: var(--blazor-load-percentage-text, "Loading");
|
||||
}
|
||||
|
||||
code {
|
||||
color: #c02d76;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.header-gutters {
|
||||
margin: 0.5rem 3rem 0.5rem 1.5rem !important;
|
||||
}
|
||||
|
||||
[dir="rtl"] .header-gutters {
|
||||
margin: 0.5rem 1.5rem 0.5rem 3rem !important;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex-direction: column !important;
|
||||
row-gap: 0 !important;
|
||||
}
|
||||
|
||||
nav.sitenav {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main-menu {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#main-menu > div:first-child:is(.expander) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navmenu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#navmenu-toggle {
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
#navmenu-toggle ~ nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#navmenu-toggle:checked ~ nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navmenu-icon {
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: unset;
|
||||
right: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
[dir="rtl"] .navmenu-icon {
|
||||
left: 20px;
|
||||
right: unset;
|
||||
}
|
||||
}
|
||||
BIN
RomRepoMgr.Blazor/wwwroot/favicon.ico
Normal file
BIN
RomRepoMgr.Blazor/wwwroot/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -74,17 +74,17 @@ public static class FAT
|
||||
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];
|
||||
byte[] fat32Id = new byte[8];
|
||||
byte[] msxId = new byte[6];
|
||||
byte[] dosOem = new byte[8];
|
||||
byte[] atariOem = new byte[6];
|
||||
ushort bootable = 0;
|
||||
|
||||
var bpbSector = new byte[512];
|
||||
var fatSector = new byte[512];
|
||||
byte[] bpbSector = new byte[512];
|
||||
byte[] fatSector = new byte[512];
|
||||
imageStream.Position = 0;
|
||||
imageStream.EnsureRead(bpbSector, 0, 512);
|
||||
imageStream.EnsureRead(fatSector, 0, 512);
|
||||
imageStream.ReadExactly(bpbSector, 0, 512);
|
||||
imageStream.ReadExactly(fatSector, 0, 512);
|
||||
|
||||
Array.Copy(bpbSector, 0x02, atariOem, 0, 6);
|
||||
Array.Copy(bpbSector, 0x03, dosOem, 0, 8);
|
||||
@@ -111,13 +111,13 @@ public static class FAT
|
||||
|
||||
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);
|
||||
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 is 1 or 2 or 4 or 8 or 16 or 32 or 64;
|
||||
|
||||
@@ -197,20 +197,20 @@ public static class FAT
|
||||
byte z80Di = bpbSector[0];
|
||||
|
||||
// First FAT1 sector resides at LBA 0x14
|
||||
var fat1Sector0 = new byte[512];
|
||||
byte[] fat1Sector0 = new byte[512];
|
||||
imageStream.Position = 0x14 * 512;
|
||||
imageStream.EnsureRead(fat1Sector0, 0, 512);
|
||||
imageStream.ReadExactly(fat1Sector0, 0, 512);
|
||||
|
||||
// First FAT2 sector resides at LBA 0x1A
|
||||
var fat2Sector0 = new byte[512];
|
||||
byte[] fat2Sector0 = new byte[512];
|
||||
imageStream.Position = 0x1A * 512;
|
||||
imageStream.EnsureRead(fat2Sector0, 0, 512);
|
||||
imageStream.ReadExactly(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];
|
||||
byte[] tmp = new byte[512];
|
||||
|
||||
foreach(long position in new long[]
|
||||
{
|
||||
@@ -218,17 +218,17 @@ public static class FAT
|
||||
})
|
||||
{
|
||||
imageStream.Position = position * 512;
|
||||
imageStream.EnsureRead(tmp, 0, 512);
|
||||
imageStream.ReadExactly(tmp, 0, 512);
|
||||
rootMs.Write(tmp, 0, tmp.Length);
|
||||
}
|
||||
|
||||
byte[] rootDir = rootMs.ToArray();
|
||||
var validRootDir = true;
|
||||
bool validRootDir = true;
|
||||
|
||||
// Iterate all root directory
|
||||
for(var e = 0; e < 96 * 32; e += 32)
|
||||
for(int e = 0; e < 96 * 32; e += 32)
|
||||
{
|
||||
for(var c = 0; c < 11; c++)
|
||||
for(int c = 0; c < 11; c++)
|
||||
{
|
||||
if((rootDir[c + e] >= 0x20 || rootDir[c + e] == 0x00 || rootDir[c + e] == 0x05) &&
|
||||
rootDir[c + e] != 0xFF &&
|
||||
|
||||
@@ -38,17 +38,17 @@ public static class Base32
|
||||
var builder = new StringBuilder(bytes.Length * _inByteSize / _outByteSize);
|
||||
|
||||
// Position in the input buffer
|
||||
var bytesPosition = 0;
|
||||
int bytesPosition = 0;
|
||||
|
||||
// Offset inside a single byte that <bytesPosition> points to (from left to right)
|
||||
// 0 - highest bit, 7 - lowest bit
|
||||
var bytesSubPosition = 0;
|
||||
int 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;
|
||||
int outputBase32BytePosition = 0;
|
||||
|
||||
// Iterate through input buffer until we reach past the end of it
|
||||
while(bytesPosition < bytes.Length)
|
||||
@@ -119,22 +119,22 @@ public static class Base32
|
||||
string base32StringUpperCase = base32String.ToUpperInvariant();
|
||||
|
||||
// Prepare output byte array
|
||||
var outputBytes = new byte[base32StringUpperCase.Length * _outByteSize / _inByteSize];
|
||||
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
|
||||
var base32Position = 0;
|
||||
int base32Position = 0;
|
||||
|
||||
// Offset inside the character in the string
|
||||
var base32SubPosition = 0;
|
||||
int base32SubPosition = 0;
|
||||
|
||||
// Position within outputBytes array
|
||||
var outputBytePosition = 0;
|
||||
int outputBytePosition = 0;
|
||||
|
||||
// The number of bits filled in the current output byte
|
||||
var outputByteSubPosition = 0;
|
||||
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
|
||||
|
||||
37
RomRepoMgr.Core/Checksums/Authors.cs
Normal file
37
RomRepoMgr.Core/Checksums/Authors.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Authors.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Aaru.Checksums.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
static class Authors
|
||||
{
|
||||
internal const string NataliaPortillo = "Natalia Portillo";
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Aaru.Helpers;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <summary>
|
||||
/// Converts base data types to an array of bytes, and an array of bytes to base data types. All info taken from
|
||||
|
||||
132
RomRepoMgr.Core/Checksums/CRC32/arm_simd.cs
Normal file
132
RomRepoMgr.Core/Checksums/CRC32/arm_simd.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : arm_simd.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
// The Chromium Authors
|
||||
//
|
||||
// Component : Checksums.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Compute CRC32 checksum using ARM special instructions..
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
|
||||
namespace RomRepoMgr.Core.Checksums.CRC32;
|
||||
|
||||
static class ArmSimd
|
||||
{
|
||||
internal static uint Step64(byte[] buf, long len, uint crc)
|
||||
{
|
||||
uint c = crc;
|
||||
|
||||
int bufPos = 0;
|
||||
|
||||
while(len >= 64)
|
||||
{
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
while(len >= 8)
|
||||
{
|
||||
c = Crc32.Arm64.ComputeCrc32(c, BitConverter.ToUInt64(buf, bufPos));
|
||||
bufPos += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
while(len-- > 0) c = Crc32.ComputeCrc32(c, buf[bufPos++]);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
internal static uint Step32(byte[] buf, long len, uint crc)
|
||||
{
|
||||
uint c = crc;
|
||||
|
||||
int bufPos = 0;
|
||||
|
||||
while(len >= 32)
|
||||
{
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
len -= 32;
|
||||
}
|
||||
|
||||
while(len >= 4)
|
||||
{
|
||||
c = Crc32.ComputeCrc32(c, BitConverter.ToUInt32(buf, bufPos));
|
||||
bufPos += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
while(len-- > 0) c = Crc32.ComputeCrc32(c, buf[bufPos++]);
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
229
RomRepoMgr.Core/Checksums/CRC32/clmul.cs
Normal file
229
RomRepoMgr.Core/Checksums/CRC32/clmul.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : clmul.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
// Wajdi Feghali <wajdi.k.feghali@intel.com>
|
||||
// Jim Guilford <james.guilford@intel.com>
|
||||
// Vinodh Gopal <vinodh.gopal@intel.com>
|
||||
// Erdinc Ozturk <erdinc.ozturk@intel.com>
|
||||
// Jim Kukunas <james.t.kukunas@linux.intel.com>
|
||||
// Marian Beermann
|
||||
//
|
||||
// Component : Checksums.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Compute the CRC32 using a parallelized folding approach with the PCLMULQDQ
|
||||
// instruction.
|
||||
//
|
||||
// A white paper describing this algorithm can be found at:
|
||||
// http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
// In no event will the authors be held liable for any damages arising from
|
||||
// the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// Copyright (c) 2016 Marian Beermann (add support for initial value, restructuring)
|
||||
// Copyright (C) 2013 Intel Corporation. All rights reserved.
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace RomRepoMgr.Core.Checksums.CRC32;
|
||||
|
||||
static class Clmul
|
||||
{
|
||||
static readonly uint[] _crcK =
|
||||
[
|
||||
0xccaa009e, 0x00000000, /* rk1 */ 0x751997d0, 0x00000001, /* rk2 */ 0xccaa009e, 0x00000000, /* rk5 */
|
||||
0x63cd6124, 0x00000001, /* rk6 */ 0xf7011640, 0x00000001, /* rk7 */ 0xdb710640, 0x00000001 /* rk8 */
|
||||
];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Fold4(ref Vector128<uint> xmmCRC0, ref Vector128<uint> xmmCRC1, ref Vector128<uint> xmmCRC2,
|
||||
ref Vector128<uint> xmmCRC3)
|
||||
{
|
||||
var xmmFold4 = Vector128.Create(0xc6e41596, 0x00000001, 0x54442bd4, 0x00000001);
|
||||
|
||||
Vector128<uint> xTmp0 = xmmCRC0;
|
||||
Vector128<uint> xTmp1 = xmmCRC1;
|
||||
Vector128<uint> xTmp2 = xmmCRC2;
|
||||
Vector128<uint> xTmp3 = xmmCRC3;
|
||||
|
||||
xmmCRC0 = Pclmulqdq.CarrylessMultiply(xmmCRC0.AsUInt64(), xmmFold4.AsUInt64(), 0x01).AsUInt32();
|
||||
xTmp0 = Pclmulqdq.CarrylessMultiply(xTmp0.AsUInt64(), xmmFold4.AsUInt64(), 0x10).AsUInt32();
|
||||
Vector128<float> psCRC0 = xmmCRC0.AsSingle();
|
||||
Vector128<float> psT0 = xTmp0.AsSingle();
|
||||
Vector128<float> psRes0 = Sse.Xor(psCRC0, psT0);
|
||||
|
||||
xmmCRC1 = Pclmulqdq.CarrylessMultiply(xmmCRC1.AsUInt64(), xmmFold4.AsUInt64(), 0x01).AsUInt32();
|
||||
xTmp1 = Pclmulqdq.CarrylessMultiply(xTmp1.AsUInt64(), xmmFold4.AsUInt64(), 0x10).AsUInt32();
|
||||
Vector128<float> psCRC1 = xmmCRC1.AsSingle();
|
||||
Vector128<float> psT1 = xTmp1.AsSingle();
|
||||
Vector128<float> psRes1 = Sse.Xor(psCRC1, psT1);
|
||||
|
||||
xmmCRC2 = Pclmulqdq.CarrylessMultiply(xmmCRC2.AsUInt64(), xmmFold4.AsUInt64(), 0x01).AsUInt32();
|
||||
xTmp2 = Pclmulqdq.CarrylessMultiply(xTmp2.AsUInt64(), xmmFold4.AsUInt64(), 0x10).AsUInt32();
|
||||
Vector128<float> psCRC2 = xmmCRC2.AsSingle();
|
||||
Vector128<float> psT2 = xTmp2.AsSingle();
|
||||
Vector128<float> psRes2 = Sse.Xor(psCRC2, psT2);
|
||||
|
||||
xmmCRC3 = Pclmulqdq.CarrylessMultiply(xmmCRC3.AsUInt64(), xmmFold4.AsUInt64(), 0x01).AsUInt32();
|
||||
xTmp3 = Pclmulqdq.CarrylessMultiply(xTmp3.AsUInt64(), xmmFold4.AsUInt64(), 0x10).AsUInt32();
|
||||
Vector128<float> psCRC3 = xmmCRC3.AsSingle();
|
||||
Vector128<float> psT3 = xTmp3.AsSingle();
|
||||
Vector128<float> psRes3 = Sse.Xor(psCRC3, psT3);
|
||||
|
||||
xmmCRC0 = psRes0.AsUInt32();
|
||||
xmmCRC1 = psRes1.AsUInt32();
|
||||
xmmCRC2 = psRes2.AsUInt32();
|
||||
xmmCRC3 = psRes3.AsUInt32();
|
||||
}
|
||||
|
||||
internal static uint Step(byte[] src, long len, uint initialCRC)
|
||||
{
|
||||
Vector128<uint> xmmInitial = Sse2.ConvertScalarToVector128UInt32(initialCRC);
|
||||
Vector128<uint> xmmCRC0 = Sse2.ConvertScalarToVector128UInt32(0x9db42487);
|
||||
Vector128<uint> xmmCRC1 = Vector128<uint>.Zero;
|
||||
Vector128<uint> xmmCRC2 = Vector128<uint>.Zero;
|
||||
Vector128<uint> xmmCRC3 = Vector128<uint>.Zero;
|
||||
int bufPos = 0;
|
||||
|
||||
bool first = true;
|
||||
|
||||
/* fold 512 to 32 step variable declarations for ISO-C90 compat. */
|
||||
var xmmMask = Vector128.Create(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000);
|
||||
var xmmMask2 = Vector128.Create(0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
|
||||
|
||||
while((len -= 64) >= 0)
|
||||
{
|
||||
var xmmT0 = Vector128.Create(BitConverter.ToUInt32(src, bufPos),
|
||||
BitConverter.ToUInt32(src, bufPos + 4),
|
||||
BitConverter.ToUInt32(src, bufPos + 8),
|
||||
BitConverter.ToUInt32(src, bufPos + 12));
|
||||
|
||||
bufPos += 16;
|
||||
|
||||
var xmmT1 = Vector128.Create(BitConverter.ToUInt32(src, bufPos),
|
||||
BitConverter.ToUInt32(src, bufPos + 4),
|
||||
BitConverter.ToUInt32(src, bufPos + 8),
|
||||
BitConverter.ToUInt32(src, bufPos + 12));
|
||||
|
||||
bufPos += 16;
|
||||
|
||||
var xmmT2 = Vector128.Create(BitConverter.ToUInt32(src, bufPos),
|
||||
BitConverter.ToUInt32(src, bufPos + 4),
|
||||
BitConverter.ToUInt32(src, bufPos + 8),
|
||||
BitConverter.ToUInt32(src, bufPos + 12));
|
||||
|
||||
bufPos += 16;
|
||||
|
||||
var xmmT3 = Vector128.Create(BitConverter.ToUInt32(src, bufPos),
|
||||
BitConverter.ToUInt32(src, bufPos + 4),
|
||||
BitConverter.ToUInt32(src, bufPos + 8),
|
||||
BitConverter.ToUInt32(src, bufPos + 12));
|
||||
|
||||
bufPos += 16;
|
||||
|
||||
if(first)
|
||||
{
|
||||
first = false;
|
||||
xmmT0 = Sse2.Xor(xmmT0, xmmInitial);
|
||||
}
|
||||
|
||||
Fold4(ref xmmCRC0, ref xmmCRC1, ref xmmCRC2, ref xmmCRC3);
|
||||
|
||||
xmmCRC0 = Sse2.Xor(xmmCRC0, xmmT0);
|
||||
xmmCRC1 = Sse2.Xor(xmmCRC1, xmmT1);
|
||||
xmmCRC2 = Sse2.Xor(xmmCRC2, xmmT2);
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xmmT3);
|
||||
}
|
||||
|
||||
/* fold 512 to 32 */
|
||||
|
||||
/*
|
||||
* k1
|
||||
*/
|
||||
var crcFold = Vector128.Create(_crcK[0], _crcK[1], _crcK[2], _crcK[3]);
|
||||
|
||||
Vector128<uint> xTmp0 = Pclmulqdq.CarrylessMultiply(xmmCRC0.AsUInt64(), crcFold.AsUInt64(), 0x10).AsUInt32();
|
||||
|
||||
xmmCRC0 = Pclmulqdq.CarrylessMultiply(xmmCRC0.AsUInt64(), crcFold.AsUInt64(), 0x01).AsUInt32();
|
||||
xmmCRC1 = Sse2.Xor(xmmCRC1, xTmp0);
|
||||
xmmCRC1 = Sse2.Xor(xmmCRC1, xmmCRC0);
|
||||
|
||||
Vector128<uint> xTmp1 = Pclmulqdq.CarrylessMultiply(xmmCRC1.AsUInt64(), crcFold.AsUInt64(), 0x10).AsUInt32();
|
||||
|
||||
xmmCRC1 = Pclmulqdq.CarrylessMultiply(xmmCRC1.AsUInt64(), crcFold.AsUInt64(), 0x01).AsUInt32();
|
||||
xmmCRC2 = Sse2.Xor(xmmCRC2, xTmp1);
|
||||
xmmCRC2 = Sse2.Xor(xmmCRC2, xmmCRC1);
|
||||
|
||||
Vector128<uint> xTmp2 = Pclmulqdq.CarrylessMultiply(xmmCRC2.AsUInt64(), crcFold.AsUInt64(), 0x10).AsUInt32();
|
||||
|
||||
xmmCRC2 = Pclmulqdq.CarrylessMultiply(xmmCRC2.AsUInt64(), crcFold.AsUInt64(), 0x01).AsUInt32();
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xTmp2);
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xmmCRC2);
|
||||
|
||||
/*
|
||||
* k5
|
||||
*/
|
||||
crcFold = Vector128.Create(_crcK[4], _crcK[5], _crcK[6], _crcK[7]);
|
||||
|
||||
xmmCRC0 = xmmCRC3;
|
||||
xmmCRC3 = Pclmulqdq.CarrylessMultiply(xmmCRC3.AsUInt64(), crcFold.AsUInt64(), 0).AsUInt32();
|
||||
xmmCRC0 = Sse2.ShiftRightLogical128BitLane(xmmCRC0, 8);
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xmmCRC0);
|
||||
|
||||
xmmCRC0 = xmmCRC3;
|
||||
xmmCRC3 = Sse2.ShiftLeftLogical128BitLane(xmmCRC3, 4);
|
||||
xmmCRC3 = Pclmulqdq.CarrylessMultiply(xmmCRC3.AsUInt64(), crcFold.AsUInt64(), 0x10).AsUInt32();
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xmmCRC0);
|
||||
xmmCRC3 = Sse2.And(xmmCRC3, xmmMask2);
|
||||
|
||||
/*
|
||||
* k7
|
||||
*/
|
||||
xmmCRC1 = xmmCRC3;
|
||||
xmmCRC2 = xmmCRC3;
|
||||
crcFold = Vector128.Create(_crcK[8], _crcK[9], _crcK[10], _crcK[11]);
|
||||
|
||||
xmmCRC3 = Pclmulqdq.CarrylessMultiply(xmmCRC3.AsUInt64(), crcFold.AsUInt64(), 0).AsUInt32();
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xmmCRC2);
|
||||
xmmCRC3 = Sse2.And(xmmCRC3, xmmMask);
|
||||
|
||||
xmmCRC2 = xmmCRC3;
|
||||
xmmCRC3 = Pclmulqdq.CarrylessMultiply(xmmCRC3.AsUInt64(), crcFold.AsUInt64(), 0x10).AsUInt32();
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xmmCRC2);
|
||||
xmmCRC3 = Sse2.Xor(xmmCRC3, xmmCRC1);
|
||||
|
||||
/*
|
||||
* could just as well write xmm_crc3[2], doing a movaps and truncating, but
|
||||
* no real advantage - it's a tiny bit slower per call, while no additional CPUs
|
||||
* would be supported by only requiring SSSE3 and CLMUL instead of SSE4.1 + CLMUL
|
||||
*/
|
||||
return ~Sse41.Extract(xmmCRC3, 2);
|
||||
}
|
||||
}
|
||||
@@ -27,48 +27,326 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.Helpers;
|
||||
using RomRepoMgr.Core.Checksums.CRC32;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Implements a CRC32 algorithm</summary>
|
||||
public sealed class Crc32Context : IChecksum
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
public sealed partial class Crc32Context : IChecksum
|
||||
{
|
||||
const uint CRC32_ISO_POLY = 0xEDB88320;
|
||||
const uint CRC32_ISO_SEED = 0xFFFFFFFF;
|
||||
|
||||
readonly uint _finalSeed;
|
||||
readonly uint[] _table;
|
||||
uint _hashInt;
|
||||
internal static readonly uint[][] ISOCrc32Table =
|
||||
[
|
||||
[
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
],
|
||||
[
|
||||
0x00000000, 0x191B3141, 0x32366282, 0x2B2D53C3, 0x646CC504, 0x7D77F445, 0x565AA786, 0x4F4196C7,
|
||||
0xC8D98A08, 0xD1C2BB49, 0xFAEFE88A, 0xE3F4D9CB, 0xACB54F0C, 0xB5AE7E4D, 0x9E832D8E, 0x87981CCF,
|
||||
0x4AC21251, 0x53D92310, 0x78F470D3, 0x61EF4192, 0x2EAED755, 0x37B5E614, 0x1C98B5D7, 0x05838496,
|
||||
0x821B9859, 0x9B00A918, 0xB02DFADB, 0xA936CB9A, 0xE6775D5D, 0xFF6C6C1C, 0xD4413FDF, 0xCD5A0E9E,
|
||||
0x958424A2, 0x8C9F15E3, 0xA7B24620, 0xBEA97761, 0xF1E8E1A6, 0xE8F3D0E7, 0xC3DE8324, 0xDAC5B265,
|
||||
0x5D5DAEAA, 0x44469FEB, 0x6F6BCC28, 0x7670FD69, 0x39316BAE, 0x202A5AEF, 0x0B07092C, 0x121C386D,
|
||||
0xDF4636F3, 0xC65D07B2, 0xED705471, 0xF46B6530, 0xBB2AF3F7, 0xA231C2B6, 0x891C9175, 0x9007A034,
|
||||
0x179FBCFB, 0x0E848DBA, 0x25A9DE79, 0x3CB2EF38, 0x73F379FF, 0x6AE848BE, 0x41C51B7D, 0x58DE2A3C,
|
||||
0xF0794F05, 0xE9627E44, 0xC24F2D87, 0xDB541CC6, 0x94158A01, 0x8D0EBB40, 0xA623E883, 0xBF38D9C2,
|
||||
0x38A0C50D, 0x21BBF44C, 0x0A96A78F, 0x138D96CE, 0x5CCC0009, 0x45D73148, 0x6EFA628B, 0x77E153CA,
|
||||
0xBABB5D54, 0xA3A06C15, 0x888D3FD6, 0x91960E97, 0xDED79850, 0xC7CCA911, 0xECE1FAD2, 0xF5FACB93,
|
||||
0x7262D75C, 0x6B79E61D, 0x4054B5DE, 0x594F849F, 0x160E1258, 0x0F152319, 0x243870DA, 0x3D23419B,
|
||||
0x65FD6BA7, 0x7CE65AE6, 0x57CB0925, 0x4ED03864, 0x0191AEA3, 0x188A9FE2, 0x33A7CC21, 0x2ABCFD60,
|
||||
0xAD24E1AF, 0xB43FD0EE, 0x9F12832D, 0x8609B26C, 0xC94824AB, 0xD05315EA, 0xFB7E4629, 0xE2657768,
|
||||
0x2F3F79F6, 0x362448B7, 0x1D091B74, 0x04122A35, 0x4B53BCF2, 0x52488DB3, 0x7965DE70, 0x607EEF31,
|
||||
0xE7E6F3FE, 0xFEFDC2BF, 0xD5D0917C, 0xCCCBA03D, 0x838A36FA, 0x9A9107BB, 0xB1BC5478, 0xA8A76539,
|
||||
0x3B83984B, 0x2298A90A, 0x09B5FAC9, 0x10AECB88, 0x5FEF5D4F, 0x46F46C0E, 0x6DD93FCD, 0x74C20E8C,
|
||||
0xF35A1243, 0xEA412302, 0xC16C70C1, 0xD8774180, 0x9736D747, 0x8E2DE606, 0xA500B5C5, 0xBC1B8484,
|
||||
0x71418A1A, 0x685ABB5B, 0x4377E898, 0x5A6CD9D9, 0x152D4F1E, 0x0C367E5F, 0x271B2D9C, 0x3E001CDD,
|
||||
0xB9980012, 0xA0833153, 0x8BAE6290, 0x92B553D1, 0xDDF4C516, 0xC4EFF457, 0xEFC2A794, 0xF6D996D5,
|
||||
0xAE07BCE9, 0xB71C8DA8, 0x9C31DE6B, 0x852AEF2A, 0xCA6B79ED, 0xD37048AC, 0xF85D1B6F, 0xE1462A2E,
|
||||
0x66DE36E1, 0x7FC507A0, 0x54E85463, 0x4DF36522, 0x02B2F3E5, 0x1BA9C2A4, 0x30849167, 0x299FA026,
|
||||
0xE4C5AEB8, 0xFDDE9FF9, 0xD6F3CC3A, 0xCFE8FD7B, 0x80A96BBC, 0x99B25AFD, 0xB29F093E, 0xAB84387F,
|
||||
0x2C1C24B0, 0x350715F1, 0x1E2A4632, 0x07317773, 0x4870E1B4, 0x516BD0F5, 0x7A468336, 0x635DB277,
|
||||
0xCBFAD74E, 0xD2E1E60F, 0xF9CCB5CC, 0xE0D7848D, 0xAF96124A, 0xB68D230B, 0x9DA070C8, 0x84BB4189,
|
||||
0x03235D46, 0x1A386C07, 0x31153FC4, 0x280E0E85, 0x674F9842, 0x7E54A903, 0x5579FAC0, 0x4C62CB81,
|
||||
0x8138C51F, 0x9823F45E, 0xB30EA79D, 0xAA1596DC, 0xE554001B, 0xFC4F315A, 0xD7626299, 0xCE7953D8,
|
||||
0x49E14F17, 0x50FA7E56, 0x7BD72D95, 0x62CC1CD4, 0x2D8D8A13, 0x3496BB52, 0x1FBBE891, 0x06A0D9D0,
|
||||
0x5E7EF3EC, 0x4765C2AD, 0x6C48916E, 0x7553A02F, 0x3A1236E8, 0x230907A9, 0x0824546A, 0x113F652B,
|
||||
0x96A779E4, 0x8FBC48A5, 0xA4911B66, 0xBD8A2A27, 0xF2CBBCE0, 0xEBD08DA1, 0xC0FDDE62, 0xD9E6EF23,
|
||||
0x14BCE1BD, 0x0DA7D0FC, 0x268A833F, 0x3F91B27E, 0x70D024B9, 0x69CB15F8, 0x42E6463B, 0x5BFD777A,
|
||||
0xDC656BB5, 0xC57E5AF4, 0xEE530937, 0xF7483876, 0xB809AEB1, 0xA1129FF0, 0x8A3FCC33, 0x9324FD72
|
||||
],
|
||||
[
|
||||
0x00000000, 0x01C26A37, 0x0384D46E, 0x0246BE59, 0x0709A8DC, 0x06CBC2EB, 0x048D7CB2, 0x054F1685,
|
||||
0x0E1351B8, 0x0FD13B8F, 0x0D9785D6, 0x0C55EFE1, 0x091AF964, 0x08D89353, 0x0A9E2D0A, 0x0B5C473D,
|
||||
0x1C26A370, 0x1DE4C947, 0x1FA2771E, 0x1E601D29, 0x1B2F0BAC, 0x1AED619B, 0x18ABDFC2, 0x1969B5F5,
|
||||
0x1235F2C8, 0x13F798FF, 0x11B126A6, 0x10734C91, 0x153C5A14, 0x14FE3023, 0x16B88E7A, 0x177AE44D,
|
||||
0x384D46E0, 0x398F2CD7, 0x3BC9928E, 0x3A0BF8B9, 0x3F44EE3C, 0x3E86840B, 0x3CC03A52, 0x3D025065,
|
||||
0x365E1758, 0x379C7D6F, 0x35DAC336, 0x3418A901, 0x3157BF84, 0x3095D5B3, 0x32D36BEA, 0x331101DD,
|
||||
0x246BE590, 0x25A98FA7, 0x27EF31FE, 0x262D5BC9, 0x23624D4C, 0x22A0277B, 0x20E69922, 0x2124F315,
|
||||
0x2A78B428, 0x2BBADE1F, 0x29FC6046, 0x283E0A71, 0x2D711CF4, 0x2CB376C3, 0x2EF5C89A, 0x2F37A2AD,
|
||||
0x709A8DC0, 0x7158E7F7, 0x731E59AE, 0x72DC3399, 0x7793251C, 0x76514F2B, 0x7417F172, 0x75D59B45,
|
||||
0x7E89DC78, 0x7F4BB64F, 0x7D0D0816, 0x7CCF6221, 0x798074A4, 0x78421E93, 0x7A04A0CA, 0x7BC6CAFD,
|
||||
0x6CBC2EB0, 0x6D7E4487, 0x6F38FADE, 0x6EFA90E9, 0x6BB5866C, 0x6A77EC5B, 0x68315202, 0x69F33835,
|
||||
0x62AF7F08, 0x636D153F, 0x612BAB66, 0x60E9C151, 0x65A6D7D4, 0x6464BDE3, 0x662203BA, 0x67E0698D,
|
||||
0x48D7CB20, 0x4915A117, 0x4B531F4E, 0x4A917579, 0x4FDE63FC, 0x4E1C09CB, 0x4C5AB792, 0x4D98DDA5,
|
||||
0x46C49A98, 0x4706F0AF, 0x45404EF6, 0x448224C1, 0x41CD3244, 0x400F5873, 0x4249E62A, 0x438B8C1D,
|
||||
0x54F16850, 0x55330267, 0x5775BC3E, 0x56B7D609, 0x53F8C08C, 0x523AAABB, 0x507C14E2, 0x51BE7ED5,
|
||||
0x5AE239E8, 0x5B2053DF, 0x5966ED86, 0x58A487B1, 0x5DEB9134, 0x5C29FB03, 0x5E6F455A, 0x5FAD2F6D,
|
||||
0xE1351B80, 0xE0F771B7, 0xE2B1CFEE, 0xE373A5D9, 0xE63CB35C, 0xE7FED96B, 0xE5B86732, 0xE47A0D05,
|
||||
0xEF264A38, 0xEEE4200F, 0xECA29E56, 0xED60F461, 0xE82FE2E4, 0xE9ED88D3, 0xEBAB368A, 0xEA695CBD,
|
||||
0xFD13B8F0, 0xFCD1D2C7, 0xFE976C9E, 0xFF5506A9, 0xFA1A102C, 0xFBD87A1B, 0xF99EC442, 0xF85CAE75,
|
||||
0xF300E948, 0xF2C2837F, 0xF0843D26, 0xF1465711, 0xF4094194, 0xF5CB2BA3, 0xF78D95FA, 0xF64FFFCD,
|
||||
0xD9785D60, 0xD8BA3757, 0xDAFC890E, 0xDB3EE339, 0xDE71F5BC, 0xDFB39F8B, 0xDDF521D2, 0xDC374BE5,
|
||||
0xD76B0CD8, 0xD6A966EF, 0xD4EFD8B6, 0xD52DB281, 0xD062A404, 0xD1A0CE33, 0xD3E6706A, 0xD2241A5D,
|
||||
0xC55EFE10, 0xC49C9427, 0xC6DA2A7E, 0xC7184049, 0xC25756CC, 0xC3953CFB, 0xC1D382A2, 0xC011E895,
|
||||
0xCB4DAFA8, 0xCA8FC59F, 0xC8C97BC6, 0xC90B11F1, 0xCC440774, 0xCD866D43, 0xCFC0D31A, 0xCE02B92D,
|
||||
0x91AF9640, 0x906DFC77, 0x922B422E, 0x93E92819, 0x96A63E9C, 0x976454AB, 0x9522EAF2, 0x94E080C5,
|
||||
0x9FBCC7F8, 0x9E7EADCF, 0x9C381396, 0x9DFA79A1, 0x98B56F24, 0x99770513, 0x9B31BB4A, 0x9AF3D17D,
|
||||
0x8D893530, 0x8C4B5F07, 0x8E0DE15E, 0x8FCF8B69, 0x8A809DEC, 0x8B42F7DB, 0x89044982, 0x88C623B5,
|
||||
0x839A6488, 0x82580EBF, 0x801EB0E6, 0x81DCDAD1, 0x8493CC54, 0x8551A663, 0x8717183A, 0x86D5720D,
|
||||
0xA9E2D0A0, 0xA820BA97, 0xAA6604CE, 0xABA46EF9, 0xAEEB787C, 0xAF29124B, 0xAD6FAC12, 0xACADC625,
|
||||
0xA7F18118, 0xA633EB2F, 0xA4755576, 0xA5B73F41, 0xA0F829C4, 0xA13A43F3, 0xA37CFDAA, 0xA2BE979D,
|
||||
0xB5C473D0, 0xB40619E7, 0xB640A7BE, 0xB782CD89, 0xB2CDDB0C, 0xB30FB13B, 0xB1490F62, 0xB08B6555,
|
||||
0xBBD72268, 0xBA15485F, 0xB853F606, 0xB9919C31, 0xBCDE8AB4, 0xBD1CE083, 0xBF5A5EDA, 0xBE9834ED
|
||||
],
|
||||
[
|
||||
0x00000000, 0xB8BC6765, 0xAA09C88B, 0x12B5AFEE, 0x8F629757, 0x37DEF032, 0x256B5FDC, 0x9DD738B9,
|
||||
0xC5B428EF, 0x7D084F8A, 0x6FBDE064, 0xD7018701, 0x4AD6BFB8, 0xF26AD8DD, 0xE0DF7733, 0x58631056,
|
||||
0x5019579F, 0xE8A530FA, 0xFA109F14, 0x42ACF871, 0xDF7BC0C8, 0x67C7A7AD, 0x75720843, 0xCDCE6F26,
|
||||
0x95AD7F70, 0x2D111815, 0x3FA4B7FB, 0x8718D09E, 0x1ACFE827, 0xA2738F42, 0xB0C620AC, 0x087A47C9,
|
||||
0xA032AF3E, 0x188EC85B, 0x0A3B67B5, 0xB28700D0, 0x2F503869, 0x97EC5F0C, 0x8559F0E2, 0x3DE59787,
|
||||
0x658687D1, 0xDD3AE0B4, 0xCF8F4F5A, 0x7733283F, 0xEAE41086, 0x525877E3, 0x40EDD80D, 0xF851BF68,
|
||||
0xF02BF8A1, 0x48979FC4, 0x5A22302A, 0xE29E574F, 0x7F496FF6, 0xC7F50893, 0xD540A77D, 0x6DFCC018,
|
||||
0x359FD04E, 0x8D23B72B, 0x9F9618C5, 0x272A7FA0, 0xBAFD4719, 0x0241207C, 0x10F48F92, 0xA848E8F7,
|
||||
0x9B14583D, 0x23A83F58, 0x311D90B6, 0x89A1F7D3, 0x1476CF6A, 0xACCAA80F, 0xBE7F07E1, 0x06C36084,
|
||||
0x5EA070D2, 0xE61C17B7, 0xF4A9B859, 0x4C15DF3C, 0xD1C2E785, 0x697E80E0, 0x7BCB2F0E, 0xC377486B,
|
||||
0xCB0D0FA2, 0x73B168C7, 0x6104C729, 0xD9B8A04C, 0x446F98F5, 0xFCD3FF90, 0xEE66507E, 0x56DA371B,
|
||||
0x0EB9274D, 0xB6054028, 0xA4B0EFC6, 0x1C0C88A3, 0x81DBB01A, 0x3967D77F, 0x2BD27891, 0x936E1FF4,
|
||||
0x3B26F703, 0x839A9066, 0x912F3F88, 0x299358ED, 0xB4446054, 0x0CF80731, 0x1E4DA8DF, 0xA6F1CFBA,
|
||||
0xFE92DFEC, 0x462EB889, 0x549B1767, 0xEC277002, 0x71F048BB, 0xC94C2FDE, 0xDBF98030, 0x6345E755,
|
||||
0x6B3FA09C, 0xD383C7F9, 0xC1366817, 0x798A0F72, 0xE45D37CB, 0x5CE150AE, 0x4E54FF40, 0xF6E89825,
|
||||
0xAE8B8873, 0x1637EF16, 0x048240F8, 0xBC3E279D, 0x21E91F24, 0x99557841, 0x8BE0D7AF, 0x335CB0CA,
|
||||
0xED59B63B, 0x55E5D15E, 0x47507EB0, 0xFFEC19D5, 0x623B216C, 0xDA874609, 0xC832E9E7, 0x708E8E82,
|
||||
0x28ED9ED4, 0x9051F9B1, 0x82E4565F, 0x3A58313A, 0xA78F0983, 0x1F336EE6, 0x0D86C108, 0xB53AA66D,
|
||||
0xBD40E1A4, 0x05FC86C1, 0x1749292F, 0xAFF54E4A, 0x322276F3, 0x8A9E1196, 0x982BBE78, 0x2097D91D,
|
||||
0x78F4C94B, 0xC048AE2E, 0xD2FD01C0, 0x6A4166A5, 0xF7965E1C, 0x4F2A3979, 0x5D9F9697, 0xE523F1F2,
|
||||
0x4D6B1905, 0xF5D77E60, 0xE762D18E, 0x5FDEB6EB, 0xC2098E52, 0x7AB5E937, 0x680046D9, 0xD0BC21BC,
|
||||
0x88DF31EA, 0x3063568F, 0x22D6F961, 0x9A6A9E04, 0x07BDA6BD, 0xBF01C1D8, 0xADB46E36, 0x15080953,
|
||||
0x1D724E9A, 0xA5CE29FF, 0xB77B8611, 0x0FC7E174, 0x9210D9CD, 0x2AACBEA8, 0x38191146, 0x80A57623,
|
||||
0xD8C66675, 0x607A0110, 0x72CFAEFE, 0xCA73C99B, 0x57A4F122, 0xEF189647, 0xFDAD39A9, 0x45115ECC,
|
||||
0x764DEE06, 0xCEF18963, 0xDC44268D, 0x64F841E8, 0xF92F7951, 0x41931E34, 0x5326B1DA, 0xEB9AD6BF,
|
||||
0xB3F9C6E9, 0x0B45A18C, 0x19F00E62, 0xA14C6907, 0x3C9B51BE, 0x842736DB, 0x96929935, 0x2E2EFE50,
|
||||
0x2654B999, 0x9EE8DEFC, 0x8C5D7112, 0x34E11677, 0xA9362ECE, 0x118A49AB, 0x033FE645, 0xBB838120,
|
||||
0xE3E09176, 0x5B5CF613, 0x49E959FD, 0xF1553E98, 0x6C820621, 0xD43E6144, 0xC68BCEAA, 0x7E37A9CF,
|
||||
0xD67F4138, 0x6EC3265D, 0x7C7689B3, 0xC4CAEED6, 0x591DD66F, 0xE1A1B10A, 0xF3141EE4, 0x4BA87981,
|
||||
0x13CB69D7, 0xAB770EB2, 0xB9C2A15C, 0x017EC639, 0x9CA9FE80, 0x241599E5, 0x36A0360B, 0x8E1C516E,
|
||||
0x866616A7, 0x3EDA71C2, 0x2C6FDE2C, 0x94D3B949, 0x090481F0, 0xB1B8E695, 0xA30D497B, 0x1BB12E1E,
|
||||
0x43D23E48, 0xFB6E592D, 0xE9DBF6C3, 0x516791A6, 0xCCB0A91F, 0x740CCE7A, 0x66B96194, 0xDE0506F1
|
||||
],
|
||||
[
|
||||
0x00000000, 0x3D6029B0, 0x7AC05360, 0x47A07AD0, 0xF580A6C0, 0xC8E08F70, 0x8F40F5A0, 0xB220DC10,
|
||||
0x30704BC1, 0x0D106271, 0x4AB018A1, 0x77D03111, 0xC5F0ED01, 0xF890C4B1, 0xBF30BE61, 0x825097D1,
|
||||
0x60E09782, 0x5D80BE32, 0x1A20C4E2, 0x2740ED52, 0x95603142, 0xA80018F2, 0xEFA06222, 0xD2C04B92,
|
||||
0x5090DC43, 0x6DF0F5F3, 0x2A508F23, 0x1730A693, 0xA5107A83, 0x98705333, 0xDFD029E3, 0xE2B00053,
|
||||
0xC1C12F04, 0xFCA106B4, 0xBB017C64, 0x866155D4, 0x344189C4, 0x0921A074, 0x4E81DAA4, 0x73E1F314,
|
||||
0xF1B164C5, 0xCCD14D75, 0x8B7137A5, 0xB6111E15, 0x0431C205, 0x3951EBB5, 0x7EF19165, 0x4391B8D5,
|
||||
0xA121B886, 0x9C419136, 0xDBE1EBE6, 0xE681C256, 0x54A11E46, 0x69C137F6, 0x2E614D26, 0x13016496,
|
||||
0x9151F347, 0xAC31DAF7, 0xEB91A027, 0xD6F18997, 0x64D15587, 0x59B17C37, 0x1E1106E7, 0x23712F57,
|
||||
0x58F35849, 0x659371F9, 0x22330B29, 0x1F532299, 0xAD73FE89, 0x9013D739, 0xD7B3ADE9, 0xEAD38459,
|
||||
0x68831388, 0x55E33A38, 0x124340E8, 0x2F236958, 0x9D03B548, 0xA0639CF8, 0xE7C3E628, 0xDAA3CF98,
|
||||
0x3813CFCB, 0x0573E67B, 0x42D39CAB, 0x7FB3B51B, 0xCD93690B, 0xF0F340BB, 0xB7533A6B, 0x8A3313DB,
|
||||
0x0863840A, 0x3503ADBA, 0x72A3D76A, 0x4FC3FEDA, 0xFDE322CA, 0xC0830B7A, 0x872371AA, 0xBA43581A,
|
||||
0x9932774D, 0xA4525EFD, 0xE3F2242D, 0xDE920D9D, 0x6CB2D18D, 0x51D2F83D, 0x167282ED, 0x2B12AB5D,
|
||||
0xA9423C8C, 0x9422153C, 0xD3826FEC, 0xEEE2465C, 0x5CC29A4C, 0x61A2B3FC, 0x2602C92C, 0x1B62E09C,
|
||||
0xF9D2E0CF, 0xC4B2C97F, 0x8312B3AF, 0xBE729A1F, 0x0C52460F, 0x31326FBF, 0x7692156F, 0x4BF23CDF,
|
||||
0xC9A2AB0E, 0xF4C282BE, 0xB362F86E, 0x8E02D1DE, 0x3C220DCE, 0x0142247E, 0x46E25EAE, 0x7B82771E,
|
||||
0xB1E6B092, 0x8C869922, 0xCB26E3F2, 0xF646CA42, 0x44661652, 0x79063FE2, 0x3EA64532, 0x03C66C82,
|
||||
0x8196FB53, 0xBCF6D2E3, 0xFB56A833, 0xC6368183, 0x74165D93, 0x49767423, 0x0ED60EF3, 0x33B62743,
|
||||
0xD1062710, 0xEC660EA0, 0xABC67470, 0x96A65DC0, 0x248681D0, 0x19E6A860, 0x5E46D2B0, 0x6326FB00,
|
||||
0xE1766CD1, 0xDC164561, 0x9BB63FB1, 0xA6D61601, 0x14F6CA11, 0x2996E3A1, 0x6E369971, 0x5356B0C1,
|
||||
0x70279F96, 0x4D47B626, 0x0AE7CCF6, 0x3787E546, 0x85A73956, 0xB8C710E6, 0xFF676A36, 0xC2074386,
|
||||
0x4057D457, 0x7D37FDE7, 0x3A978737, 0x07F7AE87, 0xB5D77297, 0x88B75B27, 0xCF1721F7, 0xF2770847,
|
||||
0x10C70814, 0x2DA721A4, 0x6A075B74, 0x576772C4, 0xE547AED4, 0xD8278764, 0x9F87FDB4, 0xA2E7D404,
|
||||
0x20B743D5, 0x1DD76A65, 0x5A7710B5, 0x67173905, 0xD537E515, 0xE857CCA5, 0xAFF7B675, 0x92979FC5,
|
||||
0xE915E8DB, 0xD475C16B, 0x93D5BBBB, 0xAEB5920B, 0x1C954E1B, 0x21F567AB, 0x66551D7B, 0x5B3534CB,
|
||||
0xD965A31A, 0xE4058AAA, 0xA3A5F07A, 0x9EC5D9CA, 0x2CE505DA, 0x11852C6A, 0x562556BA, 0x6B457F0A,
|
||||
0x89F57F59, 0xB49556E9, 0xF3352C39, 0xCE550589, 0x7C75D999, 0x4115F029, 0x06B58AF9, 0x3BD5A349,
|
||||
0xB9853498, 0x84E51D28, 0xC34567F8, 0xFE254E48, 0x4C059258, 0x7165BBE8, 0x36C5C138, 0x0BA5E888,
|
||||
0x28D4C7DF, 0x15B4EE6F, 0x521494BF, 0x6F74BD0F, 0xDD54611F, 0xE03448AF, 0xA794327F, 0x9AF41BCF,
|
||||
0x18A48C1E, 0x25C4A5AE, 0x6264DF7E, 0x5F04F6CE, 0xED242ADE, 0xD044036E, 0x97E479BE, 0xAA84500E,
|
||||
0x4834505D, 0x755479ED, 0x32F4033D, 0x0F942A8D, 0xBDB4F69D, 0x80D4DF2D, 0xC774A5FD, 0xFA148C4D,
|
||||
0x78441B9C, 0x4524322C, 0x028448FC, 0x3FE4614C, 0x8DC4BD5C, 0xB0A494EC, 0xF704EE3C, 0xCA64C78C
|
||||
],
|
||||
[
|
||||
0x00000000, 0xCB5CD3A5, 0x4DC8A10B, 0x869472AE, 0x9B914216, 0x50CD91B3, 0xD659E31D, 0x1D0530B8,
|
||||
0xEC53826D, 0x270F51C8, 0xA19B2366, 0x6AC7F0C3, 0x77C2C07B, 0xBC9E13DE, 0x3A0A6170, 0xF156B2D5,
|
||||
0x03D6029B, 0xC88AD13E, 0x4E1EA390, 0x85427035, 0x9847408D, 0x531B9328, 0xD58FE186, 0x1ED33223,
|
||||
0xEF8580F6, 0x24D95353, 0xA24D21FD, 0x6911F258, 0x7414C2E0, 0xBF481145, 0x39DC63EB, 0xF280B04E,
|
||||
0x07AC0536, 0xCCF0D693, 0x4A64A43D, 0x81387798, 0x9C3D4720, 0x57619485, 0xD1F5E62B, 0x1AA9358E,
|
||||
0xEBFF875B, 0x20A354FE, 0xA6372650, 0x6D6BF5F5, 0x706EC54D, 0xBB3216E8, 0x3DA66446, 0xF6FAB7E3,
|
||||
0x047A07AD, 0xCF26D408, 0x49B2A6A6, 0x82EE7503, 0x9FEB45BB, 0x54B7961E, 0xD223E4B0, 0x197F3715,
|
||||
0xE82985C0, 0x23755665, 0xA5E124CB, 0x6EBDF76E, 0x73B8C7D6, 0xB8E41473, 0x3E7066DD, 0xF52CB578,
|
||||
0x0F580A6C, 0xC404D9C9, 0x4290AB67, 0x89CC78C2, 0x94C9487A, 0x5F959BDF, 0xD901E971, 0x125D3AD4,
|
||||
0xE30B8801, 0x28575BA4, 0xAEC3290A, 0x659FFAAF, 0x789ACA17, 0xB3C619B2, 0x35526B1C, 0xFE0EB8B9,
|
||||
0x0C8E08F7, 0xC7D2DB52, 0x4146A9FC, 0x8A1A7A59, 0x971F4AE1, 0x5C439944, 0xDAD7EBEA, 0x118B384F,
|
||||
0xE0DD8A9A, 0x2B81593F, 0xAD152B91, 0x6649F834, 0x7B4CC88C, 0xB0101B29, 0x36846987, 0xFDD8BA22,
|
||||
0x08F40F5A, 0xC3A8DCFF, 0x453CAE51, 0x8E607DF4, 0x93654D4C, 0x58399EE9, 0xDEADEC47, 0x15F13FE2,
|
||||
0xE4A78D37, 0x2FFB5E92, 0xA96F2C3C, 0x6233FF99, 0x7F36CF21, 0xB46A1C84, 0x32FE6E2A, 0xF9A2BD8F,
|
||||
0x0B220DC1, 0xC07EDE64, 0x46EAACCA, 0x8DB67F6F, 0x90B34FD7, 0x5BEF9C72, 0xDD7BEEDC, 0x16273D79,
|
||||
0xE7718FAC, 0x2C2D5C09, 0xAAB92EA7, 0x61E5FD02, 0x7CE0CDBA, 0xB7BC1E1F, 0x31286CB1, 0xFA74BF14,
|
||||
0x1EB014D8, 0xD5ECC77D, 0x5378B5D3, 0x98246676, 0x852156CE, 0x4E7D856B, 0xC8E9F7C5, 0x03B52460,
|
||||
0xF2E396B5, 0x39BF4510, 0xBF2B37BE, 0x7477E41B, 0x6972D4A3, 0xA22E0706, 0x24BA75A8, 0xEFE6A60D,
|
||||
0x1D661643, 0xD63AC5E6, 0x50AEB748, 0x9BF264ED, 0x86F75455, 0x4DAB87F0, 0xCB3FF55E, 0x006326FB,
|
||||
0xF135942E, 0x3A69478B, 0xBCFD3525, 0x77A1E680, 0x6AA4D638, 0xA1F8059D, 0x276C7733, 0xEC30A496,
|
||||
0x191C11EE, 0xD240C24B, 0x54D4B0E5, 0x9F886340, 0x828D53F8, 0x49D1805D, 0xCF45F2F3, 0x04192156,
|
||||
0xF54F9383, 0x3E134026, 0xB8873288, 0x73DBE12D, 0x6EDED195, 0xA5820230, 0x2316709E, 0xE84AA33B,
|
||||
0x1ACA1375, 0xD196C0D0, 0x5702B27E, 0x9C5E61DB, 0x815B5163, 0x4A0782C6, 0xCC93F068, 0x07CF23CD,
|
||||
0xF6999118, 0x3DC542BD, 0xBB513013, 0x700DE3B6, 0x6D08D30E, 0xA65400AB, 0x20C07205, 0xEB9CA1A0,
|
||||
0x11E81EB4, 0xDAB4CD11, 0x5C20BFBF, 0x977C6C1A, 0x8A795CA2, 0x41258F07, 0xC7B1FDA9, 0x0CED2E0C,
|
||||
0xFDBB9CD9, 0x36E74F7C, 0xB0733DD2, 0x7B2FEE77, 0x662ADECF, 0xAD760D6A, 0x2BE27FC4, 0xE0BEAC61,
|
||||
0x123E1C2F, 0xD962CF8A, 0x5FF6BD24, 0x94AA6E81, 0x89AF5E39, 0x42F38D9C, 0xC467FF32, 0x0F3B2C97,
|
||||
0xFE6D9E42, 0x35314DE7, 0xB3A53F49, 0x78F9ECEC, 0x65FCDC54, 0xAEA00FF1, 0x28347D5F, 0xE368AEFA,
|
||||
0x16441B82, 0xDD18C827, 0x5B8CBA89, 0x90D0692C, 0x8DD55994, 0x46898A31, 0xC01DF89F, 0x0B412B3A,
|
||||
0xFA1799EF, 0x314B4A4A, 0xB7DF38E4, 0x7C83EB41, 0x6186DBF9, 0xAADA085C, 0x2C4E7AF2, 0xE712A957,
|
||||
0x15921919, 0xDECECABC, 0x585AB812, 0x93066BB7, 0x8E035B0F, 0x455F88AA, 0xC3CBFA04, 0x089729A1,
|
||||
0xF9C19B74, 0x329D48D1, 0xB4093A7F, 0x7F55E9DA, 0x6250D962, 0xA90C0AC7, 0x2F987869, 0xE4C4ABCC
|
||||
],
|
||||
[
|
||||
0x00000000, 0xA6770BB4, 0x979F1129, 0x31E81A9D, 0xF44F2413, 0x52382FA7, 0x63D0353A, 0xC5A73E8E,
|
||||
0x33EF4E67, 0x959845D3, 0xA4705F4E, 0x020754FA, 0xC7A06A74, 0x61D761C0, 0x503F7B5D, 0xF64870E9,
|
||||
0x67DE9CCE, 0xC1A9977A, 0xF0418DE7, 0x56368653, 0x9391B8DD, 0x35E6B369, 0x040EA9F4, 0xA279A240,
|
||||
0x5431D2A9, 0xF246D91D, 0xC3AEC380, 0x65D9C834, 0xA07EF6BA, 0x0609FD0E, 0x37E1E793, 0x9196EC27,
|
||||
0xCFBD399C, 0x69CA3228, 0x582228B5, 0xFE552301, 0x3BF21D8F, 0x9D85163B, 0xAC6D0CA6, 0x0A1A0712,
|
||||
0xFC5277FB, 0x5A257C4F, 0x6BCD66D2, 0xCDBA6D66, 0x081D53E8, 0xAE6A585C, 0x9F8242C1, 0x39F54975,
|
||||
0xA863A552, 0x0E14AEE6, 0x3FFCB47B, 0x998BBFCF, 0x5C2C8141, 0xFA5B8AF5, 0xCBB39068, 0x6DC49BDC,
|
||||
0x9B8CEB35, 0x3DFBE081, 0x0C13FA1C, 0xAA64F1A8, 0x6FC3CF26, 0xC9B4C492, 0xF85CDE0F, 0x5E2BD5BB,
|
||||
0x440B7579, 0xE27C7ECD, 0xD3946450, 0x75E36FE4, 0xB044516A, 0x16335ADE, 0x27DB4043, 0x81AC4BF7,
|
||||
0x77E43B1E, 0xD19330AA, 0xE07B2A37, 0x460C2183, 0x83AB1F0D, 0x25DC14B9, 0x14340E24, 0xB2430590,
|
||||
0x23D5E9B7, 0x85A2E203, 0xB44AF89E, 0x123DF32A, 0xD79ACDA4, 0x71EDC610, 0x4005DC8D, 0xE672D739,
|
||||
0x103AA7D0, 0xB64DAC64, 0x87A5B6F9, 0x21D2BD4D, 0xE47583C3, 0x42028877, 0x73EA92EA, 0xD59D995E,
|
||||
0x8BB64CE5, 0x2DC14751, 0x1C295DCC, 0xBA5E5678, 0x7FF968F6, 0xD98E6342, 0xE86679DF, 0x4E11726B,
|
||||
0xB8590282, 0x1E2E0936, 0x2FC613AB, 0x89B1181F, 0x4C162691, 0xEA612D25, 0xDB8937B8, 0x7DFE3C0C,
|
||||
0xEC68D02B, 0x4A1FDB9F, 0x7BF7C102, 0xDD80CAB6, 0x1827F438, 0xBE50FF8C, 0x8FB8E511, 0x29CFEEA5,
|
||||
0xDF879E4C, 0x79F095F8, 0x48188F65, 0xEE6F84D1, 0x2BC8BA5F, 0x8DBFB1EB, 0xBC57AB76, 0x1A20A0C2,
|
||||
0x8816EAF2, 0x2E61E146, 0x1F89FBDB, 0xB9FEF06F, 0x7C59CEE1, 0xDA2EC555, 0xEBC6DFC8, 0x4DB1D47C,
|
||||
0xBBF9A495, 0x1D8EAF21, 0x2C66B5BC, 0x8A11BE08, 0x4FB68086, 0xE9C18B32, 0xD82991AF, 0x7E5E9A1B,
|
||||
0xEFC8763C, 0x49BF7D88, 0x78576715, 0xDE206CA1, 0x1B87522F, 0xBDF0599B, 0x8C184306, 0x2A6F48B2,
|
||||
0xDC27385B, 0x7A5033EF, 0x4BB82972, 0xEDCF22C6, 0x28681C48, 0x8E1F17FC, 0xBFF70D61, 0x198006D5,
|
||||
0x47ABD36E, 0xE1DCD8DA, 0xD034C247, 0x7643C9F3, 0xB3E4F77D, 0x1593FCC9, 0x247BE654, 0x820CEDE0,
|
||||
0x74449D09, 0xD23396BD, 0xE3DB8C20, 0x45AC8794, 0x800BB91A, 0x267CB2AE, 0x1794A833, 0xB1E3A387,
|
||||
0x20754FA0, 0x86024414, 0xB7EA5E89, 0x119D553D, 0xD43A6BB3, 0x724D6007, 0x43A57A9A, 0xE5D2712E,
|
||||
0x139A01C7, 0xB5ED0A73, 0x840510EE, 0x22721B5A, 0xE7D525D4, 0x41A22E60, 0x704A34FD, 0xD63D3F49,
|
||||
0xCC1D9F8B, 0x6A6A943F, 0x5B828EA2, 0xFDF58516, 0x3852BB98, 0x9E25B02C, 0xAFCDAAB1, 0x09BAA105,
|
||||
0xFFF2D1EC, 0x5985DA58, 0x686DC0C5, 0xCE1ACB71, 0x0BBDF5FF, 0xADCAFE4B, 0x9C22E4D6, 0x3A55EF62,
|
||||
0xABC30345, 0x0DB408F1, 0x3C5C126C, 0x9A2B19D8, 0x5F8C2756, 0xF9FB2CE2, 0xC813367F, 0x6E643DCB,
|
||||
0x982C4D22, 0x3E5B4696, 0x0FB35C0B, 0xA9C457BF, 0x6C636931, 0xCA146285, 0xFBFC7818, 0x5D8B73AC,
|
||||
0x03A0A617, 0xA5D7ADA3, 0x943FB73E, 0x3248BC8A, 0xF7EF8204, 0x519889B0, 0x6070932D, 0xC6079899,
|
||||
0x304FE870, 0x9638E3C4, 0xA7D0F959, 0x01A7F2ED, 0xC400CC63, 0x6277C7D7, 0x539FDD4A, 0xF5E8D6FE,
|
||||
0x647E3AD9, 0xC209316D, 0xF3E12BF0, 0x55962044, 0x90311ECA, 0x3646157E, 0x07AE0FE3, 0xA1D90457,
|
||||
0x579174BE, 0xF1E67F0A, 0xC00E6597, 0x66796E23, 0xA3DE50AD, 0x05A95B19, 0x34414184, 0x92364A30
|
||||
],
|
||||
[
|
||||
0x00000000, 0xCCAA009E, 0x4225077D, 0x8E8F07E3, 0x844A0EFA, 0x48E00E64, 0xC66F0987, 0x0AC50919,
|
||||
0xD3E51BB5, 0x1F4F1B2B, 0x91C01CC8, 0x5D6A1C56, 0x57AF154F, 0x9B0515D1, 0x158A1232, 0xD92012AC,
|
||||
0x7CBB312B, 0xB01131B5, 0x3E9E3656, 0xF23436C8, 0xF8F13FD1, 0x345B3F4F, 0xBAD438AC, 0x767E3832,
|
||||
0xAF5E2A9E, 0x63F42A00, 0xED7B2DE3, 0x21D12D7D, 0x2B142464, 0xE7BE24FA, 0x69312319, 0xA59B2387,
|
||||
0xF9766256, 0x35DC62C8, 0xBB53652B, 0x77F965B5, 0x7D3C6CAC, 0xB1966C32, 0x3F196BD1, 0xF3B36B4F,
|
||||
0x2A9379E3, 0xE639797D, 0x68B67E9E, 0xA41C7E00, 0xAED97719, 0x62737787, 0xECFC7064, 0x205670FA,
|
||||
0x85CD537D, 0x496753E3, 0xC7E85400, 0x0B42549E, 0x01875D87, 0xCD2D5D19, 0x43A25AFA, 0x8F085A64,
|
||||
0x562848C8, 0x9A824856, 0x140D4FB5, 0xD8A74F2B, 0xD2624632, 0x1EC846AC, 0x9047414F, 0x5CED41D1,
|
||||
0x299DC2ED, 0xE537C273, 0x6BB8C590, 0xA712C50E, 0xADD7CC17, 0x617DCC89, 0xEFF2CB6A, 0x2358CBF4,
|
||||
0xFA78D958, 0x36D2D9C6, 0xB85DDE25, 0x74F7DEBB, 0x7E32D7A2, 0xB298D73C, 0x3C17D0DF, 0xF0BDD041,
|
||||
0x5526F3C6, 0x998CF358, 0x1703F4BB, 0xDBA9F425, 0xD16CFD3C, 0x1DC6FDA2, 0x9349FA41, 0x5FE3FADF,
|
||||
0x86C3E873, 0x4A69E8ED, 0xC4E6EF0E, 0x084CEF90, 0x0289E689, 0xCE23E617, 0x40ACE1F4, 0x8C06E16A,
|
||||
0xD0EBA0BB, 0x1C41A025, 0x92CEA7C6, 0x5E64A758, 0x54A1AE41, 0x980BAEDF, 0x1684A93C, 0xDA2EA9A2,
|
||||
0x030EBB0E, 0xCFA4BB90, 0x412BBC73, 0x8D81BCED, 0x8744B5F4, 0x4BEEB56A, 0xC561B289, 0x09CBB217,
|
||||
0xAC509190, 0x60FA910E, 0xEE7596ED, 0x22DF9673, 0x281A9F6A, 0xE4B09FF4, 0x6A3F9817, 0xA6959889,
|
||||
0x7FB58A25, 0xB31F8ABB, 0x3D908D58, 0xF13A8DC6, 0xFBFF84DF, 0x37558441, 0xB9DA83A2, 0x7570833C,
|
||||
0x533B85DA, 0x9F918544, 0x111E82A7, 0xDDB48239, 0xD7718B20, 0x1BDB8BBE, 0x95548C5D, 0x59FE8CC3,
|
||||
0x80DE9E6F, 0x4C749EF1, 0xC2FB9912, 0x0E51998C, 0x04949095, 0xC83E900B, 0x46B197E8, 0x8A1B9776,
|
||||
0x2F80B4F1, 0xE32AB46F, 0x6DA5B38C, 0xA10FB312, 0xABCABA0B, 0x6760BA95, 0xE9EFBD76, 0x2545BDE8,
|
||||
0xFC65AF44, 0x30CFAFDA, 0xBE40A839, 0x72EAA8A7, 0x782FA1BE, 0xB485A120, 0x3A0AA6C3, 0xF6A0A65D,
|
||||
0xAA4DE78C, 0x66E7E712, 0xE868E0F1, 0x24C2E06F, 0x2E07E976, 0xE2ADE9E8, 0x6C22EE0B, 0xA088EE95,
|
||||
0x79A8FC39, 0xB502FCA7, 0x3B8DFB44, 0xF727FBDA, 0xFDE2F2C3, 0x3148F25D, 0xBFC7F5BE, 0x736DF520,
|
||||
0xD6F6D6A7, 0x1A5CD639, 0x94D3D1DA, 0x5879D144, 0x52BCD85D, 0x9E16D8C3, 0x1099DF20, 0xDC33DFBE,
|
||||
0x0513CD12, 0xC9B9CD8C, 0x4736CA6F, 0x8B9CCAF1, 0x8159C3E8, 0x4DF3C376, 0xC37CC495, 0x0FD6C40B,
|
||||
0x7AA64737, 0xB60C47A9, 0x3883404A, 0xF42940D4, 0xFEEC49CD, 0x32464953, 0xBCC94EB0, 0x70634E2E,
|
||||
0xA9435C82, 0x65E95C1C, 0xEB665BFF, 0x27CC5B61, 0x2D095278, 0xE1A352E6, 0x6F2C5505, 0xA386559B,
|
||||
0x061D761C, 0xCAB77682, 0x44387161, 0x889271FF, 0x825778E6, 0x4EFD7878, 0xC0727F9B, 0x0CD87F05,
|
||||
0xD5F86DA9, 0x19526D37, 0x97DD6AD4, 0x5B776A4A, 0x51B26353, 0x9D1863CD, 0x1397642E, 0xDF3D64B0,
|
||||
0x83D02561, 0x4F7A25FF, 0xC1F5221C, 0x0D5F2282, 0x079A2B9B, 0xCB302B05, 0x45BF2CE6, 0x89152C78,
|
||||
0x50353ED4, 0x9C9F3E4A, 0x121039A9, 0xDEBA3937, 0xD47F302E, 0x18D530B0, 0x965A3753, 0x5AF037CD,
|
||||
0xFF6B144A, 0x33C114D4, 0xBD4E1337, 0x71E413A9, 0x7B211AB0, 0xB78B1A2E, 0x39041DCD, 0xF5AE1D53,
|
||||
0x2C8E0FFF, 0xE0240F61, 0x6EAB0882, 0xA201081C, 0xA8C40105, 0x646E019B, 0xEAE10678, 0x264B06E6
|
||||
]
|
||||
];
|
||||
|
||||
readonly uint _finalSeed;
|
||||
readonly IntPtr _nativeContext;
|
||||
readonly uint[][] _table;
|
||||
readonly bool _useIso;
|
||||
readonly bool _useNative;
|
||||
uint _hashInt;
|
||||
|
||||
/// <summary>Initializes the CRC32 table and seed as CRC32-ISO</summary>
|
||||
public Crc32Context()
|
||||
{
|
||||
_hashInt = CRC32_ISO_SEED;
|
||||
_finalSeed = CRC32_ISO_SEED;
|
||||
_table = ISOCrc32Table;
|
||||
_useIso = true;
|
||||
|
||||
_table = new uint[256];
|
||||
if(!Native.IsSupported) return;
|
||||
|
||||
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 ^ CRC32_ISO_POLY;
|
||||
else
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
_table[i] = entry;
|
||||
}
|
||||
_nativeContext = crc32_init();
|
||||
_useNative = _nativeContext != IntPtr.Zero;
|
||||
}
|
||||
|
||||
/// <summary>Initializes the CRC32 table with a custom polynomial and seed</summary>
|
||||
@@ -76,14 +354,40 @@ public sealed class Crc32Context : IChecksum
|
||||
{
|
||||
_hashInt = seed;
|
||||
_finalSeed = seed;
|
||||
_useIso = polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED;
|
||||
|
||||
_table = new uint[256];
|
||||
|
||||
for(var i = 0; i < 256; i++)
|
||||
if(Native.IsSupported && _useIso)
|
||||
{
|
||||
var entry = (uint)i;
|
||||
_nativeContext = crc32_init();
|
||||
_useNative = _nativeContext != IntPtr.Zero;
|
||||
}
|
||||
else
|
||||
_table = GenerateTable(polynomial);
|
||||
}
|
||||
|
||||
for(var j = 0; j < 8; j++)
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial IntPtr crc32_init();
|
||||
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial int crc32_update(IntPtr ctx, byte[] data, uint len);
|
||||
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial int crc32_final(IntPtr ctx, ref uint crc);
|
||||
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial void crc32_free(IntPtr ctx);
|
||||
|
||||
static uint[][] GenerateTable(uint polynomial)
|
||||
{
|
||||
uint[][] table = new uint[8][];
|
||||
|
||||
for(int i = 0; i < 8; i++) table[i] = 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;
|
||||
@@ -91,42 +395,103 @@ public sealed class Crc32Context : IChecksum
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
_table[i] = entry;
|
||||
table[0][i] = entry;
|
||||
}
|
||||
|
||||
for(int slice = 1; slice < 8; slice++)
|
||||
{
|
||||
for(int i = 0; i < 256; i++)
|
||||
table[slice][i] = table[slice - 1][i] >> 8 ^ table[0][table[slice - 1][i] & 0xFF];
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len)
|
||||
static void Step(ref uint previousCrc, uint[][] table, byte[] data, uint len, bool useIso, bool useNative,
|
||||
IntPtr nativeContext)
|
||||
{
|
||||
for(var i = 0; i < len; i++) _hashInt = _hashInt >> 8 ^ _table[data[i] ^ _hashInt & 0xff];
|
||||
}
|
||||
if(useNative && useIso)
|
||||
{
|
||||
crc32_update(nativeContext, data, len);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed);
|
||||
int currentPos = 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
var crc32Output = new StringBuilder();
|
||||
if(useIso)
|
||||
{
|
||||
if(Pclmulqdq.IsSupported && Sse41.IsSupported && Ssse3.IsSupported && Sse2.IsSupported)
|
||||
{
|
||||
// Only works in blocks of 16 bytes
|
||||
uint blocks = len / 64;
|
||||
|
||||
for(var i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed).Length; i++)
|
||||
crc32Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed)[i].ToString("x2"));
|
||||
if(blocks > 0)
|
||||
{
|
||||
previousCrc = ~Clmul.Step(data, blocks * 64, ~previousCrc);
|
||||
|
||||
return crc32Output.ToString();
|
||||
currentPos = (int)(blocks * 64);
|
||||
len -= blocks * 64;
|
||||
}
|
||||
|
||||
if(len == 0) return;
|
||||
}
|
||||
|
||||
if(Crc32.Arm64.IsSupported)
|
||||
{
|
||||
previousCrc = ArmSimd.Step64(data, len, previousCrc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(Crc32.IsSupported)
|
||||
{
|
||||
previousCrc = ArmSimd.Step32(data, len, previousCrc);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Unroll according to Intel slicing by uint8_t
|
||||
// http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf
|
||||
// http://sourceforge.net/projects/slicing-by-8/
|
||||
const int unroll = 4;
|
||||
const int bytesAtOnce = 8 * unroll;
|
||||
uint crc = previousCrc;
|
||||
|
||||
while(len >= bytesAtOnce)
|
||||
{
|
||||
int unrolling;
|
||||
|
||||
for(unrolling = 0; unrolling < unroll; unrolling++)
|
||||
{
|
||||
uint one = BitConverter.ToUInt32(data, currentPos) ^ crc;
|
||||
currentPos += 4;
|
||||
uint two = BitConverter.ToUInt32(data, currentPos);
|
||||
currentPos += 4;
|
||||
|
||||
crc = table[0][two >> 24 & 0xFF] ^
|
||||
table[1][two >> 16 & 0xFF] ^
|
||||
table[2][two >> 8 & 0xFF] ^
|
||||
table[3][two & 0xFF] ^
|
||||
table[4][one >> 24 & 0xFF] ^
|
||||
table[5][one >> 16 & 0xFF] ^
|
||||
table[6][one >> 8 & 0xFF] ^
|
||||
table[7][one & 0xFF];
|
||||
}
|
||||
|
||||
len -= bytesAtOnce;
|
||||
}
|
||||
|
||||
while(len-- != 0) crc = crc >> 8 ^ table[0][crc & 0xFF ^ data[currentPos++]];
|
||||
|
||||
previousCrc = crc;
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Global
|
||||
public static byte[] File(string filename)
|
||||
{
|
||||
File(filename, out byte[] hash);
|
||||
@@ -147,32 +512,41 @@ public sealed class Crc32Context : IChecksum
|
||||
/// <param name="seed">CRC seed</param>
|
||||
public static string File(string filename, out byte[] hash, uint polynomial, uint seed)
|
||||
{
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
bool useIso = polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED;
|
||||
bool useNative = Native.IsSupported;
|
||||
IntPtr nativeContext = IntPtr.Zero;
|
||||
|
||||
uint localhashInt = seed;
|
||||
|
||||
var localTable = new uint[256];
|
||||
|
||||
for(var i = 0; i < 256; i++)
|
||||
if(useNative && useIso)
|
||||
{
|
||||
var entry = (uint)i;
|
||||
|
||||
for(var j = 0; j < 8; j++)
|
||||
{
|
||||
if((entry & 1) == 1)
|
||||
entry = entry >> 1 ^ polynomial;
|
||||
else
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
localTable[i] = entry;
|
||||
nativeContext = crc32_init();
|
||||
useNative = nativeContext != IntPtr.Zero;
|
||||
}
|
||||
|
||||
for(var i = 0; i < fileStream.Length; i++)
|
||||
localhashInt = localhashInt >> 8 ^ localTable[fileStream.ReadByte() ^ localhashInt & 0xff];
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
|
||||
localhashInt ^= seed;
|
||||
hash = BigEndianBitConverter.GetBytes(localhashInt);
|
||||
uint localHashInt = seed;
|
||||
|
||||
uint[][] localTable = GenerateTable(polynomial);
|
||||
|
||||
byte[] buffer = new byte[65536];
|
||||
int read = fileStream.EnsureRead(buffer, 0, 65536);
|
||||
|
||||
while(read > 0)
|
||||
{
|
||||
Step(ref localHashInt, localTable, buffer, (uint)read, useIso, useNative, nativeContext);
|
||||
|
||||
read = fileStream.EnsureRead(buffer, 0, 65536);
|
||||
}
|
||||
|
||||
localHashInt ^= seed;
|
||||
|
||||
if(useNative && useIso)
|
||||
{
|
||||
crc32_final(nativeContext, ref localHashInt);
|
||||
crc32_free(nativeContext);
|
||||
}
|
||||
|
||||
hash = BigEndianBitConverter.GetBytes(localHashInt);
|
||||
|
||||
var crc32Output = new StringBuilder();
|
||||
|
||||
@@ -198,29 +572,31 @@ public sealed class Crc32Context : IChecksum
|
||||
/// <param name="seed">CRC seed</param>
|
||||
public static string Data(byte[] data, uint len, out byte[] hash, uint polynomial, uint seed)
|
||||
{
|
||||
uint localhashInt = seed;
|
||||
bool useIso = polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED;
|
||||
bool useNative = Native.IsSupported;
|
||||
IntPtr nativeContext = IntPtr.Zero;
|
||||
|
||||
var localTable = new uint[256];
|
||||
|
||||
for(var i = 0; i < 256; i++)
|
||||
if(useNative && useIso)
|
||||
{
|
||||
var entry = (uint)i;
|
||||
|
||||
for(var j = 0; j < 8; j++)
|
||||
{
|
||||
if((entry & 1) == 1)
|
||||
entry = entry >> 1 ^ polynomial;
|
||||
else
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
localTable[i] = entry;
|
||||
nativeContext = crc32_init();
|
||||
useNative = nativeContext != IntPtr.Zero;
|
||||
}
|
||||
|
||||
for(var i = 0; i < len; i++) localhashInt = localhashInt >> 8 ^ localTable[data[i] ^ localhashInt & 0xff];
|
||||
uint localHashInt = seed;
|
||||
|
||||
localhashInt ^= seed;
|
||||
hash = BigEndianBitConverter.GetBytes(localhashInt);
|
||||
uint[][] localTable = GenerateTable(polynomial);
|
||||
|
||||
Step(ref localHashInt, localTable, data, len, useIso, useNative, nativeContext);
|
||||
|
||||
localHashInt ^= seed;
|
||||
|
||||
if(useNative && useIso)
|
||||
{
|
||||
crc32_final(nativeContext, ref localHashInt);
|
||||
crc32_free(nativeContext);
|
||||
}
|
||||
|
||||
hash = BigEndianBitConverter.GetBytes(localHashInt);
|
||||
|
||||
var crc32Output = new StringBuilder();
|
||||
|
||||
@@ -233,4 +609,63 @@ public sealed class Crc32Context : IChecksum
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.CRC32_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("BCC4E18A-79CD-4B52-8A57-2B599E5176B3");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) =>
|
||||
Step(ref _hashInt, _table, data, len, _useIso, _useNative, _nativeContext);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
uint crc = _hashInt ^ _finalSeed;
|
||||
|
||||
if(!_useNative || !_useIso) return BigEndianBitConverter.GetBytes(crc);
|
||||
|
||||
crc32_final(_nativeContext, ref crc);
|
||||
crc32_free(_nativeContext);
|
||||
|
||||
return BigEndianBitConverter.GetBytes(crc);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
uint crc = _hashInt ^ _finalSeed;
|
||||
|
||||
var crc32Output = new StringBuilder();
|
||||
|
||||
if(_useNative && _useIso)
|
||||
{
|
||||
crc32_final(_nativeContext, ref crc);
|
||||
crc32_free(_nativeContext);
|
||||
}
|
||||
|
||||
for(int i = 0; i < BigEndianBitConverter.GetBytes(crc).Length; i++)
|
||||
crc32Output.Append(BigEndianBitConverter.GetBytes(crc)[i].ToString("x2"));
|
||||
|
||||
return crc32Output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
122
RomRepoMgr.Core/Checksums/CRC64/clmul.cs
Normal file
122
RomRepoMgr.Core/Checksums/CRC64/clmul.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : clmul.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Checksums.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Compute the CRC64 using a parallelized folding approach with the PCLMULQDQ
|
||||
// instruction.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This file is under the public domain:
|
||||
// https://github.com/rawrunprotected/crc
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace RomRepoMgr.Core.Checksums.CRC64;
|
||||
|
||||
static class Clmul
|
||||
{
|
||||
static readonly byte[] _shuffleMasks =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x8f, 0x8e,
|
||||
0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80
|
||||
];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void ShiftRight128(Vector128<ulong> initial, uint n, out Vector128<ulong> outLeft,
|
||||
out Vector128<ulong> outRight)
|
||||
{
|
||||
uint maskPos = 16 - n;
|
||||
|
||||
var maskA = Vector128.Create(_shuffleMasks[maskPos],
|
||||
_shuffleMasks[maskPos + 1],
|
||||
_shuffleMasks[maskPos + 2],
|
||||
_shuffleMasks[maskPos + 3],
|
||||
_shuffleMasks[maskPos + 4],
|
||||
_shuffleMasks[maskPos + 5],
|
||||
_shuffleMasks[maskPos + 6],
|
||||
_shuffleMasks[maskPos + 7],
|
||||
_shuffleMasks[maskPos + 8],
|
||||
_shuffleMasks[maskPos + 9],
|
||||
_shuffleMasks[maskPos + 10],
|
||||
_shuffleMasks[maskPos + 11],
|
||||
_shuffleMasks[maskPos + 12],
|
||||
_shuffleMasks[maskPos + 13],
|
||||
_shuffleMasks[maskPos + 14],
|
||||
_shuffleMasks[maskPos + 15]);
|
||||
|
||||
Vector128<byte> maskB = Sse2.Xor(maskA, Sse2.CompareEqual(Vector128<byte>.Zero, Vector128<byte>.Zero));
|
||||
|
||||
outLeft = Ssse3.Shuffle(initial.AsByte(), maskB).AsUInt64();
|
||||
outRight = Ssse3.Shuffle(initial.AsByte(), maskA).AsUInt64();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static Vector128<ulong> Fold(Vector128<ulong> input, Vector128<ulong> foldConstants) =>
|
||||
Sse2.Xor(Pclmulqdq.CarrylessMultiply(input, foldConstants, 0x00),
|
||||
Pclmulqdq.CarrylessMultiply(input, foldConstants, 0x11));
|
||||
|
||||
internal static ulong Step(ulong crc, byte[] data, uint length)
|
||||
{
|
||||
int bufPos = 16;
|
||||
const ulong k1 = 0xe05dd497ca393ae4;
|
||||
const ulong k2 = 0xdabe95afc7875f40;
|
||||
const ulong mu = 0x9c3e466c172963d5;
|
||||
const ulong pol = 0x92d8af2baf0e1e85;
|
||||
var foldConstants1 = Vector128.Create(k1, k2);
|
||||
var foldConstants2 = Vector128.Create(mu, pol);
|
||||
var initialCrc = Vector128.Create(~crc, 0);
|
||||
length -= 16;
|
||||
|
||||
// Initial CRC can simply be added to data
|
||||
ShiftRight128(initialCrc, 0, out Vector128<ulong> crc0, out Vector128<ulong> crc1);
|
||||
|
||||
Vector128<ulong> accumulator =
|
||||
Sse2.Xor(Fold(Sse2.Xor(crc0,
|
||||
Vector128.Create(BitConverter.ToUInt64(data, 0), BitConverter.ToUInt64(data, 8))),
|
||||
foldConstants1),
|
||||
crc1);
|
||||
|
||||
while(length >= 32)
|
||||
{
|
||||
accumulator =
|
||||
Fold(Sse2.Xor(Vector128.Create(BitConverter.ToUInt64(data, bufPos),
|
||||
BitConverter.ToUInt64(data, bufPos + 8)),
|
||||
accumulator),
|
||||
foldConstants1);
|
||||
|
||||
length -= 16;
|
||||
bufPos += 16;
|
||||
}
|
||||
|
||||
Vector128<ulong> p = Sse2.Xor(accumulator,
|
||||
Vector128.Create(BitConverter.ToUInt64(data, bufPos),
|
||||
BitConverter.ToUInt64(data, bufPos + 8)));
|
||||
|
||||
Vector128<ulong> r = Sse2.Xor(Pclmulqdq.CarrylessMultiply(p, foldConstants1, 0x10),
|
||||
Sse2.ShiftRightLogical128BitLane(p, 8));
|
||||
|
||||
// Final Barrett reduction
|
||||
Vector128<ulong> t1 = Pclmulqdq.CarrylessMultiply(r, foldConstants2, 0x00);
|
||||
|
||||
Vector128<ulong> t2 = Sse2.Xor(Sse2.Xor(Pclmulqdq.CarrylessMultiply(t1, foldConstants2, 0x10),
|
||||
Sse2.ShiftLeftLogical128BitLane(t1, 8)),
|
||||
r);
|
||||
|
||||
return ~((ulong)Sse41.Extract(t2.AsUInt32(), 3) << 32 | Sse41.Extract(t2.AsUInt32(), 2));
|
||||
}
|
||||
}
|
||||
@@ -27,63 +27,312 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.Helpers;
|
||||
using RomRepoMgr.Core.Checksums.CRC64;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Implements a CRC64 algorithm</summary>
|
||||
public sealed class Crc64Context : IChecksum
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
public sealed partial class Crc64Context : IChecksum
|
||||
{
|
||||
/// <summary>ECMA CRC64 polynomial</summary>
|
||||
const ulong CRC64_ECMA_POLY = 0xC96C5795D7870F42;
|
||||
/// <summary>ECMA CRC64 seed</summary>
|
||||
const ulong CRC64_ECMA_SEED = 0xFFFFFFFFFFFFFFFF;
|
||||
|
||||
readonly ulong _finalSeed;
|
||||
readonly ulong[] _table;
|
||||
ulong _hashInt;
|
||||
static readonly ulong[][] _ecmaCrc64Table =
|
||||
[
|
||||
[
|
||||
0x0000000000000000, 0xB32E4CBE03A75F6F, 0xF4843657A840A05B, 0x47AA7AE9ABE7FF34, 0x7BD0C384FF8F5E33,
|
||||
0xC8FE8F3AFC28015C, 0x8F54F5D357CFFE68, 0x3C7AB96D5468A107, 0xF7A18709FF1EBC66, 0x448FCBB7FCB9E309,
|
||||
0x0325B15E575E1C3D, 0xB00BFDE054F94352, 0x8C71448D0091E255, 0x3F5F08330336BD3A, 0x78F572DAA8D1420E,
|
||||
0xCBDB3E64AB761D61, 0x7D9BA13851336649, 0xCEB5ED8652943926, 0x891F976FF973C612, 0x3A31DBD1FAD4997D,
|
||||
0x064B62BCAEBC387A, 0xB5652E02AD1B6715, 0xF2CF54EB06FC9821, 0x41E11855055BC74E, 0x8A3A2631AE2DDA2F,
|
||||
0x39146A8FAD8A8540, 0x7EBE1066066D7A74, 0xCD905CD805CA251B, 0xF1EAE5B551A2841C, 0x42C4A90B5205DB73,
|
||||
0x056ED3E2F9E22447, 0xB6409F5CFA457B28, 0xFB374270A266CC92, 0x48190ECEA1C193FD, 0x0FB374270A266CC9,
|
||||
0xBC9D3899098133A6, 0x80E781F45DE992A1, 0x33C9CD4A5E4ECDCE, 0x7463B7A3F5A932FA, 0xC74DFB1DF60E6D95,
|
||||
0x0C96C5795D7870F4, 0xBFB889C75EDF2F9B, 0xF812F32EF538D0AF, 0x4B3CBF90F69F8FC0, 0x774606FDA2F72EC7,
|
||||
0xC4684A43A15071A8, 0x83C230AA0AB78E9C, 0x30EC7C140910D1F3, 0x86ACE348F355AADB, 0x3582AFF6F0F2F5B4,
|
||||
0x7228D51F5B150A80, 0xC10699A158B255EF, 0xFD7C20CC0CDAF4E8, 0x4E526C720F7DAB87, 0x09F8169BA49A54B3,
|
||||
0xBAD65A25A73D0BDC, 0x710D64410C4B16BD, 0xC22328FF0FEC49D2, 0x85895216A40BB6E6, 0x36A71EA8A7ACE989,
|
||||
0x0ADDA7C5F3C4488E, 0xB9F3EB7BF06317E1, 0xFE5991925B84E8D5, 0x4D77DD2C5823B7BA, 0x64B62BCAEBC387A1,
|
||||
0xD7986774E864D8CE, 0x90321D9D438327FA, 0x231C512340247895, 0x1F66E84E144CD992, 0xAC48A4F017EB86FD,
|
||||
0xEBE2DE19BC0C79C9, 0x58CC92A7BFAB26A6, 0x9317ACC314DD3BC7, 0x2039E07D177A64A8, 0x67939A94BC9D9B9C,
|
||||
0xD4BDD62ABF3AC4F3, 0xE8C76F47EB5265F4, 0x5BE923F9E8F53A9B, 0x1C4359104312C5AF, 0xAF6D15AE40B59AC0,
|
||||
0x192D8AF2BAF0E1E8, 0xAA03C64CB957BE87, 0xEDA9BCA512B041B3, 0x5E87F01B11171EDC, 0x62FD4976457FBFDB,
|
||||
0xD1D305C846D8E0B4, 0x96797F21ED3F1F80, 0x2557339FEE9840EF, 0xEE8C0DFB45EE5D8E, 0x5DA24145464902E1,
|
||||
0x1A083BACEDAEFDD5, 0xA9267712EE09A2BA, 0x955CCE7FBA6103BD, 0x267282C1B9C65CD2, 0x61D8F8281221A3E6,
|
||||
0xD2F6B4961186FC89, 0x9F8169BA49A54B33, 0x2CAF25044A02145C, 0x6B055FEDE1E5EB68, 0xD82B1353E242B407,
|
||||
0xE451AA3EB62A1500, 0x577FE680B58D4A6F, 0x10D59C691E6AB55B, 0xA3FBD0D71DCDEA34, 0x6820EEB3B6BBF755,
|
||||
0xDB0EA20DB51CA83A, 0x9CA4D8E41EFB570E, 0x2F8A945A1D5C0861, 0x13F02D374934A966, 0xA0DE61894A93F609,
|
||||
0xE7741B60E174093D, 0x545A57DEE2D35652, 0xE21AC88218962D7A, 0x5134843C1B317215, 0x169EFED5B0D68D21,
|
||||
0xA5B0B26BB371D24E, 0x99CA0B06E7197349, 0x2AE447B8E4BE2C26, 0x6D4E3D514F59D312, 0xDE6071EF4CFE8C7D,
|
||||
0x15BB4F8BE788911C, 0xA6950335E42FCE73, 0xE13F79DC4FC83147, 0x521135624C6F6E28, 0x6E6B8C0F1807CF2F,
|
||||
0xDD45C0B11BA09040, 0x9AEFBA58B0476F74, 0x29C1F6E6B3E0301B, 0xC96C5795D7870F42, 0x7A421B2BD420502D,
|
||||
0x3DE861C27FC7AF19, 0x8EC62D7C7C60F076, 0xB2BC941128085171, 0x0192D8AF2BAF0E1E, 0x4638A2468048F12A,
|
||||
0xF516EEF883EFAE45, 0x3ECDD09C2899B324, 0x8DE39C222B3EEC4B, 0xCA49E6CB80D9137F, 0x7967AA75837E4C10,
|
||||
0x451D1318D716ED17, 0xF6335FA6D4B1B278, 0xB199254F7F564D4C, 0x02B769F17CF11223, 0xB4F7F6AD86B4690B,
|
||||
0x07D9BA1385133664, 0x4073C0FA2EF4C950, 0xF35D8C442D53963F, 0xCF273529793B3738, 0x7C0979977A9C6857,
|
||||
0x3BA3037ED17B9763, 0x888D4FC0D2DCC80C, 0x435671A479AAD56D, 0xF0783D1A7A0D8A02, 0xB7D247F3D1EA7536,
|
||||
0x04FC0B4DD24D2A59, 0x3886B22086258B5E, 0x8BA8FE9E8582D431, 0xCC0284772E652B05, 0x7F2CC8C92DC2746A,
|
||||
0x325B15E575E1C3D0, 0x8175595B76469CBF, 0xC6DF23B2DDA1638B, 0x75F16F0CDE063CE4, 0x498BD6618A6E9DE3,
|
||||
0xFAA59ADF89C9C28C, 0xBD0FE036222E3DB8, 0x0E21AC88218962D7, 0xC5FA92EC8AFF7FB6, 0x76D4DE52895820D9,
|
||||
0x317EA4BB22BFDFED, 0x8250E80521188082, 0xBE2A516875702185, 0x0D041DD676D77EEA, 0x4AAE673FDD3081DE,
|
||||
0xF9802B81DE97DEB1, 0x4FC0B4DD24D2A599, 0xFCEEF8632775FAF6, 0xBB44828A8C9205C2, 0x086ACE348F355AAD,
|
||||
0x34107759DB5DFBAA, 0x873E3BE7D8FAA4C5, 0xC094410E731D5BF1, 0x73BA0DB070BA049E, 0xB86133D4DBCC19FF,
|
||||
0x0B4F7F6AD86B4690, 0x4CE50583738CB9A4, 0xFFCB493D702BE6CB, 0xC3B1F050244347CC, 0x709FBCEE27E418A3,
|
||||
0x3735C6078C03E797, 0x841B8AB98FA4B8F8, 0xADDA7C5F3C4488E3, 0x1EF430E13FE3D78C, 0x595E4A08940428B8,
|
||||
0xEA7006B697A377D7, 0xD60ABFDBC3CBD6D0, 0x6524F365C06C89BF, 0x228E898C6B8B768B, 0x91A0C532682C29E4,
|
||||
0x5A7BFB56C35A3485, 0xE955B7E8C0FD6BEA, 0xAEFFCD016B1A94DE, 0x1DD181BF68BDCBB1, 0x21AB38D23CD56AB6,
|
||||
0x9285746C3F7235D9, 0xD52F0E859495CAED, 0x6601423B97329582, 0xD041DD676D77EEAA, 0x636F91D96ED0B1C5,
|
||||
0x24C5EB30C5374EF1, 0x97EBA78EC690119E, 0xAB911EE392F8B099, 0x18BF525D915FEFF6, 0x5F1528B43AB810C2,
|
||||
0xEC3B640A391F4FAD, 0x27E05A6E926952CC, 0x94CE16D091CE0DA3, 0xD3646C393A29F297, 0x604A2087398EADF8,
|
||||
0x5C3099EA6DE60CFF, 0xEF1ED5546E415390, 0xA8B4AFBDC5A6ACA4, 0x1B9AE303C601F3CB, 0x56ED3E2F9E224471,
|
||||
0xE5C372919D851B1E, 0xA26908783662E42A, 0x114744C635C5BB45, 0x2D3DFDAB61AD1A42, 0x9E13B115620A452D,
|
||||
0xD9B9CBFCC9EDBA19, 0x6A978742CA4AE576, 0xA14CB926613CF817, 0x1262F598629BA778, 0x55C88F71C97C584C,
|
||||
0xE6E6C3CFCADB0723, 0xDA9C7AA29EB3A624, 0x69B2361C9D14F94B, 0x2E184CF536F3067F, 0x9D36004B35545910,
|
||||
0x2B769F17CF112238, 0x9858D3A9CCB67D57, 0xDFF2A94067518263, 0x6CDCE5FE64F6DD0C, 0x50A65C93309E7C0B,
|
||||
0xE388102D33392364, 0xA4226AC498DEDC50, 0x170C267A9B79833F, 0xDCD7181E300F9E5E, 0x6FF954A033A8C131,
|
||||
0x28532E49984F3E05, 0x9B7D62F79BE8616A, 0xA707DB9ACF80C06D, 0x14299724CC279F02, 0x5383EDCD67C06036,
|
||||
0xE0ADA17364673F59
|
||||
],
|
||||
[
|
||||
0x0000000000000000, 0x54E979925CD0F10D, 0xA9D2F324B9A1E21A, 0xFD3B8AB6E5711317, 0xC17D4962DC4DDAB1,
|
||||
0x959430F0809D2BBC, 0x68AFBA4665EC38AB, 0x3C46C3D4393CC9A6, 0x10223DEE1795ABE7, 0x44CB447C4B455AEA,
|
||||
0xB9F0CECAAE3449FD, 0xED19B758F2E4B8F0, 0xD15F748CCBD87156, 0x85B60D1E9708805B, 0x788D87A87279934C,
|
||||
0x2C64FE3A2EA96241, 0x20447BDC2F2B57CE, 0x74AD024E73FBA6C3, 0x899688F8968AB5D4, 0xDD7FF16ACA5A44D9,
|
||||
0xE13932BEF3668D7F, 0xB5D04B2CAFB67C72, 0x48EBC19A4AC76F65, 0x1C02B80816179E68, 0x3066463238BEFC29,
|
||||
0x648F3FA0646E0D24, 0x99B4B516811F1E33, 0xCD5DCC84DDCFEF3E, 0xF11B0F50E4F32698, 0xA5F276C2B823D795,
|
||||
0x58C9FC745D52C482, 0x0C2085E60182358F, 0x4088F7B85E56AF9C, 0x14618E2A02865E91, 0xE95A049CE7F74D86,
|
||||
0xBDB37D0EBB27BC8B, 0x81F5BEDA821B752D, 0xD51CC748DECB8420, 0x28274DFE3BBA9737, 0x7CCE346C676A663A,
|
||||
0x50AACA5649C3047B, 0x0443B3C41513F576, 0xF9783972F062E661, 0xAD9140E0ACB2176C, 0x91D78334958EDECA,
|
||||
0xC53EFAA6C95E2FC7, 0x380570102C2F3CD0, 0x6CEC098270FFCDDD, 0x60CC8C64717DF852, 0x3425F5F62DAD095F,
|
||||
0xC91E7F40C8DC1A48, 0x9DF706D2940CEB45, 0xA1B1C506AD3022E3, 0xF558BC94F1E0D3EE, 0x086336221491C0F9,
|
||||
0x5C8A4FB0484131F4, 0x70EEB18A66E853B5, 0x2407C8183A38A2B8, 0xD93C42AEDF49B1AF, 0x8DD53B3C839940A2,
|
||||
0xB193F8E8BAA58904, 0xE57A817AE6757809, 0x18410BCC03046B1E, 0x4CA8725E5FD49A13, 0x8111EF70BCAD5F38,
|
||||
0xD5F896E2E07DAE35, 0x28C31C54050CBD22, 0x7C2A65C659DC4C2F, 0x406CA61260E08589, 0x1485DF803C307484,
|
||||
0xE9BE5536D9416793, 0xBD572CA48591969E, 0x9133D29EAB38F4DF, 0xC5DAAB0CF7E805D2, 0x38E121BA129916C5,
|
||||
0x6C0858284E49E7C8, 0x504E9BFC77752E6E, 0x04A7E26E2BA5DF63, 0xF99C68D8CED4CC74, 0xAD75114A92043D79,
|
||||
0xA15594AC938608F6, 0xF5BCED3ECF56F9FB, 0x088767882A27EAEC, 0x5C6E1E1A76F71BE1, 0x6028DDCE4FCBD247,
|
||||
0x34C1A45C131B234A, 0xC9FA2EEAF66A305D, 0x9D135778AABAC150, 0xB177A9428413A311, 0xE59ED0D0D8C3521C,
|
||||
0x18A55A663DB2410B, 0x4C4C23F46162B006, 0x700AE020585E79A0, 0x24E399B2048E88AD, 0xD9D81304E1FF9BBA,
|
||||
0x8D316A96BD2F6AB7, 0xC19918C8E2FBF0A4, 0x9570615ABE2B01A9, 0x684BEBEC5B5A12BE, 0x3CA2927E078AE3B3,
|
||||
0x00E451AA3EB62A15, 0x540D28386266DB18, 0xA936A28E8717C80F, 0xFDDFDB1CDBC73902, 0xD1BB2526F56E5B43,
|
||||
0x85525CB4A9BEAA4E, 0x7869D6024CCFB959, 0x2C80AF90101F4854, 0x10C66C44292381F2, 0x442F15D675F370FF,
|
||||
0xB9149F60908263E8, 0xEDFDE6F2CC5292E5, 0xE1DD6314CDD0A76A, 0xB5341A8691005667, 0x480F903074714570,
|
||||
0x1CE6E9A228A1B47D, 0x20A02A76119D7DDB, 0x744953E44D4D8CD6, 0x8972D952A83C9FC1, 0xDD9BA0C0F4EC6ECC,
|
||||
0xF1FF5EFADA450C8D, 0xA51627688695FD80, 0x582DADDE63E4EE97, 0x0CC4D44C3F341F9A, 0x308217980608D63C,
|
||||
0x646B6E0A5AD82731, 0x9950E4BCBFA93426, 0xCDB99D2EE379C52B, 0x90FB71CAD654A0F5, 0xC41208588A8451F8,
|
||||
0x392982EE6FF542EF, 0x6DC0FB7C3325B3E2, 0x518638A80A197A44, 0x056F413A56C98B49, 0xF854CB8CB3B8985E,
|
||||
0xACBDB21EEF686953, 0x80D94C24C1C10B12, 0xD43035B69D11FA1F, 0x290BBF007860E908, 0x7DE2C69224B01805,
|
||||
0x41A405461D8CD1A3, 0x154D7CD4415C20AE, 0xE876F662A42D33B9, 0xBC9F8FF0F8FDC2B4, 0xB0BF0A16F97FF73B,
|
||||
0xE4567384A5AF0636, 0x196DF93240DE1521, 0x4D8480A01C0EE42C, 0x71C2437425322D8A, 0x252B3AE679E2DC87,
|
||||
0xD810B0509C93CF90, 0x8CF9C9C2C0433E9D, 0xA09D37F8EEEA5CDC, 0xF4744E6AB23AADD1, 0x094FC4DC574BBEC6,
|
||||
0x5DA6BD4E0B9B4FCB, 0x61E07E9A32A7866D, 0x350907086E777760, 0xC8328DBE8B066477, 0x9CDBF42CD7D6957A,
|
||||
0xD073867288020F69, 0x849AFFE0D4D2FE64, 0x79A1755631A3ED73, 0x2D480CC46D731C7E, 0x110ECF10544FD5D8,
|
||||
0x45E7B682089F24D5, 0xB8DC3C34EDEE37C2, 0xEC3545A6B13EC6CF, 0xC051BB9C9F97A48E, 0x94B8C20EC3475583,
|
||||
0x698348B826364694, 0x3D6A312A7AE6B799, 0x012CF2FE43DA7E3F, 0x55C58B6C1F0A8F32, 0xA8FE01DAFA7B9C25,
|
||||
0xFC177848A6AB6D28, 0xF037FDAEA72958A7, 0xA4DE843CFBF9A9AA, 0x59E50E8A1E88BABD, 0x0D0C771842584BB0,
|
||||
0x314AB4CC7B648216, 0x65A3CD5E27B4731B, 0x989847E8C2C5600C, 0xCC713E7A9E159101, 0xE015C040B0BCF340,
|
||||
0xB4FCB9D2EC6C024D, 0x49C73364091D115A, 0x1D2E4AF655CDE057, 0x216889226CF129F1, 0x7581F0B03021D8FC,
|
||||
0x88BA7A06D550CBEB, 0xDC53039489803AE6, 0x11EA9EBA6AF9FFCD, 0x4503E72836290EC0, 0xB8386D9ED3581DD7,
|
||||
0xECD1140C8F88ECDA, 0xD097D7D8B6B4257C, 0x847EAE4AEA64D471, 0x794524FC0F15C766, 0x2DAC5D6E53C5366B,
|
||||
0x01C8A3547D6C542A, 0x5521DAC621BCA527, 0xA81A5070C4CDB630, 0xFCF329E2981D473D, 0xC0B5EA36A1218E9B,
|
||||
0x945C93A4FDF17F96, 0x6967191218806C81, 0x3D8E608044509D8C, 0x31AEE56645D2A803, 0x65479CF41902590E,
|
||||
0x987C1642FC734A19, 0xCC956FD0A0A3BB14, 0xF0D3AC04999F72B2, 0xA43AD596C54F83BF, 0x59015F20203E90A8,
|
||||
0x0DE826B27CEE61A5, 0x218CD888524703E4, 0x7565A11A0E97F2E9, 0x885E2BACEBE6E1FE, 0xDCB7523EB73610F3,
|
||||
0xE0F191EA8E0AD955, 0xB418E878D2DA2858, 0x492362CE37AB3B4F, 0x1DCA1B5C6B7BCA42, 0x5162690234AF5051,
|
||||
0x058B1090687FA15C, 0xF8B09A268D0EB24B, 0xAC59E3B4D1DE4346, 0x901F2060E8E28AE0, 0xC4F659F2B4327BED,
|
||||
0x39CDD344514368FA, 0x6D24AAD60D9399F7, 0x414054EC233AFBB6, 0x15A92D7E7FEA0ABB, 0xE892A7C89A9B19AC,
|
||||
0xBC7BDE5AC64BE8A1, 0x803D1D8EFF772107, 0xD4D4641CA3A7D00A, 0x29EFEEAA46D6C31D, 0x7D0697381A063210,
|
||||
0x712612DE1B84079F, 0x25CF6B4C4754F692, 0xD8F4E1FAA225E585, 0x8C1D9868FEF51488, 0xB05B5BBCC7C9DD2E,
|
||||
0xE4B2222E9B192C23, 0x1989A8987E683F34, 0x4D60D10A22B8CE39, 0x61042F300C11AC78, 0x35ED56A250C15D75,
|
||||
0xC8D6DC14B5B04E62, 0x9C3FA586E960BF6F, 0xA0796652D05C76C9, 0xF4901FC08C8C87C4, 0x09AB957669FD94D3,
|
||||
0x5D42ECE4352D65DE
|
||||
],
|
||||
[
|
||||
0x0000000000000000, 0x3F0BE14A916A6DCB, 0x7E17C29522D4DB96, 0x411C23DFB3BEB65D, 0xFC2F852A45A9B72C,
|
||||
0xC3246460D4C3DAE7, 0x823847BF677D6CBA, 0xBD33A6F5F6170171, 0x6A87A57F245D70DD, 0x558C4435B5371D16,
|
||||
0x149067EA0689AB4B, 0x2B9B86A097E3C680, 0x96A8205561F4C7F1, 0xA9A3C11FF09EAA3A, 0xE8BFE2C043201C67,
|
||||
0xD7B4038AD24A71AC, 0xD50F4AFE48BAE1BA, 0xEA04ABB4D9D08C71, 0xAB18886B6A6E3A2C, 0x94136921FB0457E7,
|
||||
0x2920CFD40D135696, 0x162B2E9E9C793B5D, 0x57370D412FC78D00, 0x683CEC0BBEADE0CB, 0xBF88EF816CE79167,
|
||||
0x80830ECBFD8DFCAC, 0xC19F2D144E334AF1, 0xFE94CC5EDF59273A, 0x43A76AAB294E264B, 0x7CAC8BE1B8244B80,
|
||||
0x3DB0A83E0B9AFDDD, 0x02BB49749AF09016, 0x38C63AD73E7BDDF1, 0x07CDDB9DAF11B03A, 0x46D1F8421CAF0667,
|
||||
0x79DA19088DC56BAC, 0xC4E9BFFD7BD26ADD, 0xFBE25EB7EAB80716, 0xBAFE7D685906B14B, 0x85F59C22C86CDC80,
|
||||
0x52419FA81A26AD2C, 0x6D4A7EE28B4CC0E7, 0x2C565D3D38F276BA, 0x135DBC77A9981B71, 0xAE6E1A825F8F1A00,
|
||||
0x9165FBC8CEE577CB, 0xD079D8177D5BC196, 0xEF72395DEC31AC5D, 0xEDC9702976C13C4B, 0xD2C29163E7AB5180,
|
||||
0x93DEB2BC5415E7DD, 0xACD553F6C57F8A16, 0x11E6F50333688B67, 0x2EED1449A202E6AC, 0x6FF1379611BC50F1,
|
||||
0x50FAD6DC80D63D3A, 0x874ED556529C4C96, 0xB845341CC3F6215D, 0xF95917C370489700, 0xC652F689E122FACB,
|
||||
0x7B61507C1735FBBA, 0x446AB136865F9671, 0x057692E935E1202C, 0x3A7D73A3A48B4DE7, 0x718C75AE7CF7BBE2,
|
||||
0x4E8794E4ED9DD629, 0x0F9BB73B5E236074, 0x30905671CF490DBF, 0x8DA3F084395E0CCE, 0xB2A811CEA8346105,
|
||||
0xF3B432111B8AD758, 0xCCBFD35B8AE0BA93, 0x1B0BD0D158AACB3F, 0x2400319BC9C0A6F4, 0x651C12447A7E10A9,
|
||||
0x5A17F30EEB147D62, 0xE72455FB1D037C13, 0xD82FB4B18C6911D8, 0x9933976E3FD7A785, 0xA6387624AEBDCA4E,
|
||||
0xA4833F50344D5A58, 0x9B88DE1AA5273793, 0xDA94FDC5169981CE, 0xE59F1C8F87F3EC05, 0x58ACBA7A71E4ED74,
|
||||
0x67A75B30E08E80BF, 0x26BB78EF533036E2, 0x19B099A5C25A5B29, 0xCE049A2F10102A85, 0xF10F7B65817A474E,
|
||||
0xB01358BA32C4F113, 0x8F18B9F0A3AE9CD8, 0x322B1F0555B99DA9, 0x0D20FE4FC4D3F062, 0x4C3CDD90776D463F,
|
||||
0x73373CDAE6072BF4, 0x494A4F79428C6613, 0x7641AE33D3E60BD8, 0x375D8DEC6058BD85, 0x08566CA6F132D04E,
|
||||
0xB565CA530725D13F, 0x8A6E2B19964FBCF4, 0xCB7208C625F10AA9, 0xF479E98CB49B6762, 0x23CDEA0666D116CE,
|
||||
0x1CC60B4CF7BB7B05, 0x5DDA28934405CD58, 0x62D1C9D9D56FA093, 0xDFE26F2C2378A1E2, 0xE0E98E66B212CC29,
|
||||
0xA1F5ADB901AC7A74, 0x9EFE4CF390C617BF, 0x9C4505870A3687A9, 0xA34EE4CD9B5CEA62, 0xE252C71228E25C3F,
|
||||
0xDD592658B98831F4, 0x606A80AD4F9F3085, 0x5F6161E7DEF55D4E, 0x1E7D42386D4BEB13, 0x2176A372FC2186D8,
|
||||
0xF6C2A0F82E6BF774, 0xC9C941B2BF019ABF, 0x88D5626D0CBF2CE2, 0xB7DE83279DD54129, 0x0AED25D26BC24058,
|
||||
0x35E6C498FAA82D93, 0x74FAE74749169BCE, 0x4BF1060DD87CF605, 0xE318EB5CF9EF77C4, 0xDC130A1668851A0F,
|
||||
0x9D0F29C9DB3BAC52, 0xA204C8834A51C199, 0x1F376E76BC46C0E8, 0x203C8F3C2D2CAD23, 0x6120ACE39E921B7E,
|
||||
0x5E2B4DA90FF876B5, 0x899F4E23DDB20719, 0xB694AF694CD86AD2, 0xF7888CB6FF66DC8F, 0xC8836DFC6E0CB144,
|
||||
0x75B0CB09981BB035, 0x4ABB2A430971DDFE, 0x0BA7099CBACF6BA3, 0x34ACE8D62BA50668, 0x3617A1A2B155967E,
|
||||
0x091C40E8203FFBB5, 0x4800633793814DE8, 0x770B827D02EB2023, 0xCA382488F4FC2152, 0xF533C5C265964C99,
|
||||
0xB42FE61DD628FAC4, 0x8B2407574742970F, 0x5C9004DD9508E6A3, 0x639BE59704628B68, 0x2287C648B7DC3D35,
|
||||
0x1D8C270226B650FE, 0xA0BF81F7D0A1518F, 0x9FB460BD41CB3C44, 0xDEA84362F2758A19, 0xE1A3A228631FE7D2,
|
||||
0xDBDED18BC794AA35, 0xE4D530C156FEC7FE, 0xA5C9131EE54071A3, 0x9AC2F254742A1C68, 0x27F154A1823D1D19,
|
||||
0x18FAB5EB135770D2, 0x59E69634A0E9C68F, 0x66ED777E3183AB44, 0xB15974F4E3C9DAE8, 0x8E5295BE72A3B723,
|
||||
0xCF4EB661C11D017E, 0xF045572B50776CB5, 0x4D76F1DEA6606DC4, 0x727D1094370A000F, 0x3361334B84B4B652,
|
||||
0x0C6AD20115DEDB99, 0x0ED19B758F2E4B8F, 0x31DA7A3F1E442644, 0x70C659E0ADFA9019, 0x4FCDB8AA3C90FDD2,
|
||||
0xF2FE1E5FCA87FCA3, 0xCDF5FF155BED9168, 0x8CE9DCCAE8532735, 0xB3E23D8079394AFE, 0x64563E0AAB733B52,
|
||||
0x5B5DDF403A195699, 0x1A41FC9F89A7E0C4, 0x254A1DD518CD8D0F, 0x9879BB20EEDA8C7E, 0xA7725A6A7FB0E1B5,
|
||||
0xE66E79B5CC0E57E8, 0xD96598FF5D643A23, 0x92949EF28518CC26, 0xAD9F7FB81472A1ED, 0xEC835C67A7CC17B0,
|
||||
0xD388BD2D36A67A7B, 0x6EBB1BD8C0B17B0A, 0x51B0FA9251DB16C1, 0x10ACD94DE265A09C, 0x2FA73807730FCD57,
|
||||
0xF8133B8DA145BCFB, 0xC718DAC7302FD130, 0x8604F9188391676D, 0xB90F185212FB0AA6, 0x043CBEA7E4EC0BD7,
|
||||
0x3B375FED7586661C, 0x7A2B7C32C638D041, 0x45209D785752BD8A, 0x479BD40CCDA22D9C, 0x789035465CC84057,
|
||||
0x398C1699EF76F60A, 0x0687F7D37E1C9BC1, 0xBBB45126880B9AB0, 0x84BFB06C1961F77B, 0xC5A393B3AADF4126,
|
||||
0xFAA872F93BB52CED, 0x2D1C7173E9FF5D41, 0x121790397895308A, 0x530BB3E6CB2B86D7, 0x6C0052AC5A41EB1C,
|
||||
0xD133F459AC56EA6D, 0xEE3815133D3C87A6, 0xAF2436CC8E8231FB, 0x902FD7861FE85C30, 0xAA52A425BB6311D7,
|
||||
0x9559456F2A097C1C, 0xD44566B099B7CA41, 0xEB4E87FA08DDA78A, 0x567D210FFECAA6FB, 0x6976C0456FA0CB30,
|
||||
0x286AE39ADC1E7D6D, 0x176102D04D7410A6, 0xC0D5015A9F3E610A, 0xFFDEE0100E540CC1, 0xBEC2C3CFBDEABA9C,
|
||||
0x81C922852C80D757, 0x3CFA8470DA97D626, 0x03F1653A4BFDBBED, 0x42ED46E5F8430DB0, 0x7DE6A7AF6929607B,
|
||||
0x7F5DEEDBF3D9F06D, 0x40560F9162B39DA6, 0x014A2C4ED10D2BFB, 0x3E41CD0440674630, 0x83726BF1B6704741,
|
||||
0xBC798ABB271A2A8A, 0xFD65A96494A49CD7, 0xC26E482E05CEF11C, 0x15DA4BA4D78480B0, 0x2AD1AAEE46EEED7B,
|
||||
0x6BCD8931F5505B26, 0x54C6687B643A36ED, 0xE9F5CE8E922D379C, 0xD6FE2FC403475A57, 0x97E20C1BB0F9EC0A,
|
||||
0xA8E9ED51219381C1
|
||||
],
|
||||
[
|
||||
0x0000000000000000, 0x1DEE8A5E222CA1DC, 0x3BDD14BC445943B8, 0x26339EE26675E264, 0x77BA297888B28770,
|
||||
0x6A54A326AA9E26AC, 0x4C673DC4CCEBC4C8, 0x5189B79AEEC76514, 0xEF7452F111650EE0, 0xF29AD8AF3349AF3C,
|
||||
0xD4A9464D553C4D58, 0xC947CC137710EC84, 0x98CE7B8999D78990, 0x8520F1D7BBFB284C, 0xA3136F35DD8ECA28,
|
||||
0xBEFDE56BFFA26BF4, 0x4C300AC98DC40345, 0x51DE8097AFE8A299, 0x77ED1E75C99D40FD, 0x6A03942BEBB1E121,
|
||||
0x3B8A23B105768435, 0x2664A9EF275A25E9, 0x0057370D412FC78D, 0x1DB9BD5363036651, 0xA34458389CA10DA5,
|
||||
0xBEAAD266BE8DAC79, 0x98994C84D8F84E1D, 0x8577C6DAFAD4EFC1, 0xD4FE714014138AD5, 0xC910FB1E363F2B09,
|
||||
0xEF2365FC504AC96D, 0xF2CDEFA2726668B1, 0x986015931B88068A, 0x858E9FCD39A4A756, 0xA3BD012F5FD14532,
|
||||
0xBE538B717DFDE4EE, 0xEFDA3CEB933A81FA, 0xF234B6B5B1162026, 0xD4072857D763C242, 0xC9E9A209F54F639E,
|
||||
0x771447620AED086A, 0x6AFACD3C28C1A9B6, 0x4CC953DE4EB44BD2, 0x5127D9806C98EA0E, 0x00AE6E1A825F8F1A,
|
||||
0x1D40E444A0732EC6, 0x3B737AA6C606CCA2, 0x269DF0F8E42A6D7E, 0xD4501F5A964C05CF, 0xC9BE9504B460A413,
|
||||
0xEF8D0BE6D2154677, 0xF26381B8F039E7AB, 0xA3EA36221EFE82BF, 0xBE04BC7C3CD22363, 0x9837229E5AA7C107,
|
||||
0x85D9A8C0788B60DB, 0x3B244DAB87290B2F, 0x26CAC7F5A505AAF3, 0x00F95917C3704897, 0x1D17D349E15CE94B,
|
||||
0x4C9E64D30F9B8C5F, 0x5170EE8D2DB72D83, 0x7743706F4BC2CFE7, 0x6AADFA3169EE6E3B, 0xA218840D981E1391,
|
||||
0xBFF60E53BA32B24D, 0x99C590B1DC475029, 0x842B1AEFFE6BF1F5, 0xD5A2AD7510AC94E1, 0xC84C272B3280353D,
|
||||
0xEE7FB9C954F5D759, 0xF391339776D97685, 0x4D6CD6FC897B1D71, 0x50825CA2AB57BCAD, 0x76B1C240CD225EC9,
|
||||
0x6B5F481EEF0EFF15, 0x3AD6FF8401C99A01, 0x273875DA23E53BDD, 0x010BEB384590D9B9, 0x1CE5616667BC7865,
|
||||
0xEE288EC415DA10D4, 0xF3C6049A37F6B108, 0xD5F59A785183536C, 0xC81B102673AFF2B0, 0x9992A7BC9D6897A4,
|
||||
0x847C2DE2BF443678, 0xA24FB300D931D41C, 0xBFA1395EFB1D75C0, 0x015CDC3504BF1E34, 0x1CB2566B2693BFE8,
|
||||
0x3A81C88940E65D8C, 0x276F42D762CAFC50, 0x76E6F54D8C0D9944, 0x6B087F13AE213898, 0x4D3BE1F1C854DAFC,
|
||||
0x50D56BAFEA787B20, 0x3A78919E8396151B, 0x27961BC0A1BAB4C7, 0x01A58522C7CF56A3, 0x1C4B0F7CE5E3F77F,
|
||||
0x4DC2B8E60B24926B, 0x502C32B8290833B7, 0x761FAC5A4F7DD1D3, 0x6BF126046D51700F, 0xD50CC36F92F31BFB,
|
||||
0xC8E24931B0DFBA27, 0xEED1D7D3D6AA5843, 0xF33F5D8DF486F99F, 0xA2B6EA171A419C8B, 0xBF586049386D3D57,
|
||||
0x996BFEAB5E18DF33, 0x848574F57C347EEF, 0x76489B570E52165E, 0x6BA611092C7EB782, 0x4D958FEB4A0B55E6,
|
||||
0x507B05B56827F43A, 0x01F2B22F86E0912E, 0x1C1C3871A4CC30F2, 0x3A2FA693C2B9D296, 0x27C12CCDE095734A,
|
||||
0x993CC9A61F3718BE, 0x84D243F83D1BB962, 0xA2E1DD1A5B6E5B06, 0xBF0F57447942FADA, 0xEE86E0DE97859FCE,
|
||||
0xF3686A80B5A93E12, 0xD55BF462D3DCDC76, 0xC8B57E3CF1F07DAA, 0xD6E9A7309F3239A7, 0xCB072D6EBD1E987B,
|
||||
0xED34B38CDB6B7A1F, 0xF0DA39D2F947DBC3, 0xA1538E481780BED7, 0xBCBD041635AC1F0B, 0x9A8E9AF453D9FD6F,
|
||||
0x876010AA71F55CB3, 0x399DF5C18E573747, 0x24737F9FAC7B969B, 0x0240E17DCA0E74FF, 0x1FAE6B23E822D523,
|
||||
0x4E27DCB906E5B037, 0x53C956E724C911EB, 0x75FAC80542BCF38F, 0x6814425B60905253, 0x9AD9ADF912F63AE2,
|
||||
0x873727A730DA9B3E, 0xA104B94556AF795A, 0xBCEA331B7483D886, 0xED6384819A44BD92, 0xF08D0EDFB8681C4E,
|
||||
0xD6BE903DDE1DFE2A, 0xCB501A63FC315FF6, 0x75ADFF0803933402, 0x6843755621BF95DE, 0x4E70EBB447CA77BA,
|
||||
0x539E61EA65E6D666, 0x0217D6708B21B372, 0x1FF95C2EA90D12AE, 0x39CAC2CCCF78F0CA, 0x24244892ED545116,
|
||||
0x4E89B2A384BA3F2D, 0x536738FDA6969EF1, 0x7554A61FC0E37C95, 0x68BA2C41E2CFDD49, 0x39339BDB0C08B85D,
|
||||
0x24DD11852E241981, 0x02EE8F674851FBE5, 0x1F0005396A7D5A39, 0xA1FDE05295DF31CD, 0xBC136A0CB7F39011,
|
||||
0x9A20F4EED1867275, 0x87CE7EB0F3AAD3A9, 0xD647C92A1D6DB6BD, 0xCBA943743F411761, 0xED9ADD965934F505,
|
||||
0xF07457C87B1854D9, 0x02B9B86A097E3C68, 0x1F5732342B529DB4, 0x3964ACD64D277FD0, 0x248A26886F0BDE0C,
|
||||
0x7503911281CCBB18, 0x68ED1B4CA3E01AC4, 0x4EDE85AEC595F8A0, 0x53300FF0E7B9597C, 0xEDCDEA9B181B3288,
|
||||
0xF02360C53A379354, 0xD610FE275C427130, 0xCBFE74797E6ED0EC, 0x9A77C3E390A9B5F8, 0x879949BDB2851424,
|
||||
0xA1AAD75FD4F0F640, 0xBC445D01F6DC579C, 0x74F1233D072C2A36, 0x691FA96325008BEA, 0x4F2C37814375698E,
|
||||
0x52C2BDDF6159C852, 0x034B0A458F9EAD46, 0x1EA5801BADB20C9A, 0x38961EF9CBC7EEFE, 0x257894A7E9EB4F22,
|
||||
0x9B8571CC164924D6, 0x866BFB923465850A, 0xA05865705210676E, 0xBDB6EF2E703CC6B2, 0xEC3F58B49EFBA3A6,
|
||||
0xF1D1D2EABCD7027A, 0xD7E24C08DAA2E01E, 0xCA0CC656F88E41C2, 0x38C129F48AE82973, 0x252FA3AAA8C488AF,
|
||||
0x031C3D48CEB16ACB, 0x1EF2B716EC9DCB17, 0x4F7B008C025AAE03, 0x52958AD220760FDF, 0x74A614304603EDBB,
|
||||
0x69489E6E642F4C67, 0xD7B57B059B8D2793, 0xCA5BF15BB9A1864F, 0xEC686FB9DFD4642B, 0xF186E5E7FDF8C5F7,
|
||||
0xA00F527D133FA0E3, 0xBDE1D8233113013F, 0x9BD246C15766E35B, 0x863CCC9F754A4287, 0xEC9136AE1CA42CBC,
|
||||
0xF17FBCF03E888D60, 0xD74C221258FD6F04, 0xCAA2A84C7AD1CED8, 0x9B2B1FD69416ABCC, 0x86C59588B63A0A10,
|
||||
0xA0F60B6AD04FE874, 0xBD188134F26349A8, 0x03E5645F0DC1225C, 0x1E0BEE012FED8380, 0x383870E3499861E4,
|
||||
0x25D6FABD6BB4C038, 0x745F4D278573A52C, 0x69B1C779A75F04F0, 0x4F82599BC12AE694, 0x526CD3C5E3064748,
|
||||
0xA0A13C6791602FF9, 0xBD4FB639B34C8E25, 0x9B7C28DBD5396C41, 0x8692A285F715CD9D, 0xD71B151F19D2A889,
|
||||
0xCAF59F413BFE0955, 0xECC601A35D8BEB31, 0xF1288BFD7FA74AED, 0x4FD56E9680052119, 0x523BE4C8A22980C5,
|
||||
0x74087A2AC45C62A1, 0x69E6F074E670C37D, 0x386F47EE08B7A669, 0x2581CDB02A9B07B5, 0x03B253524CEEE5D1,
|
||||
0x1E5CD90C6EC2440D
|
||||
]
|
||||
];
|
||||
|
||||
readonly ulong _finalSeed;
|
||||
readonly IntPtr _nativeContext;
|
||||
readonly ulong[][] _table;
|
||||
readonly bool _useEcma;
|
||||
readonly bool _useNative;
|
||||
ulong _hashInt;
|
||||
|
||||
/// <summary>Initializes the CRC64 table and seed as CRC64-ECMA</summary>
|
||||
public Crc64Context()
|
||||
{
|
||||
_hashInt = CRC64_ECMA_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 ^ CRC64_ECMA_POLY;
|
||||
else
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
_table[i] = entry;
|
||||
}
|
||||
|
||||
_hashInt = CRC64_ECMA_SEED;
|
||||
_table = _ecmaCrc64Table;
|
||||
_finalSeed = CRC64_ECMA_SEED;
|
||||
_useEcma = true;
|
||||
|
||||
if(!Native.IsSupported) return;
|
||||
|
||||
_nativeContext = crc64_init();
|
||||
_useNative = _nativeContext != IntPtr.Zero;
|
||||
}
|
||||
|
||||
/// <summary>Initializes the CRC16 table with a custom polynomial and seed</summary>
|
||||
public Crc64Context(ulong polynomial, ulong seed)
|
||||
{
|
||||
_hashInt = seed;
|
||||
_hashInt = seed;
|
||||
_finalSeed = seed;
|
||||
_useEcma = polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED;
|
||||
|
||||
_table = new ulong[256];
|
||||
|
||||
for(var i = 0; i < 256; i++)
|
||||
if(Native.IsSupported && _useEcma)
|
||||
{
|
||||
var entry = (ulong)i;
|
||||
_nativeContext = crc64_init();
|
||||
_useNative = _nativeContext != IntPtr.Zero;
|
||||
}
|
||||
else
|
||||
_table = GenerateTable(polynomial);
|
||||
}
|
||||
|
||||
for(var j = 0; j < 8; j++)
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial IntPtr crc64_init();
|
||||
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial int crc64_update(IntPtr ctx, byte[] data, uint len);
|
||||
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial int crc64_final(IntPtr ctx, ref ulong crc);
|
||||
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial void crc64_free(IntPtr ctx);
|
||||
|
||||
static ulong[][] GenerateTable(ulong polynomial)
|
||||
{
|
||||
ulong[][] table = new ulong[8][];
|
||||
|
||||
for(int i = 0; i < 8; i++) table[i] = 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;
|
||||
@@ -91,44 +340,79 @@ public sealed class Crc64Context : IChecksum
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
_table[i] = entry;
|
||||
table[0][i] = entry;
|
||||
}
|
||||
|
||||
_finalSeed = seed;
|
||||
for(int slice = 1; slice < 4; slice++)
|
||||
{
|
||||
for(int i = 0; i < 256; i++)
|
||||
table[slice][i] = table[slice - 1][i] >> 8 ^ table[0][table[slice - 1][i] & 0xFF];
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len)
|
||||
static void Step(ref ulong previousCrc, ulong[][] table, byte[] data, uint len, bool useEcma, bool useNative,
|
||||
IntPtr nativeContext)
|
||||
{
|
||||
for(var i = 0; i < len; i++) _hashInt = _hashInt >> 8 ^ _table[data[i] ^ _hashInt & 0xff];
|
||||
}
|
||||
if(useNative && useEcma)
|
||||
{
|
||||
crc64_update(nativeContext, data, len);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed);
|
||||
int dataOff = 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
var crc64Output = new StringBuilder();
|
||||
if(useEcma && Pclmulqdq.IsSupported && Sse41.IsSupported && Ssse3.IsSupported && Sse2.IsSupported)
|
||||
{
|
||||
// Only works in blocks of 32 bytes
|
||||
uint blocks = len / 32;
|
||||
|
||||
for(var i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed).Length; i++)
|
||||
crc64Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed)[i].ToString("x2"));
|
||||
if(blocks > 0)
|
||||
{
|
||||
previousCrc = ~Clmul.Step(~previousCrc, data, blocks * 32);
|
||||
|
||||
return crc64Output.ToString();
|
||||
dataOff = (int)(blocks * 32);
|
||||
len -= blocks * 32;
|
||||
}
|
||||
|
||||
if(len == 0) return;
|
||||
}
|
||||
|
||||
// Unroll according to Intel slicing by uint8_t
|
||||
// http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf
|
||||
// http://sourceforge.net/projects/slicing-by-8/
|
||||
|
||||
ulong crc = previousCrc;
|
||||
|
||||
if(len > 4)
|
||||
{
|
||||
long limit = dataOff + (len & ~(uint)3);
|
||||
len &= 3;
|
||||
|
||||
while(dataOff < limit)
|
||||
{
|
||||
uint tmp = (uint)(crc ^ BitConverter.ToUInt32(data, dataOff));
|
||||
dataOff += 4;
|
||||
|
||||
crc = table[3][tmp & 0xFF] ^
|
||||
table[2][tmp >> 8 & 0xFF] ^
|
||||
crc >> 32 ^
|
||||
table[1][tmp >> 16 & 0xFF] ^
|
||||
table[0][tmp >> 24];
|
||||
}
|
||||
}
|
||||
|
||||
while(len-- != 0) crc = table[0][data[dataOff++] ^ crc & 0xFF] ^ crc >> 8;
|
||||
|
||||
previousCrc = crc;
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Global
|
||||
public static byte[] File(string filename)
|
||||
{
|
||||
File(filename, out byte[] localHash);
|
||||
@@ -149,32 +433,41 @@ public sealed class Crc64Context : IChecksum
|
||||
/// <param name="seed">CRC seed</param>
|
||||
public static string File(string filename, out byte[] hash, ulong polynomial, ulong seed)
|
||||
{
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
bool useEcma = polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED;
|
||||
bool useNative = Native.IsSupported;
|
||||
IntPtr nativeContext = IntPtr.Zero;
|
||||
|
||||
ulong localhashInt = seed;
|
||||
|
||||
var localTable = new ulong[256];
|
||||
|
||||
for(var i = 0; i < 256; i++)
|
||||
if(useNative && useEcma)
|
||||
{
|
||||
var entry = (ulong)i;
|
||||
|
||||
for(var j = 0; j < 8; j++)
|
||||
{
|
||||
if((entry & 1) == 1)
|
||||
entry = entry >> 1 ^ polynomial;
|
||||
else
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
localTable[i] = entry;
|
||||
nativeContext = crc64_init();
|
||||
useNative = nativeContext != IntPtr.Zero;
|
||||
}
|
||||
|
||||
for(var i = 0; i < fileStream.Length; i++)
|
||||
localhashInt = localhashInt >> 8 ^ localTable[(ulong)fileStream.ReadByte() ^ localhashInt & 0xffL];
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
|
||||
localhashInt ^= seed;
|
||||
hash = BigEndianBitConverter.GetBytes(localhashInt);
|
||||
ulong localHashInt = seed;
|
||||
|
||||
ulong[][] localTable = GenerateTable(polynomial);
|
||||
|
||||
byte[] buffer = new byte[65536];
|
||||
int read = fileStream.EnsureRead(buffer, 0, 65536);
|
||||
|
||||
while(read > 0)
|
||||
{
|
||||
Step(ref localHashInt, localTable, buffer, (uint)read, useEcma, useNative, nativeContext);
|
||||
|
||||
read = fileStream.EnsureRead(buffer, 0, 65536);
|
||||
}
|
||||
|
||||
localHashInt ^= seed;
|
||||
|
||||
if(useNative && useEcma)
|
||||
{
|
||||
crc64_final(nativeContext, ref localHashInt);
|
||||
crc64_free(nativeContext);
|
||||
}
|
||||
|
||||
hash = BigEndianBitConverter.GetBytes(localHashInt);
|
||||
|
||||
var crc64Output = new StringBuilder();
|
||||
|
||||
@@ -200,29 +493,31 @@ public sealed class Crc64Context : IChecksum
|
||||
/// <param name="seed">CRC seed</param>
|
||||
public static string Data(byte[] data, uint len, out byte[] hash, ulong polynomial, ulong seed)
|
||||
{
|
||||
ulong localhashInt = seed;
|
||||
bool useEcma = polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED;
|
||||
bool useNative = Native.IsSupported;
|
||||
IntPtr nativeContext = IntPtr.Zero;
|
||||
|
||||
var localTable = new ulong[256];
|
||||
|
||||
for(var i = 0; i < 256; i++)
|
||||
if(useNative && useEcma)
|
||||
{
|
||||
var entry = (ulong)i;
|
||||
|
||||
for(var j = 0; j < 8; j++)
|
||||
{
|
||||
if((entry & 1) == 1)
|
||||
entry = entry >> 1 ^ polynomial;
|
||||
else
|
||||
entry >>= 1;
|
||||
}
|
||||
|
||||
localTable[i] = entry;
|
||||
nativeContext = crc64_init();
|
||||
useNative = nativeContext != IntPtr.Zero;
|
||||
}
|
||||
|
||||
for(var i = 0; i < len; i++) localhashInt = localhashInt >> 8 ^ localTable[data[i] ^ localhashInt & 0xff];
|
||||
ulong localHashInt = seed;
|
||||
|
||||
localhashInt ^= seed;
|
||||
hash = BigEndianBitConverter.GetBytes(localhashInt);
|
||||
ulong[][] localTable = GenerateTable(polynomial);
|
||||
|
||||
Step(ref localHashInt, localTable, data, len, useEcma, useNative, nativeContext);
|
||||
|
||||
localHashInt ^= seed;
|
||||
|
||||
if(useNative && useEcma)
|
||||
{
|
||||
crc64_final(nativeContext, ref localHashInt);
|
||||
crc64_free(nativeContext);
|
||||
}
|
||||
|
||||
hash = BigEndianBitConverter.GetBytes(localHashInt);
|
||||
|
||||
var crc64Output = new StringBuilder();
|
||||
|
||||
@@ -235,4 +530,63 @@ public sealed class Crc64Context : IChecksum
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.CRC64_ECMA_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("D0C0D902-420A-45DA-A235-9D48BEE4B1CE");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) =>
|
||||
Step(ref _hashInt, _table, data, len, _useEcma, _useNative, _nativeContext);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
ulong crc = _hashInt ^ _finalSeed;
|
||||
|
||||
if(!_useNative || !_useEcma) return BigEndianBitConverter.GetBytes(crc);
|
||||
|
||||
crc64_final(_nativeContext, ref crc);
|
||||
crc64_free(_nativeContext);
|
||||
|
||||
return BigEndianBitConverter.GetBytes(crc);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
ulong crc = _hashInt ^ _finalSeed;
|
||||
|
||||
var crc64Output = new StringBuilder();
|
||||
|
||||
if(_useNative && _useEcma)
|
||||
{
|
||||
crc64_final(_nativeContext, ref crc);
|
||||
crc64_free(_nativeContext);
|
||||
}
|
||||
|
||||
for(int i = 0; i < BigEndianBitConverter.GetBytes(crc).Length; i++)
|
||||
crc64Output.Append(BigEndianBitConverter.GetBytes(crc)[i].ToString("x2"));
|
||||
|
||||
return crc64Output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -36,7 +36,7 @@
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
namespace Aaru.CommonTypes.Interfaces;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
public interface IChecksum
|
||||
{
|
||||
|
||||
417
RomRepoMgr.Core/Checksums/Localization/Localization.Designer.cs
generated
Normal file
417
RomRepoMgr.Core/Checksums/Localization/Localization.Designer.cs
generated
Normal file
@@ -0,0 +1,417 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace RomRepoMgr.Core.Checksums.Localization {
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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 global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Localization() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Aaru.Checksums.Localization.Localization", typeof(Localization).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adler-32.
|
||||
/// </summary>
|
||||
internal static string Adler32_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Adler32_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Assertion failed.
|
||||
/// </summary>
|
||||
internal static string Assertion_failed {
|
||||
get {
|
||||
return ResourceManager.GetString("Assertion_failed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CD-Text Pack 4 CRC 0x{0:X4}, expected 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string CD_Text_Pack_four_CRC_0_expected_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("CD_Text_Pack_four_CRC_0_expected_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CD-Text Pack 1 CRC 0x{0:X4}, expected 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string CD_Text_Pack_one_CRC_0_expected_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("CD_Text_Pack_one_CRC_0_expected_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CD-Text Pack 3 CRC 0x{0:X4}, expected 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string CD_Text_Pack_three_CRC_0_expected_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("CD_Text_Pack_three_CRC_0_expected_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CD-Text Pack 2 CRC 0x{0:X4}, expected 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string CD_Text_Pack_two_CRC_0_expected_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("CD_Text_Pack_two_CRC_0_expected_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CRC-16 (CCITT).
|
||||
/// </summary>
|
||||
internal static string CRC16_CCITT_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("CRC16_CCITT_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CRC-16 (IBM).
|
||||
/// </summary>
|
||||
internal static string CRC16_IBM_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("CRC16_IBM_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CRC-32.
|
||||
/// </summary>
|
||||
internal static string CRC32_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("CRC32_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CRC-64 (ECMA).
|
||||
/// </summary>
|
||||
internal static string CRC64_ECMA_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("CRC64_ECMA_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cyclic CDTP2 0x{0:X4}, Calc CDTP2 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string Cyclic_CDTP2_0_Calc_CDTP2_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Cyclic_CDTP2_0_Calc_CDTP2_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cyclic CDTP3 0x{0:X4}, Calc CDTP3 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string Cyclic_CDTP3_0_Calc_CDTP3_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Cyclic_CDTP3_0_Calc_CDTP3_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cyclic CDTP4 0x{0:X4}, Calc CDTP4 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string Cyclic_CDTP4_0_Calc_CDTP4_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Cyclic_CDTP4_0_Calc_CDTP4_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected CD+EG Pack in subchannel.
|
||||
/// </summary>
|
||||
internal static string Detected_CD_EG_Pack_in_subchannel {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_CD_EG_Pack_in_subchannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected CD+G Pack in subchannel.
|
||||
/// </summary>
|
||||
internal static string Detected_CD_G_Pack_in_subchannel {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_CD_G_Pack_in_subchannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected CD+MIDI Pack in subchannel.
|
||||
/// </summary>
|
||||
internal static string Detected_CD_MIDI_Pack_in_subchannel {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_CD_MIDI_Pack_in_subchannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected CD-TEXT Pack in subchannel.
|
||||
/// </summary>
|
||||
internal static string Detected_CD_TEXT_Pack_in_subchannel {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_CD_TEXT_Pack_in_subchannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected Line Graphics Pack in subchannel.
|
||||
/// </summary>
|
||||
internal static string Detected_Line_Graphics_Pack_in_subchannel {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_Line_Graphics_Pack_in_subchannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected unknown Pack type in subchannel: mode {0}, item {1}.
|
||||
/// </summary>
|
||||
internal static string Detected_unknown_Pack_type_in_subchannel_mode_0_item_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_unknown_Pack_type_in_subchannel_mode_0_item_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected User Pack in subchannel.
|
||||
/// </summary>
|
||||
internal static string Detected_User_Pack_in_subchannel {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_User_Pack_in_subchannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detected Zero Pack in subchannel.
|
||||
/// </summary>
|
||||
internal static string Detected_Zero_Pack_in_subchannel {
|
||||
get {
|
||||
return ResourceManager.GetString("Detected_Zero_Pack_in_subchannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to
|
||||
/// Erasure positions as determined by roots of Eras Loc Poly:
|
||||
///.
|
||||
/// </summary>
|
||||
internal static string Erasure_positions_as_determined_by_roots_of_Eras_Loc_Poly {
|
||||
get {
|
||||
return ResourceManager.GetString("Erasure_positions_as_determined_by_roots_of_Eras_Loc_Poly", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to
|
||||
/// ERROR: denominator = 0
|
||||
///.
|
||||
/// </summary>
|
||||
internal static string ERROR_denominator_equals_zero {
|
||||
get {
|
||||
return ResourceManager.GetString("ERROR_denominator_equals_zero", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to
|
||||
/// Final error positions: .
|
||||
/// </summary>
|
||||
internal static string Final_error_positions {
|
||||
get {
|
||||
return ResourceManager.GetString("Final_error_positions", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fletcher-16.
|
||||
/// </summary>
|
||||
internal static string Fletcher16_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Fletcher16_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fletcher-32.
|
||||
/// </summary>
|
||||
internal static string Fletcher32_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Fletcher32_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to
|
||||
/// lambda(x) is WRONG
|
||||
///.
|
||||
/// </summary>
|
||||
internal static string lambda_is_wrong {
|
||||
get {
|
||||
return ResourceManager.GetString("lambda_is_wrong", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to m must be between 2 and 16 inclusive.
|
||||
/// </summary>
|
||||
internal static string m_must_be_between_2_and_16_inclusive {
|
||||
get {
|
||||
return ResourceManager.GetString("m_must_be_between_2_and_16_inclusive", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to MD5.
|
||||
/// </summary>
|
||||
internal static string MD5_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("MD5_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not yet implemented..
|
||||
/// </summary>
|
||||
internal static string Not_yet_implemented {
|
||||
get {
|
||||
return ResourceManager.GetString("Not_yet_implemented", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Q subchannel CRC 0x{0:X4}, expected 0x{1:X4}.
|
||||
/// </summary>
|
||||
internal static string Q_subchannel_CRC_0_expected_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Q_subchannel_CRC_0_expected_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SHA1.
|
||||
/// </summary>
|
||||
internal static string SHA1_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("SHA1_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SHA256.
|
||||
/// </summary>
|
||||
internal static string SHA256_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("SHA256_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SHA384.
|
||||
/// </summary>
|
||||
internal static string SHA384_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("SHA384_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SHA512.
|
||||
/// </summary>
|
||||
internal static string SHA512_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("SHA512_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SpamSum does not have a binary representation..
|
||||
/// </summary>
|
||||
internal static string SpamSum_does_not_have_a_binary_representation {
|
||||
get {
|
||||
return ResourceManager.GetString("SpamSum_does_not_have_a_binary_representation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SpamSum.
|
||||
/// </summary>
|
||||
internal static string SpamSum_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("SpamSum_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The input exceeds data types..
|
||||
/// </summary>
|
||||
internal static string The_input_exceeds_data_types {
|
||||
get {
|
||||
return ResourceManager.GetString("The_input_exceeds_data_types", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Trying to calculate RS without initializing!.
|
||||
/// </summary>
|
||||
internal static string Trying_to_calculate_RS_without_initializing {
|
||||
get {
|
||||
return ResourceManager.GetString("Trying_to_calculate_RS_without_initializing", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
RomRepoMgr.Core/Checksums/Localization/Localization.es.resx
Normal file
136
RomRepoMgr.Core/Checksums/Localization/Localization.es.resx
Normal file
@@ -0,0 +1,136 @@
|
||||
<root>
|
||||
<!-- ReSharper disable MarkupTextTypo -->
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<data name="Assertion_failed" xml:space="preserve">
|
||||
<value>Error de aserción</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_four_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CRC del paquete 4 de CD-Text es 0x{0:X4} se esperaba 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_one_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CRC del paquete 1 de CD-Text es 0x{0:X4} se esperaba 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_three_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CRC del paquete 3 de CD-Text es 0x{0:X4} se esperaba 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_two_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CRC del paquete 4 de CD-Text es 0x{0:X4} se esperaba 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="Cyclic_CDTP2_0_Calc_CDTP2_1" xml:space="preserve">
|
||||
<value>CDTP2 cíclico 0x{0:X4}, CDTP2 calculado 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="Cyclic_CDTP3_0_Calc_CDTP3_1" xml:space="preserve">
|
||||
<value>CDTP3 cíclico 0x{0:X4}, CDTP3 calculado 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="Cyclic_CDTP4_0_Calc_CDTP4_1" xml:space="preserve">
|
||||
<value>CDTP4 cíclico 0x{0:X4}, CDTP4 calculado 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="Detected_CD_EG_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detectado paquete CD+EG en el subcanal</value>
|
||||
</data>
|
||||
<data name="Detected_CD_G_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detectado paquete CD+G en el subcanal</value>
|
||||
</data>
|
||||
<data name="Detected_CD_MIDI_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detectado paquete CD+MIDI en el subcanal</value>
|
||||
</data>
|
||||
<data name="Detected_CD_TEXT_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detectado paquete CD-TEXT en el subcanal</value>
|
||||
</data>
|
||||
<data name="Detected_Line_Graphics_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detectado paquete de gráficos lineales en el subcanal</value>
|
||||
</data>
|
||||
<data name="Detected_unknown_Pack_type_in_subchannel_mode_0_item_1" xml:space="preserve">
|
||||
<value>Detectado paquete de tipo desconocido en el subcanal: modo {0}, elemento {1}</value>
|
||||
</data>
|
||||
<data name="Detected_User_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detectado paquete de usuario en el subcanal</value>
|
||||
</data>
|
||||
<data name="Detected_Zero_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detectado paquete cero en el subcanal</value>
|
||||
</data>
|
||||
<data name="Erasure_positions_as_determined_by_roots_of_Eras_Loc_Poly" xml:space="preserve">
|
||||
<value>Posiciones de borrado determinadas por el polinomio:</value>
|
||||
</data>
|
||||
<data name="ERROR_denominator_equals_zero" xml:space="preserve">
|
||||
<value>ERROR: denominador = 0</value>
|
||||
</data>
|
||||
<data name="Final_error_positions" xml:space="preserve">
|
||||
<value>Posiciones finales de error:</value>
|
||||
</data>
|
||||
<data name="lambda_is_wrong" xml:space="preserve">
|
||||
<value>lambda(x) es INCORRECTO</value>
|
||||
</data>
|
||||
<data name="m_must_be_between_2_and_16_inclusive" xml:space="preserve">
|
||||
<value>m debe estar entre 2 y 16 inclusivos</value>
|
||||
</data>
|
||||
<data name="Not_yet_implemented" xml:space="preserve">
|
||||
<value>Aún no implementado.</value>
|
||||
</data>
|
||||
<data name="Q_subchannel_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CRC del subcanal Q es 0x{0:X4}, se esperaba 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="SpamSum_does_not_have_a_binary_representation" xml:space="preserve">
|
||||
<value>SpamSum no posee una representación binaria.</value>
|
||||
</data>
|
||||
<data name="The_input_exceeds_data_types" xml:space="preserve">
|
||||
<value>La entrada excede los tipos de datos</value>
|
||||
</data>
|
||||
<data name="Trying_to_calculate_RS_without_initializing" xml:space="preserve">
|
||||
<value>¡Intentando calcular RS sin inicializar!</value>
|
||||
</data>
|
||||
<data name="Adler32_Name" xml:space="preserve">
|
||||
<value>Adler-32</value>
|
||||
</data>
|
||||
<data name="CRC16_CCITT_Name" xml:space="preserve">
|
||||
<value>CRC-16 (CCITT)</value>
|
||||
</data>
|
||||
<data name="CRC16_IBM_Name" xml:space="preserve">
|
||||
<value>CRC-16 (IBM)</value>
|
||||
</data>
|
||||
<data name="CRC32_Name" xml:space="preserve">
|
||||
<value>CRC-32</value>
|
||||
</data>
|
||||
<data name="CRC64_ECMA_Name" xml:space="preserve">
|
||||
<value>CRC-64 (ECMA)</value>
|
||||
</data>
|
||||
<data name="Fletcher32_Name" xml:space="preserve">
|
||||
<value>Fletcher-32</value>
|
||||
</data>
|
||||
<data name="Fletcher16_Name" xml:space="preserve">
|
||||
<value>Fletcher-16</value>
|
||||
</data>
|
||||
<data name="MD5_Name" xml:space="preserve">
|
||||
<value>MD5</value>
|
||||
</data>
|
||||
<data name="SHA1_Name" xml:space="preserve">
|
||||
<value>SHA1</value>
|
||||
</data>
|
||||
<data name="SHA256_Name" xml:space="preserve">
|
||||
<value>SHA256</value>
|
||||
</data>
|
||||
<data name="SHA384_Name" xml:space="preserve">
|
||||
<value>SHA384</value>
|
||||
</data>
|
||||
<data name="SHA512_Name" xml:space="preserve">
|
||||
<value>SHA512</value>
|
||||
</data>
|
||||
<data name="SpamSum_Name" xml:space="preserve">
|
||||
<value>SpamSum</value>
|
||||
</data>
|
||||
</root>
|
||||
148
RomRepoMgr.Core/Checksums/Localization/Localization.resx
Normal file
148
RomRepoMgr.Core/Checksums/Localization/Localization.resx
Normal file
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<root>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
id="root" xmlns="">
|
||||
<xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<data name="Detected_Zero_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detected Zero Pack in subchannel</value>
|
||||
</data>
|
||||
<data name="Detected_Line_Graphics_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detected Line Graphics Pack in subchannel</value>
|
||||
</data>
|
||||
<data name="Detected_CD_G_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detected CD+G Pack in subchannel</value>
|
||||
</data>
|
||||
<data name="Detected_CD_EG_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detected CD+EG Pack in subchannel</value>
|
||||
</data>
|
||||
<data name="Detected_CD_TEXT_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detected CD-TEXT Pack in subchannel</value>
|
||||
</data>
|
||||
<data name="Detected_CD_MIDI_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detected CD+MIDI Pack in subchannel</value>
|
||||
</data>
|
||||
<data name="Detected_User_Pack_in_subchannel" xml:space="preserve">
|
||||
<value>Detected User Pack in subchannel</value>
|
||||
</data>
|
||||
<data name="Detected_unknown_Pack_type_in_subchannel_mode_0_item_1" xml:space="preserve">
|
||||
<value>Detected unknown Pack type in subchannel: mode {0}, item {1}</value>
|
||||
</data>
|
||||
<data name="Q_subchannel_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>Q subchannel CRC 0x{0:X4}, expected 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_one_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CD-Text Pack 1 CRC 0x{0:X4}, expected 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="Cyclic_CDTP2_0_Calc_CDTP2_1" xml:space="preserve">
|
||||
<value>Cyclic CDTP2 0x{0:X4}, Calc CDTP2 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_two_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CD-Text Pack 2 CRC 0x{0:X4}, expected 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="Cyclic_CDTP3_0_Calc_CDTP3_1" xml:space="preserve">
|
||||
<value>Cyclic CDTP3 0x{0:X4}, Calc CDTP3 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_three_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CD-Text Pack 3 CRC 0x{0:X4}, expected 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="Cyclic_CDTP4_0_Calc_CDTP4_1" xml:space="preserve">
|
||||
<value>Cyclic CDTP4 0x{0:X4}, Calc CDTP4 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="CD_Text_Pack_four_CRC_0_expected_1" xml:space="preserve">
|
||||
<value>CD-Text Pack 4 CRC 0x{0:X4}, expected 0x{1:X4}</value>
|
||||
</data>
|
||||
<data name="m_must_be_between_2_and_16_inclusive" xml:space="preserve">
|
||||
<value>m must be between 2 and 16 inclusive</value>
|
||||
</data>
|
||||
<data name="Trying_to_calculate_RS_without_initializing" xml:space="preserve">
|
||||
<value>Trying to calculate RS without initializing!</value>
|
||||
</data>
|
||||
<data name="lambda_is_wrong" xml:space="preserve">
|
||||
<value>
|
||||
lambda(x) is WRONG
|
||||
</value>
|
||||
</data>
|
||||
<data name="Erasure_positions_as_determined_by_roots_of_Eras_Loc_Poly" xml:space="preserve">
|
||||
<value>
|
||||
Erasure positions as determined by roots of Eras Loc Poly:
|
||||
</value>
|
||||
</data>
|
||||
<data name="Final_error_positions" xml:space="preserve">
|
||||
<value>
|
||||
Final error positions: </value>
|
||||
</data>
|
||||
<data name="ERROR_denominator_equals_zero" xml:space="preserve">
|
||||
<value>
|
||||
ERROR: denominator = 0
|
||||
</value>
|
||||
</data>
|
||||
<data name="SpamSum_does_not_have_a_binary_representation" xml:space="preserve">
|
||||
<value>SpamSum does not have a binary representation.</value>
|
||||
</data>
|
||||
<data name="Assertion_failed" xml:space="preserve">
|
||||
<value>Assertion failed</value>
|
||||
</data>
|
||||
<data name="The_input_exceeds_data_types" xml:space="preserve">
|
||||
<value>The input exceeds data types.</value>
|
||||
</data>
|
||||
<data name="Not_yet_implemented" xml:space="preserve">
|
||||
<value>Not yet implemented.</value>
|
||||
</data>
|
||||
<data name="Adler32_Name" xml:space="preserve">
|
||||
<value>Adler-32</value>
|
||||
</data>
|
||||
<data name="CRC16_CCITT_Name" xml:space="preserve">
|
||||
<value>CRC-16 (CCITT)</value>
|
||||
</data>
|
||||
<data name="CRC16_IBM_Name" xml:space="preserve">
|
||||
<value>CRC-16 (IBM)</value>
|
||||
</data>
|
||||
<data name="CRC32_Name" xml:space="preserve">
|
||||
<value>CRC-32</value>
|
||||
</data>
|
||||
<data name="CRC64_ECMA_Name" xml:space="preserve">
|
||||
<value>CRC-64 (ECMA)</value>
|
||||
</data>
|
||||
<data name="Fletcher32_Name" xml:space="preserve">
|
||||
<value>Fletcher-32</value>
|
||||
</data>
|
||||
<data name="Fletcher16_Name" xml:space="preserve">
|
||||
<value>Fletcher-16</value>
|
||||
</data>
|
||||
<data name="MD5_Name" xml:space="preserve">
|
||||
<value>MD5</value>
|
||||
</data>
|
||||
<data name="SHA1_Name" xml:space="preserve">
|
||||
<value>SHA1</value>
|
||||
</data>
|
||||
<data name="SHA256_Name" xml:space="preserve">
|
||||
<value>SHA256</value>
|
||||
</data>
|
||||
<data name="SHA384_Name" xml:space="preserve">
|
||||
<value>SHA384</value>
|
||||
</data>
|
||||
<data name="SHA512_Name" xml:space="preserve">
|
||||
<value>SHA512</value>
|
||||
</data>
|
||||
<data name="SpamSum_Name" xml:space="preserve">
|
||||
<value>SpamSum</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -27,16 +27,17 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Wraps up .NET MD5 implementation to a Init(), Update(), Final() context.</summary>
|
||||
public sealed class Md5Context : IChecksum
|
||||
{
|
||||
@@ -45,44 +46,12 @@ public sealed class Md5Context : IChecksum
|
||||
/// <summary>Initializes the MD5 hash provider</summary>
|
||||
public Md5Context() => _provider = MD5.Create();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var md5Output = new StringBuilder();
|
||||
|
||||
foreach(byte h in _provider.Hash) md5Output.Append(h.ToString("x2"));
|
||||
|
||||
return md5Output.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
public static byte[] File(string filename)
|
||||
{
|
||||
var localMd5Provider = MD5.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
byte[] result = localMd5Provider.ComputeHash(fileStream);
|
||||
fileStream.Close();
|
||||
|
||||
@@ -95,7 +64,7 @@ public sealed class Md5Context : IChecksum
|
||||
public static string File(string filename, out byte[] hash)
|
||||
{
|
||||
var localMd5Provider = MD5.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
hash = localMd5Provider.ComputeHash(fileStream);
|
||||
var md5Output = new StringBuilder();
|
||||
|
||||
@@ -125,4 +94,51 @@ public sealed class Md5Context : IChecksum
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.MD5_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("C78674C4-F699-4FAB-A618-1661AF659A7C");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var md5Output = new StringBuilder();
|
||||
|
||||
if(_provider.Hash is null) return null;
|
||||
|
||||
foreach(byte h in _provider.Hash) md5Output.Append(h.ToString("x2"));
|
||||
|
||||
return md5Output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
81
RomRepoMgr.Core/Checksums/Native.cs
Normal file
81
RomRepoMgr.Core/Checksums/Native.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Native.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Checksums.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Checks that Aaru.Checksums.Native library is available and usable.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <summary>Handles native implementations of compression algorithms</summary>
|
||||
public static partial class Native
|
||||
{
|
||||
static bool _checked;
|
||||
static bool _supported;
|
||||
|
||||
/// <summary>Set to return native as never supported</summary>
|
||||
public static bool ForceManaged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set to <c>true</c> the native library was found and loaded correctly and its reported version is
|
||||
/// compatible.
|
||||
/// </summary>
|
||||
public static bool IsSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
if(ForceManaged) return false;
|
||||
|
||||
if(_checked) return _supported;
|
||||
|
||||
ulong version;
|
||||
_checked = true;
|
||||
|
||||
try
|
||||
{
|
||||
version = get_acn_version();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_supported = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Check version compatibility
|
||||
_supported = version >= 0x06000000;
|
||||
|
||||
return _supported;
|
||||
}
|
||||
}
|
||||
|
||||
[LibraryImport("libAaru.Checksums.Native", SetLastError = true)]
|
||||
private static partial ulong get_acn_version();
|
||||
}
|
||||
@@ -27,17 +27,22 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Wraps up .NET SHA1 implementation to a Init(), Update(), Final() context.</summary>
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
public sealed class Sha1Context : IChecksum
|
||||
{
|
||||
readonly SHA1 _provider;
|
||||
@@ -45,44 +50,14 @@ public sealed class Sha1Context : IChecksum
|
||||
/// <summary>Initializes the SHA1 hash provider</summary>
|
||||
public Sha1Context() => _provider = SHA1.Create();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha1Output = new StringBuilder();
|
||||
|
||||
foreach(byte h in _provider.Hash) sha1Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha1Output.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Global
|
||||
public static byte[] File(string filename)
|
||||
{
|
||||
var localSha1Provider = SHA1.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
byte[] result = localSha1Provider.ComputeHash(fileStream);
|
||||
fileStream.Close();
|
||||
|
||||
@@ -95,7 +70,7 @@ public sealed class Sha1Context : IChecksum
|
||||
public static string File(string filename, out byte[] hash)
|
||||
{
|
||||
var localSha1Provider = SHA1.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
hash = localSha1Provider.ComputeHash(fileStream);
|
||||
var sha1Output = new StringBuilder();
|
||||
|
||||
@@ -125,4 +100,51 @@ public sealed class Sha1Context : IChecksum
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.SHA1_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("5C28939D-DCBB-4C6E-8498-C509ABD99FC2");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha1Output = new StringBuilder();
|
||||
|
||||
if(_provider.Hash is null) return null;
|
||||
|
||||
foreach(byte h in _provider.Hash) sha1Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha1Output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -27,17 +27,22 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Wraps up .NET SHA256 implementation to a Init(), Update(), Final() context.</summary>
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
public sealed class Sha256Context : IChecksum
|
||||
{
|
||||
readonly SHA256 _provider;
|
||||
@@ -45,44 +50,14 @@ public sealed class Sha256Context : IChecksum
|
||||
/// <summary>Initializes the SHA256 hash provider</summary>
|
||||
public Sha256Context() => _provider = SHA256.Create();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha256Output = new StringBuilder();
|
||||
|
||||
foreach(byte h in _provider.Hash) sha256Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha256Output.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Global
|
||||
public static byte[] File(string filename)
|
||||
{
|
||||
var localSha256Provider = SHA256.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
byte[] result = localSha256Provider.ComputeHash(fileStream);
|
||||
fileStream.Close();
|
||||
|
||||
@@ -95,7 +70,7 @@ public sealed class Sha256Context : IChecksum
|
||||
public static string File(string filename, out byte[] hash)
|
||||
{
|
||||
var localSha256Provider = SHA256.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
hash = localSha256Provider.ComputeHash(fileStream);
|
||||
var sha256Output = new StringBuilder();
|
||||
|
||||
@@ -125,4 +100,51 @@ public sealed class Sha256Context : IChecksum
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.SHA256_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("A6F0EF52-064D-41D1-8619-240481749B70");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha256Output = new StringBuilder();
|
||||
|
||||
if(_provider.Hash is null) return null;
|
||||
|
||||
foreach(byte h in _provider.Hash) sha256Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha256Output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -27,17 +27,23 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Wraps up .NET SHA384 implementation to a Init(), Update(), Final() context.</summary>
|
||||
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public sealed class Sha384Context : IChecksum
|
||||
{
|
||||
readonly SHA384 _provider;
|
||||
@@ -45,44 +51,14 @@ public sealed class Sha384Context : IChecksum
|
||||
/// <summary>Initializes the SHA384 hash provider</summary>
|
||||
public Sha384Context() => _provider = SHA384.Create();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha384Output = new StringBuilder();
|
||||
|
||||
foreach(byte h in _provider.Hash) sha384Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha384Output.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Global
|
||||
public static byte[] File(string filename)
|
||||
{
|
||||
var localSha384Provider = SHA384.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
byte[] result = localSha384Provider.ComputeHash(fileStream);
|
||||
fileStream.Close();
|
||||
|
||||
@@ -95,7 +71,7 @@ public sealed class Sha384Context : IChecksum
|
||||
public static string File(string filename, out byte[] hash)
|
||||
{
|
||||
var localSha384Provider = SHA384.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
hash = localSha384Provider.ComputeHash(fileStream);
|
||||
var sha384Output = new StringBuilder();
|
||||
|
||||
@@ -125,4 +101,51 @@ public sealed class Sha384Context : IChecksum
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.SHA384_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("4A2A1820-E157-4842-B1E2-0E629FA60DDD");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha384Output = new StringBuilder();
|
||||
|
||||
if(_provider.Hash is null) return null;
|
||||
|
||||
foreach(byte h in _provider.Hash) sha384Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha384Output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -27,17 +27,23 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Wraps up .NET SHA512 implementation to a Init(), Update(), Final() context.</summary>
|
||||
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public sealed class Sha512Context : IChecksum
|
||||
{
|
||||
readonly SHA512 _provider;
|
||||
@@ -45,44 +51,12 @@ public sealed class Sha512Context : IChecksum
|
||||
/// <summary>Initializes the SHA512 hash provider</summary>
|
||||
public Sha512Context() => _provider = SHA512.Create();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha512Output = new StringBuilder();
|
||||
|
||||
foreach(byte h in _provider.Hash) sha512Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha512Output.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
public static byte[] File(string filename)
|
||||
{
|
||||
var localSha512Provider = SHA512.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
byte[] result = localSha512Provider.ComputeHash(fileStream);
|
||||
fileStream.Close();
|
||||
|
||||
@@ -95,7 +69,7 @@ public sealed class Sha512Context : IChecksum
|
||||
public static string File(string filename, out byte[] hash)
|
||||
{
|
||||
var localSha512Provider = SHA512.Create();
|
||||
var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
var fileStream = new FileStream(filename, FileMode.Open);
|
||||
hash = localSha512Provider.ComputeHash(fileStream);
|
||||
var sha512Output = new StringBuilder();
|
||||
|
||||
@@ -125,4 +99,51 @@ public sealed class Sha512Context : IChecksum
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string Data(byte[] data, out byte[] hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.SHA512_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("1E167BCB-2362-44DA-B5B0-B7ED3A22D5A6");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len) => _provider.TransformBlock(data, 0, (int)len, data, 0);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
|
||||
return _provider.Hash;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
_provider.TransformFinalBlock([], 0, 0);
|
||||
var sha512Output = new StringBuilder();
|
||||
|
||||
if(_provider.Hash is null) return null;
|
||||
|
||||
foreach(byte h in _provider.Hash) sha512Output.Append(h.ToString("x2"));
|
||||
|
||||
return sha512Output.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// Based on ssdeep
|
||||
@@ -40,14 +40,19 @@
|
||||
// http://ssdeep.sf.net/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using RomRepoMgr.Core.Resources;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
namespace RomRepoMgr.Core.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Implements the SpamSum fuzzy hashing algorithm.</summary>
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "OutParameterValueIsAlwaysDiscarded.Global")]
|
||||
public sealed class SpamSumContext : IChecksum
|
||||
{
|
||||
const uint ROLLING_WINDOW = 7;
|
||||
@@ -59,15 +64,8 @@ public sealed class SpamSumContext : IChecksum
|
||||
const uint FUZZY_MAX_RESULT = 2 * SPAMSUM_LENGTH + 20;
|
||||
|
||||
//"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
readonly byte[] _b64 =
|
||||
[
|
||||
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
|
||||
];
|
||||
|
||||
FuzzyState _self;
|
||||
readonly byte[] _b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"u8.ToArray();
|
||||
FuzzyState _self;
|
||||
|
||||
/// <summary>Initializes the SpamSum structures</summary>
|
||||
public SpamSumContext()
|
||||
@@ -77,7 +75,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
Bh = new BlockhashContext[NUM_BLOCKHASHES]
|
||||
};
|
||||
|
||||
for(var i = 0; i < NUM_BLOCKHASHES; i++) _self.Bh[i].Digest = new byte[SPAMSUM_LENGTH];
|
||||
for(int i = 0; i < NUM_BLOCKHASHES; i++) _self.Bh[i].Digest = new byte[SPAMSUM_LENGTH];
|
||||
|
||||
_self.Bhstart = 0;
|
||||
_self.Bhend = 1;
|
||||
@@ -90,35 +88,6 @@ public sealed class SpamSumContext : IChecksum
|
||||
roll_init();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len)
|
||||
{
|
||||
_self.TotalSize += len;
|
||||
|
||||
for(var i = 0; i < len; i++) fuzzy_engine_step(data[i]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final() => throw new NotImplementedException(Localization.Spamsum_no_binary);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a base64 representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
FuzzyDigest(out byte[] result);
|
||||
|
||||
return CToString(result);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void roll_init() => _self.Roll = new RollState
|
||||
{
|
||||
@@ -167,10 +136,15 @@ public sealed class SpamSumContext : IChecksum
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void fuzzy_try_fork_blockhash()
|
||||
{
|
||||
if(_self.Bhend >= NUM_BLOCKHASHES) return;
|
||||
switch(_self.Bhend)
|
||||
{
|
||||
case >= NUM_BLOCKHASHES:
|
||||
return;
|
||||
|
||||
if(_self.Bhend == 0) // assert
|
||||
throw new Exception(Localization.Assertion_failed);
|
||||
// assert
|
||||
case 0:
|
||||
throw new Exception(Localization.Localization.Assertion_failed);
|
||||
}
|
||||
|
||||
uint obh = _self.Bhend - 1;
|
||||
uint nbh = _self.Bhend;
|
||||
@@ -185,7 +159,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void fuzzy_try_reduce_blockhash()
|
||||
{
|
||||
if(_self.Bhstart >= _self.Bhend) throw new Exception(Localization.Assertion_failed);
|
||||
if(_self.Bhstart >= _self.Bhend) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
if(_self.Bhend - _self.Bhstart < 2)
|
||||
/* Need at least two working hashes. */
|
||||
@@ -266,21 +240,20 @@ public sealed class SpamSumContext : IChecksum
|
||||
var sb = new StringBuilder();
|
||||
uint bi = _self.Bhstart;
|
||||
uint h = roll_sum();
|
||||
var remain = (int)(FUZZY_MAX_RESULT - 1); /* Exclude terminating '\0'. */
|
||||
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);
|
||||
|
||||
var resultOff = 0;
|
||||
throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
/* 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);
|
||||
if(bi >= NUM_BLOCKHASHES)
|
||||
throw new OverflowException(Localization.Localization.The_input_exceeds_data_types);
|
||||
}
|
||||
|
||||
/* Adapt blocksize guess to actual digest length. */
|
||||
@@ -288,26 +261,27 @@ public sealed class SpamSumContext : IChecksum
|
||||
|
||||
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);
|
||||
if(bi > 0 && _self.Bh[bi].Dlen < SPAMSUM_LENGTH / 2)
|
||||
throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
sb.AppendFormat("{0}:", SSDEEP_BS(bi));
|
||||
sb.Append($"{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);
|
||||
throw new OverflowException(Localization.Localization.The_input_exceeds_data_types);
|
||||
|
||||
if(i >= remain) throw new Exception(Localization.Assertion_failed);
|
||||
if(i >= remain) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
remain -= i;
|
||||
|
||||
Array.Copy(Encoding.ASCII.GetBytes(sb.ToString()), 0, result, 0, i);
|
||||
|
||||
resultOff = i;
|
||||
int resultOff = i;
|
||||
|
||||
i = (int)_self.Bh[bi].Dlen;
|
||||
|
||||
if(i > remain) throw new Exception(Localization.Assertion_failed);
|
||||
if(i > remain) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
Array.Copy(_self.Bh[bi].Digest, 0, result, resultOff, i);
|
||||
resultOff += i;
|
||||
@@ -315,7 +289,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
|
||||
if(h != 0)
|
||||
{
|
||||
if(remain <= 0) throw new Exception(Localization.Assertion_failed);
|
||||
if(remain <= 0) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
result[resultOff] = _b64[_self.Bh[bi].H % 64];
|
||||
|
||||
@@ -330,7 +304,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
}
|
||||
else if(_self.Bh[bi].Digest[i] != 0)
|
||||
{
|
||||
if(remain <= 0) throw new Exception(Localization.Assertion_failed);
|
||||
if(remain <= 0) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
result[resultOff] = _self.Bh[bi].Digest[i];
|
||||
|
||||
@@ -344,7 +318,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
}
|
||||
}
|
||||
|
||||
if(remain <= 0) throw new Exception(Localization.Assertion_failed);
|
||||
if(remain <= 0) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
result[resultOff++] = 0x3A; // ':'
|
||||
--remain;
|
||||
@@ -354,7 +328,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
++bi;
|
||||
i = (int)_self.Bh[bi].Dlen;
|
||||
|
||||
if(i > remain) throw new Exception(Localization.Assertion_failed);
|
||||
if(i > remain) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
Array.Copy(_self.Bh[bi].Digest, 0, result, resultOff, i);
|
||||
resultOff += i;
|
||||
@@ -362,7 +336,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
|
||||
if(h != 0)
|
||||
{
|
||||
if(remain <= 0) throw new Exception(Localization.Assertion_failed);
|
||||
if(remain <= 0) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
h = _self.Bh[bi].Halfh;
|
||||
result[resultOff] = _b64[h % 64];
|
||||
@@ -382,7 +356,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
|
||||
if(i != 0)
|
||||
{
|
||||
if(remain <= 0) throw new Exception(Localization.Assertion_failed);
|
||||
if(remain <= 0) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
result[resultOff] = (byte)i;
|
||||
|
||||
@@ -399,9 +373,9 @@ public sealed class SpamSumContext : IChecksum
|
||||
}
|
||||
else if(h != 0)
|
||||
{
|
||||
if(_self.Bh[bi].Dlen != 0) throw new Exception(Localization.Assertion_failed);
|
||||
if(_self.Bh[bi].Dlen != 0) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
if(remain <= 0) throw new Exception(Localization.Assertion_failed);
|
||||
if(remain <= 0) throw new Exception(Localization.Localization.Assertion_failed);
|
||||
|
||||
result[resultOff++] = _b64[_self.Bh[bi].H % 64];
|
||||
/* No need to bother with FUZZY_FLAG_ELIMSEQ, because this
|
||||
@@ -414,13 +388,14 @@ public sealed class SpamSumContext : IChecksum
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
public static byte[] File(string filename) => throw new NotImplementedException(Localization.Spamsum_no_binary);
|
||||
public static byte[] File(string filename) =>
|
||||
throw new NotImplementedException(Localization.Localization.SpamSum_does_not_have_a_binary_representation);
|
||||
|
||||
/// <summary>Gets the hash of a file in hexadecimal and as a byte array.</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string File(string filename, out byte[] hash) =>
|
||||
throw new NotImplementedException(Localization.Not_yet_implemented);
|
||||
throw new NotImplementedException(Localization.Localization.Not_yet_implemented);
|
||||
|
||||
/// <summary>Gets the hash of the specified data buffer.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
@@ -448,7 +423,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static string CToString(byte[] cString)
|
||||
{
|
||||
var count = 0;
|
||||
int count = 0;
|
||||
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
// LINQ is six times slower
|
||||
@@ -462,16 +437,7 @@ public sealed class SpamSumContext : IChecksum
|
||||
return Encoding.ASCII.GetString(cString, 0, count);
|
||||
}
|
||||
|
||||
struct RollState
|
||||
{
|
||||
public byte[] Window;
|
||||
|
||||
// ROLLING_WINDOW
|
||||
public uint H1;
|
||||
public uint H2;
|
||||
public uint H3;
|
||||
public uint N;
|
||||
}
|
||||
#region Nested type: BlockhashContext
|
||||
|
||||
/* 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
|
||||
@@ -489,6 +455,10 @@ public sealed class SpamSumContext : IChecksum
|
||||
public uint Dlen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: FuzzyState
|
||||
|
||||
struct FuzzyState
|
||||
{
|
||||
public uint Bhstart;
|
||||
@@ -499,4 +469,64 @@ public sealed class SpamSumContext : IChecksum
|
||||
public ulong TotalSize;
|
||||
public RollState Roll;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: RollState
|
||||
|
||||
struct RollState
|
||||
{
|
||||
public byte[] Window;
|
||||
|
||||
// ROLLING_WINDOW
|
||||
public uint H1;
|
||||
public uint H2;
|
||||
public uint H3;
|
||||
public uint N;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Localization.SpamSum_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("DA692981-3291-47D8-B8B9-A87F0605F6E9");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len)
|
||||
{
|
||||
_self.TotalSize += len;
|
||||
|
||||
for(int i = 0; i < len; i++) fuzzy_engine_step(data[i]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final() =>
|
||||
throw new NotImplementedException(Localization.Localization.SpamSum_does_not_have_a_binary_representation);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a base64 representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
FuzzyDigest(out byte[] result);
|
||||
|
||||
return CToString(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public static class Extensions
|
||||
/// </returns>
|
||||
public static int EnsureRead(this Stream s, byte[] buffer, int offset, int count)
|
||||
{
|
||||
var pos = 0;
|
||||
int pos = 0;
|
||||
int read;
|
||||
|
||||
do
|
||||
|
||||
@@ -509,7 +509,7 @@ public sealed class Fuse : FileSystem
|
||||
{
|
||||
xattr = new byte[hash.Length / 2];
|
||||
|
||||
for(var i = 0; i < xattr.Length; i++)
|
||||
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);
|
||||
@@ -823,8 +823,8 @@ public sealed class Fuse : FileSystem
|
||||
|
||||
public void Umount()
|
||||
{
|
||||
var rnd = new Random();
|
||||
var token = new byte[64];
|
||||
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);
|
||||
|
||||
@@ -7,10 +7,12 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using ZstdSharp;
|
||||
|
||||
namespace RomRepoMgr.Core.Filesystem;
|
||||
|
||||
@@ -18,7 +20,7 @@ namespace RomRepoMgr.Core.Filesystem;
|
||||
// TODO: Invalidate caches
|
||||
// TODO: Mount options
|
||||
// TODO: Do not show machines or romsets with no ROMs in repo
|
||||
public class Vfs : IDisposable
|
||||
public class Vfs(ILoggerFactory loggerFactory) : IDisposable
|
||||
{
|
||||
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>> _machineDisksCache = [];
|
||||
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>> _machineFilesCache = [];
|
||||
@@ -98,7 +100,7 @@ public class Vfs : IDisposable
|
||||
|
||||
internal void GetInfo(out ulong files, out ulong totalSize)
|
||||
{
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
totalSize = (ulong)(ctx.Files.Where(f => f.IsInRepo).Sum(f => (double)f.Size) +
|
||||
ctx.Disks.Where(f => f.IsInRepo).Sum(f => (double)f.Size) +
|
||||
@@ -114,7 +116,7 @@ public class Vfs : IDisposable
|
||||
|
||||
void FillRootDirectoryCache()
|
||||
{
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
var rootCache = new ConcurrentDictionary<string, long>();
|
||||
|
||||
@@ -180,7 +182,7 @@ public class Vfs : IDisposable
|
||||
{
|
||||
if(_romSetsCache.TryGetValue(id, out RomSet romSet)) return romSet;
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
romSet = ctx.RomSets.Find(id);
|
||||
|
||||
@@ -199,7 +201,7 @@ public class Vfs : IDisposable
|
||||
|
||||
cachedMachines = [];
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
foreach(Machine mach in ctx.Machines.Where(m => m.RomSet.Id == id))
|
||||
{
|
||||
@@ -231,7 +233,7 @@ public class Vfs : IDisposable
|
||||
|
||||
if(cachedMachineFiles != null) return cachedMachineFiles;
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
cachedMachineFiles = [];
|
||||
|
||||
@@ -267,7 +269,7 @@ public class Vfs : IDisposable
|
||||
|
||||
if(cachedMachineDisks != null) return cachedMachineDisks;
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
cachedMachineDisks = [];
|
||||
|
||||
@@ -301,7 +303,7 @@ public class Vfs : IDisposable
|
||||
|
||||
if(cachedMachineMedias != null) return cachedMachineMedias;
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
cachedMachineMedias = [];
|
||||
|
||||
@@ -364,9 +366,9 @@ public class Vfs : IDisposable
|
||||
|
||||
internal long Open(string sha384, long fileSize)
|
||||
{
|
||||
var sha384Bytes = new byte[48];
|
||||
byte[] sha384Bytes = new byte[48];
|
||||
|
||||
for(var i = 0; i < 48; i++)
|
||||
for(int i = 0; i < 48; i++)
|
||||
{
|
||||
if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39)
|
||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
|
||||
@@ -385,19 +387,66 @@ public class Vfs : IDisposable
|
||||
|
||||
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;
|
||||
string repoPath;
|
||||
|
||||
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");
|
||||
|
||||
long handle;
|
||||
|
||||
if(!File.Exists(repoPath))
|
||||
{
|
||||
repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath,
|
||||
"files",
|
||||
sha384B32[0].ToString(),
|
||||
sha384B32[1].ToString(),
|
||||
sha384B32[2].ToString(),
|
||||
sha384B32[3].ToString(),
|
||||
sha384B32[4].ToString(),
|
||||
sha384B32 + ".zst");
|
||||
|
||||
if(!File.Exists(repoPath))
|
||||
{
|
||||
repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath,
|
||||
"files",
|
||||
sha384B32[0].ToString(),
|
||||
sha384B32[1].ToString(),
|
||||
sha384B32[2].ToString(),
|
||||
sha384B32[3].ToString(),
|
||||
sha384B32[4].ToString(),
|
||||
sha384B32);
|
||||
|
||||
if(!File.Exists(repoPath)) return -1;
|
||||
|
||||
_lastHandle++;
|
||||
handle = _lastHandle;
|
||||
|
||||
_streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
_lastHandle++;
|
||||
handle = _lastHandle;
|
||||
|
||||
_streamsCache[handle] =
|
||||
Stream.Synchronized(new ForcedSeekStream<DecompressionStream>(fileSize,
|
||||
new FileStream(repoPath,
|
||||
FileMode.Open,
|
||||
FileAccess.Read)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
_lastHandle++;
|
||||
long handle = _lastHandle;
|
||||
handle = _lastHandle;
|
||||
|
||||
_streamsCache[handle] =
|
||||
Stream.Synchronized(new ForcedSeekStream<LZipStream>(fileSize,
|
||||
@@ -456,9 +505,9 @@ public class Vfs : IDisposable
|
||||
|
||||
if(sha1 != null)
|
||||
{
|
||||
var sha1Bytes = new byte[20];
|
||||
byte[] sha1Bytes = new byte[20];
|
||||
|
||||
for(var i = 0; i < 20; i++)
|
||||
for(int i = 0; i < 20; i++)
|
||||
{
|
||||
if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39)
|
||||
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10);
|
||||
@@ -489,9 +538,9 @@ public class Vfs : IDisposable
|
||||
|
||||
if(md5 != null)
|
||||
{
|
||||
var md5Bytes = new byte[16];
|
||||
byte[] md5Bytes = new byte[16];
|
||||
|
||||
for(var i = 0; i < 16; i++)
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39)
|
||||
md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10);
|
||||
@@ -544,9 +593,9 @@ public class Vfs : IDisposable
|
||||
|
||||
if(sha256 != null)
|
||||
{
|
||||
var sha256Bytes = new byte[32];
|
||||
byte[] sha256Bytes = new byte[32];
|
||||
|
||||
for(var i = 0; i < 32; i++)
|
||||
for(int i = 0; i < 32; i++)
|
||||
{
|
||||
if(sha256[i * 2] >= 0x30 && sha256[i * 2] <= 0x39)
|
||||
sha256Bytes[i] = (byte)((sha256[i * 2] - 0x30) * 0x10);
|
||||
@@ -578,9 +627,9 @@ public class Vfs : IDisposable
|
||||
|
||||
if(sha1 != null)
|
||||
{
|
||||
var sha1Bytes = new byte[20];
|
||||
byte[] sha1Bytes = new byte[20];
|
||||
|
||||
for(var i = 0; i < 20; i++)
|
||||
for(int i = 0; i < 20; i++)
|
||||
{
|
||||
if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39)
|
||||
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10);
|
||||
@@ -611,9 +660,9 @@ public class Vfs : IDisposable
|
||||
|
||||
if(md5 != null)
|
||||
{
|
||||
var md5Bytes = new byte[16];
|
||||
byte[] md5Bytes = new byte[16];
|
||||
|
||||
for(var i = 0; i < 16; i++)
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39)
|
||||
md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10);
|
||||
|
||||
@@ -369,7 +369,7 @@ public class Winfsp(Vfs vfs) : FileSystemBase
|
||||
|
||||
if(fileNode is not FileNode { Handle: > 0 } node) return STATUS_INVALID_HANDLE;
|
||||
|
||||
var buf = new byte[length];
|
||||
byte[] buf = new byte[length];
|
||||
|
||||
int ret = vfs.Read(node.Handle, buf, (long)offset);
|
||||
|
||||
@@ -575,10 +575,10 @@ public class Winfsp(Vfs vfs) : FileSystemBase
|
||||
|
||||
if(securityDescriptor == null) return STATUS_SUCCESS;
|
||||
|
||||
var rootSddl = "O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)";
|
||||
string rootSddl = "O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)";
|
||||
|
||||
var rootSecurityDescriptor = new RawSecurityDescriptor(rootSddl);
|
||||
var fileSecurity = new byte[rootSecurityDescriptor.BinaryLength];
|
||||
var rootSecurityDescriptor = new RawSecurityDescriptor(rootSddl);
|
||||
byte[] fileSecurity = new byte[rootSecurityDescriptor.BinaryLength];
|
||||
rootSecurityDescriptor.GetBinaryForm(fileSecurity, 0);
|
||||
securityDescriptor = fileSecurity;
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ internal sealed class ForcedSeekStream<T> : Stream where T : Stream
|
||||
|
||||
do
|
||||
{
|
||||
var buffer = new byte[BUFFER_LEN];
|
||||
byte[] buffer = new byte[BUFFER_LEN];
|
||||
read = _baseStream.Read(buffer, 0, BUFFER_LEN);
|
||||
_backStream.Write(buffer, 0, read);
|
||||
} while(read == BUFFER_LEN);
|
||||
@@ -111,19 +111,19 @@ internal sealed class ForcedSeekStream<T> : Stream where T : Stream
|
||||
|
||||
_backStream.Position = _backStream.Length;
|
||||
long toPosition = position - _backStream.Position;
|
||||
var fullBufferReads = (int)(toPosition / BUFFER_LEN);
|
||||
var restToRead = (int)(toPosition % BUFFER_LEN);
|
||||
int fullBufferReads = (int)(toPosition / BUFFER_LEN);
|
||||
int restToRead = (int)(toPosition % BUFFER_LEN);
|
||||
byte[] buffer;
|
||||
|
||||
for(var i = 0; i < fullBufferReads; i++)
|
||||
for(int i = 0; i < fullBufferReads; i++)
|
||||
{
|
||||
buffer = new byte[BUFFER_LEN];
|
||||
_baseStream.EnsureRead(buffer, 0, BUFFER_LEN);
|
||||
_baseStream.ReadExactly(buffer, 0, BUFFER_LEN);
|
||||
_backStream.Write(buffer, 0, BUFFER_LEN);
|
||||
}
|
||||
|
||||
buffer = new byte[restToRead];
|
||||
_baseStream.EnsureRead(buffer, 0, restToRead);
|
||||
_baseStream.ReadExactly(buffer, 0, restToRead);
|
||||
_backStream.Write(buffer, 0, restToRead);
|
||||
}
|
||||
|
||||
|
||||
96
RomRepoMgr.Core/Models/lsar.cs
Normal file
96
RomRepoMgr.Core/Models/lsar.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace RomRepoMgr.Core.Models;
|
||||
|
||||
[JsonSerializable(typeof(lsar))]
|
||||
public partial class lsarJsonContext : JsonSerializerContext {}
|
||||
|
||||
public class lsar
|
||||
{
|
||||
public int lsarFormatVersion { get; set; }
|
||||
public string lsarEncoding { get; set; }
|
||||
public int lsarConfidence { get; set; }
|
||||
public string lsarFormatName { get; set; }
|
||||
public lsarProperties lsarProperties { get; set; }
|
||||
public lsarContents[] lsarContents { get; set; }
|
||||
public int lsarError { get; set; }
|
||||
public string lsarInnerFormatName { get; set; }
|
||||
public lsarProperties lsarInnerProperties { get; set; }
|
||||
}
|
||||
|
||||
public class lsarProperties
|
||||
{
|
||||
public string[] XADVolumes { get; set; }
|
||||
public string XADComment { get; set; }
|
||||
public string XADArchiveName { get; set; }
|
||||
public bool XADIsSolid { get; set; }
|
||||
public DateTime XADCreationDate { get; set; }
|
||||
public DateTime XADLastModificationDate { get; set; }
|
||||
public string ARJOriginalArchiveName { get; set; }
|
||||
}
|
||||
|
||||
public class lsarContents
|
||||
{
|
||||
public long XADCompressedSize { get; set; }
|
||||
public long XADDataLength { get; set; }
|
||||
public short ZipFlags { get; set; }
|
||||
public short ZipFileAttributes { get; set; }
|
||||
public long XADDataOffset { get; set; }
|
||||
public long XADIndex { get; set; }
|
||||
public string ZipOSName { get; set; }
|
||||
public short XADPosixPermissions { get; set; }
|
||||
public uint ZipCRC32 { get; set; }
|
||||
public int ZipLocalDate { get; set; }
|
||||
public short ZipOS { get; set; }
|
||||
public short ZipCompressionMethod { get; set; }
|
||||
public string XADCompressionName { get; set; }
|
||||
public short ZipExtractVersion { get; set; }
|
||||
public string XADFileName { get; set; }
|
||||
public DateTime XADLastModificationDate { get; set; }
|
||||
public short XADDOSFileAttributes { get; set; }
|
||||
public long XADFileSize { get; set; }
|
||||
public bool TARIsSparseFile { get; set; }
|
||||
public long XADFirstSolidEntry { get; set; }
|
||||
public string XADSolidObject { get; set; }
|
||||
public long XADFirstSolidIndex { get; set; }
|
||||
public short XADPosixUser { get; set; }
|
||||
public short XADPosixGroup { get; set; }
|
||||
public bool XADIsSolid { get; set; }
|
||||
public DateTime XADLastAccessDate { get; set; }
|
||||
public int ARJCRC32 { get; set; }
|
||||
public int ARJMethod { get; set; }
|
||||
public int ARJMinimumVersion { get; set; }
|
||||
public int ARJFileType { get; set; }
|
||||
public int ARJFlags { get; set; }
|
||||
public string ARJOSName { get; set; }
|
||||
public int ARJOS { get; set; }
|
||||
public int ARJVersion { get; set; }
|
||||
public int GzipExtraFlags { get; set; }
|
||||
public string GzipFilename { get; set; }
|
||||
public int GzipOS { get; set; }
|
||||
public short LHAHeaderLevel { get; set; }
|
||||
public string LHAExtFileNameData { get; set; }
|
||||
public short LHACRC16 { get; set; }
|
||||
public string LHAOSName { get; set; }
|
||||
public int LHAOS { get; set; }
|
||||
public string RAR5OSName { get; set; }
|
||||
public int RAR5Attributes { get; set; }
|
||||
public long RAR5DataLength { get; set; }
|
||||
public long RAR5CompressionMethod { get; set; }
|
||||
public long RAR5DictionarySize { get; set; }
|
||||
public uint RAR5CRC32 { get; set; }
|
||||
public int RAR5Flags { get; set; }
|
||||
public int RAR5DataOffset { get; set; }
|
||||
public int RAR5CompressionVersion { get; set; }
|
||||
public int RAR5CompressionInformation { get; set; }
|
||||
public int RAR5OS { get; set; }
|
||||
public RAR5InputParts[] RAR5InputParts { get; set; }
|
||||
}
|
||||
|
||||
public class RAR5InputParts
|
||||
{
|
||||
public uint CRC32 { get; set; }
|
||||
public long InputLength { get; set; }
|
||||
public long Offset { get; set; }
|
||||
}
|
||||
818
RomRepoMgr.Core/Resources/Localization.Designer.cs
generated
818
RomRepoMgr.Core/Resources/Localization.Designer.cs
generated
@@ -11,46 +11,32 @@ namespace RomRepoMgr.Core.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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()]
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Localization {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
private static System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
private static System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Localization() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RomRepoMgr.Core.Resources.Localization", typeof(Localization).Assembly);
|
||||
if (object.Equals(null, resourceMan)) {
|
||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RomRepoMgr.Core.Resources.Localization", typeof(Localization).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -59,525 +45,357 @@ namespace RomRepoMgr.Core.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adding DAT to database....
|
||||
/// </summary>
|
||||
internal static string AddingDatToDatabase {
|
||||
get {
|
||||
return ResourceManager.GetString("AddingDatToDatabase", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adding disks....
|
||||
/// </summary>
|
||||
internal static string AddingDisks {
|
||||
get {
|
||||
return ResourceManager.GetString("AddingDisks", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adding machines (games)....
|
||||
/// </summary>
|
||||
internal static string AddingMachines {
|
||||
get {
|
||||
return ResourceManager.GetString("AddingMachines", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adding medias....
|
||||
/// </summary>
|
||||
internal static string AddingMedias {
|
||||
get {
|
||||
return ResourceManager.GetString("AddingMedias", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adding ROMs....
|
||||
/// </summary>
|
||||
internal static string AddingRoms {
|
||||
get {
|
||||
return ResourceManager.GetString("AddingRoms", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Assertion failed.
|
||||
/// </summary>
|
||||
internal static string Assertion_failed {
|
||||
get {
|
||||
return ResourceManager.GetString("Assertion_failed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Specified string is not valid Base32 format because character "{0}" does not exist in Base32 alphabet.
|
||||
/// </summary>
|
||||
internal static string Base32_Invalid_format {
|
||||
get {
|
||||
return ResourceManager.GetString("Base32_Invalid_format", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
internal static string Base32_Not_enought_data {
|
||||
get {
|
||||
return ResourceManager.GetString("Base32_Not_enought_data", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot seek after stream end..
|
||||
/// </summary>
|
||||
internal static string Cannot_seek_after_end {
|
||||
internal static string Base32_Invalid_format {
|
||||
get {
|
||||
return ResourceManager.GetString("Cannot_seek_after_end", resourceCulture);
|
||||
return ResourceManager.GetString("Base32_Invalid_format", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot seek before stream start..
|
||||
/// </summary>
|
||||
internal static string Cannot_seek_before_start {
|
||||
get {
|
||||
return ResourceManager.GetString("Cannot_seek_before_start", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot find file with hash {0} in the repository.
|
||||
/// </summary>
|
||||
internal static string CannotFindHashInRepository {
|
||||
internal static string Cannot_seek_after_end {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotFindHashInRepository", resourceCulture);
|
||||
return ResourceManager.GetString("Cannot_seek_after_end", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot find lsar executable..
|
||||
/// </summary>
|
||||
internal static string CannotFindLsAr {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotFindLsAr", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot find unar executable at {0}..
|
||||
/// </summary>
|
||||
internal static string CannotFindUnArAtPath {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotFindUnArAtPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot find requested zip entry in hashes dictionary.
|
||||
/// </summary>
|
||||
internal static string CannotFindZipEntryInDictionary {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotFindZipEntryInDictionary", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot run lsar..
|
||||
/// </summary>
|
||||
internal static string CannotRunLsAr {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotRunLsAr", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot run unar..
|
||||
/// </summary>
|
||||
internal static string CannotRunUnAr {
|
||||
get {
|
||||
return ResourceManager.GetString("CannotRunUnAr", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Checking if file is an archive....
|
||||
/// </summary>
|
||||
internal static string CheckingIfFIleIsAnArchive {
|
||||
get {
|
||||
return ResourceManager.GetString("CheckingIfFIleIsAnArchive", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Compressing {0}....
|
||||
/// </summary>
|
||||
internal static string Compressing {
|
||||
get {
|
||||
return ResourceManager.GetString("Compressing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Compressing DAT file....
|
||||
/// </summary>
|
||||
internal static string CompressingDatFile {
|
||||
get {
|
||||
return ResourceManager.GetString("CompressingDatFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Compressing file....
|
||||
/// </summary>
|
||||
internal static string CompressingFile {
|
||||
get {
|
||||
return ResourceManager.GetString("CompressingFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copying {0}....
|
||||
/// </summary>
|
||||
internal static string Copying {
|
||||
get {
|
||||
return ResourceManager.GetString("Copying", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copying file....
|
||||
/// </summary>
|
||||
internal static string CopyingFile {
|
||||
get {
|
||||
return ResourceManager.GetString("CopyingFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not find ROM set in database..
|
||||
/// </summary>
|
||||
internal static string CouldNotFindRomSetInDatabase {
|
||||
get {
|
||||
return ResourceManager.GetString("CouldNotFindRomSetInDatabase", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to File exists.
|
||||
/// </summary>
|
||||
internal static string DatabaseFileExistsMsgBoxTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("DatabaseFileExistsMsgBoxTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to DAT file is already in database, not importing duplicates..
|
||||
/// </summary>
|
||||
internal static string DatAlreadyInDatabase {
|
||||
get {
|
||||
return ResourceManager.GetString("DatAlreadyInDatabase", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Imported {0} machines with {1} ROMs..
|
||||
/// </summary>
|
||||
internal static string DatImportSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("DatImportSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enumerating files....
|
||||
/// </summary>
|
||||
internal static string EnumeratingFiles {
|
||||
get {
|
||||
return ResourceManager.GetString("EnumeratingFiles", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error: {0}.
|
||||
/// </summary>
|
||||
internal static string ErrorWithMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorWithMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exporting ROMs....
|
||||
/// </summary>
|
||||
internal static string ExportingRoms {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportingRoms", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Extracted contents.
|
||||
/// </summary>
|
||||
internal static string ExtractedContents {
|
||||
get {
|
||||
return ResourceManager.GetString("ExtractedContents", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Extracting archive contents....
|
||||
/// </summary>
|
||||
internal static string ExtractingArchive {
|
||||
get {
|
||||
return ResourceManager.GetString("ExtractingArchive", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Finished.
|
||||
/// </summary>
|
||||
internal static string Finished {
|
||||
get {
|
||||
return ResourceManager.GetString("Finished", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Finishing....
|
||||
/// </summary>
|
||||
internal static string Finishing {
|
||||
get {
|
||||
return ResourceManager.GetString("Finishing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Found a disk with an unknown machine, this should not happen..
|
||||
/// </summary>
|
||||
internal static string FoundDiskWithoutMachine {
|
||||
get {
|
||||
return ResourceManager.GetString("FoundDiskWithoutMachine", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Found media with an unknown machine, this should not happen..
|
||||
/// </summary>
|
||||
internal static string FoundMediaWithoutMachine {
|
||||
get {
|
||||
return ResourceManager.GetString("FoundMediaWithoutMachine", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Found a ROM with an unknown machine, this should not happen..
|
||||
/// </summary>
|
||||
internal static string FoundRomWithoutMachine {
|
||||
get {
|
||||
return ResourceManager.GetString("FoundRomWithoutMachine", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Getting machine (game) names....
|
||||
/// </summary>
|
||||
internal static string GettingMachineNames {
|
||||
get {
|
||||
return ResourceManager.GetString("GettingMachineNames", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Hashing DAT file....
|
||||
/// </summary>
|
||||
internal static string HashingDatFile {
|
||||
get {
|
||||
return ResourceManager.GetString("HashingDatFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Hashing file....
|
||||
/// </summary>
|
||||
internal static string HashingFile {
|
||||
get {
|
||||
return ResourceManager.GetString("HashingFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Importing {0}....
|
||||
/// </summary>
|
||||
internal static string Importing {
|
||||
get {
|
||||
return ResourceManager.GetString("Importing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No checksums found..
|
||||
/// </summary>
|
||||
internal static string NoChecksumsFound {
|
||||
get {
|
||||
return ResourceManager.GetString("NoChecksumsFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not yet implemented..
|
||||
/// </summary>
|
||||
internal static string Not_yet_implemented {
|
||||
get {
|
||||
return ResourceManager.GetString("Not_yet_implemented", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not a CHD file..
|
||||
/// </summary>
|
||||
internal static string NotAChdFile {
|
||||
get {
|
||||
return ResourceManager.GetString("NotAChdFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not an AaruFormat file..
|
||||
/// </summary>
|
||||
internal static string NotAnAaruFormatFile {
|
||||
get {
|
||||
return ResourceManager.GetString("NotAnAaruFormatFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not the correct lsar executable.
|
||||
/// </summary>
|
||||
internal static string NotCorrectLsAr {
|
||||
get {
|
||||
return ResourceManager.GetString("NotCorrectLsAr", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not the correct unar executable.
|
||||
/// </summary>
|
||||
internal static string NotCorrectUnAr {
|
||||
get {
|
||||
return ResourceManager.GetString("NotCorrectUnAr", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// </summary>
|
||||
internal static string OK {
|
||||
get {
|
||||
return ResourceManager.GetString("OK", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Parsing DAT file....
|
||||
/// </summary>
|
||||
internal static string ParsinDatFile {
|
||||
get {
|
||||
return ResourceManager.GetString("ParsinDatFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Removing temporary path....
|
||||
/// </summary>
|
||||
internal static string RemovingTemporaryPath {
|
||||
get {
|
||||
return ResourceManager.GetString("RemovingTemporaryPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Retrieving ROMs and disks....
|
||||
/// </summary>
|
||||
internal static string RetrievingRomsAndDisks {
|
||||
get {
|
||||
return ResourceManager.GetString("RetrievingRomsAndDisks", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Retrieving ROM set from database..
|
||||
/// </summary>
|
||||
internal static string RetrievingRomSetFromDatabase {
|
||||
get {
|
||||
return ResourceManager.GetString("RetrievingRomSetFromDatabase", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Saving changes to database....
|
||||
/// </summary>
|
||||
internal static string SavingChangesToDatabase {
|
||||
get {
|
||||
return ResourceManager.GetString("SavingChangesToDatabase", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The input exceeds data types..
|
||||
/// </summary>
|
||||
internal static string Spamsum_Input_exceeds_data {
|
||||
get {
|
||||
return ResourceManager.GetString("Spamsum_Input_exceeds_data", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SpamSum does not have a binary representation..
|
||||
/// </summary>
|
||||
internal static string Spamsum_no_binary {
|
||||
get {
|
||||
return ResourceManager.GetString("Spamsum_no_binary", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to unar path is not set..
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unhandled exception occurred..
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unhandled exception when importing file..
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unknown file..
|
||||
/// </summary>
|
||||
internal static string UnknownFile {
|
||||
internal static string AddingDisks {
|
||||
get {
|
||||
return ResourceManager.GetString("UnknownFile", resourceCulture);
|
||||
return ResourceManager.GetString("AddingDisks", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string FoundDiskWithoutMachine {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string AddingMedias {
|
||||
get {
|
||||
return ResourceManager.GetString("AddingMedias", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string FoundMediaWithoutMachine {
|
||||
get {
|
||||
return ResourceManager.GetString("FoundMediaWithoutMachine", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string NotAnAaruFormatFile {
|
||||
get {
|
||||
return ResourceManager.GetString("NotAnAaruFormatFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string DatImportSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("DatImportSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ImportingFile {
|
||||
get {
|
||||
return ResourceManager.GetString("ImportingFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,4 +186,7 @@
|
||||
<data name="AddingMedias" xml:space="preserve">
|
||||
<value>Añadiendo medios...</value>
|
||||
</data>
|
||||
<data name="ImportingFile" xml:space="preserve">
|
||||
<value>Importando fichero...</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<root>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
id="root"
|
||||
xmlns="">
|
||||
<xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
|
||||
</xsd:schema>
|
||||
@@ -195,4 +196,7 @@
|
||||
<data name="DatImportSuccess" xml:space="preserve">
|
||||
<value>Imported {0} machines with {1} ROMs.</value>
|
||||
</data>
|
||||
<data name="ImportingFile" xml:space="preserve">
|
||||
<value>Importing file...</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,6 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.0.0-beta.1</Version>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<InformationalVersion>1.0.0-beta.1</InformationalVersion>
|
||||
<Copyright>© $([System.DateTime]::Now.Year) Natalia Portillo</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aaru.Checksums.Native"/>
|
||||
<PackageReference Include="DotNetZip"/>
|
||||
<PackageReference Include="EFCore.BulkExtensions"/>
|
||||
<PackageReference Include="Mono.Fuse.NETStandard"/>
|
||||
@@ -18,6 +27,7 @@
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers"/>
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer"/>
|
||||
<PackageReference Include="Text.Analyzers"/>
|
||||
<PackageReference Include="ZstdSharp.Port"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -25,7 +35,7 @@
|
||||
<ProjectReference Include="..\RomRepoMgr.Settings\RomRepoMgr.Settings.csproj"/>
|
||||
<ProjectReference Include="..\SabreTools\SabreTools.DatFiles\SabreTools.DatFiles.csproj"/>
|
||||
<ProjectReference Include="..\SabreTools\SabreTools.DatItems\SabreTools.DatItems.csproj"/>
|
||||
<ProjectReference Include="..\SabreTools\SabreTools.DatTools\SabreTools.DatTools.csproj" />
|
||||
<ProjectReference Include="..\SabreTools\SabreTools.DatTools\SabreTools.DatTools.csproj"/>
|
||||
<ProjectReference Include="..\SabreTools\SabreTools.FileTypes\SabreTools.FileTypes.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Aaru.Checksums;
|
||||
using RomRepoMgr.Core.Checksums;
|
||||
using Crc32Context = RomRepoMgr.Core.Checksums.Crc32Context;
|
||||
|
||||
namespace RomRepoMgr.Core.Workers;
|
||||
|
||||
|
||||
@@ -28,8 +28,11 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Resources;
|
||||
using RomRepoMgr.Settings;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using ZstdSharp;
|
||||
using ZstdSharp.Unsafe;
|
||||
using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs;
|
||||
|
||||
namespace RomRepoMgr.Core.Workers;
|
||||
@@ -45,11 +48,32 @@ public sealed class Compression
|
||||
|
||||
public void CompressFile(string source, string destination)
|
||||
{
|
||||
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);
|
||||
var inFs = new FileStream(source, FileMode.Open, FileAccess.Read);
|
||||
var outFs = new FileStream(destination, FileMode.CreateNew, FileAccess.Write);
|
||||
|
||||
var buffer = new byte[BUFFER_SIZE];
|
||||
Stream zStream;
|
||||
|
||||
switch(Settings.Settings.Current.Compression)
|
||||
{
|
||||
case CompressionType.Zstd:
|
||||
{
|
||||
var zstdStream = new CompressionStream(outFs, 15);
|
||||
zstdStream.SetParameter(ZSTD_cParameter.ZSTD_c_nbWorkers, Environment.ProcessorCount);
|
||||
zStream = zstdStream;
|
||||
|
||||
break;
|
||||
}
|
||||
case CompressionType.None:
|
||||
zStream = outFs;
|
||||
|
||||
break;
|
||||
default:
|
||||
zStream = new LZipStream(outFs, CompressionMode.Compress);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
SetProgressBounds?.Invoke(this,
|
||||
new ProgressBoundsEventArgs
|
||||
@@ -66,7 +90,7 @@ public sealed class Compression
|
||||
Value = inFs.Position
|
||||
});
|
||||
|
||||
inFs.EnsureRead(buffer, 0, buffer.Length);
|
||||
inFs.ReadExactly(buffer, 0, buffer.Length);
|
||||
zStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
@@ -79,7 +103,7 @@ public sealed class Compression
|
||||
Maximum = inFs.Length
|
||||
});
|
||||
|
||||
inFs.EnsureRead(buffer, 0, buffer.Length);
|
||||
inFs.ReadExactly(buffer, 0, buffer.Length);
|
||||
zStream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
inFs.Close();
|
||||
@@ -89,9 +113,19 @@ public sealed class Compression
|
||||
|
||||
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);
|
||||
var inFs = new FileStream(source, FileMode.Open, FileAccess.Read);
|
||||
var outFs = new FileStream(destination, FileMode.Create, FileAccess.Write);
|
||||
Stream zStream;
|
||||
|
||||
if(Path.GetExtension(source) == ".zst")
|
||||
zStream = new DecompressionStream(inFs);
|
||||
else if(Path.GetExtension(source) == ".lz")
|
||||
zStream = new LZipStream(inFs, CompressionMode.Decompress);
|
||||
else if(string.IsNullOrWhiteSpace(Path.GetExtension(source)))
|
||||
zStream = inFs;
|
||||
else
|
||||
throw new ArgumentException($"Invalid compression extension {Path.GetExtension(source)}");
|
||||
|
||||
|
||||
zStream.CopyTo(outFs);
|
||||
|
||||
|
||||
@@ -30,9 +30,11 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Aaru.Checksums;
|
||||
using System.Threading;
|
||||
using EFCore.BulkExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RomRepoMgr.Core.Checksums;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Models;
|
||||
using RomRepoMgr.Core.Resources;
|
||||
@@ -53,15 +55,18 @@ namespace RomRepoMgr.Core.Workers;
|
||||
|
||||
public sealed class DatImporter
|
||||
{
|
||||
readonly string _category;
|
||||
readonly string _datFilesPath;
|
||||
readonly string _datPath;
|
||||
bool _aborted;
|
||||
static readonly Lock DbLock = new();
|
||||
readonly string _category;
|
||||
readonly string _datFilesPath;
|
||||
readonly string _datPath;
|
||||
readonly ILoggerFactory _loggerFactory;
|
||||
bool _aborted;
|
||||
|
||||
public DatImporter(string datPath, string category)
|
||||
public DatImporter(string datPath, string category, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_datPath = datPath;
|
||||
_datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles");
|
||||
_datPath = datPath;
|
||||
_datFilesPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "datfiles");
|
||||
_loggerFactory = loggerFactory;
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(category)) _category = category;
|
||||
}
|
||||
@@ -70,7 +75,7 @@ public sealed class DatImporter
|
||||
{
|
||||
try
|
||||
{
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, _loggerFactory);
|
||||
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
@@ -132,8 +137,11 @@ public sealed class DatImporter
|
||||
Category = _category
|
||||
};
|
||||
|
||||
ctx.RomSets.Add(romSet);
|
||||
ctx.SaveChanges();
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.RomSets.Add(romSet);
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
SetMessage?.Invoke(this,
|
||||
new MessageEventArgs
|
||||
@@ -167,7 +175,7 @@ public sealed class DatImporter
|
||||
Maximum = machineNames.Count
|
||||
});
|
||||
|
||||
var position = 0;
|
||||
int position = 0;
|
||||
var machines = new Dictionary<string, Machine>();
|
||||
|
||||
foreach(string name in machineNames)
|
||||
@@ -199,8 +207,11 @@ public sealed class DatImporter
|
||||
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
ctx.BulkInsert(machines.Values.ToList(), b => b.SetOutputIdentity = true);
|
||||
ctx.SaveChanges();
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.BulkInsert(machines.Values.ToList(), b => b.SetOutputIdentity = true);
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
SetMessage?.Invoke(this,
|
||||
new MessageEventArgs
|
||||
@@ -212,29 +223,29 @@ public sealed class DatImporter
|
||||
var disks = new List<Disk>();
|
||||
var medias = new List<Media>();
|
||||
|
||||
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();
|
||||
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();
|
||||
|
||||
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;
|
||||
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();
|
||||
@@ -248,358 +259,329 @@ public sealed class DatImporter
|
||||
Maximum = datFile.Items.SortedKeys.Length
|
||||
});
|
||||
|
||||
using(DbTransaction dbTransaction = dbConnection.BeginTransaction())
|
||||
List<DbFile> pendingFilesByCrcList;
|
||||
List<DbFile> pendingFilesByMd5List;
|
||||
List<DbFile> pendingFilesBySha1List;
|
||||
List<DbFile> pendingFilesBySha256List;
|
||||
List<DbFile> pendingFilesBySha384List;
|
||||
List<DbFile> pendingFilesBySha512List;
|
||||
Dictionary<string, DbDisk> pendingDisksByMd5;
|
||||
Dictionary<string, DbDisk> pendingDisksBySha1;
|
||||
Dictionary<string, DbMedia> pendingMediasByMd5;
|
||||
Dictionary<string, DbMedia> pendingMediasBySha1;
|
||||
Dictionary<string, DbMedia> pendingMediasBySha256;
|
||||
|
||||
lock(DbLock)
|
||||
{
|
||||
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(string key in datFile.Items.SortedKeys)
|
||||
using(DbTransaction dbTransaction = dbConnection.BeginTransaction())
|
||||
{
|
||||
SetProgress?.Invoke(this,
|
||||
new ProgressEventArgs
|
||||
{
|
||||
Value = position
|
||||
});
|
||||
DbCommand dbcc = dbConnection.CreateCommand();
|
||||
|
||||
foreach(DatItem item in datFile.GetItemsForBucket(key))
|
||||
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(string key in datFile.Items.SortedKeys)
|
||||
{
|
||||
switch(item)
|
||||
SetProgress?.Invoke(this,
|
||||
new ProgressEventArgs
|
||||
{
|
||||
Value = position
|
||||
});
|
||||
|
||||
foreach(DatItem item in datFile.GetItemsForBucket(key))
|
||||
{
|
||||
case Rom rom:
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
switch(item)
|
||||
{
|
||||
case Rom rom:
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomCrc32Table}\" (\"Size\", \"Crc32\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomCrc32Table}\" (\"Size\", \"Crc32\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
romsHaveCrc = true;
|
||||
}
|
||||
romsHaveCrc = true;
|
||||
}
|
||||
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomMd5Table}\" (\"Size\", \"Md5\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomMd5Table}\" (\"Size\", \"Md5\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
romsHaveMd5 = true;
|
||||
}
|
||||
romsHaveMd5 = true;
|
||||
}
|
||||
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha1Table}\" (\"Size\", \"Sha1\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha1Table}\" (\"Size\", \"Sha1\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
romsHaveSha1 = true;
|
||||
}
|
||||
romsHaveSha1 = true;
|
||||
}
|
||||
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA256Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA256Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha256Table}\" (\"Size\", \"Sha256\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA256Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha256Table}\" (\"Size\", \"Sha256\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA256Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
romsHaveSha256 = true;
|
||||
}
|
||||
romsHaveSha256 = true;
|
||||
}
|
||||
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA384Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA384Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha384Table}\" (\"Size\", \"Sha384\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA384Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha384Table}\" (\"Size\", \"Sha384\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA384Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
romsHaveSha384 = true;
|
||||
}
|
||||
romsHaveSha384 = true;
|
||||
}
|
||||
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA512Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA512Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha512Table}\" (\"Size\", \"Sha512\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA512Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpRomSha512Table}\" (\"Size\", \"Sha512\") VALUES (\"{(ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA512Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
romsHaveSha512 = true;
|
||||
}
|
||||
romsHaveSha512 = true;
|
||||
}
|
||||
|
||||
roms.Add(rom);
|
||||
roms.Add(rom);
|
||||
|
||||
continue;
|
||||
case Disk disk:
|
||||
if(disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
continue;
|
||||
case Disk disk:
|
||||
if(disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpDiskMd5Table}\" (\"Md5\") VALUES (\"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpDiskMd5Table}\" (\"Md5\") VALUES (\"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
disksHaveMd5 = true;
|
||||
}
|
||||
disksHaveMd5 = true;
|
||||
}
|
||||
|
||||
if(disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.SHA1Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.SHA1Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpDiskSha1Table}\" (\"Sha1\") VALUES (\"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.SHA1Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpDiskSha1Table}\" (\"Sha1\") VALUES (\"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.SHA1Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
disksHaveSha1 = true;
|
||||
}
|
||||
disksHaveSha1 = true;
|
||||
}
|
||||
|
||||
disks.Add(disk);
|
||||
disks.Add(disk);
|
||||
|
||||
continue;
|
||||
case Media media:
|
||||
if(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
continue;
|
||||
case Media media:
|
||||
if(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpMediaMd5Table}\" (\"Md5\") VALUES (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpMediaMd5Table}\" (\"Md5\") VALUES (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
mediasHaveMd5 = true;
|
||||
}
|
||||
mediasHaveMd5 = true;
|
||||
}
|
||||
|
||||
if(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA1Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA1Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpMediaSha1Table}\" (\"Sha1\") VALUES (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA1Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpMediaSha1Table}\" (\"Sha1\") VALUES (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA1Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
mediasHaveSha1 = true;
|
||||
}
|
||||
mediasHaveSha1 = true;
|
||||
}
|
||||
|
||||
if(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA256Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
if(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA256Key) != null)
|
||||
{
|
||||
dbcc = dbConnection.CreateCommand();
|
||||
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpMediaSha256Table}\" (\"Sha256\") VALUES (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA256Key)}\");";
|
||||
dbcc.CommandText =
|
||||
$"INSERT INTO \"{tmpMediaSha256Table}\" (\"Sha256\") VALUES (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA256Key)}\");";
|
||||
|
||||
dbcc.ExecuteNonQuery();
|
||||
dbcc.ExecuteNonQuery();
|
||||
|
||||
mediasHaveSha256 = true;
|
||||
}
|
||||
mediasHaveSha256 = true;
|
||||
}
|
||||
|
||||
medias.Add(media);
|
||||
medias.Add(media);
|
||||
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
position++;
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
dbTransaction.Commit();
|
||||
}
|
||||
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
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()
|
||||
: [];
|
||||
|
||||
dbTransaction.Commit();
|
||||
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()
|
||||
: [];
|
||||
|
||||
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()
|
||||
: [];
|
||||
|
||||
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()
|
||||
: [];
|
||||
|
||||
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()
|
||||
: [];
|
||||
|
||||
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()
|
||||
: [];
|
||||
|
||||
pendingDisksByMd5 = disksHaveMd5
|
||||
? ctx.Disks
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskMd5Table}] AS t WHERE f.Md5 = t.Md5")
|
||||
.ToDictionary(f => f.Md5)
|
||||
: [];
|
||||
|
||||
pendingDisksBySha1 = disksHaveSha1
|
||||
? ctx.Disks
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskSha1Table}] AS t WHERE f.Sha1 = t.Sha1")
|
||||
.ToDictionary(f => f.Sha1)
|
||||
: [];
|
||||
|
||||
pendingMediasByMd5 = mediasHaveMd5
|
||||
? ctx.Medias
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaMd5Table}] AS t WHERE f.Md5 = t.Md5")
|
||||
.ToDictionary(f => f.Md5)
|
||||
: [];
|
||||
|
||||
pendingMediasBySha1 = mediasHaveSha1
|
||||
? ctx.Medias
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha1Table}] AS t WHERE f.Sha1 = t.Sha1")
|
||||
.ToDictionary(f => f.Sha1)
|
||||
: [];
|
||||
|
||||
pendingMediasBySha256 = mediasHaveSha256
|
||||
? ctx.Medias
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha256Table}] AS t WHERE f.Sha256 = t.Sha256")
|
||||
.ToDictionary(f => f.Sha256)
|
||||
: [];
|
||||
}
|
||||
|
||||
List<DbFile> 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()
|
||||
: [];
|
||||
|
||||
List<DbFile> 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()
|
||||
: [];
|
||||
|
||||
List<DbFile> 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()
|
||||
: [];
|
||||
|
||||
List<DbFile> 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()
|
||||
: [];
|
||||
|
||||
List<DbFile> 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()
|
||||
: [];
|
||||
|
||||
List<DbFile> 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()
|
||||
: [];
|
||||
|
||||
Dictionary<string, DbDisk> pendingDisksByMd5 =
|
||||
disksHaveMd5
|
||||
? ctx.Disks
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskMd5Table}] AS t WHERE f.Md5 = t.Md5")
|
||||
.ToDictionary(f => f.Md5)
|
||||
: [];
|
||||
|
||||
Dictionary<string, DbDisk> pendingDisksBySha1 =
|
||||
disksHaveSha1
|
||||
? ctx.Disks
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Disks AS f, [{tmpDiskSha1Table}] AS t WHERE f.Sha1 = t.Sha1")
|
||||
.ToDictionary(f => f.Sha1)
|
||||
: [];
|
||||
|
||||
Dictionary<string, DbMedia> pendingMediasByMd5 =
|
||||
mediasHaveMd5
|
||||
? ctx.Medias
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaMd5Table}] AS t WHERE f.Md5 = t.Md5")
|
||||
.ToDictionary(f => f.Md5)
|
||||
: [];
|
||||
|
||||
Dictionary<string, DbMedia> pendingMediasBySha1 =
|
||||
mediasHaveSha1
|
||||
? ctx.Medias
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha1Table}] AS t WHERE f.Sha1 = t.Sha1")
|
||||
.ToDictionary(f => f.Sha1)
|
||||
: [];
|
||||
|
||||
Dictionary<string, DbMedia> pendingMediasBySha256 =
|
||||
mediasHaveSha256
|
||||
? ctx.Medias
|
||||
.FromSqlRaw($"SELECT DISTINCT f.* FROM Medias AS f, [{tmpMediaSha256Table}] AS t WHERE f.Sha256 = t.Sha256")
|
||||
.ToDictionary(f => f.Sha256)
|
||||
: [];
|
||||
|
||||
var pendingFilesByCrc = new Dictionary<string, DbFile>();
|
||||
var pendingFilesByMd5 = new Dictionary<string, DbFile>();
|
||||
var pendingFilesBySha1 = new Dictionary<string, DbFile>();
|
||||
var pendingFilesBySha256 = new Dictionary<string, DbFile>();
|
||||
var pendingFilesBySha384 = new Dictionary<string, DbFile>();
|
||||
var pendingFilesBySha512 = new Dictionary<string, DbFile>();
|
||||
var pendingFiles = new List<DbFile>();
|
||||
|
||||
// This is because of hash collisions.
|
||||
foreach(DbFile item in pendingFilesByCrcList)
|
||||
{
|
||||
if(pendingFilesByCrc.ContainsKey(item.Crc32))
|
||||
pendingFiles.Add(item);
|
||||
else
|
||||
pendingFilesByCrc[item.Crc32] = item;
|
||||
}
|
||||
var pendingFiles = pendingFilesByCrcList.Where(item => !pendingFilesByCrc.TryAdd(item.Crc32, item))
|
||||
.ToList();
|
||||
|
||||
foreach(DbFile item in pendingFilesByMd5List)
|
||||
{
|
||||
if(pendingFilesByMd5.ContainsKey(item.Md5))
|
||||
pendingFiles.Add(item);
|
||||
else
|
||||
pendingFilesByMd5[item.Md5] = item;
|
||||
}
|
||||
pendingFiles.AddRange(pendingFilesByMd5List.Where(item => !pendingFilesByMd5.TryAdd(item.Md5, item)));
|
||||
pendingFiles.AddRange(pendingFilesBySha1List.Where(item => !pendingFilesBySha1.TryAdd(item.Sha1, item)));
|
||||
|
||||
foreach(DbFile item in pendingFilesBySha1List)
|
||||
{
|
||||
if(pendingFilesBySha1.ContainsKey(item.Sha1))
|
||||
pendingFiles.Add(item);
|
||||
else
|
||||
pendingFilesBySha1[item.Sha1] = item;
|
||||
}
|
||||
pendingFiles.AddRange(pendingFilesBySha256List.Where(item => !pendingFilesBySha256
|
||||
.TryAdd(item.Sha256, item)));
|
||||
|
||||
foreach(DbFile item in pendingFilesBySha256List)
|
||||
{
|
||||
if(pendingFilesBySha256.ContainsKey(item.Sha256))
|
||||
pendingFiles.Add(item);
|
||||
else
|
||||
pendingFilesBySha256[item.Sha256] = item;
|
||||
}
|
||||
pendingFiles.AddRange(pendingFilesBySha384List.Where(item => !pendingFilesBySha384
|
||||
.TryAdd(item.Sha384, 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;
|
||||
}
|
||||
pendingFiles.AddRange(pendingFilesBySha512List.Where(item => !pendingFilesBySha512
|
||||
.TryAdd(item.Sha512, item)));
|
||||
|
||||
// Clear some memory
|
||||
pendingFilesByCrcList.Clear();
|
||||
@@ -609,17 +591,20 @@ public sealed class DatImporter
|
||||
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}]");
|
||||
lock(DbLock)
|
||||
{
|
||||
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
|
||||
@@ -641,7 +626,7 @@ public sealed class DatImporter
|
||||
|
||||
foreach(Rom rom in roms)
|
||||
{
|
||||
var hashCollision = false;
|
||||
bool hashCollision = false;
|
||||
|
||||
SetProgress?.Invoke(this,
|
||||
new ProgressEventArgs
|
||||
@@ -663,7 +648,7 @@ public sealed class DatImporter
|
||||
return;
|
||||
}
|
||||
|
||||
var uSize = (ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey);
|
||||
ulong uSize = (ulong)rom.GetInt64FieldValue(SabreTools.Models.Metadata.Rom.SizeKey);
|
||||
|
||||
DbFile file = null;
|
||||
|
||||
@@ -923,7 +908,10 @@ public sealed class DatImporter
|
||||
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
ctx.BulkInsert(newFiles, b => b.SetOutputIdentity = true);
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.BulkInsert(newFiles, b => b.SetOutputIdentity = true);
|
||||
}
|
||||
|
||||
foreach(FileByMachine fbm in newFilesByMachine)
|
||||
{
|
||||
@@ -931,9 +919,12 @@ public sealed class DatImporter
|
||||
fbm.MachineId = fbm.Machine.Id;
|
||||
}
|
||||
|
||||
ctx.BulkInsert(newFilesByMachine);
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.BulkInsert(newFilesByMachine);
|
||||
|
||||
ctx.SaveChanges();
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
pendingFilesBySha512.Clear();
|
||||
pendingFilesBySha384.Clear();
|
||||
@@ -1056,7 +1047,10 @@ public sealed class DatImporter
|
||||
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
ctx.BulkInsert(newDisks, b => b.SetOutputIdentity = true);
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.BulkInsert(newDisks, b => b.SetOutputIdentity = true);
|
||||
}
|
||||
|
||||
foreach(DiskByMachine dbm in newDisksByMachine)
|
||||
{
|
||||
@@ -1064,9 +1058,12 @@ public sealed class DatImporter
|
||||
dbm.MachineId = dbm.Machine.Id;
|
||||
}
|
||||
|
||||
ctx.BulkInsert(newDisksByMachine);
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.BulkInsert(newDisksByMachine);
|
||||
|
||||
ctx.SaveChanges();
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
pendingDisksBySha1.Clear();
|
||||
pendingDisksByMd5.Clear();
|
||||
@@ -1203,7 +1200,10 @@ public sealed class DatImporter
|
||||
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
ctx.BulkInsert(newMedias, b => b.SetOutputIdentity = true);
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.BulkInsert(newMedias, b => b.SetOutputIdentity = true);
|
||||
}
|
||||
|
||||
foreach(MediaByMachine mbm in newMediasByMachine)
|
||||
{
|
||||
@@ -1211,63 +1211,70 @@ public sealed class DatImporter
|
||||
mbm.MachineId = mbm.Machine.Id;
|
||||
}
|
||||
|
||||
ctx.BulkInsert(newMediasByMachine);
|
||||
lock(DbLock)
|
||||
{
|
||||
ctx.BulkInsert(newMediasByMachine);
|
||||
|
||||
ctx.SaveChanges();
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
pendingMediasBySha256.Clear();
|
||||
pendingMediasBySha1.Clear();
|
||||
pendingMediasByMd5.Clear();
|
||||
newMedias.Clear();
|
||||
newMediasByMachine.Clear();
|
||||
RomSetStat stats;
|
||||
|
||||
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();
|
||||
lock(DbLock)
|
||||
{
|
||||
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);
|
||||
RomSetStat oldStats = ctx.RomSetStats.Find(stats.RomSetId);
|
||||
|
||||
if(oldStats != null) ctx.Remove(oldStats);
|
||||
if(oldStats != null) ctx.Remove(oldStats);
|
||||
|
||||
ctx.RomSetStats.Add(stats);
|
||||
ctx.RomSetStats.Add(stats);
|
||||
|
||||
ctx.SaveChanges();
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
WorkFinished?.Invoke(this,
|
||||
new MessageEventArgs
|
||||
|
||||
@@ -6,16 +6,18 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Ionic.Zip;
|
||||
using Ionic.Zlib;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Resources;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using ZstdSharp;
|
||||
using CompressionMode = SharpCompress.Compressors.CompressionMode;
|
||||
|
||||
namespace RomRepoMgr.Core.Workers;
|
||||
|
||||
public class FileExporter(long romSetId, string outPath)
|
||||
public class FileExporter(long romSetId, string outPath, ILoggerFactory loggerFactory)
|
||||
{
|
||||
const long BUFFER_SIZE = 131072;
|
||||
long _filePosition;
|
||||
@@ -43,7 +45,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Message = Localization.RetrievingRomSetFromDatabase
|
||||
});
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
RomSet romSet = ctx.RomSets.Find(romSetId);
|
||||
|
||||
@@ -108,7 +110,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Message = machine.Name
|
||||
});
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath, loggerFactory);
|
||||
|
||||
string machineName = machine.Name;
|
||||
|
||||
@@ -154,10 +156,10 @@ public class FileExporter(long romSetId, string outPath)
|
||||
|
||||
if(media.Sha256 != null)
|
||||
{
|
||||
var sha256Bytes = new byte[32];
|
||||
byte[] sha256Bytes = new byte[32];
|
||||
string sha256 = media.Sha256;
|
||||
|
||||
for(var i = 0; i < 32; i++)
|
||||
for(int i = 0; i < 32; i++)
|
||||
{
|
||||
if(sha256[i * 2] >= 0x30 && sha256[i * 2] <= 0x39)
|
||||
sha256Bytes[i] = (byte)((sha256[i * 2] - 0x30) * 0x10);
|
||||
@@ -189,10 +191,10 @@ public class FileExporter(long romSetId, string outPath)
|
||||
|
||||
if(media.Sha1 != null)
|
||||
{
|
||||
var sha1Bytes = new byte[20];
|
||||
byte[] sha1Bytes = new byte[20];
|
||||
string sha1 = media.Sha1;
|
||||
|
||||
for(var i = 0; i < 20; i++)
|
||||
for(int i = 0; i < 20; i++)
|
||||
{
|
||||
if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39)
|
||||
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10);
|
||||
@@ -224,10 +226,10 @@ public class FileExporter(long romSetId, string outPath)
|
||||
|
||||
if(media.Md5 != null)
|
||||
{
|
||||
var md5Bytes = new byte[16];
|
||||
byte[] md5Bytes = new byte[16];
|
||||
string md5 = media.Md5;
|
||||
|
||||
for(var i = 0; i < 16; i++)
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39)
|
||||
md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10);
|
||||
@@ -285,7 +287,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Maximum = inFs.Length
|
||||
});
|
||||
|
||||
var buffer = new byte[BUFFER_SIZE];
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
while(inFs.Position + BUFFER_SIZE <= inFs.Length)
|
||||
{
|
||||
@@ -295,7 +297,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Value = inFs.Position
|
||||
});
|
||||
|
||||
inFs.EnsureRead(buffer, 0, buffer.Length);
|
||||
inFs.ReadExactly(buffer, 0, buffer.Length);
|
||||
outFs.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
@@ -307,7 +309,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Value = inFs.Position
|
||||
});
|
||||
|
||||
inFs.EnsureRead(buffer, 0, buffer.Length);
|
||||
inFs.ReadExactly(buffer, 0, buffer.Length);
|
||||
outFs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
inFs.Close();
|
||||
@@ -358,10 +360,10 @@ public class FileExporter(long romSetId, string outPath)
|
||||
|
||||
if(disk.Sha1 != null)
|
||||
{
|
||||
var sha1Bytes = new byte[20];
|
||||
byte[] sha1Bytes = new byte[20];
|
||||
string sha1 = disk.Sha1;
|
||||
|
||||
for(var i = 0; i < 20; i++)
|
||||
for(int i = 0; i < 20; i++)
|
||||
{
|
||||
if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39)
|
||||
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10);
|
||||
@@ -393,10 +395,10 @@ public class FileExporter(long romSetId, string outPath)
|
||||
|
||||
if(disk.Md5 != null)
|
||||
{
|
||||
var md5Bytes = new byte[16];
|
||||
byte[] md5Bytes = new byte[16];
|
||||
string md5 = disk.Md5;
|
||||
|
||||
for(var i = 0; i < 16; i++)
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39)
|
||||
md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10);
|
||||
@@ -452,7 +454,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Maximum = inFs.Length
|
||||
});
|
||||
|
||||
var buffer = new byte[BUFFER_SIZE];
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
while(inFs.Position + BUFFER_SIZE <= inFs.Length)
|
||||
{
|
||||
@@ -462,7 +464,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Value = inFs.Position
|
||||
});
|
||||
|
||||
inFs.EnsureRead(buffer, 0, buffer.Length);
|
||||
inFs.ReadExactly(buffer, 0, buffer.Length);
|
||||
outFs.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
@@ -474,7 +476,7 @@ public class FileExporter(long romSetId, string outPath)
|
||||
Value = inFs.Position
|
||||
});
|
||||
|
||||
inFs.EnsureRead(buffer, 0, buffer.Length);
|
||||
inFs.ReadExactly(buffer, 0, buffer.Length);
|
||||
outFs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
inFs.Close();
|
||||
@@ -557,10 +559,10 @@ public class FileExporter(long romSetId, string outPath)
|
||||
// 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];
|
||||
byte[] sha384Bytes = new byte[48];
|
||||
string sha384 = file.Sha384;
|
||||
|
||||
for(var i = 0; i < 48; i++)
|
||||
for(int i = 0; i < 48; i++)
|
||||
{
|
||||
if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39)
|
||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
|
||||
@@ -588,10 +590,43 @@ public class FileExporter(long romSetId, string outPath)
|
||||
sha384B32[4].ToString(),
|
||||
sha384B32 + ".lz");
|
||||
|
||||
if(!File.Exists(repoPath))
|
||||
throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository, file.Sha256));
|
||||
FileStream inFs;
|
||||
|
||||
var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read);
|
||||
// Try ZSTD
|
||||
if(!File.Exists(repoPath))
|
||||
{
|
||||
repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath,
|
||||
"files",
|
||||
sha384B32[0].ToString(),
|
||||
sha384B32[1].ToString(),
|
||||
sha384B32[2].ToString(),
|
||||
sha384B32[3].ToString(),
|
||||
sha384B32[4].ToString(),
|
||||
sha384B32 + ".zst");
|
||||
|
||||
if(!File.Exists(repoPath))
|
||||
{
|
||||
repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath,
|
||||
"files",
|
||||
sha384B32[0].ToString(),
|
||||
sha384B32[1].ToString(),
|
||||
sha384B32[2].ToString(),
|
||||
sha384B32[3].ToString(),
|
||||
sha384B32[4].ToString(),
|
||||
sha384B32);
|
||||
|
||||
return !File.Exists(repoPath)
|
||||
? throw new ArgumentException(string.Format(Localization.CannotFindHashInRepository,
|
||||
file.Sha256))
|
||||
: new FileStream(repoPath, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
return new StreamWithLength(new DecompressionStream(inFs), (long)file.Size);
|
||||
}
|
||||
|
||||
inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
return new StreamWithLength(new LZipStream(inFs, CompressionMode.Decompress), (long)file.Size);
|
||||
}
|
||||
@@ -611,11 +646,9 @@ public class FileExporter(long romSetId, string outPath)
|
||||
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);
|
||||
}
|
||||
if(!_filesByMachine.TryGetValue(e.CurrentEntry.FileName, out FileByMachine fileByMachine) &&
|
||||
!_filesByMachine.TryGetValue(e.CurrentEntry.FileName.Replace('/', '\\'), out fileByMachine))
|
||||
throw new ArgumentException(Localization.CannotFindZipEntryInDictionary);
|
||||
|
||||
DbFile currentFile = fileByMachine.File;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,13 +41,13 @@ public sealed class Context(DbContextOptions options) : DbContext(options)
|
||||
public DbSet<MediaByMachine> MediasByMachines { get; set; }
|
||||
public DbSet<RomSetStat> RomSetStats { get; set; }
|
||||
|
||||
public static Context Create(string dbPath)
|
||||
public static Context Create(string dbPath, ILoggerFactory loggerFactory)
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder();
|
||||
|
||||
optionsBuilder.UseLazyLoadingProxies()
|
||||
#if DEBUG
|
||||
.UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()))
|
||||
.UseLoggerFactory(loggerFactory)
|
||||
#endif
|
||||
.UseSqlite($"Data Source={dbPath}");
|
||||
|
||||
|
||||
@@ -24,10 +24,12 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.Database;
|
||||
|
||||
public class ContextFactory : IDesignTimeDbContextFactory<Context>
|
||||
{
|
||||
public Context CreateDbContext(string[] args) => Context.Create("romrepo.db");
|
||||
public Context CreateDbContext(string[] args) =>
|
||||
Context.Create("romrepo.db", LoggerFactory.Create(builder => builder.AddConsole()));
|
||||
}
|
||||
@@ -11,46 +11,32 @@ namespace RomRepoMgr.Database.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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()]
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Localization {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
private static System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
private static System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Localization() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RomRepoMgr.Database.Resources.Localization", typeof(Localization).Assembly);
|
||||
if (object.Equals(null, resourceMan)) {
|
||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RomRepoMgr.Database.Resources.Localization", typeof(Localization).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -59,9 +45,6 @@ namespace RomRepoMgr.Database.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Settings are not initialized!.
|
||||
/// </summary>
|
||||
internal static string Settings_not_initialized {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_not_initialized", resourceCulture);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<root>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
id="root"
|
||||
xmlns="">
|
||||
<xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
|
||||
</xsd:schema>
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Version>1.0.0-beta.1</Version>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<InformationalVersion>1.0.0-beta.1</InformationalVersion>
|
||||
<Copyright>© $([System.DateTime]::Now.Year) Natalia Portillo</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console"/>
|
||||
<PackageReference Include="AsyncFixer"/>
|
||||
<PackageReference Include="ErrorProne.NET.CoreAnalyzers"/>
|
||||
|
||||
@@ -232,7 +232,7 @@ public static class DetectOS
|
||||
/// <returns>Current operating system version</returns>
|
||||
public static string GetVersion()
|
||||
{
|
||||
var environ = Environment.OSVersion.Version.ToString();
|
||||
string environ = Environment.OSVersion.Version.ToString();
|
||||
|
||||
switch(GetRealPlatformID())
|
||||
{
|
||||
|
||||
@@ -11,46 +11,32 @@ namespace RomRepoMgr.Settings.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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()]
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Localization {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
private static System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
private static System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Localization() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RomRepoMgr.Settings.Resources.Localization", typeof(Localization).Assembly);
|
||||
if (object.Equals(null, resourceMan)) {
|
||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RomRepoMgr.Settings.Resources.Localization", typeof(Localization).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -59,9 +45,6 @@ namespace RomRepoMgr.Settings.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unhandled exception calling uname: {0}.
|
||||
/// </summary>
|
||||
internal static string Unhandled_exception_uname {
|
||||
get {
|
||||
return ResourceManager.GetString("Unhandled_exception_uname", resourceCulture);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<root>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
id="root"
|
||||
xmlns="">
|
||||
<xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
|
||||
</xsd:schema>
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Version>1.0.0-beta.1</Version>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<InformationalVersion>1.0.0-beta.1</InformationalVersion>
|
||||
<Copyright>© $([System.DateTime]::Now.Year) Natalia Portillo</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Win32.Registry"/>
|
||||
|
||||
@@ -33,12 +33,21 @@ using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
|
||||
|
||||
namespace RomRepoMgr.Settings;
|
||||
|
||||
public enum CompressionType
|
||||
{
|
||||
Lzip = 0,
|
||||
Zstd,
|
||||
None
|
||||
}
|
||||
|
||||
public sealed class SetSettings
|
||||
{
|
||||
public string DatabasePath { get; set; }
|
||||
public string RepositoryPath { get; set; }
|
||||
public string TemporaryFolder { get; set; }
|
||||
public string UnArchiverPath { get; set; }
|
||||
public string DatabasePath { get; set; }
|
||||
public string RepositoryPath { get; set; }
|
||||
public string TemporaryFolder { get; set; }
|
||||
public string UnArchiverPath { get; set; }
|
||||
public CompressionType Compression { get; set; }
|
||||
public bool UseInternalDecompressor { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Manages statistics</summary>
|
||||
@@ -48,7 +57,7 @@ public static class Settings
|
||||
const string XDG_CONFIG_HOME_RESOLVED = ".config";
|
||||
/// <summary>Current statistics</summary>
|
||||
public static SetSettings Current;
|
||||
public static bool UnArUsable { get; set; }
|
||||
public static bool CanDecompress { get; set; }
|
||||
|
||||
/// <summary>Loads settings</summary>
|
||||
public static void LoadSettings()
|
||||
@@ -105,6 +114,15 @@ public static class Settings
|
||||
? ((NSString)obj).ToString()
|
||||
: null;
|
||||
|
||||
Current.Compression = parsedPreferences.TryGetValue("Compression", out obj)
|
||||
? (CompressionType)Enum.Parse(typeof(CompressionType),
|
||||
((NSNumber)obj).ToString())
|
||||
: CompressionType.Lzip;
|
||||
|
||||
Current.UseInternalDecompressor =
|
||||
parsedPreferences.TryGetValue("UseInternalDecompressor", out obj) &&
|
||||
((NSNumber)obj).ToBool();
|
||||
|
||||
prefsFs.Close();
|
||||
}
|
||||
else
|
||||
@@ -146,10 +164,16 @@ public static class Settings
|
||||
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;
|
||||
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;
|
||||
Current.UseInternalDecompressor = key.GetValue("UseInternalDecompressor") as int? > 0;
|
||||
|
||||
if(key.GetValue("Compression") is int compression)
|
||||
Current.Compression = (CompressionType)compression;
|
||||
else
|
||||
Current.Compression = CompressionType.Lzip;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -222,6 +246,12 @@ public static class Settings
|
||||
},
|
||||
{
|
||||
"UnArchiverPath", Current.UnArchiverPath
|
||||
},
|
||||
{
|
||||
"Compression", (NSNumber)(int)Current.Compression
|
||||
},
|
||||
{
|
||||
"UseInternalDecompressor", new NSNumber(Current.UseInternalDecompressor ? 1 : 0)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -252,10 +282,12 @@ public static class Settings
|
||||
|
||||
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);
|
||||
key?.SetValue("DatabasePath", Current.DatabasePath);
|
||||
key?.SetValue("RepositoryPath", Current.RepositoryPath);
|
||||
key?.SetValue("TemporaryFolder", Current.TemporaryFolder);
|
||||
key?.SetValue("UnArchiverPath", Current.UnArchiverPath);
|
||||
key?.SetValue("Compression", (int)Current.Compression);
|
||||
key?.SetValue("UseInternalDecompressor", Current.UseInternalDecompressor ? 1 : 0);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -309,7 +341,8 @@ public static class Settings
|
||||
{
|
||||
DatabasePath = Path.Combine(dataPath, "romrepo.db"),
|
||||
RepositoryPath = Path.Combine(dataPath, "repo"),
|
||||
TemporaryFolder = Path.GetTempPath()
|
||||
TemporaryFolder = Path.GetTempPath(),
|
||||
Compression = CompressionType.Lzip
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
Directory.Packages.props = Directory.Packages.props
|
||||
global.json = global.json
|
||||
README.md = README.md
|
||||
logo.png = logo.png
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.DatTools", "SabreTools\SabreTools.DatTools\SabreTools.DatTools.csproj", "{20CC9ED6-9F56-49B5-A265-DC246C424F27}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Reports", "SabreTools\SabreTools.Reports\SabreTools.Reports.csproj", "{E73767A7-0A65-4F89-B149-A520874F7B32}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RomRepoMgr.Blazor", "RomRepoMgr.Blazor\RomRepoMgr.Blazor.csproj", "{30DA0637-76C5-43DE-8203-403AECF5F859}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -78,5 +83,9 @@ Global
|
||||
{E73767A7-0A65-4F89-B149-A520874F7B32}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E73767A7-0A65-4F89-B149-A520874F7B32}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E73767A7-0A65-4F89-B149-A520874F7B32}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xml:space="preserve">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Aaru/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Claunia/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=datfiles/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=dlclose/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=HKLM/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=libdl/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=lsar/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=romrepo/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=romrepombgrfs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=romrepomgr/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=setxattr/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Umounted/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unar/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Winfsp/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=xattr/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=xattrs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=_0020_007B_000A_0020_0020_0020_0020/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Claunia/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=datfiles/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=dlclose/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=HKLM/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=libdl/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=lsar/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=romrepo/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=romrepombgrfs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=romrepomgr/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=setxattr/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Umounted/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unar/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Winfsp/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=xattr/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=xattrs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean
|
||||
x:Key="/Default/UserDictionary/Words/=_0020_007B_000A_0020_0020_0020_0020/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
BIN
RomRepoMgr/Assets/RomRepoMgr.icns
Normal file
BIN
RomRepoMgr/Assets/RomRepoMgr.icns
Normal file
Binary file not shown.
BIN
RomRepoMgr/Assets/romrepomgr.ico
Normal file
BIN
RomRepoMgr/Assets/romrepomgr.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 317 KiB |
64
RomRepoMgr/Models/DatImporter.cs
Normal file
64
RomRepoMgr/Models/DatImporter.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
|
||||
namespace RomRepoMgr.Models;
|
||||
|
||||
public partial class DatImporter : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
bool _indeterminate;
|
||||
[ObservableProperty]
|
||||
double _maximum;
|
||||
[ObservableProperty]
|
||||
double _minimum;
|
||||
[ObservableProperty]
|
||||
double _progress;
|
||||
[ObservableProperty]
|
||||
Color _statusColor;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
public string Filename { get; internal init; }
|
||||
public Task Task { get; set; }
|
||||
public bool Running { get; private set; } = true;
|
||||
|
||||
internal void OnErrorOccurred(object sender, ErrorEventArgs e) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
StatusMessage = e.Message;
|
||||
StatusColor = Colors.Red;
|
||||
|
||||
if(!Indeterminate) return;
|
||||
|
||||
Indeterminate = false;
|
||||
Progress = 0;
|
||||
});
|
||||
|
||||
internal void OnSetIndeterminateProgress(object sender, EventArgs e) =>
|
||||
Dispatcher.UIThread.Post(() => Indeterminate = true);
|
||||
|
||||
internal void OnSetMessage(object sender, MessageEventArgs e) =>
|
||||
Dispatcher.UIThread.Post(() => StatusMessage = e.Message);
|
||||
|
||||
internal void OnSetProgress(object sender, ProgressEventArgs e) =>
|
||||
Dispatcher.UIThread.Post(() => Progress = e.Value);
|
||||
|
||||
internal void OnSetProgressBounds(object sender, ProgressBoundsEventArgs e) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
Indeterminate = false;
|
||||
Maximum = e.Maximum;
|
||||
Minimum = e.Minimum;
|
||||
});
|
||||
|
||||
internal void OnWorkFinished(object sender, MessageEventArgs e) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
Indeterminate = false;
|
||||
Maximum = 1;
|
||||
Minimum = 0;
|
||||
Progress = 1;
|
||||
StatusMessage = e.Message;
|
||||
Running = false;
|
||||
});
|
||||
}
|
||||
70
RomRepoMgr/Models/RomImporter.cs
Normal file
70
RomRepoMgr/Models/RomImporter.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Avalonia.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
|
||||
namespace RomRepoMgr.Models;
|
||||
|
||||
public partial class RomImporter : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
bool _indeterminate;
|
||||
[ObservableProperty]
|
||||
double _maximum;
|
||||
[ObservableProperty]
|
||||
double _minimum;
|
||||
[ObservableProperty]
|
||||
double _progress;
|
||||
[ObservableProperty]
|
||||
bool _progressVisible = true;
|
||||
[ObservableProperty]
|
||||
Color _statusColor;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
public string Filename { get; internal init; }
|
||||
public bool Running { get; private set; } = true;
|
||||
|
||||
internal void OnSetIndeterminateProgress(object sender, EventArgs e)
|
||||
{
|
||||
Indeterminate = true;
|
||||
}
|
||||
|
||||
internal void OnSetMessage(object sender, MessageEventArgs e)
|
||||
{
|
||||
StatusMessage = e.Message;
|
||||
}
|
||||
|
||||
internal void OnSetProgress(object sender, ProgressEventArgs e)
|
||||
{
|
||||
Progress = e.Value;
|
||||
}
|
||||
|
||||
internal void OnSetProgressBounds(object sender, ProgressBoundsEventArgs e)
|
||||
{
|
||||
Indeterminate = false;
|
||||
Maximum = e.Maximum;
|
||||
Minimum = e.Minimum;
|
||||
}
|
||||
|
||||
internal void OnWorkFinished(object sender, MessageEventArgs e)
|
||||
{
|
||||
Indeterminate = false;
|
||||
Maximum = 1;
|
||||
Minimum = 0;
|
||||
Progress = 1;
|
||||
StatusMessage = e.Message;
|
||||
Running = false;
|
||||
ProgressVisible = false;
|
||||
}
|
||||
|
||||
public void OnImportedRom(object sender, ImportedRomItemEventArgs e)
|
||||
{
|
||||
Indeterminate = false;
|
||||
Maximum = 1;
|
||||
Minimum = 0;
|
||||
Progress = 1;
|
||||
StatusMessage = e.Item.Status;
|
||||
Running = false;
|
||||
ProgressVisible = false;
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,10 @@
|
||||
// Copyright © 2020-2024 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Logging;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Serilog;
|
||||
|
||||
namespace RomRepoMgr;
|
||||
|
||||
@@ -34,11 +35,39 @@ internal static 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);
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
#if DEBUG
|
||||
.MinimumLevel.Debug()
|
||||
#else
|
||||
.MinimumLevel.Information()
|
||||
#endif
|
||||
.WriteTo.Console()
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Starting up");
|
||||
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Application start-up failed");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace(LogEventLevel.Debug)
|
||||
.UseReactiveUI();
|
||||
#if DEBUG
|
||||
.LogToSerilog(LogEventLevel.Debug);
|
||||
#else
|
||||
.LogToSerilog(LogEventLevel.Information);
|
||||
#endif
|
||||
}
|
||||
24
RomRepoMgr/Resources/Localization.Designer.cs
generated
24
RomRepoMgr/Resources/Localization.Designer.cs
generated
@@ -764,5 +764,29 @@ namespace RomRepoMgr.Resources {
|
||||
return ResourceManager.GetString("NativeMenuQuitText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ProgressLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("ProgressLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string CompressionType {
|
||||
get {
|
||||
return ResourceManager.GetString("CompressionType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string UseInternalDecompressorLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("UseInternalDecompressorLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ProcessingArchive {
|
||||
get {
|
||||
return ResourceManager.GetString("ProcessingArchive", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,4 +378,16 @@ Tardará mucho tiempo...</value>
|
||||
<data name="NativeMenuQuitText" xml:space="preserve">
|
||||
<value>_Salir</value>
|
||||
</data>
|
||||
<data name="ProgressLabel" xml:space="preserve">
|
||||
<value>Progreso</value>
|
||||
</data>
|
||||
<data name="CompressionType" xml:space="preserve">
|
||||
<value>Compresión</value>
|
||||
</data>
|
||||
<data name="UseInternalDecompressorLabel" xml:space="preserve">
|
||||
<value>Usar decompresor interno (soporta menos formatos)</value>
|
||||
</data>
|
||||
<data name="ProcessingArchive" xml:space="preserve">
|
||||
<value>Procesando archivo: {0}</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -386,4 +386,16 @@ This will take a long time...</value>
|
||||
<data name="NativeMenuQuitText" xml:space="preserve">
|
||||
<value>_Quit</value>
|
||||
</data>
|
||||
<data name="ProgressLabel" xml:space="preserve">
|
||||
<value>Progress</value>
|
||||
</data>
|
||||
<data name="CompressionType" xml:space="preserve">
|
||||
<value>Compression</value>
|
||||
</data>
|
||||
<data name="UseInternalDecompressorLabel" xml:space="preserve">
|
||||
<value>Use internal decompressor (supports less formats)</value>
|
||||
</data>
|
||||
<data name="ProcessingArchive" xml:space="preserve">
|
||||
<value>Processing archive: {0}</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,6 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Version>1.0.0-beta.1</Version>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<InformationalVersion>1.0.0-beta.1</InformationalVersion>
|
||||
<Copyright>© $([System.DateTime]::Now.Year) Natalia Portillo</Copyright>
|
||||
<ApplicationIcon>Assets/romrepomgr.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="**\*.xaml.cs">
|
||||
@@ -19,9 +31,9 @@
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid"/>
|
||||
<PackageReference Include="Avalonia.Desktop"/>
|
||||
<PackageReference Include="Avalonia.Diagnostics"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI"/>
|
||||
<PackageReference Include="Avalonia.Svg.Skia"/>
|
||||
<PackageReference Include="Avalonia.Themes.Fluent"/>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm"/>
|
||||
<PackageReference Include="MessageBox.Avalonia"/>
|
||||
<PackageReference Include="AsyncFixer"/>
|
||||
<PackageReference Include="ErrorProne.NET.CoreAnalyzers"/>
|
||||
@@ -31,9 +43,12 @@
|
||||
<PackageReference Include="Roslynator.Analyzers"/>
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers"/>
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers"/>
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux"/>
|
||||
<PackageReference Include="Serilog"/>
|
||||
<PackageReference Include="Serilog.Extensions.Logging"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console"/>
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer"/>
|
||||
<PackageReference Include="Text.Analyzers"/>
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Condition="$(RuntimeIdentifier.StartsWith('linux'))"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RomRepoMgr.Database\RomRepoMgr.Database.csproj"/>
|
||||
@@ -46,4 +61,39 @@
|
||||
<LastGenOutput>Localization.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CreateMacAppBundle" AfterTargets="Publish" Condition="'$(RuntimeIdentifier)' == 'osx-x64' Or '$(RuntimeIdentifier)' == 'osx-arm64'">
|
||||
<!-- Create Directory Structure -->
|
||||
<MakeDir Directories="
|
||||
$(PublishDir)MacPackage/RomRepoMgr.app/Contents/MacOS;
|
||||
$(PublishDir)MacPackage/RomRepoMgr.app/Contents/Resources" />
|
||||
|
||||
<!-- Copy Executable -->
|
||||
<Copy SourceFiles="$(PublishDir)RomRepoMgr" DestinationFiles="$(PublishDir)MacPackage/RomRepoMgr.app/Contents/MacOS/RomRepoMgr" />
|
||||
|
||||
<!-- Copy .dylib files -->
|
||||
<ItemGroup>
|
||||
<DylibFiles Include="$(PublishDir)**\*.dylib" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(DylibFiles)" DestinationFolder="$(PublishDir)MacPackage/RomRepoMgr.app/Contents/MacOS\%(RecursiveDir)" />
|
||||
|
||||
<!-- Copy Icon (optional) -->
|
||||
<Copy SourceFiles="Assets/RomRepoMgr.icns" DestinationFiles="$(PublishDir)MacPackage/RomRepoMgr.app/Contents/Resources/RomRepoMgr.icns" />
|
||||
|
||||
<!-- Generate Info.plist -->
|
||||
<WriteLinesToFile File="$(PublishDir)MacPackage/RomRepoMgr.app/Contents/Info.plist" Lines="
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>RomRepoMgr</string>
|
||||
<key>CFBundleIdentifier</key><string>es.natportillo.romrepomgr</string>
|
||||
<key>CFBundleVersion</key><string>1.0</string>
|
||||
<key>CFBundleExecutable</key><string>RomRepoMgr</string>
|
||||
<key>CFBundleIconFile</key><string>RomRepoMgr.icns</string>
|
||||
<key>NSHumanReadableCopyright</key><string>© 2025 Nat Portillo.</string>
|
||||
</dict>
|
||||
</plist>" Overwrite="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
53
RomRepoMgr/SerilogSink.cs
Normal file
53
RomRepoMgr/SerilogSink.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Logging;
|
||||
|
||||
namespace RomRepoMgr;
|
||||
|
||||
public class SerilogSink(LogEventLevel minimumLevel, IList<string>? areas = null) : ILogSink
|
||||
{
|
||||
private readonly IList<string>? _areas = areas?.Count > 0 ? areas : null;
|
||||
|
||||
public bool IsEnabled(LogEventLevel level, string area) =>
|
||||
level >= minimumLevel && (_areas?.Contains(area) ?? true);
|
||||
|
||||
public void Log(LogEventLevel level, string area, object? source, string messageTemplate)
|
||||
{
|
||||
if(IsEnabled(level, area))
|
||||
{
|
||||
Serilog.Log.Write(LogLevelToSerilogLevel(level),
|
||||
"[{Area} {Source}] {MessageTemplate}",
|
||||
area,
|
||||
source,
|
||||
messageTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(LogEventLevel level, string area, object? source, string messageTemplate,
|
||||
params object?[] propertyValues)
|
||||
{
|
||||
if(IsEnabled(level, area))
|
||||
{
|
||||
Serilog.Log.Write(LogLevelToSerilogLevel(level),
|
||||
"[{Area} {Source}] {MessageTemplate}",
|
||||
propertyValues,
|
||||
area,
|
||||
source,
|
||||
messageTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
private static Serilog.Events.LogEventLevel LogLevelToSerilogLevel(LogEventLevel level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
LogEventLevel.Verbose => Serilog.Events.LogEventLevel.Verbose,
|
||||
LogEventLevel.Debug => Serilog.Events.LogEventLevel.Debug,
|
||||
LogEventLevel.Information => Serilog.Events.LogEventLevel.Information,
|
||||
LogEventLevel.Warning => Serilog.Events.LogEventLevel.Warning,
|
||||
LogEventLevel.Error => Serilog.Events.LogEventLevel.Error,
|
||||
LogEventLevel.Fatal => Serilog.Events.LogEventLevel.Fatal,
|
||||
_ => Serilog.Events.LogEventLevel.Verbose
|
||||
};
|
||||
}
|
||||
}
|
||||
15
RomRepoMgr/SerilogSinkExtensions.cs
Normal file
15
RomRepoMgr/SerilogSinkExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Logging;
|
||||
|
||||
namespace RomRepoMgr;
|
||||
|
||||
public static class SerilogSinkExtensions
|
||||
{
|
||||
public static AppBuilder LogToSerilog(this AppBuilder builder, LogEventLevel level = LogEventLevel.Warning,
|
||||
params string[] areas)
|
||||
{
|
||||
Logger.Sink = new SerilogSink(level, areas);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -26,20 +26,22 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using RomRepoMgr.Core.Models;
|
||||
using RomRepoMgr.Views;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class AboutViewModel : ViewModelBase
|
||||
public sealed partial class AboutViewModel : ViewModelBase
|
||||
{
|
||||
readonly About _view;
|
||||
string _versionText;
|
||||
[ObservableProperty]
|
||||
string _versionText;
|
||||
|
||||
public AboutViewModel()
|
||||
{
|
||||
@@ -57,26 +59,20 @@ public sealed class AboutViewModel : ViewModelBase
|
||||
public string SuiteName => "ROM Repository Manager";
|
||||
public string Copyright => "© 2020-2024 Natalia Portillo";
|
||||
public string Website => "https://www.claunia.com";
|
||||
public ReactiveCommand<Unit, Unit> WebsiteCommand { get; private set; }
|
||||
public ReactiveCommand<Unit, Unit> LicenseCommand { get; private set; }
|
||||
public ReactiveCommand<Unit, Unit> CloseCommand { get; private set; }
|
||||
public ICommand WebsiteCommand { get; private set; }
|
||||
public ICommand LicenseCommand { get; private set; }
|
||||
public ICommand CloseCommand { get; private set; }
|
||||
public ObservableCollection<AssemblyModel> Assemblies { get; private set; }
|
||||
|
||||
public string VersionText
|
||||
{
|
||||
get => _versionText;
|
||||
private set => this.RaiseAndSetIfChanged(ref _versionText, value);
|
||||
}
|
||||
|
||||
void LoadData()
|
||||
{
|
||||
VersionText =
|
||||
(Attribute.GetCustomAttribute(typeof(App).Assembly, typeof(AssemblyInformationalVersionAttribute)) as
|
||||
AssemblyInformationalVersionAttribute)?.InformationalVersion;
|
||||
|
||||
WebsiteCommand = ReactiveCommand.Create(ExecuteWebsiteCommand);
|
||||
LicenseCommand = ReactiveCommand.Create(ExecuteLicenseCommand);
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
WebsiteCommand = new RelayCommand(ExecuteWebsiteCommand);
|
||||
LicenseCommand = new RelayCommand(ExecuteLicenseCommand);
|
||||
CloseCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
|
||||
Assemblies = [];
|
||||
|
||||
|
||||
@@ -24,18 +24,21 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using ReactiveUI;
|
||||
using System.Windows.Input;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Models;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public class EditDatViewModel : ViewModelBase
|
||||
public partial class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
readonly RomSetModel _romSet;
|
||||
readonly EditDat _view;
|
||||
@@ -45,9 +48,10 @@ public class EditDatViewModel : ViewModelBase
|
||||
string _date;
|
||||
string _description;
|
||||
string _homepage;
|
||||
bool _modified;
|
||||
string _name;
|
||||
string _version;
|
||||
[ObservableProperty]
|
||||
bool _modified;
|
||||
string _name;
|
||||
string _version;
|
||||
|
||||
// Mock
|
||||
public EditDatViewModel()
|
||||
@@ -96,26 +100,20 @@ public class EditDatViewModel : ViewModelBase
|
||||
_date = romSet.Date;
|
||||
_description = romSet.Description;
|
||||
_homepage = romSet.Homepage;
|
||||
SaveCommand = ReactiveCommand.CreateFromTask(ExecuteSaveCommandAsync);
|
||||
CancelCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
SaveCommand = new AsyncRelayCommand(ExecuteSaveCommandAsync);
|
||||
CancelCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
CloseCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> CancelCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> 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 ICommand SaveCommand { get; }
|
||||
public ICommand CancelCommand { get; }
|
||||
public ICommand 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 string Name
|
||||
{
|
||||
@@ -124,7 +122,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _name) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _name, value);
|
||||
SetProperty(ref _name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +133,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _version) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _version, value);
|
||||
SetProperty(ref _version, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +144,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _author) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _author, value);
|
||||
SetProperty(ref _author, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +155,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _comment) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _comment, value);
|
||||
SetProperty(ref _comment, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +166,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _category) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _category, value);
|
||||
SetProperty(ref _category, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +177,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _date) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _date, value);
|
||||
SetProperty(ref _date, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +188,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _description) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _description, value);
|
||||
SetProperty(ref _description, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +199,7 @@ public class EditDatViewModel : ViewModelBase
|
||||
{
|
||||
if(value != _homepage) Modified = true;
|
||||
|
||||
this.RaiseAndSetIfChanged(ref _homepage, value);
|
||||
SetProperty(ref _homepage, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +209,8 @@ public class EditDatViewModel : ViewModelBase
|
||||
|
||||
async Task ExecuteSaveCommandAsync()
|
||||
{
|
||||
await using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
await using var ctx =
|
||||
Context.Create(Settings.Settings.Current.DatabasePath, new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
RomSet romSetDb = await ctx.RomSets.FindAsync(_romSet.Id);
|
||||
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using System.IO;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using RomRepoMgr.Core;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
@@ -37,17 +38,22 @@ using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class ExportDatViewModel : ViewModelBase
|
||||
public sealed partial 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;
|
||||
[ObservableProperty]
|
||||
bool _canClose;
|
||||
[ObservableProperty]
|
||||
string _errorMessage;
|
||||
[ObservableProperty]
|
||||
bool _errorVisible;
|
||||
[ObservableProperty]
|
||||
bool _progressVisible;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
|
||||
// Mock
|
||||
public ExportDatViewModel() {}
|
||||
@@ -57,7 +63,7 @@ public sealed class ExportDatViewModel : ViewModelBase
|
||||
_view = view;
|
||||
_datHash = datHash;
|
||||
_outPath = outPath;
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
CloseCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
ProgressVisible = false;
|
||||
ErrorVisible = false;
|
||||
_worker = new Compression();
|
||||
@@ -65,37 +71,7 @@ public sealed class ExportDatViewModel : ViewModelBase
|
||||
_worker.FailedWithText += OnWorkerOnFailedWithText;
|
||||
}
|
||||
|
||||
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 ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
|
||||
void OnWorkerOnFinishedWithText(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
@@ -119,9 +95,9 @@ public sealed class ExportDatViewModel : ViewModelBase
|
||||
ProgressVisible = true;
|
||||
StatusMessage = Localization.DecompressingDat;
|
||||
|
||||
var sha384Bytes = new byte[48];
|
||||
byte[] sha384Bytes = new byte[48];
|
||||
|
||||
for(var i = 0; i < 48; i++)
|
||||
for(int i = 0; i < 48; i++)
|
||||
{
|
||||
if(_datHash[i * 2] >= 0x30 && _datHash[i * 2] <= 0x39)
|
||||
sha384Bytes[i] = (byte)((_datHash[i * 2] - 0x30) * 0x10);
|
||||
|
||||
@@ -24,39 +24,61 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class ExportRomsViewModel : ViewModelBase
|
||||
public sealed partial 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;
|
||||
[ObservableProperty]
|
||||
bool _canClose;
|
||||
[ObservableProperty]
|
||||
bool _progress2IsIndeterminate;
|
||||
[ObservableProperty]
|
||||
double _progress2Maximum;
|
||||
[ObservableProperty]
|
||||
double _progress2Minimum;
|
||||
[ObservableProperty]
|
||||
double _progress2Value;
|
||||
[ObservableProperty]
|
||||
bool _progress2Visible;
|
||||
[ObservableProperty]
|
||||
bool _progress3IsIndeterminate;
|
||||
[ObservableProperty]
|
||||
double _progress3Maximum;
|
||||
[ObservableProperty]
|
||||
double _progress3Minimum;
|
||||
[ObservableProperty]
|
||||
double _progress3Value;
|
||||
[ObservableProperty]
|
||||
bool _progress3Visible;
|
||||
[ObservableProperty]
|
||||
bool _progressIsIndeterminate;
|
||||
[ObservableProperty]
|
||||
double _progressMaximum;
|
||||
[ObservableProperty]
|
||||
double _progressMinimum;
|
||||
[ObservableProperty]
|
||||
double _progressValue;
|
||||
[ObservableProperty]
|
||||
bool _progressVisible;
|
||||
[ObservableProperty]
|
||||
string _status2Message;
|
||||
[ObservableProperty]
|
||||
string _status3Message;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
|
||||
// Mock
|
||||
public ExportRomsViewModel()
|
||||
@@ -71,127 +93,12 @@ public sealed class ExportRomsViewModel : ViewModelBase
|
||||
_view = view;
|
||||
_romSetId = romSetId;
|
||||
FolderPath = folderPath;
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
CloseCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
CanClose = false;
|
||||
}
|
||||
|
||||
public string FolderPath { get; }
|
||||
|
||||
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 Progress3Visible
|
||||
{
|
||||
get => _progress3Visible;
|
||||
set => this.RaiseAndSetIfChanged(ref _progress3Visible, 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 Progress3Maximum
|
||||
{
|
||||
get => _progress3Maximum;
|
||||
set => this.RaiseAndSetIfChanged(ref _progress3Maximum, 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 CanClose
|
||||
{
|
||||
get => _canClose;
|
||||
set => this.RaiseAndSetIfChanged(ref _canClose, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public string FolderPath { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
|
||||
void ExecuteCloseCommand() => _view.Close();
|
||||
|
||||
@@ -249,7 +156,7 @@ public sealed class ExportRomsViewModel : ViewModelBase
|
||||
|
||||
public void OnOpened()
|
||||
{
|
||||
var worker = new FileExporter(_romSetId, FolderPath);
|
||||
var worker = new FileExporter(_romSetId, FolderPath, new SerilogLoggerFactory(Log.Logger));
|
||||
worker.SetMessage += OnWorkerOnSetMessage;
|
||||
worker.SetProgress += OnWorkerOnSetProgress;
|
||||
worker.SetProgressBounds += OnWorkerOnSetProgressBounds;
|
||||
|
||||
@@ -1,96 +1,75 @@
|
||||
/******************************************************************************
|
||||
// RomRepoMgr - ROM repository manager
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2020-2024 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Models;
|
||||
using RomRepoMgr.Resources;
|
||||
using RomRepoMgr.Views;
|
||||
using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class ImportDatFolderViewModel : ViewModelBase
|
||||
public sealed partial 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;
|
||||
readonly Stopwatch _stopwatch = new();
|
||||
bool _allFilesChecked;
|
||||
[ObservableProperty]
|
||||
bool _canClose;
|
||||
[ObservableProperty]
|
||||
bool _canStart;
|
||||
[ObservableProperty]
|
||||
string _category;
|
||||
string[] _datFiles;
|
||||
[ObservableProperty]
|
||||
string _folderPath;
|
||||
[ObservableProperty]
|
||||
bool _isImporting;
|
||||
[ObservableProperty]
|
||||
bool _isReady;
|
||||
int _listPosition;
|
||||
[ObservableProperty]
|
||||
bool _progressIsIndeterminate;
|
||||
[ObservableProperty]
|
||||
double _progressMaximum;
|
||||
[ObservableProperty]
|
||||
double _progressMinimum;
|
||||
[ObservableProperty]
|
||||
double _progressValue;
|
||||
[ObservableProperty]
|
||||
bool _progressVisible;
|
||||
bool _recursiveChecked;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
int _workers;
|
||||
|
||||
// Mock
|
||||
public ImportDatFolderViewModel()
|
||||
{
|
||||
#pragma warning disable PH2080
|
||||
FolderPath = "C:\\ROMs";
|
||||
#pragma warning restore PH2080
|
||||
CanClose = true;
|
||||
IsReady = true;
|
||||
SelectFolderCommand = new AsyncRelayCommand(SelectFolderAsync);
|
||||
CloseCommand = new RelayCommand(Close);
|
||||
StartCommand = new RelayCommand(Start);
|
||||
}
|
||||
|
||||
public ImportDatFolderViewModel(ImportDatFolder view, string folderPath)
|
||||
{
|
||||
_view = view;
|
||||
FolderPath = folderPath;
|
||||
_allFilesChecked = false;
|
||||
_recursiveChecked = true;
|
||||
ImportResults = [];
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
|
||||
}
|
||||
|
||||
public string FolderPath { get; }
|
||||
public ICommand SelectFolderCommand { get; }
|
||||
public Window View { get; init; }
|
||||
|
||||
public bool AllFilesChecked
|
||||
{
|
||||
get => _allFilesChecked;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _allFilesChecked, value);
|
||||
SetProperty(ref _allFilesChecked, value);
|
||||
RefreshFiles();
|
||||
}
|
||||
}
|
||||
@@ -100,120 +79,122 @@ public sealed class ImportDatFolderViewModel : ViewModelBase
|
||||
get => _recursiveChecked;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _recursiveChecked, value);
|
||||
SetProperty(ref _recursiveChecked, value);
|
||||
RefreshFiles();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReady
|
||||
public ICommand CloseCommand { get; }
|
||||
public ICommand StartCommand { get; }
|
||||
public ObservableCollection<DatImporter> Importers { get; } = [];
|
||||
|
||||
void Start()
|
||||
{
|
||||
get => _isReady;
|
||||
set => this.RaiseAndSetIfChanged(ref _isReady, value);
|
||||
_listPosition = 0;
|
||||
ProgressMinimum = 0;
|
||||
ProgressMaximum = _datFiles.Length;
|
||||
ProgressValue = 0;
|
||||
ProgressIsIndeterminate = false;
|
||||
ProgressVisible = true;
|
||||
CanClose = false;
|
||||
CanStart = false;
|
||||
IsReady = false;
|
||||
IsImporting = true;
|
||||
_workers = 0;
|
||||
_stopwatch.Restart();
|
||||
|
||||
Import();
|
||||
}
|
||||
|
||||
public bool ProgressVisible
|
||||
void Import()
|
||||
{
|
||||
get => _progressVisible;
|
||||
set => this.RaiseAndSetIfChanged(ref _progressVisible, value);
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if(_listPosition >= _datFiles.Length)
|
||||
{
|
||||
if(_workers != 0) return;
|
||||
|
||||
ProgressVisible = false;
|
||||
StatusMessage = Localization.Finished;
|
||||
CanClose = true;
|
||||
CanStart = false;
|
||||
IsReady = true;
|
||||
_stopwatch.Stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
StatusMessage = string.Format(Localization.ImportingItem, Path.GetFileName(_datFiles[_listPosition]));
|
||||
ProgressValue = _listPosition;
|
||||
|
||||
var model = new DatImporter
|
||||
{
|
||||
Filename = Path.GetFileName(_datFiles[_listPosition]),
|
||||
Minimum = 0,
|
||||
Maximum = _datFiles.Length,
|
||||
Progress = 0,
|
||||
Indeterminate = false
|
||||
};
|
||||
|
||||
var worker =
|
||||
new Core.Workers.DatImporter(_datFiles[_listPosition], Category, new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
worker.ErrorOccurred += model.OnErrorOccurred;
|
||||
worker.SetIndeterminateProgress += model.OnSetIndeterminateProgress;
|
||||
worker.SetMessage += model.OnSetMessage;
|
||||
worker.SetProgress += model.OnSetProgress;
|
||||
worker.SetProgressBounds += model.OnSetProgressBounds;
|
||||
worker.WorkFinished += model.OnWorkFinished;
|
||||
worker.RomSetAdded += RomSetAdded;
|
||||
|
||||
worker.WorkFinished += (_, _) =>
|
||||
{
|
||||
_workers--;
|
||||
|
||||
if(_workers < Environment.ProcessorCount) Import();
|
||||
};
|
||||
|
||||
worker.ErrorOccurred += (_, _) =>
|
||||
{
|
||||
_workers--;
|
||||
|
||||
if(_workers < Environment.ProcessorCount) Import();
|
||||
};
|
||||
|
||||
Importers.Add(model);
|
||||
|
||||
model.Task = Task.Run(worker.Import);
|
||||
|
||||
_workers++;
|
||||
_listPosition++;
|
||||
|
||||
if(_workers < Environment.ProcessorCount) Import();
|
||||
});
|
||||
}
|
||||
|
||||
public string StatusMessage
|
||||
public event EventHandler<RomSetEventArgs> RomSetAdded;
|
||||
|
||||
void Close()
|
||||
{
|
||||
get => _statusMessage;
|
||||
set => this.RaiseAndSetIfChanged(ref _statusMessage, value);
|
||||
View.Close();
|
||||
}
|
||||
|
||||
public double ProgressMinimum
|
||||
async Task SelectFolderAsync()
|
||||
{
|
||||
get => _progressMinimum;
|
||||
set => this.RaiseAndSetIfChanged(ref _progressMinimum, value);
|
||||
IReadOnlyList<IStorageFolder> result =
|
||||
await View.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = Localization.ImportDatFolderDialogTitle
|
||||
});
|
||||
|
||||
if(result.Count < 1) return;
|
||||
|
||||
FolderPath = result[0].TryGetLocalPath() ?? string.Empty;
|
||||
RecursiveChecked = true;
|
||||
AllFilesChecked = false;
|
||||
RefreshFiles();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public ObservableCollection<ImportDatFolderItem> ImportResults { get; }
|
||||
|
||||
public bool CanClose
|
||||
{
|
||||
get => _canClose;
|
||||
set => this.RaiseAndSetIfChanged(ref _canClose, value);
|
||||
}
|
||||
|
||||
public bool CanStart
|
||||
{
|
||||
get => _canStart;
|
||||
set => this.RaiseAndSetIfChanged(ref _canStart, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> StartCommand { get; }
|
||||
|
||||
internal void OnOpened() => RefreshFiles();
|
||||
|
||||
void RefreshFiles()
|
||||
{
|
||||
_ = Task.Run(() =>
|
||||
@@ -222,7 +203,6 @@ public sealed class ImportDatFolderViewModel : ViewModelBase
|
||||
{
|
||||
IsReady = false;
|
||||
ProgressVisible = true;
|
||||
Progress2Visible = false;
|
||||
ProgressIsIndeterminate = true;
|
||||
StatusMessage = Localization.SearchingForFiles;
|
||||
});
|
||||
@@ -264,99 +244,4 @@ public sealed class ImportDatFolderViewModel : ViewModelBase
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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<RomSetEventArgs> RomSetAdded;
|
||||
}
|
||||
|
||||
public sealed class ImportDatFolderItem
|
||||
{
|
||||
public string Filename { get; set; }
|
||||
public string Status { get; set; }
|
||||
}
|
||||
@@ -24,29 +24,41 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class ImportDatViewModel : ViewModelBase
|
||||
public sealed partial 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;
|
||||
[ObservableProperty]
|
||||
bool _canClose;
|
||||
[ObservableProperty]
|
||||
double _currentValue;
|
||||
[ObservableProperty]
|
||||
string _errorMessage;
|
||||
[ObservableProperty]
|
||||
bool _errorVisible;
|
||||
[ObservableProperty]
|
||||
bool _indeterminateProgress;
|
||||
[ObservableProperty]
|
||||
double _maximumValue;
|
||||
[ObservableProperty]
|
||||
double _minimumValue;
|
||||
[ObservableProperty]
|
||||
bool _progressVisible;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
|
||||
// Mock
|
||||
public ImportDatViewModel() {}
|
||||
@@ -54,11 +66,11 @@ public sealed class ImportDatViewModel : ViewModelBase
|
||||
public ImportDatViewModel(ImportDat view, string datPath)
|
||||
{
|
||||
_view = view;
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
CloseCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
IndeterminateProgress = true;
|
||||
ProgressVisible = false;
|
||||
ErrorVisible = false;
|
||||
_worker = new DatImporter(datPath, null);
|
||||
_worker = new DatImporter(datPath, null, new SerilogLoggerFactory(Log.Logger));
|
||||
_worker.ErrorOccurred += OnWorkerOnErrorOccurred;
|
||||
_worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress;
|
||||
_worker.SetMessage += OnWorkerOnSetMessage;
|
||||
@@ -67,61 +79,7 @@ public sealed class ImportDatViewModel : ViewModelBase
|
||||
_worker.WorkFinished += OnWorkerOnWorkFinished;
|
||||
}
|
||||
|
||||
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 ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
|
||||
void OnWorkerOnWorkFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
|
||||
@@ -1,110 +1,105 @@
|
||||
/******************************************************************************
|
||||
// RomRepoMgr - ROM repository manager
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2020-2024 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Models;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using RomRepoMgr.Models;
|
||||
using RomRepoMgr.Resources;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class ImportRomFolderViewModel : ViewModelBase
|
||||
public sealed partial 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;
|
||||
readonly Context _ctx =
|
||||
Context.Create(Settings.Settings.Current.DatabasePath, new SerilogLoggerFactory(Log.Logger));
|
||||
readonly Stopwatch _mainStopwatch = new();
|
||||
readonly ConcurrentBag<DbDisk> _newDisks = [];
|
||||
readonly ConcurrentBag<DbFile> _newFiles = [];
|
||||
readonly ConcurrentBag<DbMedia> _newMedias = [];
|
||||
readonly Stopwatch _stopwatch = new();
|
||||
[ObservableProperty]
|
||||
bool _canChoose;
|
||||
[ObservableProperty]
|
||||
bool _canClose;
|
||||
[ObservableProperty]
|
||||
bool _canStart;
|
||||
[ObservableProperty]
|
||||
string _folderPath;
|
||||
[ObservableProperty]
|
||||
bool _isImporting;
|
||||
[ObservableProperty]
|
||||
bool _isReady;
|
||||
[ObservableProperty]
|
||||
bool _knownOnlyChecked;
|
||||
int _listPosition;
|
||||
[ObservableProperty]
|
||||
bool _progress2IsIndeterminate;
|
||||
[ObservableProperty]
|
||||
double _progress2Maximum;
|
||||
[ObservableProperty]
|
||||
double _progress2Minimum;
|
||||
[ObservableProperty]
|
||||
double _progress2Value;
|
||||
[ObservableProperty]
|
||||
bool _progress2Visible;
|
||||
[ObservableProperty]
|
||||
bool _progressIsIndeterminate;
|
||||
[ObservableProperty]
|
||||
double _progressMaximum;
|
||||
[ObservableProperty]
|
||||
double _progressMinimum;
|
||||
[ObservableProperty]
|
||||
double _progressValue;
|
||||
[ObservableProperty]
|
||||
bool _progressVisible;
|
||||
bool _recurseArchivesChecked;
|
||||
[ObservableProperty]
|
||||
bool _removeFilesChecked;
|
||||
[ObservableProperty]
|
||||
bool _removeFilesEnabled;
|
||||
FileImporter _rootImporter;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
[ObservableProperty]
|
||||
string _statusMessage2;
|
||||
[ObservableProperty]
|
||||
bool _statusMessage2Visible;
|
||||
|
||||
// Mock
|
||||
public ImportRomFolderViewModel()
|
||||
{
|
||||
#pragma warning disable PH2080
|
||||
FolderPath = "C:\\ROMs";
|
||||
#pragma warning restore PH2080
|
||||
SelectFolderCommand = new AsyncRelayCommand(SelectFolderAsync);
|
||||
CloseCommand = new RelayCommand(Close);
|
||||
StartCommand = new RelayCommand(Start);
|
||||
CanClose = true;
|
||||
RemoveFilesChecked = false;
|
||||
KnownOnlyChecked = true;
|
||||
RecurseArchivesChecked = Settings.Settings.CanDecompress;
|
||||
RemoveFilesEnabled = false;
|
||||
CanChoose = true;
|
||||
}
|
||||
|
||||
public ImportRomFolderViewModel(ImportRomFolder view, string folderPath)
|
||||
{
|
||||
_view = view;
|
||||
FolderPath = folderPath;
|
||||
_removeFilesChecked = false;
|
||||
_knownOnlyChecked = true;
|
||||
_recurseArchivesChecked = Settings.Settings.UnArUsable;
|
||||
ImportResults = [];
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
|
||||
IsReady = true;
|
||||
CanStart = true;
|
||||
CanClose = true;
|
||||
_removeFilesEnabled = false;
|
||||
}
|
||||
public ICommand SelectFolderCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
public ICommand StartCommand { get; }
|
||||
public Window View { get; init; }
|
||||
|
||||
public string FolderPath { get; }
|
||||
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 RecurseArchivesEnabled => Settings.Settings.CanDecompress;
|
||||
|
||||
public bool RecurseArchivesChecked
|
||||
{
|
||||
@@ -114,177 +109,382 @@ public sealed class ImportRomFolderViewModel : ViewModelBase
|
||||
if(value) RemoveFilesChecked = false;
|
||||
|
||||
RemoveFilesEnabled = !value;
|
||||
this.RaiseAndSetIfChanged(ref _recurseArchivesChecked, value);
|
||||
SetProperty(ref _recurseArchivesChecked, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReady
|
||||
public ObservableCollection<RomImporter> Importers { get; } = [];
|
||||
|
||||
void Start()
|
||||
{
|
||||
get => _isReady;
|
||||
set => this.RaiseAndSetIfChanged(ref _isReady, value);
|
||||
_rootImporter = new FileImporter(_ctx, _newFiles, _newDisks, _newMedias, KnownOnlyChecked, RemoveFilesChecked);
|
||||
_rootImporter.SetMessage += SetMessage;
|
||||
_rootImporter.SetIndeterminateProgress += SetIndeterminateProgress;
|
||||
_rootImporter.SetProgress += SetProgress;
|
||||
_rootImporter.SetProgressBounds += SetProgressBounds;
|
||||
_rootImporter.Finished += EnumeratingFilesFinished;
|
||||
ProgressIsIndeterminate = true;
|
||||
ProgressVisible = true;
|
||||
CanClose = false;
|
||||
CanStart = false;
|
||||
IsImporting = true;
|
||||
IsReady = false;
|
||||
CanChoose = false;
|
||||
_mainStopwatch.Start();
|
||||
|
||||
_ = Task.Run(() => _rootImporter.FindFiles(FolderPath));
|
||||
}
|
||||
|
||||
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 ObservableCollection<ImportRomItem> ImportResults { get; }
|
||||
|
||||
public bool CanClose
|
||||
{
|
||||
get => _canClose;
|
||||
set => this.RaiseAndSetIfChanged(ref _canClose, value);
|
||||
}
|
||||
|
||||
public bool CanStart
|
||||
{
|
||||
get => _canStart;
|
||||
set => this.RaiseAndSetIfChanged(ref _canStart, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> 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(() =>
|
||||
void SetProgressBounds(object sender, ProgressBoundsEventArgs e) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ProgressIsIndeterminate = false;
|
||||
ProgressMaximum = args.Maximum;
|
||||
ProgressMinimum = args.Minimum;
|
||||
ProgressMaximum = e.Maximum;
|
||||
ProgressMinimum = e.Minimum;
|
||||
});
|
||||
|
||||
void OnWorkerOnSetProgress(object sender, ProgressEventArgs args) =>
|
||||
Dispatcher.UIThread.Post(() => ProgressValue = args.Value);
|
||||
void SetProgress(object sender, ProgressEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => ProgressValue = e.Value);
|
||||
}
|
||||
|
||||
void OnWorkerOnSetMessage(object sender, MessageEventArgs args) =>
|
||||
Dispatcher.UIThread.Post(() => StatusMessage = args.Message);
|
||||
|
||||
void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) =>
|
||||
void SetIndeterminateProgress(object sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => ProgressIsIndeterminate = true);
|
||||
}
|
||||
|
||||
void OnWorkerOnSetProgressBounds2(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() =>
|
||||
void SetProgress2Bounds(object sender, ProgressBoundsEventArgs e) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
Progress2IsIndeterminate = false;
|
||||
Progress2Maximum = args.Maximum;
|
||||
Progress2Minimum = args.Minimum;
|
||||
Progress2Maximum = e.Maximum;
|
||||
Progress2Minimum = e.Minimum;
|
||||
});
|
||||
|
||||
void OnWorkerOnSetProgress2(object sender, ProgressEventArgs args) =>
|
||||
Dispatcher.UIThread.Post(() => Progress2Value = args.Value);
|
||||
void SetProgress2(object sender, ProgressEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => Progress2Value = e.Value);
|
||||
}
|
||||
|
||||
void OnWorkerOnSetMessage2(object sender, MessageEventArgs args) =>
|
||||
Dispatcher.UIThread.Post(() => Status2Message = args.Message);
|
||||
|
||||
void OnWorkerOnSetIndeterminateProgress2(object sender, EventArgs args) =>
|
||||
void SetIndeterminateProgress2(object sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => Progress2IsIndeterminate = true);
|
||||
}
|
||||
|
||||
void EnumeratingFilesFinished(object sender, EventArgs e)
|
||||
{
|
||||
_rootImporter.Finished -= EnumeratingFilesFinished;
|
||||
|
||||
if(RecurseArchivesChecked)
|
||||
{
|
||||
Progress2Visible = true;
|
||||
StatusMessage2Visible = true;
|
||||
_rootImporter.SetMessage2 += SetMessage2;
|
||||
_rootImporter.SetIndeterminateProgress2 += SetIndeterminateProgress2;
|
||||
_rootImporter.SetProgress2 += SetProgress2;
|
||||
_rootImporter.SetProgressBounds2 += SetProgress2Bounds;
|
||||
|
||||
_rootImporter.Finished += CheckArchivesFinished;
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(Settings.Settings.Current.UseInternalDecompressor)
|
||||
_rootImporter.SeparateFilesAndArchivesManaged();
|
||||
else
|
||||
_rootImporter.SeparateFilesAndArchives();
|
||||
});
|
||||
}
|
||||
else
|
||||
ProcessFiles();
|
||||
}
|
||||
|
||||
void ProcessFiles()
|
||||
{
|
||||
_listPosition = 0;
|
||||
ProgressMinimum = 0;
|
||||
ProgressMaximum = _rootImporter.Files.Count;
|
||||
ProgressValue = 0;
|
||||
ProgressIsIndeterminate = false;
|
||||
ProgressVisible = true;
|
||||
CanClose = false;
|
||||
CanStart = false;
|
||||
IsReady = false;
|
||||
IsImporting = true;
|
||||
_stopwatch.Restart();
|
||||
|
||||
Parallel.ForEach(_rootImporter.Files,
|
||||
file =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
StatusMessage = string.Format(Localization.ImportingItem, Path.GetFileName(file));
|
||||
ProgressValue = _listPosition;
|
||||
});
|
||||
|
||||
var model = new RomImporter
|
||||
{
|
||||
Filename = Path.GetFileName(file),
|
||||
Indeterminate = true
|
||||
};
|
||||
|
||||
var worker = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
worker.SetIndeterminateProgress2 += model.OnSetIndeterminateProgress;
|
||||
worker.SetMessage2 += model.OnSetMessage;
|
||||
worker.SetProgress2 += model.OnSetProgress;
|
||||
worker.SetProgressBounds2 += model.OnSetProgressBounds;
|
||||
worker.ImportedRom += model.OnImportedRom;
|
||||
worker.WorkFinished += model.OnWorkFinished;
|
||||
|
||||
Dispatcher.UIThread.Post(() => Importers.Add(model));
|
||||
|
||||
worker.ImportFile(file);
|
||||
|
||||
Interlocked.Increment(ref _listPosition);
|
||||
});
|
||||
|
||||
_stopwatch.Stop();
|
||||
Log.Debug("Took {TotalSeconds} seconds to process files", _stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
_rootImporter.SaveChanges();
|
||||
|
||||
_rootImporter.UpdateRomStats();
|
||||
|
||||
_listPosition = 0;
|
||||
ProgressMinimum = 0;
|
||||
ProgressMaximum = 1;
|
||||
ProgressValue = 0;
|
||||
ProgressIsIndeterminate = false;
|
||||
ProgressVisible = false;
|
||||
CanClose = true;
|
||||
CanStart = false;
|
||||
IsReady = false;
|
||||
IsImporting = false;
|
||||
StatusMessage = Localization.Finished;
|
||||
_mainStopwatch.Stop();
|
||||
|
||||
Log.Debug("Took {TotalSeconds} seconds to import ROMs", _mainStopwatch.Elapsed.TotalSeconds);
|
||||
}
|
||||
|
||||
void ProcessArchives()
|
||||
{
|
||||
// For each archive
|
||||
ProgressMaximum = _rootImporter.Archives.Count;
|
||||
ProgressMinimum = 0;
|
||||
ProgressValue = 0;
|
||||
ProgressIsIndeterminate = false;
|
||||
Progress2Visible = false;
|
||||
StatusMessage2Visible = false;
|
||||
_listPosition = 0;
|
||||
_stopwatch.Restart();
|
||||
|
||||
Parallel.ForEach(_rootImporter.Archives,
|
||||
archive =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
StatusMessage = "Processing archive: " + Path.GetFileName(archive);
|
||||
ProgressValue = _listPosition;
|
||||
});
|
||||
|
||||
// Create FileImporter
|
||||
var archiveImporter = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
// Extract archive
|
||||
bool ret = Settings.Settings.Current.UseInternalDecompressor
|
||||
? archiveImporter.ExtractArchiveManaged(archive)
|
||||
: archiveImporter.ExtractArchive(archive);
|
||||
|
||||
if(!ret) return;
|
||||
|
||||
// Process files in archive
|
||||
foreach(string file in archiveImporter.Files)
|
||||
{
|
||||
var model = new RomImporter
|
||||
{
|
||||
Filename = Path.GetFileName(file),
|
||||
Indeterminate = true
|
||||
};
|
||||
|
||||
var worker = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
worker.SetIndeterminateProgress2 += model.OnSetIndeterminateProgress;
|
||||
worker.SetMessage2 += model.OnSetMessage;
|
||||
worker.SetProgress2 += model.OnSetProgress;
|
||||
worker.SetProgressBounds2 += model.OnSetProgressBounds;
|
||||
worker.ImportedRom += model.OnImportedRom;
|
||||
worker.WorkFinished += model.OnWorkFinished;
|
||||
|
||||
Dispatcher.UIThread.Post(() => Importers.Add(model));
|
||||
|
||||
worker.ImportFile(file);
|
||||
|
||||
worker.Files.Clear();
|
||||
}
|
||||
|
||||
// Remove temporary files
|
||||
archiveImporter.CleanupExtractedArchive();
|
||||
|
||||
Interlocked.Increment(ref _listPosition);
|
||||
});
|
||||
|
||||
_stopwatch.Stop();
|
||||
Log.Debug("Took {TotalSeconds} seconds to process archives", _stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
Progress2Visible = false;
|
||||
StatusMessage2Visible = false;
|
||||
|
||||
ProcessFiles();
|
||||
}
|
||||
|
||||
void ProcessArchivesManaged()
|
||||
{
|
||||
// For each archive
|
||||
ProgressMaximum = _rootImporter.Archives.Count;
|
||||
ProgressMinimum = 0;
|
||||
ProgressValue = 0;
|
||||
ProgressIsIndeterminate = false;
|
||||
Progress2Visible = false;
|
||||
StatusMessage2Visible = false;
|
||||
_listPosition = 0;
|
||||
_stopwatch.Restart();
|
||||
|
||||
Parallel.ForEach(_rootImporter.Archives,
|
||||
archive =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
StatusMessage =
|
||||
string.Format(Localization.ProcessingArchive, Path.GetFileName(archive));
|
||||
|
||||
ProgressValue = _listPosition;
|
||||
});
|
||||
|
||||
// Create FileImporter
|
||||
var archiveImporter = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
// Open archive
|
||||
try
|
||||
{
|
||||
using var fs = new FileStream(archive, FileMode.Open, FileAccess.Read);
|
||||
using IReader reader = ReaderFactory.Open(fs);
|
||||
|
||||
// Process files in archive
|
||||
while(reader.MoveToNextEntry())
|
||||
{
|
||||
if(reader.Entry.IsDirectory) continue;
|
||||
|
||||
if(reader.Entry.Crc == 0 && KnownOnlyChecked) continue;
|
||||
|
||||
if(!archiveImporter.IsCrcInDb(reader.Entry.Crc) && KnownOnlyChecked) continue;
|
||||
|
||||
var model = new RomImporter
|
||||
{
|
||||
Filename = Path.GetFileName(reader.Entry.Key),
|
||||
Indeterminate = true
|
||||
};
|
||||
|
||||
var worker = new FileImporter(_ctx,
|
||||
_newFiles,
|
||||
_newDisks,
|
||||
_newMedias,
|
||||
KnownOnlyChecked,
|
||||
RemoveFilesChecked);
|
||||
|
||||
worker.SetIndeterminateProgress2 += model.OnSetIndeterminateProgress;
|
||||
worker.SetMessage2 += model.OnSetMessage;
|
||||
worker.SetProgress2 += model.OnSetProgress;
|
||||
worker.SetProgressBounds2 += model.OnSetProgressBounds;
|
||||
worker.ImportedRom += model.OnImportedRom;
|
||||
worker.WorkFinished += model.OnWorkFinished;
|
||||
|
||||
Dispatcher.UIThread.Post(() => Importers.Add(model));
|
||||
|
||||
worker.ImportAndHashRom(reader.OpenEntryStream(),
|
||||
reader.Entry.Key,
|
||||
Path.Combine(Settings.Settings.Current.RepositoryPath,
|
||||
Path.GetFileName(Path.GetTempFileName())),
|
||||
reader.Entry.Size);
|
||||
}
|
||||
}
|
||||
catch(InvalidOperationException) {}
|
||||
finally
|
||||
{
|
||||
Interlocked.Increment(ref _listPosition);
|
||||
}
|
||||
});
|
||||
|
||||
_stopwatch.Stop();
|
||||
Log.Debug("Took {TotalSeconds} seconds to process archives", _stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
Progress2Visible = false;
|
||||
StatusMessage2Visible = false;
|
||||
|
||||
ProcessFiles();
|
||||
}
|
||||
|
||||
void CheckArchivesFinished(object sender, EventArgs e)
|
||||
{
|
||||
_stopwatch.Stop();
|
||||
Log.Debug("Took {TotalSeconds} seconds to check archives", _stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
Progress2Visible = false;
|
||||
StatusMessage2Visible = false;
|
||||
|
||||
_rootImporter.Finished -= CheckArchivesFinished;
|
||||
|
||||
if(Settings.Settings.Current.UseInternalDecompressor)
|
||||
ProcessArchivesManaged();
|
||||
else
|
||||
ProcessArchives();
|
||||
}
|
||||
|
||||
void SetMessage(object sender, MessageEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => StatusMessage = e.Message);
|
||||
}
|
||||
|
||||
void SetMessage2(object sender, MessageEventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => StatusMessage2 = e.Message);
|
||||
}
|
||||
|
||||
void Close() => View.Close();
|
||||
|
||||
async Task SelectFolderAsync()
|
||||
{
|
||||
IReadOnlyList<IStorageFolder> result =
|
||||
await View.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = Localization.ImportRomsFolderDialogTitle
|
||||
});
|
||||
|
||||
if(result.Count < 1) return;
|
||||
|
||||
FolderPath = result[0].TryGetLocalPath() ?? string.Empty;
|
||||
|
||||
IsReady = true;
|
||||
CanStart = true;
|
||||
CanClose = true;
|
||||
}
|
||||
}
|
||||
@@ -28,29 +28,34 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Filesystem;
|
||||
using RomRepoMgr.Core.Models;
|
||||
using RomRepoMgr.Resources;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public class MainWindowViewModel : ViewModelBase
|
||||
public sealed partial class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
readonly MainWindow _view;
|
||||
RomSetModel _selectedRomSet;
|
||||
Vfs _vfs;
|
||||
[ObservableProperty]
|
||||
RomSetModel _selectedRomSet;
|
||||
[ObservableProperty]
|
||||
Vfs _vfs;
|
||||
|
||||
// Mock
|
||||
public MainWindowViewModel() {}
|
||||
@@ -58,19 +63,19 @@ public class MainWindowViewModel : ViewModelBase
|
||||
public MainWindowViewModel(MainWindow view, List<RomSetModel> romSets)
|
||||
{
|
||||
_view = view;
|
||||
ExitCommand = ReactiveCommand.Create(ExecuteExitCommand);
|
||||
SettingsCommand = ReactiveCommand.CreateFromTask(ExecuteSettingsCommandAsync);
|
||||
AboutCommand = ReactiveCommand.Create(ExecuteAboutCommand);
|
||||
ImportDatCommand = ReactiveCommand.CreateFromTask(ExecuteImportDatCommandAsync);
|
||||
ImportDatFolderCommand = ReactiveCommand.CreateFromTask(ExecuteImportDatFolderCommandAsync);
|
||||
ImportRomFolderCommand = ReactiveCommand.CreateFromTask(ExecuteImportRomFolderCommandAsync);
|
||||
DeleteRomSetCommand = ReactiveCommand.CreateFromTask(ExecuteDeleteRomSetCommandAsync);
|
||||
EditRomSetCommand = ReactiveCommand.Create(ExecuteEditRomSetCommand);
|
||||
ExportDatCommand = ReactiveCommand.CreateFromTask(ExecuteExportDatCommandAsync);
|
||||
ExportRomsCommand = ReactiveCommand.CreateFromTask(ExecuteExportRomsCommandAsync);
|
||||
MountCommand = ReactiveCommand.CreateFromTask(ExecuteMountCommandAsync);
|
||||
UmountCommand = ReactiveCommand.Create(ExecuteUmountCommand);
|
||||
UpdateStatsCommand = ReactiveCommand.CreateFromTask(ExecuteUpdateStatsCommandAsync);
|
||||
ExitCommand = new RelayCommand(ExecuteExitCommand);
|
||||
SettingsCommand = new AsyncRelayCommand(ExecuteSettingsCommandAsync);
|
||||
AboutCommand = new RelayCommand(ExecuteAboutCommand);
|
||||
ImportDatCommand = new AsyncRelayCommand(ExecuteImportDatCommandAsync);
|
||||
ImportDatFolderCommand = new AsyncRelayCommand(ExecuteImportDatFolderCommandAsync);
|
||||
ImportRomFolderCommand = new AsyncRelayCommand(ExecuteImportRomFolderCommandAsync);
|
||||
DeleteRomSetCommand = new AsyncRelayCommand(ExecuteDeleteRomSetCommandAsync);
|
||||
EditRomSetCommand = new RelayCommand(ExecuteEditRomSetCommand);
|
||||
ExportDatCommand = new AsyncRelayCommand(ExecuteExportDatCommandAsync);
|
||||
ExportRomsCommand = new AsyncRelayCommand(ExecuteExportRomsCommandAsync);
|
||||
MountCommand = new AsyncRelayCommand(ExecuteMountCommandAsync);
|
||||
UmountCommand = new RelayCommand(ExecuteUmountCommand);
|
||||
UpdateStatsCommand = new AsyncRelayCommand(ExecuteUpdateStatsCommandAsync);
|
||||
RomSets = new ObservableCollection<RomSetModel>(romSets);
|
||||
}
|
||||
|
||||
@@ -81,31 +86,19 @@ public class MainWindowViewModel : ViewModelBase
|
||||
NativeMenu.GetIsNativeMenuExported((Application.Current.ApplicationLifetime as
|
||||
IClassicDesktopStyleApplicationLifetime)?.MainWindow);
|
||||
|
||||
public ReactiveCommand<Unit, Unit> AboutCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> ExitCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> SettingsCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDatCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDatFolderCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportRomFolderCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> DeleteRomSetCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> EditRomSetCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> ExportDatCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> ExportRomsCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> MountCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> UmountCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> UpdateStatsCommand { get; }
|
||||
|
||||
public Vfs Vfs
|
||||
{
|
||||
get => _vfs;
|
||||
set => this.RaiseAndSetIfChanged(ref _vfs, value);
|
||||
}
|
||||
|
||||
public RomSetModel SelectedRomSet
|
||||
{
|
||||
get => _selectedRomSet;
|
||||
set => this.RaiseAndSetIfChanged(ref _selectedRomSet, value);
|
||||
}
|
||||
public ICommand AboutCommand { get; }
|
||||
public ICommand ExitCommand { get; }
|
||||
public ICommand SettingsCommand { get; }
|
||||
public ICommand ImportDatCommand { get; }
|
||||
public ICommand ImportDatFolderCommand { get; }
|
||||
public ICommand ImportRomFolderCommand { get; }
|
||||
public ICommand DeleteRomSetCommand { get; }
|
||||
public ICommand EditRomSetCommand { get; }
|
||||
public ICommand ExportDatCommand { get; }
|
||||
public ICommand ExportRomsCommand { get; }
|
||||
public ICommand MountCommand { get; }
|
||||
public ICommand UmountCommand { get; }
|
||||
public ICommand UpdateStatsCommand { get; }
|
||||
|
||||
internal Task ExecuteSettingsCommandAsync()
|
||||
{
|
||||
@@ -156,34 +149,29 @@ public class MainWindowViewModel : ViewModelBase
|
||||
|
||||
async Task ExecuteImportDatFolderCommandAsync()
|
||||
{
|
||||
IReadOnlyList<IStorageFolder> result =
|
||||
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = Localization.ImportDatFolderDialogTitle
|
||||
});
|
||||
var dialog = new ImportDatFolder();
|
||||
|
||||
if(result.Count < 1) return;
|
||||
var viewModel = new ImportDatFolderViewModel
|
||||
{
|
||||
View = dialog
|
||||
};
|
||||
|
||||
var dialog = new ImportDatFolder();
|
||||
var importDatFolderViewModel = new ImportDatFolderViewModel(dialog, result[0].Path.LocalPath);
|
||||
importDatFolderViewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded;
|
||||
dialog.DataContext = importDatFolderViewModel;
|
||||
_ = dialog.ShowDialog(_view);
|
||||
viewModel.RomSetAdded += ImportDatViewModelOnRomSetAdded;
|
||||
|
||||
dialog.DataContext = viewModel;
|
||||
_ = dialog.ShowDialog(_view);
|
||||
}
|
||||
|
||||
async Task ExecuteImportRomFolderCommandAsync()
|
||||
{
|
||||
IReadOnlyList<IStorageFolder> result =
|
||||
await _view.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = Localization.ImportRomsFolderDialogTitle
|
||||
});
|
||||
var dialog = new ImportRomFolder();
|
||||
|
||||
if(result.Count < 1) return;
|
||||
var viewModel = new ImportRomFolderViewModel
|
||||
{
|
||||
View = dialog
|
||||
};
|
||||
|
||||
var dialog = new ImportRomFolder();
|
||||
var importRomFolderViewModel = new ImportRomFolderViewModel(dialog, result[0].Path.LocalPath);
|
||||
dialog.DataContext = importRomFolderViewModel;
|
||||
dialog.DataContext = viewModel;
|
||||
_ = dialog.ShowDialog(_view);
|
||||
}
|
||||
|
||||
@@ -280,12 +268,14 @@ public class MainWindowViewModel : ViewModelBase
|
||||
|
||||
try
|
||||
{
|
||||
Vfs = new Vfs();
|
||||
Vfs = new Vfs(new SerilogLoggerFactory(Log.Logger));
|
||||
Vfs.Umounted += VfsOnUmounted;
|
||||
Vfs.MountTo(result[0].Path.LocalPath);
|
||||
}
|
||||
catch(Exception)
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error mounting VFS");
|
||||
|
||||
if(Debugger.IsAttached) throw;
|
||||
|
||||
Vfs = null;
|
||||
|
||||
@@ -26,20 +26,23 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using RomRepoMgr.Core;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using RomRepoMgr.Resources;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class RemoveDatViewModel : ViewModelBase
|
||||
public sealed partial class RemoveDatViewModel : ViewModelBase
|
||||
{
|
||||
readonly long _romSetId;
|
||||
readonly RemoveDat _view;
|
||||
string _statusMessage;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
|
||||
// Mock
|
||||
public RemoveDatViewModel() {}
|
||||
@@ -50,17 +53,12 @@ public sealed class RemoveDatViewModel : ViewModelBase
|
||||
_romSetId = romSetId;
|
||||
}
|
||||
|
||||
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);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath,
|
||||
new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RetrievingRomSetFromDatabase);
|
||||
|
||||
@@ -78,10 +76,10 @@ public sealed class RemoveDatViewModel : ViewModelBase
|
||||
|
||||
Dispatcher.UIThread.Post(() => StatusMessage = Localization.RemovingDatFileFromRepo);
|
||||
|
||||
var sha384Bytes = new byte[48];
|
||||
byte[] sha384Bytes = new byte[48];
|
||||
string sha384 = romSet.Sha384;
|
||||
|
||||
for(var i = 0; i < 48; i++)
|
||||
for(int i = 0; i < 48; i++)
|
||||
{
|
||||
if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39)
|
||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
|
||||
|
||||
@@ -26,35 +26,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reactive;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Resources;
|
||||
using RomRepoMgr.Settings;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class SettingsViewModel : ViewModelBase
|
||||
public sealed partial class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
readonly SettingsDialog _view;
|
||||
bool _databaseChanged;
|
||||
string _databasePath;
|
||||
bool _repositoryChanged;
|
||||
string _repositoryPath;
|
||||
bool _temporaryChanged;
|
||||
string _temporaryPath;
|
||||
bool _unArChanged;
|
||||
string _unArPath;
|
||||
string _unArVersion;
|
||||
|
||||
CompressionType _compression;
|
||||
bool _compressionChanged;
|
||||
bool _databaseChanged;
|
||||
string _databasePath;
|
||||
bool _repositoryChanged;
|
||||
string _repositoryPath;
|
||||
bool _temporaryChanged;
|
||||
string _temporaryPath;
|
||||
bool _unArChanged;
|
||||
[ObservableProperty]
|
||||
string _unArPath;
|
||||
[ObservableProperty]
|
||||
string _unArVersion;
|
||||
bool _useInternalDecompressor;
|
||||
bool _useInternalDecompressorChanged;
|
||||
|
||||
// Mock
|
||||
public SettingsViewModel() {}
|
||||
@@ -67,34 +79,39 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
_temporaryChanged = false;
|
||||
_unArChanged = false;
|
||||
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
UnArCommand = ReactiveCommand.CreateFromTask(ExecuteUnArCommandAsync);
|
||||
TemporaryCommand = ReactiveCommand.CreateFromTask(ExecuteTemporaryCommandAsync);
|
||||
RepositoryCommand = ReactiveCommand.CreateFromTask(ExecuteRepositoryCommandAsync);
|
||||
DatabaseCommand = ReactiveCommand.CreateFromTask(ExecuteDatabaseCommandAsync);
|
||||
SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand);
|
||||
CloseCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
UnArCommand = new AsyncRelayCommand(ExecuteUnArCommandAsync);
|
||||
TemporaryCommand = new AsyncRelayCommand(ExecuteTemporaryCommandAsync);
|
||||
RepositoryCommand = new AsyncRelayCommand(ExecuteRepositoryCommandAsync);
|
||||
DatabaseCommand = new AsyncRelayCommand(ExecuteDatabaseCommandAsync);
|
||||
SaveCommand = new RelayCommand(ExecuteSaveCommand);
|
||||
|
||||
DatabasePath = Settings.Settings.Current.DatabasePath;
|
||||
RepositoryPath = Settings.Settings.Current.RepositoryPath;
|
||||
TemporaryPath = Settings.Settings.Current.TemporaryFolder;
|
||||
UnArPath = Settings.Settings.Current.UnArchiverPath;
|
||||
DatabasePath = Settings.Settings.Current.DatabasePath;
|
||||
RepositoryPath = Settings.Settings.Current.RepositoryPath;
|
||||
TemporaryPath = Settings.Settings.Current.TemporaryFolder;
|
||||
UnArPath = Settings.Settings.Current.UnArchiverPath;
|
||||
Compression = Settings.Settings.Current.Compression;
|
||||
UseInternalDecompressor = Settings.Settings.Current.UseInternalDecompressor;
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(UnArPath)) CheckUnAr();
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> UnArCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> TemporaryCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> RepositoryCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> DatabaseCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
|
||||
public List<CompressionType> CompressionTypes { get; } =
|
||||
Enum.GetValues(typeof(CompressionType)).Cast<CompressionType>().ToList();
|
||||
|
||||
public ICommand UnArCommand { get; }
|
||||
public ICommand TemporaryCommand { get; }
|
||||
public ICommand RepositoryCommand { get; }
|
||||
public ICommand DatabaseCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
public ICommand SaveCommand { get; }
|
||||
|
||||
public string DatabasePath
|
||||
{
|
||||
get => _databasePath;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _databasePath, value);
|
||||
SetProperty(ref _databasePath, value);
|
||||
_databaseChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -104,7 +121,7 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
get => _repositoryPath;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _repositoryPath, value);
|
||||
SetProperty(ref _repositoryPath, value);
|
||||
|
||||
// TODO: Refresh repository existing files
|
||||
_repositoryChanged = true;
|
||||
@@ -116,21 +133,29 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
get => _temporaryPath;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _temporaryPath, value);
|
||||
SetProperty(ref _temporaryPath, value);
|
||||
_temporaryChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string UnArPath
|
||||
public CompressionType Compression
|
||||
{
|
||||
get => _unArPath;
|
||||
set => this.RaiseAndSetIfChanged(ref _unArPath, value);
|
||||
get => _compression;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _compression, value);
|
||||
_compressionChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string UnArVersion
|
||||
public bool UseInternalDecompressor
|
||||
{
|
||||
get => _unArVersion;
|
||||
set => this.RaiseAndSetIfChanged(ref _unArVersion, value);
|
||||
get => _useInternalDecompressor;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _useInternalDecompressor, value);
|
||||
_useInternalDecompressorChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckUnAr()
|
||||
@@ -145,11 +170,14 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
|
||||
void CheckUnArFailed(object sender, ErrorEventArgs args)
|
||||
{
|
||||
UnArVersion = "";
|
||||
UnArPath = "";
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
UnArVersion = "";
|
||||
UnArPath = "";
|
||||
|
||||
_ = MessageBoxManager.GetMessageBoxStandard(Localization.Error, args.Message, ButtonEnum.Ok, Icon.Error)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
_ = MessageBoxManager.GetMessageBoxStandard(Localization.Error, args.Message, ButtonEnum.Ok, Icon.Error)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
});
|
||||
}
|
||||
|
||||
void CheckUnArFinished(object sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() =>
|
||||
@@ -233,10 +261,10 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
try
|
||||
{
|
||||
var ctx = Context.Create(result);
|
||||
var ctx = Context.Create(result, new SerilogLoggerFactory(Log.Logger));
|
||||
await ctx.Database.MigrateAsync();
|
||||
}
|
||||
catch(Exception)
|
||||
catch
|
||||
{
|
||||
btnResult = await MessageBoxManager
|
||||
.GetMessageBoxStandard(Localization.DatabaseFileUnusableMsgBoxTitle,
|
||||
@@ -251,7 +279,7 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
File.Delete(result);
|
||||
}
|
||||
catch(Exception)
|
||||
catch
|
||||
{
|
||||
await MessageBoxManager
|
||||
.GetMessageBoxStandard(Localization.DatabaseFileCannotDeleteTitle,
|
||||
@@ -260,9 +288,13 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
Icon.Error)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
|
||||
#pragma warning disable ERP022
|
||||
return;
|
||||
#pragma warning restore ERP022
|
||||
}
|
||||
#pragma warning disable ERP022
|
||||
}
|
||||
#pragma warning restore ERP022
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -279,7 +311,7 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
File.Delete(result);
|
||||
}
|
||||
catch(Exception)
|
||||
catch
|
||||
{
|
||||
await MessageBoxManager
|
||||
.GetMessageBoxStandard(Localization.DatabaseFileCannotDeleteTitle,
|
||||
@@ -288,17 +320,19 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
Icon.Error)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
|
||||
#pragma warning disable ERP022
|
||||
return;
|
||||
#pragma warning restore ERP022
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ctx = Context.Create(result);
|
||||
var ctx = Context.Create(result, new SerilogLoggerFactory(Log.Logger));
|
||||
await ctx.Database.MigrateAsync();
|
||||
}
|
||||
catch(Exception)
|
||||
catch
|
||||
{
|
||||
await MessageBoxManager
|
||||
.GetMessageBoxStandard(Localization.DatabaseFileUnusableMsgBoxTitle,
|
||||
@@ -307,7 +341,9 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
Icon.Error)
|
||||
.ShowWindowDialogAsync(_view);
|
||||
|
||||
#pragma warning disable ERP022
|
||||
return;
|
||||
#pragma warning restore ERP022
|
||||
}
|
||||
|
||||
DatabasePath = result;
|
||||
@@ -324,10 +360,19 @@ public sealed class SettingsViewModel : ViewModelBase
|
||||
if(_unArChanged)
|
||||
{
|
||||
Settings.Settings.Current.UnArchiverPath = UnArPath;
|
||||
Settings.Settings.UnArUsable = true;
|
||||
Settings.Settings.CanDecompress = true;
|
||||
}
|
||||
|
||||
if(_databaseChanged || _repositoryChanged || _temporaryChanged || _unArChanged)
|
||||
if(_compressionChanged) Settings.Settings.Current.Compression = Compression;
|
||||
|
||||
if(_useInternalDecompressorChanged) Settings.Settings.Current.UseInternalDecompressor = UseInternalDecompressor;
|
||||
|
||||
if(_databaseChanged ||
|
||||
_repositoryChanged ||
|
||||
_temporaryChanged ||
|
||||
_unArChanged ||
|
||||
_compressionChanged ||
|
||||
_useInternalDecompressorChanged)
|
||||
Settings.Settings.SaveSettings();
|
||||
|
||||
_view.Close();
|
||||
|
||||
@@ -26,42 +26,61 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ReactiveUI;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Models;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Database;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class SplashWindowViewModel : ViewModelBase
|
||||
public sealed partial class SplashWindowViewModel : ViewModelBase
|
||||
{
|
||||
[ObservableProperty]
|
||||
bool _checkingUnArError;
|
||||
[ObservableProperty]
|
||||
bool _checkingUnArOk;
|
||||
[ObservableProperty]
|
||||
bool _checkingUnArUnknown;
|
||||
[ObservableProperty]
|
||||
bool _exitVisible;
|
||||
[ObservableProperty]
|
||||
bool _loadingDatabaseError;
|
||||
[ObservableProperty]
|
||||
bool _loadingDatabaseOk;
|
||||
[ObservableProperty]
|
||||
bool _loadingDatabaseUnknown;
|
||||
[ObservableProperty]
|
||||
bool _loadingRomSetsError;
|
||||
[ObservableProperty]
|
||||
bool _loadingRomSetsOk;
|
||||
[ObservableProperty]
|
||||
bool _loadingRomSetsUnknown;
|
||||
[ObservableProperty]
|
||||
bool _loadingSettingsError;
|
||||
[ObservableProperty]
|
||||
bool _loadingSettingsOk;
|
||||
[ObservableProperty]
|
||||
bool _loadingSettingsUnknown;
|
||||
[ObservableProperty]
|
||||
bool _migratingDatabaseError;
|
||||
[ObservableProperty]
|
||||
bool _migratingDatabaseOk;
|
||||
[ObservableProperty]
|
||||
bool _migratingDatabaseUnknown;
|
||||
|
||||
public SplashWindowViewModel()
|
||||
{
|
||||
ExitCommand = ReactiveCommand.Create(ExecuteExitCommand);
|
||||
ExitCommand = new RelayCommand(ExecuteExitCommand);
|
||||
|
||||
LoadingSettingsOk = false;
|
||||
LoadingSettingsError = false;
|
||||
@@ -81,103 +100,7 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
ExitVisible = false;
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> 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 ICommand ExitCommand { get; }
|
||||
|
||||
public string LoadingText => "ROM Repository Manager";
|
||||
|
||||
@@ -198,7 +121,7 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// TODO: Log error
|
||||
Log.Error(e, "Error loading settings");
|
||||
Dispatcher.UIThread.Post(FailedLoadingSettings);
|
||||
}
|
||||
});
|
||||
@@ -221,13 +144,15 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
try
|
||||
{
|
||||
var worker = new Compression();
|
||||
Settings.Settings.UnArUsable = worker.CheckUnAr(Settings.Settings.Current.UnArchiverPath);
|
||||
|
||||
Settings.Settings.CanDecompress = worker.CheckUnAr(Settings.Settings.Current.UnArchiverPath) ||
|
||||
Settings.Settings.Current.UseInternalDecompressor;
|
||||
|
||||
Dispatcher.UIThread.Post(LoadDatabase);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// TODO: Log error
|
||||
Log.Error(e, "Error checking unar");
|
||||
Dispatcher.UIThread.Post(FailedCheckUnAr);
|
||||
}
|
||||
});
|
||||
@@ -253,13 +178,14 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
|
||||
if(!Directory.Exists(dbPathFolder)) Directory.CreateDirectory(dbPathFolder);
|
||||
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath,
|
||||
new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
Dispatcher.UIThread.Post(MigrateDatabase);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// TODO: Log error
|
||||
Log.Error(e, "Error loading database");
|
||||
Dispatcher.UIThread.Post(FailedLoadingDatabase);
|
||||
}
|
||||
});
|
||||
@@ -281,7 +207,8 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
{
|
||||
try
|
||||
{
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath,
|
||||
new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
ctx.Database.Migrate();
|
||||
|
||||
@@ -289,7 +216,7 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// TODO: Log error
|
||||
Log.Error(e, "Error migrating database");
|
||||
Dispatcher.UIThread.Post(FailedMigratingDatabase);
|
||||
}
|
||||
});
|
||||
@@ -311,7 +238,8 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
{
|
||||
try
|
||||
{
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath,
|
||||
new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
GotRomSets?.Invoke(this,
|
||||
new RomSetsEventArgs
|
||||
@@ -349,7 +277,7 @@ public sealed class SplashWindowViewModel : ViewModelBase
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// TODO: Log error
|
||||
Log.Error(e, "Error loading ROM sets");
|
||||
Dispatcher.UIThread.Post(FailedLoadingRomSets);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,33 +23,43 @@
|
||||
// Copyright © 2020-2024 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ReactiveUI;
|
||||
using RomRepoMgr.Core.Models;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using RomRepoMgr.Resources;
|
||||
using RomRepoMgr.Views;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public sealed class UpdateStatsViewModel : ViewModelBase
|
||||
public sealed partial class UpdateStatsViewModel : ViewModelBase
|
||||
{
|
||||
readonly UpdateStats _view;
|
||||
bool _canClose;
|
||||
double _currentValue;
|
||||
bool _indeterminateProgress;
|
||||
double _maximumValue;
|
||||
double _minimumValue;
|
||||
bool _progressVisible;
|
||||
RomSetModel _selectedRomSet;
|
||||
string _statusMessage;
|
||||
[ObservableProperty]
|
||||
bool _canClose;
|
||||
[ObservableProperty]
|
||||
double _currentValue;
|
||||
[ObservableProperty]
|
||||
bool _indeterminateProgress;
|
||||
[ObservableProperty]
|
||||
double _maximumValue;
|
||||
[ObservableProperty]
|
||||
double _minimumValue;
|
||||
[ObservableProperty]
|
||||
bool _progressVisible;
|
||||
[ObservableProperty]
|
||||
RomSetModel _selectedRomSet;
|
||||
[ObservableProperty]
|
||||
string _statusMessage;
|
||||
|
||||
// Mock
|
||||
public UpdateStatsViewModel() {}
|
||||
@@ -57,69 +67,22 @@ public sealed class UpdateStatsViewModel : ViewModelBase
|
||||
public UpdateStatsViewModel(UpdateStats view)
|
||||
{
|
||||
_view = view;
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
CloseCommand = new RelayCommand(ExecuteCloseCommand);
|
||||
IndeterminateProgress = true;
|
||||
ProgressVisible = false;
|
||||
RomSets = [];
|
||||
}
|
||||
|
||||
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<RomSetModel> RomSets { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
|
||||
internal void OnOpened()
|
||||
{
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
|
||||
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath,
|
||||
new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
@@ -226,10 +189,12 @@ public sealed class UpdateStatsViewModel : ViewModelBase
|
||||
});
|
||||
});
|
||||
}
|
||||
catch(Exception)
|
||||
catch
|
||||
#pragma warning disable PH2098
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
#pragma warning restore PH2098
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
// Copyright © 2020-2024 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using ReactiveUI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace RomRepoMgr.ViewModels;
|
||||
|
||||
public class ViewModelBase : ReactiveObject {}
|
||||
public class ViewModelBase : ObservableObject;
|
||||
@@ -40,106 +40,106 @@
|
||||
<Design.DataContext>
|
||||
<vm:AboutViewModel />
|
||||
</Design.DataContext>
|
||||
<Border Padding="15">
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<Grid Grid.Row="0"
|
||||
ColumnDefinitions="Auto,*">
|
||||
<Border Grid.Column="0"
|
||||
BorderThickness="5">
|
||||
<Image Source="/Assets/avalonia-logo.ico"
|
||||
Width="48"
|
||||
Height="48" />
|
||||
</Border>
|
||||
<Grid Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
RowDefinitions="Auto,Auto">
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="{Binding SoftwareName, Mode=OneWay}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="1"
|
||||
Text="{Binding VersionText, Mode=OneWay}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid RowDefinitions="Auto,*,Auto"
|
||||
Margin="16"
|
||||
RowSpacing="8">
|
||||
<Grid Grid.Row="0"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="Auto,*">
|
||||
<Image Source="/Assets/avalonia-logo.ico"
|
||||
Width="48"
|
||||
Height="48" />
|
||||
<Grid Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
RowDefinitions="Auto,Auto"
|
||||
RowSpacing="8">
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="{Binding SoftwareName, Mode=OneWay}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="1"
|
||||
Text="{Binding VersionText, Mode=OneWay}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<TabControl Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AboutLabel}" />
|
||||
</TabItem.Header>
|
||||
<Grid RowDefinitions="Auto,12,Auto,12,Auto,Auto,*">
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="{Binding SuiteName, Mode=OneWay}" />
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding Copyright, Mode=OneWay}" />
|
||||
<Button Grid.Row="4"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0"
|
||||
Command="{Binding WebsiteCommand, Mode=OneWay}">
|
||||
<!-- TODO: TextDecorations="Underline" in next Avalonia UI version -->
|
||||
<TextBlock Text="{Binding Website, Mode=OneWay}"
|
||||
Foreground="Blue" />
|
||||
</Button>
|
||||
<Button Grid.Row="5"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0"
|
||||
Command="{Binding LicenseCommand, Mode=OneWay}">
|
||||
<!-- TODO: TextDecorations="Underline" in next Avalonia UI version -->
|
||||
<TextBlock Text="{x:Static resources:Localization.LicenseLabel}"
|
||||
Foreground="Blue" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.LibrariesLabel}" />
|
||||
</TabItem.Header>
|
||||
<DataGrid ItemsSource="{Binding Assemblies, Mode=OneWay}"
|
||||
HorizontalScrollBarVisibility="Visible">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Name, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AssembliesLibraryText}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding Version, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AssembliesVersionText}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AuthorsLabel}" />
|
||||
</TabItem.Header>
|
||||
<TextBox IsReadOnly="True"
|
||||
Text="{x:Static resources:Localization.AuthorsText}" />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
<Button Grid.Row="2"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding CloseCommand, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
<TabControl Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AboutLabel}" />
|
||||
</TabItem.Header>
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,*"
|
||||
RowSpacing="8">
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="{Binding SuiteName, Mode=OneWay}" />
|
||||
<TextBlock Grid.Row="1"
|
||||
Text="{Binding Copyright, Mode=OneWay}" />
|
||||
<Button Grid.Row="2"
|
||||
Background="Transparent"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0"
|
||||
BorderThickness="0"
|
||||
Command="{Binding WebsiteCommand, Mode=OneWay}">
|
||||
<TextBlock Text="{Binding Website, Mode=OneWay}"
|
||||
Foreground="Blue"
|
||||
TextDecorations="Underline" />
|
||||
</Button>
|
||||
<Button Grid.Row="3"
|
||||
Background="Transparent"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0"
|
||||
BorderThickness="0"
|
||||
Command="{Binding LicenseCommand, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.LicenseLabel}"
|
||||
Foreground="Blue"
|
||||
TextDecorations="Underline" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.LibrariesLabel}" />
|
||||
</TabItem.Header>
|
||||
<DataGrid ItemsSource="{Binding Assemblies, Mode=OneWay}"
|
||||
HorizontalScrollBarVisibility="Visible">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Name, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AssembliesLibraryText}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding Version, Mode=OneWay}"
|
||||
Width="Auto"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AssembliesVersionText}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{x:Static resources:Localization.AuthorsLabel}" />
|
||||
</TabItem.Header>
|
||||
<TextBox IsReadOnly="True"
|
||||
Text="{x:Static resources:Localization.AuthorsText}" />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
<Button Grid.Row="2"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding CloseCommand, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -41,226 +41,212 @@
|
||||
<Design.DataContext>
|
||||
<vm:EditDatViewModel />
|
||||
</Design.DataContext>
|
||||
<Border Padding="15">
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
<Grid Grid.Row="0"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetNameLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="1"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetVersionLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Version, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="2"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetAuthorLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Author, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="3"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetCategoryLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Category, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="4"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetCommentLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Comment, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="5"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetDateLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Date, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="6"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetDescriptionLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Description, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="7"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.HomepageLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Homepage, Mode=TwoWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.TotalMachinesLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding TotalMachines, Mode=OneWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="9"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.CompleteMachinesLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding CompleteMachines, Mode=OneWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="10"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.IncompleteMachinesLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding IncompleteMachines, Mode=OneWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="11"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.TotalRomsLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding TotalRoms, Mode=OneWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="12"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.HaveRomsLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding HaveRoms, Mode=OneWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="13"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.MissRomsLabel}"
|
||||
FontWeight="Bold"
|
||||
Padding="5" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding MissRoms, Mode=OneWay}"
|
||||
Padding="5" />
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="14"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right">
|
||||
<Button HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding SaveCommand, Mode=OneWay}"
|
||||
IsVisible="{Binding Modified, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.SaveLabel}" />
|
||||
</Button>
|
||||
<Button HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding CancelCommand, Mode=OneWay}"
|
||||
IsVisible="{Binding Modified, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CancelLabel}" />
|
||||
</Button>
|
||||
<Button HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding CloseCommand, Mode=OneWay}"
|
||||
IsVisible="{Binding !Modified, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"
|
||||
Margin="16"
|
||||
RowSpacing="8">
|
||||
<Grid Grid.Row="0"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetNameLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Grid Grid.Row="1"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetVersionLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Version, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="2"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetAuthorLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Author, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="3"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetCategoryLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Category, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="4"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetCommentLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Comment, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="5"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetDateLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Date, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="6"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.RomSetDescriptionLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Description, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="7"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.HomepageLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Homepage, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="8"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.TotalMachinesLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding TotalMachines, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="9"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.CompleteMachinesLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding CompleteMachines, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="10"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.IncompleteMachinesLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding IncompleteMachines, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="11"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.TotalRomsLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding TotalRoms, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="12"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.HaveRomsLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding HaveRoms, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="13"
|
||||
ColumnSpacing="8"
|
||||
ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resources:Localization.MissRomsLabel}"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding MissRoms, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="14"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right">
|
||||
<Button HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding SaveCommand, Mode=OneWay}"
|
||||
IsVisible="{Binding Modified, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.SaveLabel}" />
|
||||
</Button>
|
||||
<Button HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding CancelCommand, Mode=OneWay}"
|
||||
IsVisible="{Binding Modified, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CancelLabel}" />
|
||||
</Button>
|
||||
<Button HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding CloseCommand, Mode=OneWay}"
|
||||
IsVisible="{Binding !Modified, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -41,27 +41,27 @@
|
||||
<Design.DataContext>
|
||||
<vm:ExportDatViewModel />
|
||||
</Design.DataContext>
|
||||
<Border Padding="15">
|
||||
<Grid RowDefinitions="Auto,auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="{Binding StatusMessage, Mode=OneWay}"
|
||||
HorizontalAlignment="Center" />
|
||||
<ProgressBar Grid.Row="1"
|
||||
IsIndeterminate="True"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding ErrorMessage, Mode=OneWay}"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="Red"
|
||||
IsVisible="{Binding ErrorVisible, Mode=OneWay}" />
|
||||
<Button Grid.Row="3"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding CanClose, Mode=OneWay}"
|
||||
Command="{Binding CloseCommand, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Grid RowDefinitions="Auto,auto,Auto,Auto"
|
||||
Margin="16"
|
||||
RowSpacing="8">
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="{Binding StatusMessage, Mode=OneWay}"
|
||||
HorizontalAlignment="Center" />
|
||||
<ProgressBar Grid.Row="1"
|
||||
IsIndeterminate="True"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding ProgressVisible, Mode=OneWay}" />
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding ErrorMessage, Mode=OneWay}"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="Red"
|
||||
IsVisible="{Binding ErrorVisible, Mode=OneWay}" />
|
||||
<Button Grid.Row="3"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding CanClose, Mode=OneWay}"
|
||||
Command="{Binding CloseCommand, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Static resources:Localization.CloseLabel}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Window>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user