mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
Create FileTypeTool and move static methods
This commit is contained in:
@@ -147,13 +147,13 @@ namespace SabreTools.DatTools
|
||||
return;
|
||||
|
||||
// Initialize possible archive variables
|
||||
BaseArchive? archive = BaseArchive.Create(item);
|
||||
BaseArchive? archive = FileTypeTool.CreateArchiveType(item);
|
||||
|
||||
// Process archives according to flags
|
||||
if (archive != null)
|
||||
{
|
||||
// Set the archive flags
|
||||
archive.AvailableHashTypes = _hashes;
|
||||
archive.SetHashTypes(_hashes);
|
||||
|
||||
// Skip if we're treating archives as files and skipping files
|
||||
#if NET20 || NET35
|
||||
@@ -406,7 +406,7 @@ namespace SabreTools.DatTools
|
||||
{
|
||||
logger.Verbose($"'{Path.GetFileName(item)}' treated like a file");
|
||||
var header = datFile.Header.GetStringFieldValue(Models.Metadata.Header.HeaderKey);
|
||||
BaseFile? baseFile = BaseFile.GetInfo(item, header, _hashes, asFiles);
|
||||
BaseFile? baseFile = FileTypeTool.GetInfo(item, header, _hashes, asFiles);
|
||||
DatItem? datItem = DatItem.Create(baseFile);
|
||||
if (datItem != null)
|
||||
ProcessFileHelper(datFile, item, datItem, basePath, string.Empty);
|
||||
|
||||
@@ -311,19 +311,19 @@ namespace SabreTools.DatTools
|
||||
bool isSingleTorrent = tgz.IsTorrent() || txz.IsTorrent();
|
||||
|
||||
// Get the base archive first
|
||||
BaseArchive? archive = BaseArchive.Create(file);
|
||||
BaseArchive? archive = FileTypeTool.CreateArchiveType(file);
|
||||
|
||||
// Now get all extracted items from the archive
|
||||
if (archive != null)
|
||||
{
|
||||
archive.AvailableHashTypes = quickScan ? [HashType.CRC32] : [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
archive.SetHashTypes(quickScan ? [HashType.CRC32] : [HashType.CRC32, HashType.MD5, HashType.SHA1]);
|
||||
entries = archive.GetChildren();
|
||||
}
|
||||
|
||||
// If the entries list is null, we encountered an error or have a file and should scan externally
|
||||
if (entries == null && System.IO.File.Exists(file))
|
||||
{
|
||||
BaseFile? internalFileInfo = BaseFile.GetInfo(file, asFiles: asFiles);
|
||||
BaseFile? internalFileInfo = FileTypeTool.GetInfo(file, asFiles: asFiles);
|
||||
|
||||
// Create the correct DatItem
|
||||
DatItem? internalDatItem;
|
||||
@@ -498,7 +498,7 @@ namespace SabreTools.DatTools
|
||||
if (rule.TransformStream(fileStream, transformStream, keepReadOpen: true, keepWriteOpen: true))
|
||||
{
|
||||
// Get the file informations that we will be using
|
||||
Rom headerless = new(BaseFile.GetInfo(transformStream, keepReadOpen: true));
|
||||
Rom headerless = new(FileTypeTool.GetInfo(transformStream, keepReadOpen: true));
|
||||
|
||||
// If we have duplicates and we're not filtering
|
||||
if (ShouldRebuild(datFile, headerless, transformStream, false, out dupes))
|
||||
@@ -572,7 +572,7 @@ namespace SabreTools.DatTools
|
||||
string? machinename = null;
|
||||
|
||||
// Get the item from the current file
|
||||
Rom item = new(BaseFile.GetInfo(stream, keepReadOpen: true));
|
||||
Rom item = new(FileTypeTool.GetInfo(stream, keepReadOpen: true));
|
||||
item.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.DescriptionKey, Path.GetFileNameWithoutExtension(item.GetName()));
|
||||
item.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.NameKey, Path.GetFileNameWithoutExtension(item.GetName()));
|
||||
|
||||
@@ -629,7 +629,7 @@ namespace SabreTools.DatTools
|
||||
string? machinename = null;
|
||||
|
||||
// Get the item from the current file
|
||||
var item = new Rom(BaseFile.GetInfo(stream, keepReadOpen: true));
|
||||
var item = new Rom(FileTypeTool.GetInfo(stream, keepReadOpen: true));
|
||||
|
||||
// Create a machine for the current item
|
||||
var machine = new Machine();
|
||||
@@ -764,7 +764,7 @@ namespace SabreTools.DatTools
|
||||
// If we have a zipfile, extract the stream to memory
|
||||
if (isZip != null)
|
||||
{
|
||||
BaseArchive? archive = BaseArchive.Create(file);
|
||||
BaseArchive? archive = FileTypeTool.CreateArchiveType(file);
|
||||
if (archive == null)
|
||||
return false;
|
||||
|
||||
@@ -823,7 +823,7 @@ namespace SabreTools.DatTools
|
||||
/// <returns>Folder configured with proper flags</returns>
|
||||
private static Folder? GetPreconfiguredFolder(DatFile datFile, bool date, OutputFormat outputFormat)
|
||||
{
|
||||
Folder? outputArchive = Folder.Create(outputFormat);
|
||||
Folder? outputArchive = FileTypeTool.CreateFolderType(outputFormat);
|
||||
if (outputArchive is BaseArchive baseArchive && date)
|
||||
baseArchive.UseDates = date;
|
||||
|
||||
|
||||
@@ -105,16 +105,16 @@ namespace SabreTools.FileTypes.Archives
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
// Catch this but don't count it as an error because SharpCompress is unsafe
|
||||
logger.Verbose(ex);
|
||||
_logger.Verbose(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Warning(ex);
|
||||
_logger.Warning(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
@@ -224,7 +224,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
BaseFile gzipEntryRom = new();
|
||||
|
||||
// Perform a quickscan, if flagged to
|
||||
if (AvailableHashTypes.Length == 1 && AvailableHashTypes[0] == HashType.CRC32)
|
||||
if (_hashTypes.Length == 1 && _hashTypes[0] == HashType.CRC32)
|
||||
{
|
||||
gzipEntryRom.Filename = gamename;
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
var gz = new gZip();
|
||||
ZipReturn ret = gz.ZipFileOpen(Filename);
|
||||
ret = gz.ZipFileOpenReadStream(0, out Stream? gzstream, out ulong streamSize);
|
||||
gzipEntryRom = GetInfo(gzstream, hashes: AvailableHashTypes);
|
||||
gzipEntryRom = GetInfo(gzstream, hashes: _hashTypes);
|
||||
gzipEntryRom.Filename = gz.GetLocalFile(0).Filename;
|
||||
gzipEntryRom.Parent = gamename;
|
||||
gzipEntryRom.Date = (gz.TimeStamp > 0 ? gz.TimeStamp.ToString() : null);
|
||||
@@ -252,7 +252,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -279,21 +279,21 @@ namespace SabreTools.FileTypes.Archives
|
||||
// If we have the romba depot files, just skip them gracefully
|
||||
if (datum == ".romba_size" || datum == ".romba_size.backup")
|
||||
{
|
||||
logger.Verbose($"Romba depot file found, skipping: {Filename}");
|
||||
_logger.Verbose($"Romba depot file found, skipping: {Filename}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the name is the right length
|
||||
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.gz"))
|
||||
{
|
||||
logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
_logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the file is at least the minimum length
|
||||
if (filesize < 40 /* bytes */)
|
||||
{
|
||||
logger.Warning($"Possibly corrupt file '{Path.GetFullPath(Filename)}' with size {filesize}");
|
||||
_logger.Warning($"Possibly corrupt file '{Path.GetFullPath(Filename)}' with size {filesize}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -340,21 +340,21 @@ namespace SabreTools.FileTypes.Archives
|
||||
// If we have the romba depot files, just skip them gracefully
|
||||
if (datum == ".romba_size" || datum == ".romba_size.backup")
|
||||
{
|
||||
logger.Verbose($"Romba depot file found, skipping: {Filename}");
|
||||
_logger.Verbose($"Romba depot file found, skipping: {Filename}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the name is the right length
|
||||
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.gz"))
|
||||
{
|
||||
logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
_logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the file is at least the minimum length
|
||||
if (filesize < 40 /* bytes */)
|
||||
{
|
||||
logger.Warning($"Possibly corrupt file '{Path.GetFullPath(Filename)}' with size {filesize}");
|
||||
_logger.Warning($"Possibly corrupt file '{Path.GetFullPath(Filename)}' with size {filesize}");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
// Check that the input file exists
|
||||
if (!File.Exists(inputFile))
|
||||
{
|
||||
logger.Warning($"File '{inputFile}' does not exist!");
|
||||
_logger.Warning($"File '{inputFile}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,16 +67,16 @@ namespace SabreTools.FileTypes.Archives
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
// Catch this but don't count it as an error because SharpCompress is unsafe
|
||||
logger.Verbose(ex);
|
||||
_logger.Verbose(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Warning(ex);
|
||||
_logger.Warning(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return (null, null);
|
||||
}
|
||||
#else
|
||||
@@ -201,7 +201,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
BaseFile rarEntryRom = new();
|
||||
|
||||
// Perform a quickscan, if flagged to
|
||||
if (AvailableHashTypes.Length == 1 && AvailableHashTypes[0] == HashType.CRC32)
|
||||
if (_hashTypes.Length == 1 && _hashTypes[0] == HashType.CRC32)
|
||||
{
|
||||
rarEntryRom.Size = entry.Size;
|
||||
rarEntryRom.CRC = BitConverter.GetBytes(entry.Crc);
|
||||
@@ -210,7 +210,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
else
|
||||
{
|
||||
using Stream entryStream = entry.OpenEntryStream();
|
||||
rarEntryRom = GetInfo(entryStream, size: entry.Size, hashes: AvailableHashTypes);
|
||||
rarEntryRom = GetInfo(entryStream, size: entry.Size, hashes: _hashTypes);
|
||||
}
|
||||
|
||||
// Fill in common details and add to the list
|
||||
@@ -225,7 +225,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
}
|
||||
|
||||
return empties;
|
||||
|
||||
@@ -118,16 +118,16 @@ namespace SabreTools.FileTypes.Archives
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
// Catch this but don't count it as an error because SharpCompress is unsafe
|
||||
logger.Verbose(ex);
|
||||
_logger.Verbose(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Warning(ex);
|
||||
_logger.Warning(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
// If we get a read error, log it and continue
|
||||
if (zr != ZipReturn.ZipGood)
|
||||
{
|
||||
logger.Warning($"An error occurred while reading archive {Filename}: Zip Error - {zr}");
|
||||
_logger.Warning($"An error occurred while reading archive {Filename}: Zip Error - {zr}");
|
||||
zr = zf.ZipFileCloseReadStream();
|
||||
continue;
|
||||
}
|
||||
@@ -274,7 +274,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
BaseFile zipEntryRom = new();
|
||||
|
||||
// Perform a quickscan, if flagged to
|
||||
if (AvailableHashTypes.Length == 1 && AvailableHashTypes[0] == HashType.CRC32)
|
||||
if (_hashTypes.Length == 1 && _hashTypes[0] == HashType.CRC32)
|
||||
{
|
||||
zipEntryRom.Size = (long)zf.GetLocalFile(i).UncompressedSize;
|
||||
zipEntryRom.CRC = zf.GetLocalFile(i).CRC;
|
||||
@@ -282,7 +282,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
// Otherwise, use the stream directly
|
||||
else
|
||||
{
|
||||
zipEntryRom = GetInfo(readStream, size: (long)zf.GetLocalFile(i).UncompressedSize, hashes: AvailableHashTypes, keepReadOpen: true);
|
||||
zipEntryRom = GetInfo(readStream, size: (long)zf.GetLocalFile(i).UncompressedSize, hashes: _hashTypes, keepReadOpen: true);
|
||||
}
|
||||
|
||||
// Fill in common details and add to the list
|
||||
@@ -297,7 +297,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
}
|
||||
|
||||
return empties;
|
||||
@@ -560,7 +560,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
success = false;
|
||||
}
|
||||
finally
|
||||
@@ -774,7 +774,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
success = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,16 +68,16 @@ namespace SabreTools.FileTypes.Archives
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
// Catch this but don't count it as an error because SharpCompress is unsafe
|
||||
logger.Verbose(ex);
|
||||
_logger.Verbose(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Warning(ex);
|
||||
_logger.Warning(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return (null, null);
|
||||
}
|
||||
#else
|
||||
@@ -194,7 +194,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
BaseFile tarEntryRom = new();
|
||||
|
||||
// Perform a quickscan, if flagged to
|
||||
if (AvailableHashTypes.Length == 1 && AvailableHashTypes[0] == HashType.CRC32)
|
||||
if (_hashTypes.Length == 1 && _hashTypes[0] == HashType.CRC32)
|
||||
{
|
||||
tarEntryRom.Size = entry.Size;
|
||||
tarEntryRom.CRC = BitConverter.GetBytes(entry.Crc);
|
||||
@@ -203,7 +203,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
else
|
||||
{
|
||||
using Stream entryStream = entry.OpenEntryStream();
|
||||
tarEntryRom = GetInfo(entryStream, size: entry.Size, hashes: AvailableHashTypes);
|
||||
tarEntryRom = GetInfo(entryStream, size: entry.Size, hashes: _hashTypes);
|
||||
}
|
||||
|
||||
// Fill in common details and add to the list
|
||||
@@ -218,7 +218,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
}
|
||||
|
||||
return empties;
|
||||
@@ -409,7 +409,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
success = false;
|
||||
}
|
||||
finally
|
||||
@@ -578,7 +578,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
success = false;
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -80,16 +80,16 @@ namespace SabreTools.FileTypes.Archives
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
// Catch this but don't count it as an error because SharpCompress is unsafe
|
||||
logger.Verbose(ex);
|
||||
_logger.Verbose(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Warning(ex);
|
||||
_logger.Warning(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return (null, null);
|
||||
}
|
||||
#else
|
||||
@@ -200,7 +200,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
BaseFile xzEntryRom = new();
|
||||
|
||||
// Perform a quickscan, if flagged to
|
||||
if (AvailableHashTypes.Length == 1 && AvailableHashTypes[0] == HashType.CRC32)
|
||||
if (_hashTypes.Length == 1 && _hashTypes[0] == HashType.CRC32)
|
||||
{
|
||||
xzEntryRom.Filename = gamename;
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
else
|
||||
{
|
||||
var xzStream = new XZStream(File.OpenRead(Filename!));
|
||||
xzEntryRom = GetInfo(xzStream, hashes: AvailableHashTypes);
|
||||
xzEntryRom = GetInfo(xzStream, hashes: _hashTypes);
|
||||
xzEntryRom.Filename = gamename;
|
||||
xzStream.Dispose();
|
||||
}
|
||||
@@ -224,7 +224,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
// Check if the name is the right length
|
||||
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz"))
|
||||
{
|
||||
logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
_logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
// Check if the name is the right length
|
||||
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz"))
|
||||
{
|
||||
logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
_logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(Filename)}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
// Check that the input file exists
|
||||
if (!File.Exists(inputFile))
|
||||
{
|
||||
logger.Warning($"File '{inputFile}' does not exist!");
|
||||
_logger.Warning($"File '{inputFile}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,16 +157,16 @@ namespace SabreTools.FileTypes.Archives
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
// Catch this but don't count it as an error because SharpCompress is unsafe
|
||||
logger.Verbose(ex);
|
||||
_logger.Verbose(ex);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Warning(ex);
|
||||
_logger.Warning(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
encounteredErrors = true;
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
@@ -345,7 +345,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
// If we get a read error, log it and continue
|
||||
if (zr != ZipReturn.ZipGood || readStream == null)
|
||||
{
|
||||
logger.Warning($"An error occurred while reading archive {Filename}: Zip Error - {zr}");
|
||||
_logger.Warning($"An error occurred while reading archive {Filename}: Zip Error - {zr}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
var zipEntryRom = new BaseFile();
|
||||
|
||||
// Perform a quickscan, if flagged to
|
||||
if (AvailableHashTypes.Length == 1 && AvailableHashTypes[0] == HashType.CRC32)
|
||||
if (_hashTypes.Length == 1 && _hashTypes[0] == HashType.CRC32)
|
||||
{
|
||||
zipEntryRom.Size = (long)localFile.UncompressedSize;
|
||||
zipEntryRom.CRC = localFile.CRC;
|
||||
@@ -363,7 +363,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
{
|
||||
zipEntryRom = GetInfo(readStream,
|
||||
size: (long)localFile.UncompressedSize,
|
||||
hashes: AvailableHashTypes,
|
||||
hashes: _hashTypes,
|
||||
keepReadOpen: true);
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
var zipEntryRom = new BaseFile();
|
||||
|
||||
// Perform a quickscan, if flagged to
|
||||
if (AvailableHashTypes.Length == 1 && AvailableHashTypes[0] == HashType.CRC32)
|
||||
if (_hashTypes.Length == 1 && _hashTypes[0] == HashType.CRC32)
|
||||
{
|
||||
zipEntryRom.Size = localFile.Length;
|
||||
#if NETCOREAPP
|
||||
@@ -420,7 +420,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
{
|
||||
zipEntryRom = GetInfo(readStream,
|
||||
size: localFile.Length,
|
||||
hashes: AvailableHashTypes,
|
||||
hashes: _hashTypes,
|
||||
keepReadOpen: false);
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
}
|
||||
|
||||
return empties;
|
||||
@@ -734,7 +734,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
success = false;
|
||||
}
|
||||
finally
|
||||
@@ -949,7 +949,7 @@ namespace SabreTools.FileTypes.Archives
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
success = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.FileTypes.Archives;
|
||||
|
||||
namespace SabreTools.FileTypes
|
||||
{
|
||||
@@ -29,84 +28,13 @@ namespace SabreTools.FileTypes
|
||||
/// <summary>
|
||||
/// Create a new Archive with no base file
|
||||
/// </summary>
|
||||
public BaseArchive()
|
||||
{
|
||||
}
|
||||
public BaseArchive() : base() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new BaseArchive from the given file
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to use</param>
|
||||
public BaseArchive(string filename)
|
||||
: base(filename)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an archive object from a filename, if possible
|
||||
/// </summary>
|
||||
/// <param name="input">Name of the file to create the archive from</param>
|
||||
/// <returns>Archive object representing the inputs</returns>
|
||||
public static BaseArchive? Create(string input)
|
||||
{
|
||||
BaseArchive? archive = null;
|
||||
|
||||
// First get the archive type
|
||||
FileType? at = GetFileType(input);
|
||||
|
||||
// If we got back null, then it's not an archive, so we we return
|
||||
if (at == null)
|
||||
return archive;
|
||||
|
||||
// Create the archive based on the type
|
||||
staticLogger.Verbose($"Found archive of type: {at}");
|
||||
switch (at)
|
||||
{
|
||||
case FileType.GZipArchive:
|
||||
archive = new GZipArchive(input);
|
||||
break;
|
||||
|
||||
case FileType.RarArchive:
|
||||
archive = new RarArchive(input);
|
||||
break;
|
||||
|
||||
case FileType.SevenZipArchive:
|
||||
archive = new SevenZipArchive(input);
|
||||
break;
|
||||
|
||||
case FileType.TapeArchive:
|
||||
archive = new TapeArchive(input);
|
||||
break;
|
||||
|
||||
case FileType.ZipArchive:
|
||||
archive = new ZipArchive(input);
|
||||
break;
|
||||
|
||||
default:
|
||||
// We ignore all other types for now
|
||||
break;
|
||||
}
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an archive object of the specified type, if possible
|
||||
/// </summary>
|
||||
/// <param name="archiveType">SharpCompress.Common.ArchiveType representing the archive to create</param>
|
||||
/// <returns>Archive object representing the inputs</returns>
|
||||
public static BaseArchive? Create(FileType archiveType)
|
||||
{
|
||||
return archiveType switch
|
||||
{
|
||||
FileType.GZipArchive => new GZipArchive(),
|
||||
FileType.RarArchive => new RarArchive(),
|
||||
FileType.SevenZipArchive => new SevenZipArchive(),
|
||||
FileType.TapeArchive => new TapeArchive(),
|
||||
FileType.ZipArchive => new ZipArchive(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
public BaseArchive(string filename) : base(filename) { }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,29 +1,7 @@
|
||||
using System.IO;
|
||||
using SabreTools.FileTypes.Aaru;
|
||||
using SabreTools.FileTypes.CHD;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Skippers;
|
||||
|
||||
namespace SabreTools.FileTypes
|
||||
namespace SabreTools.FileTypes
|
||||
{
|
||||
public class BaseFile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
protected static readonly byte[] SevenZipSignature = [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c];
|
||||
protected static readonly byte[] AaruFormatSignature = [0x41, 0x41, 0x52, 0x55, 0x46, 0x52, 0x4d, 0x54];
|
||||
protected static readonly byte[] CHDSignature = [0x4d, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x48, 0x44];
|
||||
protected static readonly byte[] GzSignature = [0x1f, 0x8b, 0x08];
|
||||
protected static readonly byte[] RarSignature = [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00];
|
||||
protected static readonly byte[] RarFiveSignature = [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00];
|
||||
protected static readonly byte[] TarSignature = [0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00];
|
||||
protected static readonly byte[] TarZeroSignature = [0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30];
|
||||
protected static readonly byte[] XZSignature = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
@@ -46,56 +24,47 @@ namespace SabreTools.FileTypes
|
||||
/// </summary>
|
||||
public long? Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hashes that are available for the file
|
||||
/// </summary>
|
||||
public HashType[] AvailableHashTypes { get; set; } = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
|
||||
/// <summary>
|
||||
/// CRC32 hash of the file
|
||||
/// </summary>
|
||||
public byte[]? CRC { get; set; } = null;
|
||||
public byte[]? CRC { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MD5 hash of the file
|
||||
/// </summary>
|
||||
public byte[]? MD5 { get; set; } = null;
|
||||
public byte[]? MD5 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA-1 hash of the file
|
||||
/// </summary>
|
||||
public byte[]? SHA1 { get; set; } = null;
|
||||
public byte[]? SHA1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 hash of the file
|
||||
/// </summary>
|
||||
public byte[]? SHA256 { get; set; } = null;
|
||||
public byte[]? SHA256 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA-384 hash of the file
|
||||
/// </summary>
|
||||
public byte[]? SHA384 { get; set; } = null;
|
||||
public byte[]? SHA384 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA-512 hash of the file
|
||||
/// </summary>
|
||||
public byte[]? SHA512 { get; set; } = null;
|
||||
public byte[]? SHA512 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SpamSum fuzzy hash of the file
|
||||
/// </summary>
|
||||
public byte[]? SpamSum { get; set; } = null;
|
||||
public byte[]? SpamSum { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construtors
|
||||
|
||||
/// <summary>
|
||||
/// Create a new BaseFile with no base file
|
||||
/// </summary>
|
||||
public BaseFile()
|
||||
{
|
||||
}
|
||||
public BaseFile() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new BaseFile from the given file
|
||||
@@ -105,235 +74,5 @@ namespace SabreTools.FileTypes
|
||||
{
|
||||
Filename = filename;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns the file type of an input file
|
||||
/// </summary>
|
||||
/// <param name="input">Input file to check</param>
|
||||
/// <returns>FileType of inputted file (null on error)</returns>
|
||||
public static FileType? GetFileType(string input)
|
||||
{
|
||||
FileType? outFileType = null;
|
||||
|
||||
// If the file is null, then we have no archive type
|
||||
if (input == null)
|
||||
return outFileType;
|
||||
|
||||
// First line of defense is going to be the extension, for better or worse
|
||||
if (!HasValidArchiveExtension(input))
|
||||
return outFileType;
|
||||
|
||||
// Read the first bytes of the file and get the magic number
|
||||
BinaryReader br = new(File.OpenRead(input));
|
||||
byte[] magic = br.ReadBytes(8);
|
||||
#if NET40_OR_GREATER
|
||||
br.Dispose();
|
||||
#endif
|
||||
|
||||
// Now try to match it to a known signature
|
||||
if (magic.StartsWith(SevenZipSignature))
|
||||
{
|
||||
outFileType = FileType.SevenZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(AaruFormatSignature))
|
||||
{
|
||||
outFileType = FileType.AaruFormat;
|
||||
}
|
||||
else if (magic.StartsWith(CHDSignature))
|
||||
{
|
||||
outFileType = FileType.CHD;
|
||||
}
|
||||
else if (magic.StartsWith(GzSignature))
|
||||
{
|
||||
outFileType = FileType.GZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(RarSignature)
|
||||
|| magic.StartsWith(RarFiveSignature))
|
||||
{
|
||||
outFileType = FileType.RarArchive;
|
||||
}
|
||||
else if (magic.StartsWith(TarSignature)
|
||||
|| magic.StartsWith(TarZeroSignature))
|
||||
{
|
||||
outFileType = FileType.TapeArchive;
|
||||
}
|
||||
else if (magic.StartsWith(XZSignature))
|
||||
{
|
||||
outFileType = FileType.XZArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Models.PKZIP.Constants.LocalFileHeaderSignatureBytes)
|
||||
|| magic.StartsWith(Models.PKZIP.Constants.EndOfCentralDirectoryRecordSignatureBytes)
|
||||
|| magic.StartsWith(Models.PKZIP.Constants.DataDescriptorSignatureBytes))
|
||||
{
|
||||
outFileType = FileType.ZipArchive;
|
||||
}
|
||||
|
||||
return outFileType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single file
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <param name="header">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
|
||||
/// <param name="hashes">Hashes to include in the information</param>
|
||||
/// <param name="asFiles">TreatAsFiles representing special format scanning</param>
|
||||
/// <returns>Populated BaseFile object if success, empty one on error</returns>
|
||||
public static BaseFile? GetInfo(string input, string? header = null, HashType[]? hashes = null, TreatAsFile asFiles = 0x00)
|
||||
{
|
||||
// Add safeguard if file doesn't exist
|
||||
if (!File.Exists(input))
|
||||
return null;
|
||||
|
||||
// If no hashes are set, use the standard array
|
||||
hashes ??= [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
|
||||
// Get input information
|
||||
var fileType = GetFileType(input);
|
||||
Stream inputStream = File.OpenRead(input);
|
||||
|
||||
// Try to match the supplied header skipper
|
||||
if (header != null)
|
||||
{
|
||||
SkipperMatch.Init();
|
||||
var rule = SkipperMatch.GetMatchingRule(input, Path.GetFileNameWithoutExtension(header));
|
||||
|
||||
// If there's a match, transform the stream before getting info
|
||||
if (rule.Tests != null && rule.Tests.Length != 0)
|
||||
{
|
||||
// Create the output stream
|
||||
MemoryStream outputStream = new();
|
||||
|
||||
// Transform the stream and get the information from it
|
||||
rule.TransformStream(inputStream, outputStream, keepReadOpen: false, keepWriteOpen: true);
|
||||
inputStream = outputStream;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the info in the proper manner
|
||||
BaseFile? baseFile;
|
||||
#if NET20 || NET35
|
||||
if (fileType == FileType.AaruFormat && (asFiles & TreatAsFile.AaruFormat) == 0)
|
||||
baseFile = AaruFormat.Create(inputStream);
|
||||
else if (fileType == FileType.CHD && (asFiles & TreatAsFile.CHD) == 0)
|
||||
baseFile = CHDFile.Create(inputStream);
|
||||
#else
|
||||
if (fileType == FileType.AaruFormat && !asFiles.HasFlag(TreatAsFile.AaruFormat))
|
||||
baseFile = AaruFormat.Create(inputStream);
|
||||
else if (fileType == FileType.CHD && !asFiles.HasFlag(TreatAsFile.CHD))
|
||||
baseFile = CHDFile.Create(inputStream);
|
||||
#endif
|
||||
else
|
||||
baseFile = GetInfo(inputStream, hashes: hashes, keepReadOpen: false);
|
||||
|
||||
// Dispose of the input stream
|
||||
inputStream?.Dispose();
|
||||
|
||||
// Add unique data from the file
|
||||
baseFile!.Filename = Path.GetFileName(input);
|
||||
baseFile.Date = new FileInfo(input).LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss");
|
||||
|
||||
return baseFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single file
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <param name="size">Size of the input stream</param>
|
||||
/// <param name="hashes">Hashes to include in the information</param>
|
||||
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
||||
/// <returns>Populated BaseFile object if success, empty one on error</returns>
|
||||
public static BaseFile GetInfo(Stream? input, long size = -1, HashType[]? hashes = null, bool keepReadOpen = false)
|
||||
{
|
||||
// If we have no stream
|
||||
if (input == null)
|
||||
return new BaseFile();
|
||||
|
||||
// If no hashes are set, use the standard array
|
||||
hashes ??= [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
|
||||
// If we want to automatically set the size
|
||||
if (size == -1)
|
||||
size = input.Length;
|
||||
|
||||
// Run the hashing on the input stream
|
||||
var hashDict = HashTool.GetStreamHashes(input, hashes);
|
||||
if (hashDict == null)
|
||||
return new BaseFile();
|
||||
|
||||
// Create a base file with the resulting hashes
|
||||
var baseFile = new BaseFile()
|
||||
{
|
||||
Size = size,
|
||||
CRC = hashDict.ContainsKey(HashType.CRC32) ? hashDict[HashType.CRC32].FromHexString() : null,
|
||||
MD5 = hashDict.ContainsKey(HashType.MD5) ? hashDict[HashType.MD5].FromHexString() : null,
|
||||
SHA1 = hashDict.ContainsKey(HashType.SHA1) ? hashDict[HashType.SHA1].FromHexString() : null,
|
||||
SHA256 = hashDict.ContainsKey(HashType.SHA256) ? hashDict[HashType.SHA256].FromHexString() : null,
|
||||
SHA384 = hashDict.ContainsKey(HashType.SHA384) ? hashDict[HashType.SHA384].FromHexString() : null,
|
||||
SHA512 = hashDict.ContainsKey(HashType.SHA512) ? hashDict[HashType.SHA512].FromHexString() : null,
|
||||
SpamSum = hashDict.ContainsKey(HashType.SpamSum) ? hashDict[HashType.SpamSum].FromHexString() : null,
|
||||
};
|
||||
|
||||
// Deal with the input stream
|
||||
if (!keepReadOpen)
|
||||
{
|
||||
input.Close();
|
||||
input.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
input.SeekIfPossible();
|
||||
}
|
||||
|
||||
return baseFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get if the given path has a valid DAT extension
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check</param>
|
||||
/// <returns>True if the extension is valid, false otherwise</returns>
|
||||
private static bool HasValidArchiveExtension(string path)
|
||||
{
|
||||
// Get the extension from the path, if possible
|
||||
string? ext = path.GetNormalizedExtension();
|
||||
|
||||
// Check against the list of known archive extensions
|
||||
return ext switch
|
||||
{
|
||||
// Aaruformat
|
||||
"aaru" => true,
|
||||
"aaruf" => true,
|
||||
"aaruformat" => true,
|
||||
"aif" => true,
|
||||
"dicf" => true,
|
||||
|
||||
// Archive
|
||||
"7z" => true,
|
||||
"gz" => true,
|
||||
"lzma" => true,
|
||||
"rar" => true,
|
||||
"rev" => true,
|
||||
"r00" => true,
|
||||
"r01" => true,
|
||||
"tar" => true,
|
||||
"tgz" => true,
|
||||
"tlz" => true,
|
||||
"zip" => true,
|
||||
"zipx" => true,
|
||||
|
||||
// CHD
|
||||
"chd" => true,
|
||||
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
303
SabreTools.FileTypes/FileTypeTool.cs
Normal file
303
SabreTools.FileTypes/FileTypeTool.cs
Normal file
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.FileTypes.Aaru;
|
||||
using SabreTools.FileTypes.Archives;
|
||||
using SabreTools.FileTypes.CHD;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Skippers;
|
||||
|
||||
namespace SabreTools.FileTypes
|
||||
{
|
||||
public static class FileTypeTool
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static readonly byte[] SevenZipSignature = [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c];
|
||||
private static readonly byte[] AaruFormatSignature = [0x41, 0x41, 0x52, 0x55, 0x46, 0x52, 0x4d, 0x54];
|
||||
private static readonly byte[] CHDSignature = [0x4d, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x48, 0x44];
|
||||
private static readonly byte[] GzSignature = [0x1f, 0x8b, 0x08];
|
||||
private static readonly byte[] RarSignature = [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00];
|
||||
private static readonly byte[] RarFiveSignature = [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00];
|
||||
private static readonly byte[] TarSignature = [0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00];
|
||||
private static readonly byte[] TarZeroSignature = [0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30];
|
||||
private static readonly byte[] XZSignature = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00];
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Info
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single file
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <param name="header">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
|
||||
/// <param name="hashes">Hashes to include in the information</param>
|
||||
/// <param name="asFiles">TreatAsFiles representing special format scanning</param>
|
||||
/// <returns>Populated BaseFile object if success, empty one on error</returns>
|
||||
public static BaseFile? GetInfo(string input, string? header = null, HashType[]? hashes = null, TreatAsFile asFiles = 0x00)
|
||||
{
|
||||
// Add safeguard if file doesn't exist
|
||||
if (!File.Exists(input))
|
||||
return null;
|
||||
|
||||
// If no hashes are set, use the standard array
|
||||
hashes ??= [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
|
||||
// Get input information
|
||||
var fileType = GetFileType(input);
|
||||
Stream inputStream = File.OpenRead(input);
|
||||
|
||||
// Try to match the supplied header skipper
|
||||
if (header != null)
|
||||
{
|
||||
SkipperMatch.Init();
|
||||
var rule = SkipperMatch.GetMatchingRule(input, Path.GetFileNameWithoutExtension(header));
|
||||
|
||||
// If there's a match, transform the stream before getting info
|
||||
if (rule.Tests != null && rule.Tests.Length != 0)
|
||||
{
|
||||
// Create the output stream
|
||||
MemoryStream outputStream = new();
|
||||
|
||||
// Transform the stream and get the information from it
|
||||
rule.TransformStream(inputStream, outputStream, keepReadOpen: false, keepWriteOpen: true);
|
||||
inputStream = outputStream;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the info in the proper manner
|
||||
BaseFile? baseFile;
|
||||
#if NET20 || NET35
|
||||
if (fileType == FileType.AaruFormat && (asFiles & TreatAsFile.AaruFormat) == 0)
|
||||
baseFile = AaruFormat.Create(inputStream);
|
||||
else if (fileType == FileType.CHD && (asFiles & TreatAsFile.CHD) == 0)
|
||||
baseFile = CHDFile.Create(inputStream);
|
||||
#else
|
||||
if (fileType == FileType.AaruFormat && !asFiles.HasFlag(TreatAsFile.AaruFormat))
|
||||
baseFile = AaruFormat.Create(inputStream);
|
||||
else if (fileType == FileType.CHD && !asFiles.HasFlag(TreatAsFile.CHD))
|
||||
baseFile = CHDFile.Create(inputStream);
|
||||
#endif
|
||||
else
|
||||
baseFile = GetInfo(inputStream, hashes: hashes, keepReadOpen: false);
|
||||
|
||||
// Dispose of the input stream
|
||||
inputStream?.Dispose();
|
||||
|
||||
// Add unique data from the file
|
||||
baseFile!.Filename = Path.GetFileName(input);
|
||||
baseFile.Date = new FileInfo(input).LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss");
|
||||
|
||||
return baseFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single file
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <param name="size">Size of the input stream</param>
|
||||
/// <param name="hashes">Hashes to include in the information</param>
|
||||
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
||||
/// <returns>Populated BaseFile object if success, empty one on error</returns>
|
||||
public static BaseFile GetInfo(Stream? input, long size = -1, HashType[]? hashes = null, bool keepReadOpen = false)
|
||||
{
|
||||
// If we have no stream
|
||||
if (input == null)
|
||||
return new BaseFile();
|
||||
|
||||
// If no hashes are set, use the standard array
|
||||
hashes ??= [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
|
||||
// If we want to automatically set the size
|
||||
if (size == -1)
|
||||
size = input.Length;
|
||||
|
||||
// Run the hashing on the input stream
|
||||
var hashDict = HashTool.GetStreamHashes(input, hashes);
|
||||
if (hashDict == null)
|
||||
return new BaseFile();
|
||||
|
||||
// Create a base file with the resulting hashes
|
||||
var baseFile = new BaseFile()
|
||||
{
|
||||
Size = size,
|
||||
CRC = hashDict.ContainsKey(HashType.CRC32) ? hashDict[HashType.CRC32].FromHexString() : null,
|
||||
MD5 = hashDict.ContainsKey(HashType.MD5) ? hashDict[HashType.MD5].FromHexString() : null,
|
||||
SHA1 = hashDict.ContainsKey(HashType.SHA1) ? hashDict[HashType.SHA1].FromHexString() : null,
|
||||
SHA256 = hashDict.ContainsKey(HashType.SHA256) ? hashDict[HashType.SHA256].FromHexString() : null,
|
||||
SHA384 = hashDict.ContainsKey(HashType.SHA384) ? hashDict[HashType.SHA384].FromHexString() : null,
|
||||
SHA512 = hashDict.ContainsKey(HashType.SHA512) ? hashDict[HashType.SHA512].FromHexString() : null,
|
||||
SpamSum = hashDict.ContainsKey(HashType.SpamSum) ? hashDict[HashType.SpamSum].FromHexString() : null,
|
||||
};
|
||||
|
||||
// Deal with the input stream
|
||||
if (!keepReadOpen)
|
||||
{
|
||||
input.Close();
|
||||
input.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
input.SeekIfPossible();
|
||||
}
|
||||
|
||||
return baseFile;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Type
|
||||
|
||||
/// <summary>
|
||||
/// Create an archive object from a filename, if possible
|
||||
/// </summary>
|
||||
/// <param name="input">Name of the file to create the archive from</param>
|
||||
/// <returns>Archive object representing the inputs</returns>
|
||||
public static BaseArchive? CreateArchiveType(string input)
|
||||
{
|
||||
FileType? fileType = GetFileType(input);
|
||||
return fileType switch
|
||||
{
|
||||
FileType.GZipArchive => new GZipArchive(input),
|
||||
FileType.RarArchive => new RarArchive(input),
|
||||
FileType.SevenZipArchive => new SevenZipArchive(input),
|
||||
FileType.TapeArchive => new TapeArchive(input),
|
||||
FileType.ZipArchive => new ZipArchive(input),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an Folder object of the specified type, if possible
|
||||
/// </summary>
|
||||
/// <param name="outputFormat">OutputFormat representing the archive to create</param>
|
||||
/// <returns>Archive object representing the inputs</returns>
|
||||
public static Folder? CreateFolderType(OutputFormat outputFormat)
|
||||
{
|
||||
return outputFormat switch
|
||||
{
|
||||
OutputFormat.Folder => new Folder(false),
|
||||
OutputFormat.ParentFolder => new Folder(true),
|
||||
OutputFormat.TapeArchive => new TapeArchive(),
|
||||
OutputFormat.Torrent7Zip => new SevenZipArchive(),
|
||||
OutputFormat.TorrentGzip => new GZipArchive(),
|
||||
OutputFormat.TorrentGzipRomba => new GZipArchive(),
|
||||
OutputFormat.TorrentRar => new RarArchive(),
|
||||
OutputFormat.TorrentXZ => new XZArchive(),
|
||||
OutputFormat.TorrentXZRomba => new XZArchive(),
|
||||
OutputFormat.TorrentZip => new ZipArchive(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the file type of an input file
|
||||
/// </summary>
|
||||
/// <param name="input">Input file to check</param>
|
||||
/// <returns>FileType of inputted file (null on error)</returns>
|
||||
public static FileType? GetFileType(string input)
|
||||
{
|
||||
FileType? outFileType = null;
|
||||
|
||||
// If the file is null, then we have no archive type
|
||||
if (input == null)
|
||||
return outFileType;
|
||||
|
||||
// First line of defense is going to be the extension, for better or worse
|
||||
if (!HasValidArchiveExtension(input))
|
||||
return outFileType;
|
||||
|
||||
// Read the first bytes of the file and get the magic number
|
||||
BinaryReader br = new(File.OpenRead(input));
|
||||
byte[] magic = br.ReadBytes(8);
|
||||
#if NET40_OR_GREATER
|
||||
br.Dispose();
|
||||
#endif
|
||||
|
||||
// Now try to match it to a known signature
|
||||
if (magic.StartsWith(SevenZipSignature))
|
||||
{
|
||||
outFileType = FileType.SevenZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(AaruFormatSignature))
|
||||
{
|
||||
outFileType = FileType.AaruFormat;
|
||||
}
|
||||
else if (magic.StartsWith(CHDSignature))
|
||||
{
|
||||
outFileType = FileType.CHD;
|
||||
}
|
||||
else if (magic.StartsWith(GzSignature))
|
||||
{
|
||||
outFileType = FileType.GZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(RarSignature)
|
||||
|| magic.StartsWith(RarFiveSignature))
|
||||
{
|
||||
outFileType = FileType.RarArchive;
|
||||
}
|
||||
else if (magic.StartsWith(TarSignature)
|
||||
|| magic.StartsWith(TarZeroSignature))
|
||||
{
|
||||
outFileType = FileType.TapeArchive;
|
||||
}
|
||||
else if (magic.StartsWith(XZSignature))
|
||||
{
|
||||
outFileType = FileType.XZArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Models.PKZIP.Constants.LocalFileHeaderSignatureBytes)
|
||||
|| magic.StartsWith(Models.PKZIP.Constants.EndOfCentralDirectoryRecordSignatureBytes)
|
||||
|| magic.StartsWith(Models.PKZIP.Constants.DataDescriptorSignatureBytes))
|
||||
{
|
||||
outFileType = FileType.ZipArchive;
|
||||
}
|
||||
|
||||
return outFileType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get if the given path has a valid DAT extension
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check</param>
|
||||
/// <returns>True if the extension is valid, false otherwise</returns>
|
||||
private static bool HasValidArchiveExtension(string path)
|
||||
{
|
||||
// Get the extension from the path, if possible
|
||||
string? ext = path.GetNormalizedExtension();
|
||||
|
||||
// Check against the list of known archive extensions
|
||||
return ext switch
|
||||
{
|
||||
// Aaruformat
|
||||
"aaru" => true,
|
||||
"aaruf" => true,
|
||||
"aaruformat" => true,
|
||||
"aif" => true,
|
||||
"dicf" => true,
|
||||
|
||||
// Archive
|
||||
"7z" => true,
|
||||
"gz" => true,
|
||||
"lzma" => true,
|
||||
"rar" => true,
|
||||
"rev" => true,
|
||||
"r00" => true,
|
||||
"r01" => true,
|
||||
"tar" => true,
|
||||
"tgz" => true,
|
||||
"tlz" => true,
|
||||
"zip" => true,
|
||||
"zipx" => true,
|
||||
|
||||
// CHD
|
||||
"chd" => true,
|
||||
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Core.Tools;
|
||||
using SabreTools.FileTypes.Archives;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.IO.Logging;
|
||||
@@ -16,17 +16,25 @@ namespace SabreTools.FileTypes
|
||||
{
|
||||
#region Protected instance variables
|
||||
|
||||
/// <summary>
|
||||
/// Hashes that are available for children
|
||||
/// </summary>
|
||||
protected HashType[] _hashTypes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
|
||||
/// <summary>
|
||||
/// Set of children file objects
|
||||
/// </summary>
|
||||
protected List<BaseFile>? _children;
|
||||
|
||||
/// <summary>
|
||||
/// Logging object
|
||||
/// </summary>
|
||||
protected Logger logger;
|
||||
protected Logger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Static logger for static methods
|
||||
/// </summary>
|
||||
protected static Logger staticLogger = new();
|
||||
protected static Logger _staticLogger = new();
|
||||
|
||||
/// <summary>
|
||||
/// Flag specific to Folder to omit Machine name from output path
|
||||
@@ -41,11 +49,10 @@ namespace SabreTools.FileTypes
|
||||
/// Create a new Folder with no base file
|
||||
/// </summary>
|
||||
/// <param name="writeToParent">True to write directly to parent, false otherwise</param>
|
||||
public Folder(bool writeToParent = false)
|
||||
: base()
|
||||
public Folder(bool writeToParent = false) : base()
|
||||
{
|
||||
_writeToParent = writeToParent;
|
||||
logger = new Logger(this);
|
||||
_logger = new Logger(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,34 +60,10 @@ namespace SabreTools.FileTypes
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the folder to use</param>
|
||||
/// <param name="writeToParent">True to write directly to parent, false otherwise</param>
|
||||
public Folder(string filename, bool writeToParent = false)
|
||||
: base(filename)
|
||||
public Folder(string filename, bool writeToParent = false) : base(filename)
|
||||
{
|
||||
_writeToParent = writeToParent;
|
||||
logger = new Logger(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an folder object of the specified type, if possible
|
||||
/// </summary>
|
||||
/// <param name="outputFormat">OutputFormat representing the archive to create</param>
|
||||
/// <returns>Archive object representing the inputs</returns>
|
||||
public static Folder? Create(OutputFormat outputFormat)
|
||||
{
|
||||
return outputFormat switch
|
||||
{
|
||||
OutputFormat.Folder => new Folder(false),
|
||||
OutputFormat.ParentFolder => new Folder(true),
|
||||
OutputFormat.TapeArchive => new TapeArchive(),
|
||||
OutputFormat.Torrent7Zip => new SevenZipArchive(),
|
||||
OutputFormat.TorrentGzip => new GZipArchive(),
|
||||
OutputFormat.TorrentGzipRomba => new GZipArchive(),
|
||||
OutputFormat.TorrentRar => new RarArchive(),
|
||||
OutputFormat.TorrentXZ => new XZArchive(),
|
||||
OutputFormat.TorrentXZRomba => new XZArchive(),
|
||||
OutputFormat.TorrentZip => new ZipArchive(),
|
||||
_ => null,
|
||||
};
|
||||
_logger = new Logger(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -109,7 +92,7 @@ namespace SabreTools.FileTypes
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -191,7 +174,7 @@ namespace SabreTools.FileTypes
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return realentry;
|
||||
}
|
||||
|
||||
@@ -232,7 +215,7 @@ namespace SabreTools.FileTypes
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
@@ -241,6 +224,18 @@ namespace SabreTools.FileTypes
|
||||
|
||||
#region Information
|
||||
|
||||
/// <summary>
|
||||
/// Set the hash type that can be included in children
|
||||
/// </summary>
|
||||
public void SetHashType(HashType hashType)
|
||||
=> SetHashTypes([hashType]);
|
||||
|
||||
/// <summary>
|
||||
/// Set the hash types that can be included in children
|
||||
/// </summary>
|
||||
public void SetHashTypes(HashType[] hashTypes)
|
||||
=> _hashTypes = hashTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of immediate children from the current folder
|
||||
/// </summary>
|
||||
@@ -260,7 +255,7 @@ namespace SabreTools.FileTypes
|
||||
foreach (string file in Directory.EnumerateFiles(Filename, "*", SearchOption.TopDirectoryOnly))
|
||||
#endif
|
||||
{
|
||||
BaseFile? nf = GetInfo(file, hashes: AvailableHashTypes);
|
||||
BaseFile? nf = FileTypeTool.GetInfo(file, hashes: _hashTypes);
|
||||
if (nf != null)
|
||||
_children.Add(nf);
|
||||
}
|
||||
@@ -384,7 +379,7 @@ namespace SabreTools.FileTypes
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
|
||||
Reference in New Issue
Block a user