Create FileTypeTool and move static methods

This commit is contained in:
Matt Nadareski
2025-01-04 21:17:02 -05:00
parent c449f34410
commit 0cab005a78
12 changed files with 423 additions and 458 deletions

View File

@@ -147,13 +147,13 @@ namespace SabreTools.DatTools
return; return;
// Initialize possible archive variables // Initialize possible archive variables
BaseArchive? archive = BaseArchive.Create(item); BaseArchive? archive = FileTypeTool.CreateArchiveType(item);
// Process archives according to flags // Process archives according to flags
if (archive != null) if (archive != null)
{ {
// Set the archive flags // Set the archive flags
archive.AvailableHashTypes = _hashes; archive.SetHashTypes(_hashes);
// Skip if we're treating archives as files and skipping files // Skip if we're treating archives as files and skipping files
#if NET20 || NET35 #if NET20 || NET35
@@ -406,7 +406,7 @@ namespace SabreTools.DatTools
{ {
logger.Verbose($"'{Path.GetFileName(item)}' treated like a file"); logger.Verbose($"'{Path.GetFileName(item)}' treated like a file");
var header = datFile.Header.GetStringFieldValue(Models.Metadata.Header.HeaderKey); 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); DatItem? datItem = DatItem.Create(baseFile);
if (datItem != null) if (datItem != null)
ProcessFileHelper(datFile, item, datItem, basePath, string.Empty); ProcessFileHelper(datFile, item, datItem, basePath, string.Empty);

View File

@@ -311,19 +311,19 @@ namespace SabreTools.DatTools
bool isSingleTorrent = tgz.IsTorrent() || txz.IsTorrent(); bool isSingleTorrent = tgz.IsTorrent() || txz.IsTorrent();
// Get the base archive first // Get the base archive first
BaseArchive? archive = BaseArchive.Create(file); BaseArchive? archive = FileTypeTool.CreateArchiveType(file);
// Now get all extracted items from the archive // Now get all extracted items from the archive
if (archive != null) 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(); entries = archive.GetChildren();
} }
// If the entries list is null, we encountered an error or have a file and should scan externally // 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)) 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 // Create the correct DatItem
DatItem? internalDatItem; DatItem? internalDatItem;
@@ -498,7 +498,7 @@ namespace SabreTools.DatTools
if (rule.TransformStream(fileStream, transformStream, keepReadOpen: true, keepWriteOpen: true)) if (rule.TransformStream(fileStream, transformStream, keepReadOpen: true, keepWriteOpen: true))
{ {
// Get the file informations that we will be using // 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 we have duplicates and we're not filtering
if (ShouldRebuild(datFile, headerless, transformStream, false, out dupes)) if (ShouldRebuild(datFile, headerless, transformStream, false, out dupes))
@@ -572,7 +572,7 @@ namespace SabreTools.DatTools
string? machinename = null; string? machinename = null;
// Get the item from the current file // 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.DescriptionKey, Path.GetFileNameWithoutExtension(item.GetName()));
item.GetFieldValue<Machine>(DatItem.MachineKey)!.SetFieldValue<string?>(Models.Metadata.Machine.NameKey, 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; string? machinename = null;
// Get the item from the current file // 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 // Create a machine for the current item
var machine = new Machine(); var machine = new Machine();
@@ -764,7 +764,7 @@ namespace SabreTools.DatTools
// If we have a zipfile, extract the stream to memory // If we have a zipfile, extract the stream to memory
if (isZip != null) if (isZip != null)
{ {
BaseArchive? archive = BaseArchive.Create(file); BaseArchive? archive = FileTypeTool.CreateArchiveType(file);
if (archive == null) if (archive == null)
return false; return false;
@@ -823,7 +823,7 @@ namespace SabreTools.DatTools
/// <returns>Folder configured with proper flags</returns> /// <returns>Folder configured with proper flags</returns>
private static Folder? GetPreconfiguredFolder(DatFile datFile, bool date, OutputFormat outputFormat) 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) if (outputArchive is BaseArchive baseArchive && date)
baseArchive.UseDates = date; baseArchive.UseDates = date;

View File

@@ -105,16 +105,16 @@ namespace SabreTools.FileTypes.Archives
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
// Catch this but don't count it as an error because SharpCompress is unsafe // Catch this but don't count it as an error because SharpCompress is unsafe
logger.Verbose(ex); _logger.Verbose(ex);
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
logger.Warning(ex); _logger.Warning(ex);
encounteredErrors = true; encounteredErrors = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
encounteredErrors = true; encounteredErrors = true;
} }
@@ -185,7 +185,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return (null, null); return (null, null);
} }
} }
@@ -224,7 +224,7 @@ namespace SabreTools.FileTypes.Archives
BaseFile gzipEntryRom = new(); BaseFile gzipEntryRom = new();
// Perform a quickscan, if flagged to // 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; gzipEntryRom.Filename = gamename;
@@ -239,7 +239,7 @@ namespace SabreTools.FileTypes.Archives
var gz = new gZip(); var gz = new gZip();
ZipReturn ret = gz.ZipFileOpen(Filename); ZipReturn ret = gz.ZipFileOpen(Filename);
ret = gz.ZipFileOpenReadStream(0, out Stream? gzstream, out ulong streamSize); 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.Filename = gz.GetLocalFile(0).Filename;
gzipEntryRom.Parent = gamename; gzipEntryRom.Parent = gamename;
gzipEntryRom.Date = (gz.TimeStamp > 0 ? gz.TimeStamp.ToString() : null); gzipEntryRom.Date = (gz.TimeStamp > 0 ? gz.TimeStamp.ToString() : null);
@@ -252,7 +252,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return null; return null;
} }
@@ -279,21 +279,21 @@ namespace SabreTools.FileTypes.Archives
// If we have the romba depot files, just skip them gracefully // If we have the romba depot files, just skip them gracefully
if (datum == ".romba_size" || datum == ".romba_size.backup") 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; return false;
} }
// Check if the name is the right length // Check if the name is the right length
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.gz")) 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; return false;
} }
// Check if the file is at least the minimum length // Check if the file is at least the minimum length
if (filesize < 40 /* bytes */) 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; return false;
} }
@@ -340,21 +340,21 @@ namespace SabreTools.FileTypes.Archives
// If we have the romba depot files, just skip them gracefully // If we have the romba depot files, just skip them gracefully
if (datum == ".romba_size" || datum == ".romba_size.backup") 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; return null;
} }
// Check if the name is the right length // Check if the name is the right length
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.gz")) 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; return null;
} }
// Check if the file is at least the minimum length // Check if the file is at least the minimum length
if (filesize < 40 /* bytes */) 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; return null;
} }
@@ -414,7 +414,7 @@ namespace SabreTools.FileTypes.Archives
// Check that the input file exists // Check that the input file exists
if (!File.Exists(inputFile)) if (!File.Exists(inputFile))
{ {
logger.Warning($"File '{inputFile}' does not exist!"); _logger.Warning($"File '{inputFile}' does not exist!");
return false; return false;
} }

View File

@@ -67,16 +67,16 @@ namespace SabreTools.FileTypes.Archives
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
// Catch this but don't count it as an error because SharpCompress is unsafe // Catch this but don't count it as an error because SharpCompress is unsafe
logger.Verbose(ex); _logger.Verbose(ex);
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
logger.Warning(ex); _logger.Warning(ex);
encounteredErrors = true; encounteredErrors = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
encounteredErrors = true; encounteredErrors = true;
} }
@@ -168,7 +168,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return (null, null); return (null, null);
} }
#else #else
@@ -201,7 +201,7 @@ namespace SabreTools.FileTypes.Archives
BaseFile rarEntryRom = new(); BaseFile rarEntryRom = new();
// Perform a quickscan, if flagged to // 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.Size = entry.Size;
rarEntryRom.CRC = BitConverter.GetBytes(entry.Crc); rarEntryRom.CRC = BitConverter.GetBytes(entry.Crc);
@@ -210,7 +210,7 @@ namespace SabreTools.FileTypes.Archives
else else
{ {
using Stream entryStream = entry.OpenEntryStream(); 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 // Fill in common details and add to the list
@@ -225,7 +225,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return null; return null;
} }
@@ -271,7 +271,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
} }
return empties; return empties;

View File

@@ -118,16 +118,16 @@ namespace SabreTools.FileTypes.Archives
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
// Catch this but don't count it as an error because SharpCompress is unsafe // Catch this but don't count it as an error because SharpCompress is unsafe
logger.Verbose(ex); _logger.Verbose(ex);
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
logger.Warning(ex); _logger.Warning(ex);
encounteredErrors = true; encounteredErrors = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
encounteredErrors = true; encounteredErrors = true;
} }
@@ -221,7 +221,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return (null, null); return (null, null);
} }
} }
@@ -265,7 +265,7 @@ namespace SabreTools.FileTypes.Archives
// If we get a read error, log it and continue // If we get a read error, log it and continue
if (zr != ZipReturn.ZipGood) 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(); zr = zf.ZipFileCloseReadStream();
continue; continue;
} }
@@ -274,7 +274,7 @@ namespace SabreTools.FileTypes.Archives
BaseFile zipEntryRom = new(); BaseFile zipEntryRom = new();
// Perform a quickscan, if flagged to // 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.Size = (long)zf.GetLocalFile(i).UncompressedSize;
zipEntryRom.CRC = zf.GetLocalFile(i).CRC; zipEntryRom.CRC = zf.GetLocalFile(i).CRC;
@@ -282,7 +282,7 @@ namespace SabreTools.FileTypes.Archives
// Otherwise, use the stream directly // Otherwise, use the stream directly
else 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 // Fill in common details and add to the list
@@ -297,7 +297,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return null; return null;
} }
@@ -347,7 +347,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
} }
return empties; return empties;
@@ -560,7 +560,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
success = false; success = false;
} }
finally finally
@@ -774,7 +774,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
success = false; success = false;
} }

View File

@@ -68,16 +68,16 @@ namespace SabreTools.FileTypes.Archives
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
// Catch this but don't count it as an error because SharpCompress is unsafe // Catch this but don't count it as an error because SharpCompress is unsafe
logger.Verbose(ex); _logger.Verbose(ex);
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
logger.Warning(ex); _logger.Warning(ex);
encounteredErrors = true; encounteredErrors = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
encounteredErrors = true; encounteredErrors = true;
} }
@@ -165,7 +165,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return (null, null); return (null, null);
} }
#else #else
@@ -194,7 +194,7 @@ namespace SabreTools.FileTypes.Archives
BaseFile tarEntryRom = new(); BaseFile tarEntryRom = new();
// Perform a quickscan, if flagged to // 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.Size = entry.Size;
tarEntryRom.CRC = BitConverter.GetBytes(entry.Crc); tarEntryRom.CRC = BitConverter.GetBytes(entry.Crc);
@@ -203,7 +203,7 @@ namespace SabreTools.FileTypes.Archives
else else
{ {
using Stream entryStream = entry.OpenEntryStream(); 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 // Fill in common details and add to the list
@@ -218,7 +218,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return null; return null;
} }
@@ -260,7 +260,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
} }
return empties; return empties;
@@ -409,7 +409,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
success = false; success = false;
} }
finally finally
@@ -578,7 +578,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
success = false; success = false;
} }
finally finally

View File

@@ -80,16 +80,16 @@ namespace SabreTools.FileTypes.Archives
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
// Catch this but don't count it as an error because SharpCompress is unsafe // Catch this but don't count it as an error because SharpCompress is unsafe
logger.Verbose(ex); _logger.Verbose(ex);
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
logger.Warning(ex); _logger.Warning(ex);
encounteredErrors = true; encounteredErrors = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
encounteredErrors = true; encounteredErrors = true;
} }
@@ -161,7 +161,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return (null, null); return (null, null);
} }
#else #else
@@ -200,7 +200,7 @@ namespace SabreTools.FileTypes.Archives
BaseFile xzEntryRom = new(); BaseFile xzEntryRom = new();
// Perform a quickscan, if flagged to // 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; xzEntryRom.Filename = gamename;
@@ -213,7 +213,7 @@ namespace SabreTools.FileTypes.Archives
else else
{ {
var xzStream = new XZStream(File.OpenRead(Filename!)); var xzStream = new XZStream(File.OpenRead(Filename!));
xzEntryRom = GetInfo(xzStream, hashes: AvailableHashTypes); xzEntryRom = GetInfo(xzStream, hashes: _hashTypes);
xzEntryRom.Filename = gamename; xzEntryRom.Filename = gamename;
xzStream.Dispose(); xzStream.Dispose();
} }
@@ -224,7 +224,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return null; return null;
} }
@@ -254,7 +254,7 @@ namespace SabreTools.FileTypes.Archives
// Check if the name is the right length // Check if the name is the right length
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz")) 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; return false;
} }
@@ -276,7 +276,7 @@ namespace SabreTools.FileTypes.Archives
// Check if the name is the right length // Check if the name is the right length
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz")) 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; return null;
} }
@@ -301,7 +301,7 @@ namespace SabreTools.FileTypes.Archives
// Check that the input file exists // Check that the input file exists
if (!File.Exists(inputFile)) if (!File.Exists(inputFile))
{ {
logger.Warning($"File '{inputFile}' does not exist!"); _logger.Warning($"File '{inputFile}' does not exist!");
return false; return false;
} }

View File

@@ -157,16 +157,16 @@ namespace SabreTools.FileTypes.Archives
catch (EndOfStreamException ex) catch (EndOfStreamException ex)
{ {
// Catch this but don't count it as an error because SharpCompress is unsafe // Catch this but don't count it as an error because SharpCompress is unsafe
logger.Verbose(ex); _logger.Verbose(ex);
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
logger.Warning(ex); _logger.Warning(ex);
encounteredErrors = true; encounteredErrors = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
encounteredErrors = true; encounteredErrors = true;
} }
@@ -296,7 +296,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return (null, null); return (null, null);
} }
} }
@@ -345,7 +345,7 @@ namespace SabreTools.FileTypes.Archives
// If we get a read error, log it and continue // If we get a read error, log it and continue
if (zr != ZipReturn.ZipGood || readStream == null) 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; continue;
} }
@@ -353,7 +353,7 @@ namespace SabreTools.FileTypes.Archives
var zipEntryRom = new BaseFile(); var zipEntryRom = new BaseFile();
// Perform a quickscan, if flagged to // 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.Size = (long)localFile.UncompressedSize;
zipEntryRom.CRC = localFile.CRC; zipEntryRom.CRC = localFile.CRC;
@@ -363,7 +363,7 @@ namespace SabreTools.FileTypes.Archives
{ {
zipEntryRom = GetInfo(readStream, zipEntryRom = GetInfo(readStream,
size: (long)localFile.UncompressedSize, size: (long)localFile.UncompressedSize,
hashes: AvailableHashTypes, hashes: _hashTypes,
keepReadOpen: true); keepReadOpen: true);
} }
@@ -406,7 +406,7 @@ namespace SabreTools.FileTypes.Archives
var zipEntryRom = new BaseFile(); var zipEntryRom = new BaseFile();
// Perform a quickscan, if flagged to // 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; zipEntryRom.Size = localFile.Length;
#if NETCOREAPP #if NETCOREAPP
@@ -420,7 +420,7 @@ namespace SabreTools.FileTypes.Archives
{ {
zipEntryRom = GetInfo(readStream, zipEntryRom = GetInfo(readStream,
size: localFile.Length, size: localFile.Length,
hashes: AvailableHashTypes, hashes: _hashTypes,
keepReadOpen: false); keepReadOpen: false);
} }
@@ -437,7 +437,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return null; return null;
} }
@@ -532,7 +532,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
} }
return empties; return empties;
@@ -734,7 +734,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
success = false; success = false;
} }
finally finally
@@ -949,7 +949,7 @@ namespace SabreTools.FileTypes.Archives
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
success = false; success = false;
} }

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using SabreTools.FileTypes.Archives;
namespace SabreTools.FileTypes namespace SabreTools.FileTypes
{ {
@@ -29,84 +28,13 @@ namespace SabreTools.FileTypes
/// <summary> /// <summary>
/// Create a new Archive with no base file /// Create a new Archive with no base file
/// </summary> /// </summary>
public BaseArchive() public BaseArchive() : base() { }
{
}
/// <summary> /// <summary>
/// Create a new BaseArchive from the given file /// Create a new BaseArchive from the given file
/// </summary> /// </summary>
/// <param name="filename">Name of the file to use</param> /// <param name="filename">Name of the file to use</param>
public BaseArchive(string filename) public BaseArchive(string filename) : base(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,
};
}
#endregion #endregion

View File

@@ -1,29 +1,7 @@
using System.IO; namespace SabreTools.FileTypes
using SabreTools.FileTypes.Aaru;
using SabreTools.FileTypes.CHD;
using SabreTools.Hashing;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Skippers;
namespace SabreTools.FileTypes
{ {
public class BaseFile 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 #region Fields
/// <summary> /// <summary>
@@ -46,56 +24,47 @@ namespace SabreTools.FileTypes
/// </summary> /// </summary>
public long? Size { get; set; } 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> /// <summary>
/// CRC32 hash of the file /// CRC32 hash of the file
/// </summary> /// </summary>
public byte[]? CRC { get; set; } = null; public byte[]? CRC { get; set; }
/// <summary> /// <summary>
/// MD5 hash of the file /// MD5 hash of the file
/// </summary> /// </summary>
public byte[]? MD5 { get; set; } = null; public byte[]? MD5 { get; set; }
/// <summary> /// <summary>
/// SHA-1 hash of the file /// SHA-1 hash of the file
/// </summary> /// </summary>
public byte[]? SHA1 { get; set; } = null; public byte[]? SHA1 { get; set; }
/// <summary> /// <summary>
/// SHA-256 hash of the file /// SHA-256 hash of the file
/// </summary> /// </summary>
public byte[]? SHA256 { get; set; } = null; public byte[]? SHA256 { get; set; }
/// <summary> /// <summary>
/// SHA-384 hash of the file /// SHA-384 hash of the file
/// </summary> /// </summary>
public byte[]? SHA384 { get; set; } = null; public byte[]? SHA384 { get; set; }
/// <summary> /// <summary>
/// SHA-512 hash of the file /// SHA-512 hash of the file
/// </summary> /// </summary>
public byte[]? SHA512 { get; set; } = null; public byte[]? SHA512 { get; set; }
/// <summary> /// <summary>
/// SpamSum fuzzy hash of the file /// SpamSum fuzzy hash of the file
/// </summary> /// </summary>
public byte[]? SpamSum { get; set; } = null; public byte[]? SpamSum { get; set; }
#endregion #endregion
#region Construtors
/// <summary> /// <summary>
/// Create a new BaseFile with no base file /// Create a new BaseFile with no base file
/// </summary> /// </summary>
public BaseFile() public BaseFile() { }
{
}
/// <summary> /// <summary>
/// Create a new BaseFile from the given file /// Create a new BaseFile from the given file
@@ -105,235 +74,5 @@ namespace SabreTools.FileTypes
{ {
Filename = filename; 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
} }
} }

View 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
}
}

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using SabreTools.Core.Tools; using SabreTools.Core.Tools;
using SabreTools.FileTypes.Archives; using SabreTools.Hashing;
using SabreTools.IO; using SabreTools.IO;
using SabreTools.IO.Extensions; using SabreTools.IO.Extensions;
using SabreTools.IO.Logging; using SabreTools.IO.Logging;
@@ -16,17 +16,25 @@ namespace SabreTools.FileTypes
{ {
#region Protected instance variables #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; protected List<BaseFile>? _children;
/// <summary> /// <summary>
/// Logging object /// Logging object
/// </summary> /// </summary>
protected Logger logger; protected Logger _logger;
/// <summary> /// <summary>
/// Static logger for static methods /// Static logger for static methods
/// </summary> /// </summary>
protected static Logger staticLogger = new(); protected static Logger _staticLogger = new();
/// <summary> /// <summary>
/// Flag specific to Folder to omit Machine name from output path /// 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 /// Create a new Folder with no base file
/// </summary> /// </summary>
/// <param name="writeToParent">True to write directly to parent, false otherwise</param> /// <param name="writeToParent">True to write directly to parent, false otherwise</param>
public Folder(bool writeToParent = false) public Folder(bool writeToParent = false) : base()
: base()
{ {
_writeToParent = writeToParent; _writeToParent = writeToParent;
logger = new Logger(this); _logger = new Logger(this);
} }
/// <summary> /// <summary>
@@ -53,34 +60,10 @@ namespace SabreTools.FileTypes
/// </summary> /// </summary>
/// <param name="filename">Name of the folder to use</param> /// <param name="filename">Name of the folder to use</param>
/// <param name="writeToParent">True to write directly to parent, false otherwise</param> /// <param name="writeToParent">True to write directly to parent, false otherwise</param>
public Folder(string filename, bool writeToParent = false) public Folder(string filename, bool writeToParent = false) : base(filename)
: base(filename)
{ {
_writeToParent = writeToParent; _writeToParent = writeToParent;
logger = new Logger(this); _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,
};
} }
#endregion #endregion
@@ -109,7 +92,7 @@ namespace SabreTools.FileTypes
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return false; return false;
} }
@@ -191,7 +174,7 @@ namespace SabreTools.FileTypes
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return realentry; return realentry;
} }
@@ -232,7 +215,7 @@ namespace SabreTools.FileTypes
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return (null, null); return (null, null);
} }
} }
@@ -241,6 +224,18 @@ namespace SabreTools.FileTypes
#region Information #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> /// <summary>
/// Generate a list of immediate children from the current folder /// Generate a list of immediate children from the current folder
/// </summary> /// </summary>
@@ -260,7 +255,7 @@ namespace SabreTools.FileTypes
foreach (string file in Directory.EnumerateFiles(Filename, "*", SearchOption.TopDirectoryOnly)) foreach (string file in Directory.EnumerateFiles(Filename, "*", SearchOption.TopDirectoryOnly))
#endif #endif
{ {
BaseFile? nf = GetInfo(file, hashes: AvailableHashTypes); BaseFile? nf = FileTypeTool.GetInfo(file, hashes: _hashTypes);
if (nf != null) if (nf != null)
_children.Add(nf); _children.Add(nf);
} }
@@ -384,7 +379,7 @@ namespace SabreTools.FileTypes
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); _logger.Error(ex);
return false; return false;
} }
finally finally