Files
romrepomgr/RomRepoMgr.Core/Filesystem/Vfs.cs

709 lines
27 KiB
C#
Raw Normal View History

2020-09-01 11:54:16 +01:00
using System;
2020-09-03 00:58:07 +01:00
using System.Collections.Concurrent;
using System.Collections.Generic;
2020-09-03 00:58:07 +01:00
using System.IO;
2020-09-03 00:14:11 +01:00
using System.Linq;
2024-11-10 04:17:05 +00:00
using System.Threading;
2020-09-01 11:54:16 +01:00
using System.Threading.Tasks;
2020-09-03 00:14:11 +01:00
using RomRepoMgr.Database;
2020-09-03 00:58:07 +01:00
using RomRepoMgr.Database.Models;
using SharpCompress.Compressors;
using SharpCompress.Compressors.LZMA;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
namespace RomRepoMgr.Core.Filesystem;
// TODO: Last handle goes negative
// TODO: Invalidate caches
// TODO: Mount options
// TODO: Do not show machines or romsets with no ROMs in repo
public class Vfs : IDisposable
2020-08-30 14:22:41 +01:00
{
2024-11-09 01:37:59 +00:00
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>> _machineDisksCache;
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>> _machineFilesCache;
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedMedia>> _machineMediasCache;
readonly ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>> _machinesStatCache;
readonly ConcurrentDictionary<long, RomSet> _romSetsCache;
readonly ConcurrentDictionary<long, Stream> _streamsCache;
Fuse _fuse;
long _lastHandle;
ConcurrentDictionary<string, long> _rootDirectoryCache;
Winfsp _winfsp;
public Vfs()
2020-08-30 14:22:41 +01:00
{
2024-11-09 01:37:59 +00:00
_rootDirectoryCache = new ConcurrentDictionary<string, long>();
_romSetsCache = new ConcurrentDictionary<long, RomSet>();
_machinesStatCache = new ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>>();
_machineFilesCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>>();
_machineDisksCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>>();
_machineMediasCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedMedia>>();
_streamsCache = new ConcurrentDictionary<long, Stream>();
_lastHandle = 0;
}
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
public static bool IsAvailable => OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()
? Fuse.IsAvailable
: OperatingSystem.IsWindows() && Winfsp.IsAvailable;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
public void Dispose() => Umount();
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
public event EventHandler<System.EventArgs> Umounted;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
public void MountTo(string mountPoint)
{
if((OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) && Fuse.IsAvailable)
2020-09-01 11:54:16 +01:00
{
2024-11-09 01:37:59 +00:00
_fuse = new Fuse(this)
2020-09-01 11:54:16 +01:00
{
2024-11-09 01:37:59 +00:00
MountPoint = mountPoint
};
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
Task.Run(() =>
{
2024-11-09 01:37:59 +00:00
_fuse.Start();
CleanUp();
2024-11-09 01:37:59 +00:00
});
2020-09-01 11:54:16 +01:00
}
2024-11-09 01:37:59 +00:00
else if(OperatingSystem.IsWindows() && Winfsp.IsAvailable)
2020-09-01 11:54:16 +01:00
{
2024-11-09 01:37:59 +00:00
_winfsp = new Winfsp(this);
bool ret = _winfsp.Mount(mountPoint);
2020-12-21 01:37:00 +00:00
2024-11-09 01:37:59 +00:00
if(ret) return;
2024-11-09 01:37:59 +00:00
_winfsp = null;
CleanUp();
}
2024-11-09 01:37:59 +00:00
else
CleanUp();
}
2024-11-09 01:37:59 +00:00
public void Umount()
{
if(OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
{
2024-11-09 01:37:59 +00:00
_fuse?.Umount();
_fuse = null;
2020-09-01 11:54:16 +01:00
}
2020-09-03 00:14:11 +01:00
2024-11-09 01:37:59 +00:00
if(OperatingSystem.IsWindows())
2020-09-03 00:14:11 +01:00
{
2024-11-09 01:37:59 +00:00
_winfsp?.Umount();
_winfsp = null;
}
2020-09-03 00:14:11 +01:00
2024-11-09 01:37:59 +00:00
CleanUp();
}
2024-11-12 06:21:00 +00:00
void CleanUp()
2024-11-09 01:37:59 +00:00
{
foreach(KeyValuePair<long, Stream> handle in _streamsCache) handle.Value.Close();
2020-09-03 00:39:05 +01:00
2024-11-09 01:37:59 +00:00
_streamsCache.Clear();
_lastHandle = 0;
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
Umounted?.Invoke(this, System.EventArgs.Empty);
}
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
internal void GetInfo(out ulong files, out ulong totalSize)
{
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
totalSize = (ulong)(ctx.Files.Where(f => f.IsInRepo).Sum(f => (double)f.Size) +
ctx.Disks.Where(f => f.IsInRepo).Sum(f => (double)f.Size) +
ctx.Medias.Where(f => f.IsInRepo).Sum(f => (double)f.Size));
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
files = (ulong)(ctx.Files.Count(f => f.IsInRepo) +
ctx.Disks.Count(f => f.IsInRepo) +
ctx.Medias.Count(f => f.IsInRepo));
}
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
internal string[] SplitPath(string path) =>
path.Split(OperatingSystem.IsWindows() ? "\\" : "/", StringSplitOptions.RemoveEmptyEntries);
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
void FillRootDirectoryCache()
{
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
var rootCache = new ConcurrentDictionary<string, long>();
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
foreach(RomSet set in ctx.RomSets)
{
string name;
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
if(OperatingSystem.IsWindows())
{
name = set.Name.Replace('/', '')
.Replace('<', '\uFF1C')
.Replace('>', '\uFF1E')
.Replace(':', '\uFF1A')
.Replace('"', '\u2033')
.Replace('\\', '')
.Replace('|', '')
.Replace('?', '')
.Replace('*', '');
if(rootCache.ContainsKey(name))
{
name = Path.GetFileNameWithoutExtension(set.Filename)
?.Replace('/', '')
.Replace('<', '\uFF1C')
.Replace('>', '\uFF1E')
.Replace(':', '\uFF1A')
.Replace('"', '\u2033')
.Replace('\\', '')
.Replace('|', '')
.Replace('?', '')
.Replace('*', '');
}
2020-09-03 00:58:07 +01:00
}
2024-11-09 01:37:59 +00:00
else
{
name = set.Name.Replace('/', '');
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
if(rootCache.ContainsKey(name))
name = Path.GetFileNameWithoutExtension(set.Filename)?.Replace('/', '');
}
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
if(name == null || rootCache.ContainsKey(name)) name = Path.GetFileNameWithoutExtension(set.Sha384);
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
if(name == null) continue;
2020-09-03 00:58:07 +01:00
2024-11-09 01:37:59 +00:00
rootCache[name] = set.Id;
_romSetsCache[set.Id] = set;
2020-09-03 00:58:07 +01:00
}
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
_rootDirectoryCache = rootCache;
}
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
internal long GetRomSetId(string name)
{
if(_rootDirectoryCache.Count == 0) FillRootDirectoryCache();
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
if(!_rootDirectoryCache.TryGetValue(name, out long romSetId)) return -1;
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
return romSetId;
}
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
internal RomSet GetRomSet(long id)
{
if(_romSetsCache.TryGetValue(id, out RomSet romSet)) return romSet;
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
romSet = ctx.RomSets.Find(id);
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
if(romSet == null) return null;
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
_romSetsCache[id] = romSet;
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
return romSet;
}
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
internal ConcurrentDictionary<string, CachedMachine> GetMachinesFromRomSet(long id)
{
_machinesStatCache.TryGetValue(id, out ConcurrentDictionary<string, CachedMachine> cachedMachines);
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
if(cachedMachines != null) return cachedMachines;
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
cachedMachines = new ConcurrentDictionary<string, CachedMachine>();
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
foreach(Machine mach in ctx.Machines.Where(m => m.RomSet.Id == id))
2020-09-03 01:25:06 +01:00
{
2024-11-09 01:37:59 +00:00
cachedMachines[mach.Name] = new CachedMachine
{
Id = mach.Id,
CreationDate = mach.CreatedOn,
ModificationDate = mach.UpdatedOn
};
}
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
_machinesStatCache[id] = cachedMachines;
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
return cachedMachines;
}
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
internal CachedMachine GetMachine(long romSetId, string name)
{
ConcurrentDictionary<string, CachedMachine> cachedMachines = GetMachinesFromRomSet(romSetId);
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
if(cachedMachines == null || !cachedMachines.TryGetValue(name, out CachedMachine machine)) return null;
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
return machine;
}
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
internal ConcurrentDictionary<string, CachedFile> GetFilesFromMachine(ulong id)
{
_machineFilesCache.TryGetValue(id, out ConcurrentDictionary<string, CachedFile> cachedMachineFiles);
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
if(cachedMachineFiles != null) return cachedMachineFiles;
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
cachedMachineFiles = new ConcurrentDictionary<string, CachedFile>();
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
foreach(FileByMachine machineFile in
ctx.FilesByMachines.Where(fbm => fbm.Machine.Id == id && fbm.File.IsInRepo))
2020-09-04 20:38:43 +01:00
{
2024-11-09 01:37:59 +00:00
var cachedFile = new CachedFile
{
Id = machineFile.File.Id,
Crc32 = machineFile.File.Crc32,
Md5 = machineFile.File.Md5,
Sha1 = machineFile.File.Sha1,
Sha256 = machineFile.File.Sha256,
Sha384 = machineFile.File.Sha384,
Sha512 = machineFile.File.Sha512,
Size = machineFile.File.Size,
CreatedOn = machineFile.File.CreatedOn,
UpdatedOn = machineFile.File.UpdatedOn,
FileLastModification = machineFile.FileLastModification
};
cachedMachineFiles[machineFile.Name] = cachedFile;
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
_machineFilesCache[id] = cachedMachineFiles;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
return cachedMachineFiles;
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
internal ConcurrentDictionary<string, CachedDisk> GetDisksFromMachine(ulong id)
{
_machineDisksCache.TryGetValue(id, out ConcurrentDictionary<string, CachedDisk> cachedMachineDisks);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(cachedMachineDisks != null) return cachedMachineDisks;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
cachedMachineDisks = new ConcurrentDictionary<string, CachedDisk>();
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
foreach(DiskByMachine machineDisk in ctx.DisksByMachines.Where(dbm => dbm.Machine.Id == id &&
dbm.Disk.IsInRepo &&
dbm.Disk.Size != null))
{
2024-11-09 01:37:59 +00:00
var cachedDisk = new CachedDisk
{
Id = machineDisk.Disk.Id,
Md5 = machineDisk.Disk.Md5,
Sha1 = machineDisk.Disk.Sha1,
Size = machineDisk.Disk.Size ?? 0,
CreatedOn = machineDisk.Disk.CreatedOn,
UpdatedOn = machineDisk.Disk.UpdatedOn
};
cachedMachineDisks[machineDisk.Name] = cachedDisk;
}
2024-11-09 01:37:59 +00:00
_machineDisksCache[id] = cachedMachineDisks;
2024-11-09 01:37:59 +00:00
return cachedMachineDisks;
}
2024-11-09 01:37:59 +00:00
internal ConcurrentDictionary<string, CachedMedia> GetMediasFromMachine(ulong id)
{
_machineMediasCache.TryGetValue(id, out ConcurrentDictionary<string, CachedMedia> cachedMachineMedias);
2024-11-09 01:37:59 +00:00
if(cachedMachineMedias != null) return cachedMachineMedias;
2024-11-09 01:37:59 +00:00
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
2024-11-09 01:37:59 +00:00
cachedMachineMedias = new ConcurrentDictionary<string, CachedMedia>();
2024-11-09 01:37:59 +00:00
foreach(MediaByMachine machineMedia in ctx.MediasByMachines.Where(mbm => mbm.Machine.Id == id &&
mbm.Media.IsInRepo &&
mbm.Media.Size != null))
2020-09-03 01:42:18 +01:00
{
2024-11-09 01:37:59 +00:00
var cachedDisk = new CachedMedia
{
Id = machineMedia.Media.Id,
Md5 = machineMedia.Media.Md5,
Sha1 = machineMedia.Media.Sha1,
Sha256 = machineMedia.Media.Sha256,
SpamSum = machineMedia.Media.SpamSum,
Size = machineMedia.Media.Size ?? 0,
CreatedOn = machineMedia.Media.CreatedOn,
UpdatedOn = machineMedia.Media.UpdatedOn
};
cachedMachineMedias[machineMedia.Name] = cachedDisk;
2020-09-03 01:42:18 +01:00
}
2024-11-09 01:37:59 +00:00
_machineMediasCache[id] = cachedMachineMedias;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
return cachedMachineMedias;
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
internal CachedFile GetFile(ulong machineId, string name)
{
ConcurrentDictionary<string, CachedFile> cachedFiles = GetFilesFromMachine(machineId);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(cachedFiles == null || !cachedFiles.TryGetValue(name, out CachedFile file)) return null;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
return file;
}
2024-11-09 01:37:59 +00:00
internal CachedDisk GetDisk(ulong machineId, string name)
{
if(name.EndsWith(".chd", StringComparison.OrdinalIgnoreCase)) name = name.Substring(0, name.Length - 4);
2024-11-09 01:37:59 +00:00
ConcurrentDictionary<string, CachedDisk> cachedDisks = GetDisksFromMachine(machineId);
2024-11-09 01:37:59 +00:00
if(cachedDisks == null || !cachedDisks.TryGetValue(name, out CachedDisk disk)) return null;
2024-11-09 01:37:59 +00:00
return disk;
}
2024-11-09 01:37:59 +00:00
internal CachedMedia GetMedia(ulong machineId, string name)
{
if(name.EndsWith(".aif", StringComparison.OrdinalIgnoreCase)) name = name.Substring(0, name.Length - 4);
2024-11-09 01:37:59 +00:00
ConcurrentDictionary<string, CachedMedia> cachedMedias = GetMediasFromMachine(machineId);
2024-11-09 01:37:59 +00:00
if(cachedMedias == null || !cachedMedias.TryGetValue(name, out CachedMedia media)) return null;
2024-11-09 01:37:59 +00:00
return media;
}
2024-11-09 01:37:59 +00:00
internal long Open(string sha384, long fileSize)
{
var sha384Bytes = new byte[48];
2024-11-09 01:37:59 +00:00
for(var i = 0; i < 48; i++)
{
if(sha384[i * 2] >= 0x30 && sha384[i * 2] <= 0x39)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
else if(sha384[i * 2] >= 0x41 && sha384[i * 2] <= 0x46)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10);
else if(sha384[i * 2] >= 0x61 && sha384[i * 2] <= 0x66)
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10);
if(sha384[i * 2 + 1] >= 0x30 && sha384[i * 2 + 1] <= 0x39)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x30);
else if(sha384[i * 2 + 1] >= 0x41 && sha384[i * 2 + 1] <= 0x46)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x37);
else if(sha384[i * 2 + 1] >= 0x61 && sha384[i * 2 + 1] <= 0x66)
sha384Bytes[i] += (byte)(sha384[i * 2 + 1] - 0x57);
}
2024-11-09 01:37:59 +00:00
string sha384B32 = Base32.ToBase32String(sha384Bytes);
2024-11-09 01:37:59 +00:00
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");
2024-11-09 01:37:59 +00:00
if(!File.Exists(repoPath)) return -1;
2024-11-09 01:37:59 +00:00
_lastHandle++;
long handle = _lastHandle;
2024-11-09 01:37:59 +00:00
_streamsCache[handle] =
Stream.Synchronized(new ForcedSeekStream<LZipStream>(fileSize,
new FileStream(repoPath,
FileMode.Open,
FileAccess.Read),
CompressionMode.Decompress));
2024-11-09 01:37:59 +00:00
return handle;
}
2020-09-03 01:58:08 +01:00
2024-11-09 01:37:59 +00:00
internal int Read(long handle, byte[] buf, long offset)
{
if(!_streamsCache.TryGetValue(handle, out Stream stream)) return -1;
2024-11-10 04:17:05 +00:00
using var rwLock = new ReaderWriterLockSlim();
try
2020-09-03 01:58:08 +01:00
{
2024-11-10 04:17:05 +00:00
rwLock.EnterReadLock();
2024-11-09 01:37:59 +00:00
stream.Position = offset;
2020-09-03 01:58:08 +01:00
2024-11-09 01:37:59 +00:00
return stream.Read(buf, 0, buf.Length);
2020-09-03 01:58:08 +01:00
}
2024-11-10 04:17:05 +00:00
finally
{
rwLock.ExitReadLock();
}
2024-11-09 01:37:59 +00:00
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
internal bool Close(long handle)
{
if(!_streamsCache.TryGetValue(handle, out Stream stream)) return false;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
stream.Close();
_streamsCache.TryRemove(handle, out _);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
return true;
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
internal IEnumerable<string> GetRootEntries()
{
if(_rootDirectoryCache.Count == 0) FillRootDirectoryCache();
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
return _rootDirectoryCache.Keys.ToArray();
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
public long OpenDisk(string sha1, string md5)
{
if(sha1 == null && md5 == null) return -1;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
string repoPath = null;
string md5Path = null;
string sha1Path = null;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(sha1 != null)
{
var sha1Bytes = new byte[20];
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
for(var i = 0; i < 20; i++)
{
if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39)
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10);
else if(sha1[i * 2] >= 0x41 && sha1[i * 2] <= 0x46)
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10);
else if(sha1[i * 2] >= 0x61 && sha1[i * 2] <= 0x66) sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10);
if(sha1[i * 2 + 1] >= 0x30 && sha1[i * 2 + 1] <= 0x39)
sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x30);
else if(sha1[i * 2 + 1] >= 0x41 && sha1[i * 2 + 1] <= 0x46)
sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x37);
else if(sha1[i * 2 + 1] >= 0x61 && sha1[i * 2 + 1] <= 0x66)
sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x57);
2020-09-04 20:38:43 +01:00
}
2024-11-09 01:37:59 +00:00
string sha1B32 = Base32.ToBase32String(sha1Bytes);
sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath,
"chd",
"sha1",
sha1B32[0].ToString(),
sha1B32[1].ToString(),
sha1B32[2].ToString(),
sha1B32[3].ToString(),
sha1B32[4].ToString(),
sha1B32 + ".chd");
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(md5 != null)
{
var md5Bytes = new byte[16];
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
for(var i = 0; i < 16; i++)
{
if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39)
md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10);
else if(md5[i * 2] >= 0x41 && md5[i * 2] <= 0x46)
md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10);
else if(md5[i * 2] >= 0x61 && md5[i * 2] <= 0x66) md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10);
if(md5[i * 2 + 1] >= 0x30 && md5[i * 2 + 1] <= 0x39)
md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x30);
else if(md5[i * 2 + 1] >= 0x41 && md5[i * 2 + 1] <= 0x46)
md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x37);
else if(md5[i * 2 + 1] >= 0x61 && md5[i * 2 + 1] <= 0x66) md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x57);
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
string md5B32 = Base32.ToBase32String(md5Bytes);
md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath,
"chd",
"md5",
md5B32[0].ToString(),
md5B32[1].ToString(),
md5B32[2].ToString(),
md5B32[3].ToString(),
md5B32[4].ToString(),
md5B32 + ".chd");
2020-09-04 20:38:43 +01:00
}
2024-11-09 01:37:59 +00:00
if(File.Exists(sha1Path))
repoPath = sha1Path;
else if(File.Exists(md5Path)) repoPath = md5Path;
2024-11-09 01:37:59 +00:00
if(repoPath == null) return -1;
2024-11-09 01:37:59 +00:00
_lastHandle++;
long handle = _lastHandle;
2024-11-09 01:37:59 +00:00
_streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read));
2024-11-09 01:37:59 +00:00
return handle;
}
2024-11-09 01:37:59 +00:00
public long OpenMedia(string sha256, string sha1, string md5)
{
if(sha256 == null && sha1 == null && md5 == null) return -1;
2024-11-09 01:37:59 +00:00
string repoPath = null;
string md5Path = null;
string sha1Path = null;
string sha256Path = null;
2024-11-09 01:37:59 +00:00
if(sha256 != null)
{
var sha256Bytes = new byte[32];
2024-11-09 01:37:59 +00:00
for(var i = 0; i < 32; i++)
{
if(sha256[i * 2] >= 0x30 && sha256[i * 2] <= 0x39)
sha256Bytes[i] = (byte)((sha256[i * 2] - 0x30) * 0x10);
else if(sha256[i * 2] >= 0x41 && sha256[i * 2] <= 0x46)
sha256Bytes[i] = (byte)((sha256[i * 2] - 0x37) * 0x10);
else if(sha256[i * 2] >= 0x61 && sha256[i * 2] <= 0x66)
sha256Bytes[i] = (byte)((sha256[i * 2] - 0x57) * 0x10);
if(sha256[i * 2 + 1] >= 0x30 && sha256[i * 2 + 1] <= 0x39)
sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x30);
else if(sha256[i * 2 + 1] >= 0x41 && sha256[i * 2 + 1] <= 0x46)
sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x37);
else if(sha256[i * 2 + 1] >= 0x61 && sha256[i * 2 + 1] <= 0x66)
sha256Bytes[i] += (byte)(sha256[i * 2 + 1] - 0x57);
}
2024-11-09 01:37:59 +00:00
string sha256B32 = Base32.ToBase32String(sha256Bytes);
sha256Path = Path.Combine(Settings.Settings.Current.RepositoryPath,
"aaru",
"sha256",
sha256B32[0].ToString(),
sha256B32[1].ToString(),
sha256B32[2].ToString(),
sha256B32[3].ToString(),
sha256B32[4].ToString(),
sha256B32 + ".aif");
}
if(sha1 != null)
{
var sha1Bytes = new byte[20];
for(var i = 0; i < 20; i++)
{
2024-11-09 01:37:59 +00:00
if(sha1[i * 2] >= 0x30 && sha1[i * 2] <= 0x39)
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10);
else if(sha1[i * 2] >= 0x41 && sha1[i * 2] <= 0x46)
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10);
else if(sha1[i * 2] >= 0x61 && sha1[i * 2] <= 0x66) sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10);
if(sha1[i * 2 + 1] >= 0x30 && sha1[i * 2 + 1] <= 0x39)
sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x30);
else if(sha1[i * 2 + 1] >= 0x41 && sha1[i * 2 + 1] <= 0x46)
sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x37);
else if(sha1[i * 2 + 1] >= 0x61 && sha1[i * 2 + 1] <= 0x66)
sha1Bytes[i] += (byte)(sha1[i * 2 + 1] - 0x57);
}
2024-11-09 01:37:59 +00:00
string sha1B32 = Base32.ToBase32String(sha1Bytes);
sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath,
"aaru",
"sha1",
sha1B32[0].ToString(),
sha1B32[1].ToString(),
sha1B32[2].ToString(),
sha1B32[3].ToString(),
sha1B32[4].ToString(),
sha1B32 + ".aif");
}
2024-11-09 01:37:59 +00:00
if(md5 != null)
{
var md5Bytes = new byte[16];
2024-11-09 01:37:59 +00:00
for(var i = 0; i < 16; i++)
{
if(md5[i * 2] >= 0x30 && md5[i * 2] <= 0x39)
md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10);
else if(md5[i * 2] >= 0x41 && md5[i * 2] <= 0x46)
md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10);
else if(md5[i * 2] >= 0x61 && md5[i * 2] <= 0x66) md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10);
if(md5[i * 2 + 1] >= 0x30 && md5[i * 2 + 1] <= 0x39)
md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x30);
else if(md5[i * 2 + 1] >= 0x41 && md5[i * 2 + 1] <= 0x46)
md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x37);
else if(md5[i * 2 + 1] >= 0x61 && md5[i * 2 + 1] <= 0x66) md5Bytes[i] += (byte)(md5[i * 2 + 1] - 0x57);
}
2024-11-09 01:37:59 +00:00
string md5B32 = Base32.ToBase32String(md5Bytes);
md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath,
"aaru",
"md5",
md5B32[0].ToString(),
md5B32[1].ToString(),
md5B32[2].ToString(),
md5B32[3].ToString(),
md5B32[4].ToString(),
md5B32 + ".aif");
}
2024-11-09 01:37:59 +00:00
if(File.Exists(sha256Path))
repoPath = sha256Path;
else if(File.Exists(sha1Path))
repoPath = sha1Path;
else if(File.Exists(md5Path)) repoPath = md5Path;
2024-11-09 01:37:59 +00:00
if(repoPath == null) return -1;
2024-11-09 01:37:59 +00:00
_lastHandle++;
long handle = _lastHandle;
2020-09-03 01:25:06 +01:00
2024-11-09 01:37:59 +00:00
_streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read));
2020-09-03 01:42:18 +01:00
2024-11-09 01:37:59 +00:00
return handle;
2020-09-03 01:42:18 +01:00
}
2024-11-09 01:37:59 +00:00
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
internal sealed class CachedMachine
{
public ulong Id { get; set; }
public DateTime CreationDate { get; set; }
public DateTime ModificationDate { get; set; }
}
2024-11-09 01:37:59 +00:00
internal sealed class CachedFile
{
public ulong Id { get; set; }
public ulong Size { get; set; }
public string Crc32 { get; set; }
public string Md5 { get; set; }
public string Sha1 { get; set; }
public string Sha256 { get; set; }
public string Sha384 { get; set; }
public string Sha512 { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
public DateTime? FileLastModification { get; set; }
}
internal sealed class CachedDisk
{
public ulong Id { get; set; }
public ulong Size { get; set; }
public string Md5 { get; set; }
public string Sha1 { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
internal sealed class CachedMedia
{
public ulong Id { get; set; }
public ulong Size { get; set; }
public string Md5 { get; set; }
public string Sha1 { get; set; }
public string Sha256 { get; set; }
public string SpamSum { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
2020-08-30 14:22:41 +01:00
}