mirror of
https://github.com/claunia/romrepomgr.git
synced 2025-12-16 19:24:51 +00:00
Implement importing files from folder.
This commit is contained in:
187
RomRepoMgr.Core/Workers/Checksum.cs
Normal file
187
RomRepoMgr.Core/Workers/Checksum.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
// /***************************************************************************
|
||||
// The Disc Image Chef
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Checksum.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Core methods.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Methods to checksum data.
|
||||
//
|
||||
// --[ 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 © 2011-2016 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Aaru.Checksums;
|
||||
|
||||
namespace RomRepoMgr.Core.Workers
|
||||
{
|
||||
internal enum ChecksumType
|
||||
{
|
||||
Crc32, Md5, Sha1,
|
||||
Sha256, Sha384, Sha512
|
||||
}
|
||||
|
||||
internal sealed class Checksum
|
||||
{
|
||||
readonly Crc32Context _crc32Ctx;
|
||||
readonly Md5Context _md5Ctx;
|
||||
readonly Sha1Context _sha1Ctx;
|
||||
readonly Sha256Context _sha256Ctx;
|
||||
readonly Sha384Context _sha384Ctx;
|
||||
readonly Sha512Context _sha512Ctx;
|
||||
Crc32Packet _crc32Pkt;
|
||||
Thread _crc32Thread;
|
||||
Md5Packet _md5Pkt;
|
||||
Thread _md5Thread;
|
||||
Sha1Packet _sha1Pkt;
|
||||
Thread _sha1Thread;
|
||||
Sha256Packet _sha256Pkt;
|
||||
Thread _sha256Thread;
|
||||
Sha384Packet _sha384Pkt;
|
||||
Thread _sha384Thread;
|
||||
Sha512Packet _sha512Pkt;
|
||||
Thread _sha512Thread;
|
||||
|
||||
internal Checksum()
|
||||
{
|
||||
_crc32Ctx = new Crc32Context();
|
||||
_md5Ctx = new Md5Context();
|
||||
_sha1Ctx = new Sha1Context();
|
||||
_sha256Ctx = new Sha256Context();
|
||||
_sha384Ctx = new Sha384Context();
|
||||
_sha512Ctx = new Sha512Context();
|
||||
|
||||
_crc32Thread = new Thread(UpdateCrc32);
|
||||
_md5Thread = new Thread(UpdateMd5);
|
||||
_sha1Thread = new Thread(UpdateSha1);
|
||||
_sha256Thread = new Thread(UpdateSha256);
|
||||
_sha384Thread = new Thread(UpdateSha384);
|
||||
_sha512Thread = new Thread(UpdateSha512);
|
||||
|
||||
_crc32Pkt = new Crc32Packet();
|
||||
_md5Pkt = new Md5Packet();
|
||||
_sha1Pkt = new Sha1Packet();
|
||||
_sha256Pkt = new Sha256Packet();
|
||||
_sha384Pkt = new Sha384Packet();
|
||||
_sha512Pkt = new Sha512Packet();
|
||||
|
||||
_crc32Pkt.context = _crc32Ctx;
|
||||
_md5Pkt.context = _md5Ctx;
|
||||
_sha1Pkt.context = _sha1Ctx;
|
||||
_sha256Pkt.context = _sha256Ctx;
|
||||
_sha384Pkt.context = _sha384Ctx;
|
||||
_sha512Pkt.context = _sha512Ctx;
|
||||
}
|
||||
|
||||
internal void Update(byte[] data)
|
||||
{
|
||||
_crc32Pkt.data = data;
|
||||
_crc32Thread.Start(_crc32Pkt);
|
||||
_md5Pkt.data = data;
|
||||
_md5Thread.Start(_md5Pkt);
|
||||
_sha1Pkt.data = data;
|
||||
_sha1Thread.Start(_sha1Pkt);
|
||||
_sha256Pkt.data = data;
|
||||
_sha256Thread.Start(_sha256Pkt);
|
||||
_sha384Pkt.data = data;
|
||||
_sha384Thread.Start(_sha384Pkt);
|
||||
_sha512Pkt.data = data;
|
||||
_sha512Thread.Start(_sha512Pkt);
|
||||
|
||||
while(_crc32Thread.IsAlive ||
|
||||
_md5Thread.IsAlive ||
|
||||
_sha1Thread.IsAlive ||
|
||||
_sha256Thread.IsAlive ||
|
||||
_sha384Thread.IsAlive ||
|
||||
_sha512Thread.IsAlive) {}
|
||||
|
||||
_crc32Thread = new Thread(UpdateCrc32);
|
||||
_md5Thread = new Thread(UpdateMd5);
|
||||
_sha1Thread = new Thread(UpdateSha1);
|
||||
_sha256Thread = new Thread(UpdateSha256);
|
||||
_sha384Thread = new Thread(UpdateSha384);
|
||||
_sha512Thread = new Thread(UpdateSha512);
|
||||
}
|
||||
|
||||
internal Dictionary<ChecksumType, string> End() => new Dictionary<ChecksumType, string>
|
||||
{
|
||||
[ChecksumType.Crc32] = _crc32Ctx.End(),
|
||||
[ChecksumType.Md5] = _md5Ctx.End(),
|
||||
[ChecksumType.Sha1] = _sha1Ctx.End(),
|
||||
[ChecksumType.Sha256] = _sha256Ctx.End(),
|
||||
[ChecksumType.Sha384] = _sha384Ctx.End(),
|
||||
[ChecksumType.Sha512] = _sha512Ctx.End()
|
||||
};
|
||||
|
||||
#region Threading helpers
|
||||
struct Crc32Packet
|
||||
{
|
||||
public Crc32Context context;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
struct Md5Packet
|
||||
{
|
||||
public Md5Context context;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
struct Sha1Packet
|
||||
{
|
||||
public Sha1Context context;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
struct Sha256Packet
|
||||
{
|
||||
public Sha256Context context;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
struct Sha384Packet
|
||||
{
|
||||
public Sha384Context context;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
struct Sha512Packet
|
||||
{
|
||||
public Sha512Context context;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
static void UpdateCrc32(object packet) => ((Crc32Packet)packet).context.Update(((Crc32Packet)packet).data);
|
||||
|
||||
static void UpdateMd5(object packet) => ((Md5Packet)packet).context.Update(((Md5Packet)packet).data);
|
||||
|
||||
static void UpdateSha1(object packet) => ((Sha1Packet)packet).context.Update(((Sha1Packet)packet).data);
|
||||
|
||||
static void UpdateSha256(object packet) => ((Sha256Packet)packet).context.Update(((Sha256Packet)packet).data);
|
||||
|
||||
static void UpdateSha384(object packet) => ((Sha384Packet)packet).context.Update(((Sha384Packet)packet).data);
|
||||
|
||||
static void UpdateSha512(object packet) => ((Sha512Packet)packet).context.Update(((Sha512Packet)packet).data);
|
||||
#endregion Threading helpers
|
||||
}
|
||||
}
|
||||
311
RomRepoMgr.Core/Workers/FileImporter.cs
Normal file
311
RomRepoMgr.Core/Workers/FileImporter.cs
Normal file
@@ -0,0 +1,311 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Database.Models;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace RomRepoMgr.Core.Workers
|
||||
{
|
||||
public class FileImporter
|
||||
{
|
||||
const long BUFFER_SIZE = 131072;
|
||||
readonly bool _deleteAfterImport;
|
||||
readonly bool _onlyKnown;
|
||||
|
||||
readonly Dictionary<string, DbFile> _pendingFiles;
|
||||
|
||||
public FileImporter(bool onlyKnown, bool deleteAfterImport)
|
||||
{
|
||||
_pendingFiles = new Dictionary<string, DbFile>();
|
||||
_onlyKnown = onlyKnown;
|
||||
_deleteAfterImport = deleteAfterImport;
|
||||
}
|
||||
|
||||
public string LastMessage { get; private set; }
|
||||
|
||||
public event EventHandler SetIndeterminateProgress;
|
||||
public event EventHandler<ProgressBoundsEventArgs> SetProgressBounds;
|
||||
public event EventHandler<ProgressEventArgs> SetProgress;
|
||||
public event EventHandler<MessageEventArgs> SetMessage;
|
||||
|
||||
public bool ImportRom(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var inFs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
|
||||
byte[] dataBuffer;
|
||||
|
||||
SetMessage?.Invoke(this, new MessageEventArgs
|
||||
{
|
||||
Message = "Hashing file..."
|
||||
});
|
||||
|
||||
var checksumWorker = new Checksum();
|
||||
|
||||
if(inFs.Length > BUFFER_SIZE)
|
||||
{
|
||||
SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = inFs.Length
|
||||
});
|
||||
|
||||
long offset;
|
||||
long remainder = inFs.Length % BUFFER_SIZE;
|
||||
|
||||
for(offset = 0; offset < inFs.Length - remainder; offset += (int)BUFFER_SIZE)
|
||||
{
|
||||
SetProgress?.Invoke(this, new ProgressEventArgs
|
||||
{
|
||||
Value = offset
|
||||
});
|
||||
|
||||
dataBuffer = new byte[BUFFER_SIZE];
|
||||
inFs.Read(dataBuffer, 0, (int)BUFFER_SIZE);
|
||||
checksumWorker.Update(dataBuffer);
|
||||
}
|
||||
|
||||
SetProgress?.Invoke(this, new ProgressEventArgs
|
||||
{
|
||||
Value = offset
|
||||
});
|
||||
|
||||
dataBuffer = new byte[remainder];
|
||||
inFs.Read(dataBuffer, 0, (int)remainder);
|
||||
checksumWorker.Update(dataBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
dataBuffer = new byte[inFs.Length];
|
||||
inFs.Read(dataBuffer, 0, (int)inFs.Length);
|
||||
checksumWorker.Update(dataBuffer);
|
||||
}
|
||||
|
||||
Dictionary<ChecksumType, string> checksums = checksumWorker.End();
|
||||
|
||||
ulong uSize = (ulong)inFs.Length;
|
||||
bool fileInDb = true;
|
||||
|
||||
bool knownFile = _pendingFiles.TryGetValue(checksums[ChecksumType.Sha512], out DbFile dbFile);
|
||||
|
||||
dbFile ??=
|
||||
((((Context.Singleton.Files.FirstOrDefault(f => f.Size == uSize &&
|
||||
f.Sha512 == checksums[ChecksumType.Sha512]) ??
|
||||
Context.Singleton.Files.FirstOrDefault(f => f.Size == uSize &&
|
||||
f.Sha384 == checksums[ChecksumType.Sha384])) ??
|
||||
Context.Singleton.Files.FirstOrDefault(f => f.Size == uSize &&
|
||||
f.Sha256 == checksums[ChecksumType.Sha256])) ??
|
||||
Context.Singleton.Files.FirstOrDefault(f => f.Size == uSize &&
|
||||
f.Sha1 == checksums[ChecksumType.Sha1])) ??
|
||||
Context.Singleton.Files.FirstOrDefault(f => f.Size == uSize &&
|
||||
f.Md5 == checksums[ChecksumType.Md5])) ??
|
||||
Context.Singleton.Files.FirstOrDefault(f => f.Size == uSize &&
|
||||
f.Crc32 == checksums[ChecksumType.Crc32]);
|
||||
|
||||
if(dbFile == null)
|
||||
{
|
||||
if(_onlyKnown)
|
||||
{
|
||||
LastMessage = "Unknown file.";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
dbFile = new DbFile
|
||||
{
|
||||
Crc32 = checksums[ChecksumType.Crc32],
|
||||
Md5 = checksums[ChecksumType.Md5],
|
||||
Sha1 = checksums[ChecksumType.Sha1],
|
||||
Sha256 = checksums[ChecksumType.Sha256],
|
||||
Sha384 = checksums[ChecksumType.Sha384],
|
||||
Sha512 = checksums[ChecksumType.Sha512],
|
||||
Size = uSize,
|
||||
CreatedOn = DateTime.UtcNow,
|
||||
UpdatedOn = DateTime.UtcNow
|
||||
};
|
||||
|
||||
fileInDb = false;
|
||||
}
|
||||
|
||||
if(!knownFile)
|
||||
_pendingFiles[checksums[ChecksumType.Sha512]] = dbFile;
|
||||
|
||||
byte[] sha384Bytes = new byte[48];
|
||||
string sha384 = checksums[ChecksumType.Sha384];
|
||||
|
||||
for(int i = 0; i < 48; i++)
|
||||
{
|
||||
if(sha384[i * 2] >= 0x30 &&
|
||||
sha384[i * 2] <= 0x39)
|
||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
|
||||
else if(sha384[i * 2] >= 0x41 &&
|
||||
sha384[i * 2] <= 0x46)
|
||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10);
|
||||
else if(sha384[i * 2] >= 0x61 &&
|
||||
sha384[i * 2] <= 0x66)
|
||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10);
|
||||
|
||||
if(sha384[(i * 2) + 1] >= 0x30 &&
|
||||
sha384[(i * 2) + 1] <= 0x39)
|
||||
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30);
|
||||
else if(sha384[(i * 2) + 1] >= 0x41 &&
|
||||
sha384[(i * 2) + 1] <= 0x46)
|
||||
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37);
|
||||
else if(sha384[(i * 2) + 1] >= 0x61 &&
|
||||
sha384[(i * 2) + 1] <= 0x66)
|
||||
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57);
|
||||
}
|
||||
|
||||
string sha384B32 = Base32.ToBase32String(sha384Bytes);
|
||||
|
||||
string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "files",
|
||||
sha384B32[0].ToString(), sha384B32[1].ToString(),
|
||||
sha384B32[2].ToString(), sha384B32[3].ToString(),
|
||||
sha384B32[4].ToString());
|
||||
|
||||
if(!Directory.Exists(repoPath))
|
||||
Directory.CreateDirectory(repoPath);
|
||||
|
||||
repoPath = Path.Combine(repoPath, sha384B32 + ".lz");
|
||||
|
||||
if(dbFile.Crc32 == null)
|
||||
{
|
||||
dbFile.Crc32 = checksums[ChecksumType.Crc32];
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if(dbFile.Md5 == null)
|
||||
{
|
||||
dbFile.Md5 = checksums[ChecksumType.Md5];
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if(dbFile.Sha1 == null)
|
||||
{
|
||||
dbFile.Sha1 = checksums[ChecksumType.Sha1];
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if(dbFile.Sha256 == null)
|
||||
{
|
||||
dbFile.Sha256 = checksums[ChecksumType.Sha256];
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if(dbFile.Sha384 == null)
|
||||
{
|
||||
dbFile.Sha384 = checksums[ChecksumType.Sha384];
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if(dbFile.Sha512 == null)
|
||||
{
|
||||
dbFile.Sha512 = checksums[ChecksumType.Sha512];
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if(File.Exists(repoPath))
|
||||
{
|
||||
dbFile.IsInRepo = true;
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
|
||||
if(!fileInDb)
|
||||
Context.Singleton.Files.Add(dbFile);
|
||||
|
||||
inFs.Close();
|
||||
|
||||
if(_deleteAfterImport)
|
||||
File.Delete(path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inFs.Position = 0;
|
||||
|
||||
var outFs = new FileStream(repoPath, FileMode.CreateNew, FileAccess.Write);
|
||||
Stream zStream = null;
|
||||
zStream = new LZipStream(outFs, CompressionMode.Compress);
|
||||
|
||||
SetProgressBounds?.Invoke(this, new ProgressBoundsEventArgs
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = inFs.Length
|
||||
});
|
||||
|
||||
SetMessage?.Invoke(this, new MessageEventArgs
|
||||
{
|
||||
Message = "Compressing file..."
|
||||
});
|
||||
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
while(inFs.Position + BUFFER_SIZE <= inFs.Length)
|
||||
{
|
||||
SetProgress?.Invoke(this, new ProgressEventArgs
|
||||
{
|
||||
Value = inFs.Position
|
||||
});
|
||||
|
||||
inFs.Read(buffer, 0, buffer.Length);
|
||||
zStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
buffer = new byte[inFs.Length - inFs.Position];
|
||||
|
||||
SetProgress?.Invoke(this, new ProgressEventArgs
|
||||
{
|
||||
Value = inFs.Position
|
||||
});
|
||||
|
||||
inFs.Read(buffer, 0, buffer.Length);
|
||||
zStream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
SetMessage?.Invoke(this, new MessageEventArgs
|
||||
{
|
||||
Message = "Finishing..."
|
||||
});
|
||||
|
||||
inFs.Close();
|
||||
zStream.Close();
|
||||
outFs.Dispose();
|
||||
|
||||
dbFile.IsInRepo = true;
|
||||
dbFile.UpdatedOn = DateTime.UtcNow;
|
||||
|
||||
if(!fileInDb)
|
||||
Context.Singleton.Files.Add(dbFile);
|
||||
|
||||
if(_deleteAfterImport)
|
||||
File.Delete(path);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LastMessage = "Unhandled exception when importing file.";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveChanges()
|
||||
{
|
||||
SetIndeterminateProgress?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
SetMessage?.Invoke(this, new MessageEventArgs
|
||||
{
|
||||
Message = "Saving changes to database..."
|
||||
});
|
||||
|
||||
Context.Singleton.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
226
RomRepoMgr.Database/Migrations/20200823155449_AddIsFileInRepo.Designer.cs
generated
Normal file
226
RomRepoMgr.Database/Migrations/20200823155449_AddIsFileInRepo.Designer.cs
generated
Normal file
@@ -0,0 +1,226 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using RomRepoMgr.Database;
|
||||
|
||||
namespace RomRepoMgr.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(Context))]
|
||||
[Migration("20200823155449_AddIsFileInRepo")]
|
||||
partial class AddIsFileInRepo
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.7");
|
||||
|
||||
modelBuilder.Entity("RomRepoMgr.Database.Models.DbFile", b =>
|
||||
{
|
||||
b.Property<ulong>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Crc32")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(8);
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsInRepo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Md5")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(32);
|
||||
|
||||
b.Property<string>("Sha1")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<string>("Sha256")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(64);
|
||||
|
||||
b.Property<string>("Sha384")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(96);
|
||||
|
||||
b.Property<string>("Sha512")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<ulong>("Size")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Crc32");
|
||||
|
||||
b.HasIndex("Sha1");
|
||||
|
||||
b.HasIndex("Sha256");
|
||||
|
||||
b.HasIndex("Sha384");
|
||||
|
||||
b.HasIndex("Sha512");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.ToTable("Files");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RomRepoMgr.Database.Models.FileByMachine", b =>
|
||||
{
|
||||
b.Property<ulong>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("FileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("MachineId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FileId");
|
||||
|
||||
b.HasIndex("MachineId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("FilesByMachines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RomRepoMgr.Database.Models.Machine", b =>
|
||||
{
|
||||
b.Property<ulong>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("RomSetId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.HasIndex("RomSetId");
|
||||
|
||||
b.ToTable("Machines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RomRepoMgr.Database.Models.RomSet", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Author")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Date")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Homepage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Sha384")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(96);
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Version")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Author");
|
||||
|
||||
b.HasIndex("Comment");
|
||||
|
||||
b.HasIndex("Date");
|
||||
|
||||
b.HasIndex("Description");
|
||||
|
||||
b.HasIndex("Filename");
|
||||
|
||||
b.HasIndex("Homepage");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.HasIndex("Sha384");
|
||||
|
||||
b.HasIndex("Version");
|
||||
|
||||
b.ToTable("RomSets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RomRepoMgr.Database.Models.FileByMachine", b =>
|
||||
{
|
||||
b.HasOne("RomRepoMgr.Database.Models.DbFile", "File")
|
||||
.WithMany("Machines")
|
||||
.HasForeignKey("FileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("RomRepoMgr.Database.Models.Machine", "Machine")
|
||||
.WithMany("Files")
|
||||
.HasForeignKey("MachineId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RomRepoMgr.Database.Models.Machine", b =>
|
||||
{
|
||||
b.HasOne("RomRepoMgr.Database.Models.RomSet", "RomSet")
|
||||
.WithMany("Machines")
|
||||
.HasForeignKey("RomSetId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
// 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 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace RomRepoMgr.Database.Migrations
|
||||
{
|
||||
public partial class AddIsFileInRepo : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder) =>
|
||||
migrationBuilder.AddColumn<bool>("IsInRepo", "Files", nullable: false, defaultValue: false);
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder) =>
|
||||
migrationBuilder.DropColumn("IsInRepo", "Files");
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ namespace RomRepoMgr.Database.Migrations
|
||||
|
||||
b.Property<DateTime>("CreatedOn").HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsInRepo").HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Md5").HasColumnType("TEXT").HasMaxLength(32);
|
||||
|
||||
b.Property<string>("Sha1").HasColumnType("TEXT").HasMaxLength(40);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace RomRepoMgr.Database.Models
|
||||
@@ -44,6 +45,8 @@ namespace RomRepoMgr.Database.Models
|
||||
public string Sha384 { get; set; }
|
||||
[StringLength(128, MinimumLength = 128)]
|
||||
public string Sha512 { get; set; }
|
||||
[DefaultValue(false)]
|
||||
public bool IsInRepo { get; set; }
|
||||
public virtual ICollection<FileByMachine> Machines { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,15 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using ReactiveUI;
|
||||
using RomRepoMgr.Core.EventArgs;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using RomRepoMgr.Views;
|
||||
|
||||
namespace RomRepoMgr.ViewModels
|
||||
@@ -54,6 +58,7 @@ namespace RomRepoMgr.ViewModels
|
||||
bool _progressVisible;
|
||||
bool _recurseArchivesChecked;
|
||||
bool _removeFilesChecked;
|
||||
bool _removeFilesEnabled;
|
||||
string _status2Message;
|
||||
string _statusMessage;
|
||||
|
||||
@@ -67,7 +72,10 @@ namespace RomRepoMgr.ViewModels
|
||||
ImportResults = new ObservableCollection<ImportRomFolderItem>();
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
|
||||
_isReady = true;
|
||||
IsReady = true;
|
||||
CanStart = true;
|
||||
CanClose = true;
|
||||
_removeFilesEnabled = true;
|
||||
}
|
||||
|
||||
public string PathLabel => "Path:";
|
||||
@@ -88,10 +96,23 @@ namespace RomRepoMgr.ViewModels
|
||||
set => this.RaiseAndSetIfChanged(ref _knownOnlyChecked, value);
|
||||
}
|
||||
|
||||
public bool RemoveFilesEnabled
|
||||
{
|
||||
get => _removeFilesEnabled;
|
||||
set => this.RaiseAndSetIfChanged(ref _removeFilesEnabled, value);
|
||||
}
|
||||
|
||||
public bool RecurseArchivesChecked
|
||||
{
|
||||
get => _recurseArchivesChecked;
|
||||
set => this.RaiseAndSetIfChanged(ref _recurseArchivesChecked, value);
|
||||
set
|
||||
{
|
||||
if(value)
|
||||
RemoveFilesChecked = false;
|
||||
|
||||
RemoveFilesEnabled = !value;
|
||||
this.RaiseAndSetIfChanged(ref _recurseArchivesChecked, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReady
|
||||
@@ -204,9 +225,88 @@ namespace RomRepoMgr.ViewModels
|
||||
|
||||
void ExecuteCloseCommand() => _view.Close();
|
||||
|
||||
void ExecuteStartCommand() {}
|
||||
void ExecuteStartCommand()
|
||||
{
|
||||
IsReady = false;
|
||||
ProgressVisible = true;
|
||||
ProgressIsIndeterminate = true;
|
||||
StatusMessage = "Enumerating files...";
|
||||
IsImporting = true;
|
||||
CanStart = false;
|
||||
CanClose = false;
|
||||
|
||||
void OnWorkerOnWorkFinished(object sender, EventArgs args) => Dispatcher.UIThread.Post(() => {});
|
||||
Task.Run(() =>
|
||||
{
|
||||
var watch = new Stopwatch();
|
||||
string[] files = Directory.GetFiles(FolderPath, "*", SearchOption.AllDirectories);
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ProgressIsIndeterminate = false;
|
||||
ProgressMinimum = 0;
|
||||
ProgressMaximum = files.LongLength;
|
||||
ProgressValue = 0;
|
||||
Progress2Visible = true;
|
||||
});
|
||||
|
||||
var worker = new FileImporter(KnownOnlyChecked, RemoveFilesChecked);
|
||||
worker.SetIndeterminateProgress += OnWorkerOnSetIndeterminateProgress;
|
||||
worker.SetMessage += OnWorkerOnSetMessage;
|
||||
worker.SetProgress += OnWorkerOnSetProgress;
|
||||
worker.SetProgressBounds += OnWorkerOnSetProgressBounds;
|
||||
|
||||
long position = 0;
|
||||
watch.Start();
|
||||
|
||||
foreach(string file in files)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
StatusMessage = string.Format("Importing {0}...", Path.GetFileName(file));
|
||||
ProgressValue = position;
|
||||
});
|
||||
|
||||
bool ret = worker.ImportRom(file);
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if(ret)
|
||||
{
|
||||
ImportResults.Add(new ImportRomFolderItem
|
||||
{
|
||||
Filename = Path.GetFileName(file),
|
||||
Status = "OK"
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ImportResults.Add(new ImportRomFolderItem
|
||||
{
|
||||
Filename = Path.GetFileName(file),
|
||||
Status = string.Format("Error: {0}", worker.LastMessage)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
worker.SaveChanges();
|
||||
|
||||
watch.Stop();
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ProgressVisible = false;
|
||||
StatusMessage = "Finished!";
|
||||
CanClose = true;
|
||||
Progress2Visible = false;
|
||||
IsReady = true;
|
||||
|
||||
Console.WriteLine($"Took {watch.Elapsed.TotalSeconds} seconds");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void OnWorkerOnSetProgressBounds(object sender, ProgressBoundsEventArgs args) => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
@@ -223,8 +323,6 @@ namespace RomRepoMgr.ViewModels
|
||||
|
||||
void OnWorkerOnSetIndeterminateProgress(object sender, EventArgs args) =>
|
||||
Dispatcher.UIThread.Post(() => Progress2IsIndeterminate = true);
|
||||
|
||||
void OnWorkerOnErrorOccurred(object sender, ErrorEventArgs args) => Dispatcher.UIThread.Post(() => {});
|
||||
}
|
||||
|
||||
public sealed class ImportRomFolderItem
|
||||
|
||||
@@ -45,10 +45,10 @@
|
||||
</StackPanel>
|
||||
<CheckBox Grid.Row="1" IsChecked="{Binding RemoveFilesChecked}" IsEnabled="{Binding IsReady}">
|
||||
<CheckBox.Content>
|
||||
<TextBlock Text="{Binding RemoveFilesLabel}" />
|
||||
<TextBlock Text="{Binding RemoveFilesLabel}" IsEnabled="{Binding RemoveFilesEnabled}" />
|
||||
</CheckBox.Content>
|
||||
</CheckBox>
|
||||
<CheckBox Grid.Row="2" IsChecked="{Binding KnownOnlyChecked}" IsEnabled="False">
|
||||
<CheckBox Grid.Row="2" IsChecked="{Binding KnownOnlyChecked}">
|
||||
<CheckBox.Content>
|
||||
<TextBlock Text="{Binding KnownOnlyLabel}" />
|
||||
</CheckBox.Content>
|
||||
|
||||
Reference in New Issue
Block a user