mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
Remove .NET Framework 4.6.2/4.7.2 (#24)
* Remove < .NET 4.8, general cleanup * Abstract * Tango * Banner * Scan no more * Common * Application * Access * Filter-feeder * Graffiti * Paint-over * Law and Order * XOR-o * Unused staircase * Maybe * Maybe not * Delete this * The word is "no" * Emit * Improper * Aye aye * Fence * Barrier * Monkey * Pail * Lines
This commit is contained in:
Binary file not shown.
@@ -21,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public AttractMode(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,25 +29,19 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse an AttractMode DAT and return all found games within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Open a file reader
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc);
|
||||
Encoding enc = FileExtensions.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(FileExtensions.TryOpenRead(filename), enc);
|
||||
|
||||
sr.ReadLine(); // Skip the first line since it's the header
|
||||
while (!sr.EndOfStream)
|
||||
@@ -83,7 +77,9 @@ namespace SabreTools.Library.DatFiles
|
||||
Size = Constants.SizeZero,
|
||||
CRC = Constants.CRCZero,
|
||||
MD5 = Constants.MD5Zero,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = Constants.RIPEMD160Zero,
|
||||
#endif
|
||||
SHA1 = Constants.SHA1Zero,
|
||||
ItemStatus = ItemStatus.None,
|
||||
|
||||
@@ -93,10 +89,13 @@ namespace SabreTools.Library.DatFiles
|
||||
Year = gameinfo[4],
|
||||
Manufacturer = gameinfo[5],
|
||||
Comment = gameinfo[15],
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
sr.Dispose();
|
||||
@@ -113,7 +112,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -122,10 +121,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false));
|
||||
svw.Quotes = false;
|
||||
svw.Separator = ';';
|
||||
svw.VerifyFieldCount = true;
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Quotes = false,
|
||||
Separator = ';',
|
||||
VerifyFieldCount = true
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(svw);
|
||||
@@ -254,23 +255,23 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
string[] fields = new string[]
|
||||
{
|
||||
datItem.GetField(Field.MachineName, ExcludeFields),
|
||||
datItem.GetField(Field.Description, ExcludeFields),
|
||||
FileName,
|
||||
datItem.GetField(Field.CloneOf, ExcludeFields),
|
||||
datItem.GetField(Field.Year, ExcludeFields),
|
||||
datItem.GetField(Field.Manufacturer, ExcludeFields),
|
||||
string.Empty, // datItem.GetField(Field.Category, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Players, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Rotation, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Control, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Status, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.DisplayCount, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.DisplayType, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.AltRomname, ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.AltTitle, ExcludeFields)
|
||||
datItem.GetField(Field.Comment, ExcludeFields),
|
||||
string.Empty, // datItem.GetField(Field.Buttons, ExcludeFields)
|
||||
datItem.GetField(Field.MachineName, DatHeader.ExcludeFields),
|
||||
datItem.GetField(Field.Description, DatHeader.ExcludeFields),
|
||||
DatHeader.FileName,
|
||||
datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields),
|
||||
datItem.GetField(Field.Year, DatHeader.ExcludeFields),
|
||||
datItem.GetField(Field.Manufacturer, DatHeader.ExcludeFields),
|
||||
string.Empty, // datItem.GetField(Field.Category, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Players, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Rotation, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Control, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.Status, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.DisplayCount, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.DisplayType, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.AltRomname, DatHeader.ExcludeFields)
|
||||
string.Empty, // datItem.GetField(Field.AltTitle, DatHeader.ExcludeFields)
|
||||
datItem.GetField(Field.Comment, DatHeader.ExcludeFields),
|
||||
string.Empty, // datItem.GetField(Field.Buttons, DatHeader.ExcludeFields)
|
||||
};
|
||||
|
||||
svw.WriteValues(fields);
|
||||
|
||||
@@ -5,10 +5,10 @@ using System.Text;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
using SabreTools.Library.Readers;
|
||||
using SabreTools.Library.Tools;
|
||||
using SabreTools.Library.Writers;
|
||||
using NaturalSort;
|
||||
using SabreTools.Library.Readers;
|
||||
|
||||
namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
@@ -22,7 +22,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public ClrMamePro(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,26 +30,22 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a ClrMamePro DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Open a file reader
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
ClrMameProReader cmpr = new ClrMameProReader(Utilities.TryOpenRead(filename), enc);
|
||||
cmpr.DosCenter = false;
|
||||
Encoding enc = FileExtensions.GetEncoding(filename);
|
||||
ClrMameProReader cmpr = new ClrMameProReader(FileExtensions.TryOpenRead(filename), enc)
|
||||
{
|
||||
DosCenter = false
|
||||
};
|
||||
|
||||
while (!cmpr.EndOfStream)
|
||||
{
|
||||
@@ -72,10 +68,10 @@ namespace SabreTools.Library.DatFiles
|
||||
case "set": // Used by the most ancient DATs
|
||||
case "game": // Used by most CMP DATs
|
||||
case "machine": // Possibly used by MAME CMP DATs
|
||||
ReadSet(cmpr, false, filename, sysid, srcid, clean, remUnicode);
|
||||
ReadSet(cmpr, false, filename, indexId);
|
||||
break;
|
||||
case "resource": // Used by some other DATs to denote a BIOS set
|
||||
ReadSet(cmpr, true, filename, sysid, srcid, clean, remUnicode);
|
||||
ReadSet(cmpr, true, filename, indexId);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -123,63 +119,63 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (itemKey)
|
||||
{
|
||||
case "name":
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? itemVal : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? itemVal : DatHeader.Name);
|
||||
superdat = superdat || itemVal.Contains(" - SuperDAT");
|
||||
|
||||
if (keep && superdat)
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? "SuperDAT" : DatHeader.Type);
|
||||
|
||||
break;
|
||||
case "description":
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? itemVal : Description);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? itemVal : DatHeader.Description);
|
||||
break;
|
||||
case "rootdir":
|
||||
RootDir = (string.IsNullOrWhiteSpace(RootDir) ? itemVal : RootDir);
|
||||
DatHeader.RootDir = (string.IsNullOrWhiteSpace(DatHeader.RootDir) ? itemVal : DatHeader.RootDir);
|
||||
break;
|
||||
case "category":
|
||||
Category = (string.IsNullOrWhiteSpace(Category) ? itemVal : Category);
|
||||
DatHeader.Category = (string.IsNullOrWhiteSpace(DatHeader.Category) ? itemVal : DatHeader.Category);
|
||||
break;
|
||||
case "version":
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? itemVal : Version);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? itemVal : DatHeader.Version);
|
||||
break;
|
||||
case "date":
|
||||
Date = (string.IsNullOrWhiteSpace(Date) ? itemVal : Date);
|
||||
DatHeader.Date = (string.IsNullOrWhiteSpace(DatHeader.Date) ? itemVal : DatHeader.Date);
|
||||
break;
|
||||
case "author":
|
||||
Author = (string.IsNullOrWhiteSpace(Author) ? itemVal : Author);
|
||||
DatHeader.Author = (string.IsNullOrWhiteSpace(DatHeader.Author) ? itemVal : DatHeader.Author);
|
||||
break;
|
||||
case "email":
|
||||
Email = (string.IsNullOrWhiteSpace(Email) ? itemVal : Email);
|
||||
DatHeader.Email = (string.IsNullOrWhiteSpace(DatHeader.Email) ? itemVal : DatHeader.Email);
|
||||
break;
|
||||
case "homepage":
|
||||
Homepage = (string.IsNullOrWhiteSpace(Homepage) ? itemVal : Homepage);
|
||||
DatHeader.Homepage = (string.IsNullOrWhiteSpace(DatHeader.Homepage) ? itemVal : DatHeader.Homepage);
|
||||
break;
|
||||
case "url":
|
||||
Url = (string.IsNullOrWhiteSpace(Url) ? itemVal : Url);
|
||||
DatHeader.Url = (string.IsNullOrWhiteSpace(DatHeader.Url) ? itemVal : DatHeader.Url);
|
||||
break;
|
||||
case "comment":
|
||||
Comment = (string.IsNullOrWhiteSpace(Comment) ? itemVal : Comment);
|
||||
DatHeader.Comment = (string.IsNullOrWhiteSpace(DatHeader.Comment) ? itemVal : DatHeader.Comment);
|
||||
break;
|
||||
case "header":
|
||||
Header = (string.IsNullOrWhiteSpace(Header) ? itemVal : Header);
|
||||
DatHeader.Header = (string.IsNullOrWhiteSpace(DatHeader.Header) ? itemVal : DatHeader.Header);
|
||||
break;
|
||||
case "type":
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? itemVal : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? itemVal : DatHeader.Type);
|
||||
superdat = superdat || itemVal.Contains("SuperDAT");
|
||||
break;
|
||||
case "forcemerging":
|
||||
if (ForceMerging == ForceMerging.None)
|
||||
ForceMerging = Utilities.GetForceMerging(itemVal);
|
||||
if (DatHeader.ForceMerging == ForceMerging.None)
|
||||
DatHeader.ForceMerging = itemVal.AsForceMerging();
|
||||
|
||||
break;
|
||||
case "forcezipping":
|
||||
if (ForcePacking == ForcePacking.None)
|
||||
ForcePacking = Utilities.GetForcePacking(itemVal);
|
||||
if (DatHeader.ForcePacking == ForcePacking.None)
|
||||
DatHeader.ForcePacking = itemVal.AsForcePacking();
|
||||
|
||||
break;
|
||||
case "forcepacking":
|
||||
if (ForcePacking == ForcePacking.None)
|
||||
ForcePacking = Utilities.GetForcePacking(itemVal);
|
||||
if (DatHeader.ForcePacking == ForcePacking.None)
|
||||
DatHeader.ForcePacking = itemVal.AsForcePacking();
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -192,22 +188,14 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="cmpr">ClrMameProReader to use to parse the header</param>
|
||||
/// <param name="resource">True if the item is a resource (bios), false otherwise</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadSet(
|
||||
ClrMameProReader cmpr,
|
||||
bool resource,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
bool containsItems = false;
|
||||
@@ -297,14 +285,13 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Create the proper DatItem based on the type
|
||||
DatItem item = Utilities.GetDatItem(itemType);
|
||||
DatItem item = DatItem.Create(itemType);
|
||||
|
||||
// Then populate it with information
|
||||
item.CopyMachineInformation(machine);
|
||||
|
||||
item.SystemID = sysid;
|
||||
item.System = filename;
|
||||
item.SourceID = srcid;
|
||||
item.IndexId = indexId;
|
||||
item.IndexSource = filename;
|
||||
|
||||
// Loop through all of the attributes
|
||||
foreach (var kvp in cmpr.Internal)
|
||||
@@ -335,53 +322,55 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
case "crc":
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
(item as Rom).CRC = Utilities.CleanHashData(attrVal, Constants.CRCLength);
|
||||
(item as Rom).CRC = attrVal;
|
||||
|
||||
break;
|
||||
case "md5":
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
(item as Rom).MD5 = Utilities.CleanHashData(attrVal, Constants.MD5Length);
|
||||
(item as Rom).MD5 = attrVal;
|
||||
else if (item.ItemType == ItemType.Disk)
|
||||
((Disk)item).MD5 = Utilities.CleanHashData(attrVal, Constants.MD5Length);
|
||||
((Disk)item).MD5 = attrVal;
|
||||
|
||||
break;
|
||||
#if NET_FRAMEWORK
|
||||
case "ripemd160":
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
(item as Rom).RIPEMD160 = Utilities.CleanHashData(attrVal, Constants.RIPEMD160Length);
|
||||
(item as Rom).RIPEMD160 = attrVal;
|
||||
else if (item.ItemType == ItemType.Disk)
|
||||
((Disk)item).RIPEMD160 = Utilities.CleanHashData(attrVal, Constants.RIPEMD160Length);
|
||||
((Disk)item).RIPEMD160 = attrVal;
|
||||
|
||||
break;
|
||||
#endif
|
||||
case "sha1":
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
(item as Rom).SHA1 = Utilities.CleanHashData(attrVal, Constants.SHA1Length);
|
||||
(item as Rom).SHA1 = attrVal;
|
||||
else if (item.ItemType == ItemType.Disk)
|
||||
((Disk)item).SHA1 = Utilities.CleanHashData(attrVal, Constants.SHA1Length);
|
||||
((Disk)item).SHA1 = attrVal;
|
||||
|
||||
break;
|
||||
case "sha256":
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
((Rom)item).SHA256 = Utilities.CleanHashData(attrVal, Constants.SHA256Length);
|
||||
((Rom)item).SHA256 = attrVal;
|
||||
else if (item.ItemType == ItemType.Disk)
|
||||
((Disk)item).SHA256 = Utilities.CleanHashData(attrVal, Constants.SHA256Length);
|
||||
((Disk)item).SHA256 = attrVal;
|
||||
|
||||
break;
|
||||
case "sha384":
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
((Rom)item).SHA384 = Utilities.CleanHashData(attrVal, Constants.SHA384Length);
|
||||
((Rom)item).SHA384 = attrVal;
|
||||
else if (item.ItemType == ItemType.Disk)
|
||||
((Disk)item).SHA384 = Utilities.CleanHashData(attrVal, Constants.SHA384Length);
|
||||
((Disk)item).SHA384 = attrVal;
|
||||
|
||||
break;
|
||||
case "sha512":
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
((Rom)item).SHA512 = Utilities.CleanHashData(attrVal, Constants.SHA512Length);
|
||||
((Rom)item).SHA512 = attrVal;
|
||||
else if (item.ItemType == ItemType.Disk)
|
||||
((Disk)item).SHA512 = Utilities.CleanHashData(attrVal, Constants.SHA512Length);
|
||||
((Disk)item).SHA512 = attrVal;
|
||||
|
||||
break;
|
||||
case "status":
|
||||
ItemStatus tempFlagStatus = Utilities.GetItemStatus(attrVal);
|
||||
ItemStatus tempFlagStatus = attrVal.AsItemStatus();
|
||||
if (item.ItemType == ItemType.Rom)
|
||||
((Rom)item).ItemStatus = tempFlagStatus;
|
||||
else if (item.ItemType == ItemType.Disk)
|
||||
@@ -397,9 +386,9 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
case "default":
|
||||
if (item.ItemType == ItemType.BiosSet)
|
||||
((BiosSet)item).Default = Utilities.GetYesNo(attrVal.ToLowerInvariant());
|
||||
((BiosSet)item).Default = attrVal.ToLowerInvariant().AsYesNo();
|
||||
else if (item.ItemType == ItemType.Release)
|
||||
((Release)item).Default = Utilities.GetYesNo(attrVal.ToLowerInvariant());
|
||||
((Release)item).Default = attrVal.ToLowerInvariant().AsYesNo();
|
||||
|
||||
break;
|
||||
case "description":
|
||||
@@ -421,7 +410,7 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(item, clean, remUnicode);
|
||||
ParseAddHelper(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,15 +419,14 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Blank blank = new Blank()
|
||||
{
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
blank.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(blank, clean, remUnicode);
|
||||
ParseAddHelper(blank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,7 +441,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -462,8 +450,10 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
ClrMameProWriter cmpw = new ClrMameProWriter(fs, new UTF8Encoding(false));
|
||||
cmpw.Quotes = true;
|
||||
ClrMameProWriter cmpw = new ClrMameProWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Quotes = true
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(cmpw);
|
||||
@@ -513,7 +503,9 @@ namespace SabreTools.Library.DatFiles
|
||||
((Rom)rom).Size = Constants.SizeZero;
|
||||
((Rom)rom).CRC = ((Rom)rom).CRC == "null" ? Constants.CRCZero : null;
|
||||
((Rom)rom).MD5 = ((Rom)rom).MD5 == "null" ? Constants.MD5Zero : null;
|
||||
#if NET_FRAMEWORK
|
||||
((Rom)rom).RIPEMD160 = ((Rom)rom).RIPEMD160 == "null" ? Constants.RIPEMD160Zero : null;
|
||||
#endif
|
||||
((Rom)rom).SHA1 = ((Rom)rom).SHA1 == "null" ? Constants.SHA1Zero : null;
|
||||
((Rom)rom).SHA256 = ((Rom)rom).SHA256 == "null" ? Constants.SHA256Zero : null;
|
||||
((Rom)rom).SHA384 = ((Rom)rom).SHA384 == "null" ? Constants.SHA384Zero : null;
|
||||
@@ -555,24 +547,24 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
cmpw.WriteStartElement("clrmamepro");
|
||||
|
||||
cmpw.WriteStandalone("name", Name);
|
||||
cmpw.WriteStandalone("description", Description);
|
||||
if (!string.IsNullOrWhiteSpace(Category))
|
||||
cmpw.WriteStandalone("category", Category);
|
||||
cmpw.WriteStandalone("version", Version);
|
||||
if (!string.IsNullOrWhiteSpace(Date))
|
||||
cmpw.WriteStandalone("date", Date);
|
||||
cmpw.WriteStandalone("author", Author);
|
||||
if (!string.IsNullOrWhiteSpace(Email))
|
||||
cmpw.WriteStandalone("email", Email);
|
||||
if (!string.IsNullOrWhiteSpace(Homepage))
|
||||
cmpw.WriteStandalone("homepage", Homepage);
|
||||
if (!string.IsNullOrWhiteSpace(Url))
|
||||
cmpw.WriteStandalone("url", Url);
|
||||
if (!string.IsNullOrWhiteSpace(Comment))
|
||||
cmpw.WriteStandalone("comment", Comment);
|
||||
|
||||
switch (ForcePacking)
|
||||
cmpw.WriteStandalone("name", DatHeader.Name);
|
||||
cmpw.WriteStandalone("description", DatHeader.Description);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Category))
|
||||
cmpw.WriteStandalone("category", DatHeader.Category);
|
||||
cmpw.WriteStandalone("version", DatHeader.Version);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Date))
|
||||
cmpw.WriteStandalone("date", DatHeader.Date);
|
||||
cmpw.WriteStandalone("author", DatHeader.Author);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Email))
|
||||
cmpw.WriteStandalone("email", DatHeader.Email);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Homepage))
|
||||
cmpw.WriteStandalone("homepage", DatHeader.Homepage);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Url))
|
||||
cmpw.WriteStandalone("url", DatHeader.Url);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Comment))
|
||||
cmpw.WriteStandalone("comment", DatHeader.Comment);
|
||||
|
||||
switch (DatHeader.ForcePacking)
|
||||
{
|
||||
case ForcePacking.Unzip:
|
||||
cmpw.WriteStandalone("forcezipping", "no", false);
|
||||
@@ -582,7 +574,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ForceMerging)
|
||||
switch (DatHeader.ForceMerging)
|
||||
{
|
||||
case ForceMerging.Full:
|
||||
cmpw.WriteStandalone("forcemerging", "full", false);
|
||||
@@ -627,20 +619,20 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Build the state based on excluded fields
|
||||
cmpw.WriteStartElement(datItem.MachineType == MachineType.Bios ? "resource" : "game");
|
||||
cmpw.WriteStandalone("name", datItem.GetField(Field.MachineName, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, ExcludeFields)))
|
||||
cmpw.WriteStandalone("name", datItem.GetField(Field.MachineName, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("romof", datItem.RomOf);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("cloneof", datItem.CloneOf);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("sampleof", datItem.SampleOf);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("description", datItem.MachineDescription);
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields)))
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("description", datItem.MachineName);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("year", datItem.Year);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("manufacturer", datItem.Manufacturer);
|
||||
|
||||
cmpw.Flush();
|
||||
@@ -665,7 +657,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
// Build the state based on excluded fields
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteStandalone("sampleof", datItem.SampleOf);
|
||||
|
||||
// End game
|
||||
@@ -706,17 +698,17 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
case ItemType.Archive:
|
||||
cmpw.WriteStartElement("archive");
|
||||
cmpw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
cmpw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
cmpw.WriteEndElement();
|
||||
break;
|
||||
|
||||
case ItemType.BiosSet:
|
||||
var biosSet = datItem as BiosSet;
|
||||
cmpw.WriteStartElement("biosset");
|
||||
cmpw.WriteAttributeString("name", biosSet.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(biosSet.GetField(Field.BiosDescription, ExcludeFields)))
|
||||
cmpw.WriteAttributeString("name", biosSet.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(biosSet.GetField(Field.BiosDescription, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("description", biosSet.Description);
|
||||
if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
cmpw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant());
|
||||
cmpw.WriteEndElement();
|
||||
break;
|
||||
@@ -724,20 +716,22 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Disk:
|
||||
var disk = datItem as Disk;
|
||||
cmpw.WriteStartElement("disk");
|
||||
cmpw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
cmpw.WriteAttributeString("name", disk.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant());
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
cmpw.WriteAttributeString("flags", disk.ItemStatus.ToString().ToLowerInvariant());
|
||||
cmpw.WriteEndElement();
|
||||
break;
|
||||
@@ -745,14 +739,14 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Release:
|
||||
var release = datItem as Release;
|
||||
cmpw.WriteStartElement("release");
|
||||
cmpw.WriteAttributeString("name", release.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields)))
|
||||
cmpw.WriteAttributeString("name", release.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("region", release.Region);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("language", release.Language);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("date", release.Date);
|
||||
if (!ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
cmpw.WriteAttributeString("default", release.Default.ToString().ToLowerInvariant());
|
||||
cmpw.WriteEndElement();
|
||||
break;
|
||||
@@ -760,33 +754,35 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
cmpw.WriteStartElement("rom");
|
||||
cmpw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
cmpw.WriteAttributeString("name", rom.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
cmpw.WriteAttributeString("size", rom.Size.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("date", rom.Date);
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
cmpw.WriteAttributeString("flags", rom.ItemStatus.ToString().ToLowerInvariant());
|
||||
cmpw.WriteEndElement();
|
||||
break;
|
||||
|
||||
case ItemType.Sample:
|
||||
cmpw.WriteStartElement("sample");
|
||||
cmpw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
cmpw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
cmpw.WriteEndElement();
|
||||
break;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using Newtonsoft.Json;
|
||||
@@ -231,7 +233,6 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <summary>
|
||||
/// Clone the current header
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object Clone()
|
||||
{
|
||||
return new DatHeader()
|
||||
@@ -273,6 +274,520 @@ namespace SabreTools.Library.DatFiles
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the standard parts of the current header
|
||||
/// </summary>
|
||||
public DatHeader CloneStandard()
|
||||
{
|
||||
return new DatHeader()
|
||||
{
|
||||
FileName = this.FileName,
|
||||
Name = this.Name,
|
||||
Description = this.Description,
|
||||
RootDir = this.RootDir,
|
||||
Category = this.Category,
|
||||
Version = this.Version,
|
||||
Date = this.Date,
|
||||
Author = this.Author,
|
||||
Email = this.Email,
|
||||
Homepage = this.Homepage,
|
||||
Url = this.Url,
|
||||
Comment = this.Comment,
|
||||
Header = this.Header,
|
||||
Type = this.Type,
|
||||
ForceMerging = this.ForceMerging,
|
||||
ForceNodump = this.ForceNodump,
|
||||
ForcePacking = this.ForcePacking,
|
||||
DatFormat = this.DatFormat,
|
||||
ExcludeFields = this.ExcludeFields,
|
||||
OneRom = this.OneRom,
|
||||
KeepEmptyGames = this.KeepEmptyGames,
|
||||
SceneDateStrip = this.SceneDateStrip,
|
||||
DedupeRoms = this.DedupeRoms,
|
||||
StripHash = this.StripHash,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the filtering parts of the current header
|
||||
/// </summary>
|
||||
public DatHeader CloneFiltering()
|
||||
{
|
||||
return new DatHeader()
|
||||
{
|
||||
DatFormat = this.DatFormat,
|
||||
ExcludeFields = this.ExcludeFields,
|
||||
OneRom = this.OneRom,
|
||||
KeepEmptyGames = this.KeepEmptyGames,
|
||||
SceneDateStrip = this.SceneDateStrip,
|
||||
DedupeRoms = this.DedupeRoms,
|
||||
StripHash = this.StripHash,
|
||||
|
||||
UseRomName = this.UseRomName,
|
||||
Prefix = this.Prefix,
|
||||
Postfix = this.Postfix,
|
||||
Quotes = this.Quotes,
|
||||
ReplaceExtension = this.ReplaceExtension,
|
||||
AddExtension = this.AddExtension,
|
||||
RemoveExtension = this.RemoveExtension,
|
||||
GameName = this.GameName,
|
||||
Romba = this.Romba,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overwrite local values from another DatHeader if not default
|
||||
/// </summary>
|
||||
/// <param name="datHeader">DatHeader to get the values from</param>
|
||||
public void ConditionalCopy(DatHeader datHeader)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.FileName))
|
||||
this.FileName = datHeader.FileName;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Name))
|
||||
this.Name = datHeader.Name;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Description))
|
||||
this.Description = datHeader.Description;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.RootDir))
|
||||
this.RootDir = datHeader.RootDir;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Category))
|
||||
this.Category = datHeader.Category;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Version))
|
||||
this.Version = datHeader.Version;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Date))
|
||||
this.Date = datHeader.Date;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Author))
|
||||
this.Author = datHeader.Author;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Email))
|
||||
this.Email = datHeader.Email;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Homepage))
|
||||
this.Homepage = datHeader.Homepage;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Url))
|
||||
this.Url = datHeader.Url;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Comment))
|
||||
this.Comment = datHeader.Comment;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Header))
|
||||
this.Header = datHeader.Header;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Type))
|
||||
this.Type = datHeader.Type;
|
||||
|
||||
if (datHeader.ForceMerging != ForceMerging.None)
|
||||
this.ForceMerging = datHeader.ForceMerging;
|
||||
|
||||
if (datHeader.ForceNodump != ForceNodump.None)
|
||||
this.ForceNodump = datHeader.ForceNodump;
|
||||
|
||||
if (datHeader.ForcePacking != ForcePacking.None)
|
||||
this.ForcePacking = datHeader.ForcePacking;
|
||||
|
||||
if (datHeader.DatFormat != 0x00)
|
||||
this.DatFormat = datHeader.DatFormat;
|
||||
|
||||
if (datHeader.ExcludeFields != null)
|
||||
this.ExcludeFields = datHeader.ExcludeFields;
|
||||
|
||||
this.OneRom = datHeader.OneRom;
|
||||
this.KeepEmptyGames = datHeader.KeepEmptyGames;
|
||||
this.SceneDateStrip = datHeader.SceneDateStrip;
|
||||
this.DedupeRoms = datHeader.DedupeRoms;
|
||||
//this.StripHash = datHeader.StripHash;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Prefix))
|
||||
this.Prefix = datHeader.Prefix;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.Postfix))
|
||||
this.Postfix = datHeader.Postfix;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.AddExtension))
|
||||
this.AddExtension = datHeader.AddExtension;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datHeader.ReplaceExtension))
|
||||
this.ReplaceExtension = datHeader.ReplaceExtension;
|
||||
|
||||
this.RemoveExtension = datHeader.RemoveExtension;
|
||||
this.Romba = datHeader.Romba;
|
||||
this.GameName = datHeader.GameName;
|
||||
this.Quotes = datHeader.Quotes;
|
||||
this.UseRomName = datHeader.UseRomName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Writing
|
||||
|
||||
/// <summary>
|
||||
/// Generate a proper outfile name based on a DAT and output directory
|
||||
/// </summary>
|
||||
/// <param name="outDir">Output directory</param>
|
||||
/// <param name="overwrite">True if we ignore existing files (default), false otherwise</param>
|
||||
/// <returns>Dictionary of output formats mapped to file names</returns>
|
||||
public Dictionary<DatFormat, string> CreateOutFileNames(string outDir, bool overwrite = true)
|
||||
{
|
||||
// Create the output dictionary
|
||||
Dictionary<DatFormat, string> outfileNames = new Dictionary<DatFormat, string>();
|
||||
|
||||
// Double check the outDir for the end delim
|
||||
if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
outDir += Path.DirectorySeparatorChar;
|
||||
|
||||
// Get all used extensions
|
||||
List<string> usedExtensions = new List<string>();
|
||||
|
||||
// Get the extensions from the output type
|
||||
// TODO: Can the system of adding be more automated?
|
||||
|
||||
#region .csv
|
||||
|
||||
// CSV
|
||||
if (DatFormat.HasFlag(DatFormat.CSV))
|
||||
{
|
||||
outfileNames.Add(DatFormat.CSV, CreateOutFileNamesHelper(outDir, ".csv", overwrite));
|
||||
usedExtensions.Add(".csv");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .dat
|
||||
|
||||
// ClrMamePro
|
||||
if (DatFormat.HasFlag(DatFormat.ClrMamePro))
|
||||
{
|
||||
outfileNames.Add(DatFormat.ClrMamePro, CreateOutFileNamesHelper(outDir, ".dat", overwrite));
|
||||
usedExtensions.Add(".dat");
|
||||
};
|
||||
|
||||
// RomCenter
|
||||
if (DatFormat.HasFlag(DatFormat.RomCenter))
|
||||
{
|
||||
if (usedExtensions.Contains(".dat"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RomCenter, CreateOutFileNamesHelper(outDir, ".rc.dat", overwrite));
|
||||
usedExtensions.Add(".rc.dat");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.RomCenter, CreateOutFileNamesHelper(outDir, ".dat", overwrite));
|
||||
usedExtensions.Add(".dat");
|
||||
}
|
||||
}
|
||||
|
||||
// DOSCenter
|
||||
if (DatFormat.HasFlag(DatFormat.DOSCenter))
|
||||
{
|
||||
if (usedExtensions.Contains(".dat"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.DOSCenter, CreateOutFileNamesHelper(outDir, ".dc.dat", overwrite));
|
||||
usedExtensions.Add(".dc.dat");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.DOSCenter, CreateOutFileNamesHelper(outDir, ".dat", overwrite));
|
||||
usedExtensions.Add(".dat");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region .json
|
||||
|
||||
// JSON
|
||||
if (DatFormat.HasFlag(DatFormat.Json))
|
||||
{
|
||||
outfileNames.Add(DatFormat.Json, CreateOutFileNamesHelper(outDir, ".json", overwrite));
|
||||
usedExtensions.Add(".json");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region .md5
|
||||
|
||||
// Redump MD5
|
||||
if (DatFormat.HasFlag(DatFormat.RedumpMD5))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpMD5, CreateOutFileNamesHelper(outDir, ".md5", overwrite));
|
||||
usedExtensions.Add(".md5");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#region .ripemd160
|
||||
|
||||
// Redump RIPEMD160
|
||||
if (DatFormat.HasFlag(DatFormat.RedumpRIPEMD160))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpRIPEMD160, CreateOutFileNamesHelper(outDir, ".ripemd160", overwrite));
|
||||
usedExtensions.Add(".ripemd160");
|
||||
};
|
||||
|
||||
#endregion
|
||||
#endif
|
||||
|
||||
#region .sfv
|
||||
|
||||
// Redump SFV
|
||||
if (DatFormat.HasFlag(DatFormat.RedumpSFV))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSFV, CreateOutFileNamesHelper(outDir, ".sfv", overwrite));
|
||||
usedExtensions.Add(".sfv");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .sha1
|
||||
|
||||
// Redump SHA-1
|
||||
if (DatFormat.HasFlag(DatFormat.RedumpSHA1))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSHA1, CreateOutFileNamesHelper(outDir, ".sha1", overwrite));
|
||||
usedExtensions.Add(".sha1");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .sha256
|
||||
|
||||
// Redump SHA-256
|
||||
if (DatFormat.HasFlag(DatFormat.RedumpSHA256))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSHA256, CreateOutFileNamesHelper(outDir, ".sha256", overwrite));
|
||||
usedExtensions.Add(".sha256");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .sha384
|
||||
|
||||
// Redump SHA-384
|
||||
if (DatFormat.HasFlag(DatFormat.RedumpSHA384))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSHA384, CreateOutFileNamesHelper(outDir, ".sha384", overwrite));
|
||||
usedExtensions.Add(".sha384");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .sha512
|
||||
|
||||
// Redump SHA-512
|
||||
if (DatFormat.HasFlag(DatFormat.RedumpSHA512))
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSHA512, CreateOutFileNamesHelper(outDir, ".sha512", overwrite));
|
||||
usedExtensions.Add(".sha512");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .ssv
|
||||
|
||||
// SSV
|
||||
if (DatFormat.HasFlag(DatFormat.SSV))
|
||||
{
|
||||
outfileNames.Add(DatFormat.SSV, CreateOutFileNamesHelper(outDir, ".ssv", overwrite));
|
||||
usedExtensions.Add(".ssv");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .tsv
|
||||
|
||||
// TSV
|
||||
if (DatFormat.HasFlag(DatFormat.TSV))
|
||||
{
|
||||
outfileNames.Add(DatFormat.TSV, CreateOutFileNamesHelper(outDir, ".tsv", overwrite));
|
||||
usedExtensions.Add(".tsv");
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region .txt
|
||||
|
||||
// AttractMode
|
||||
if (DatFormat.HasFlag(DatFormat.AttractMode))
|
||||
{
|
||||
outfileNames.Add(DatFormat.AttractMode, CreateOutFileNamesHelper(outDir, ".txt", overwrite));
|
||||
usedExtensions.Add(".txt");
|
||||
}
|
||||
|
||||
// MAME Listroms
|
||||
if (DatFormat.HasFlag(DatFormat.Listrom))
|
||||
{
|
||||
if (usedExtensions.Contains(".txt"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.Listrom, CreateOutFileNamesHelper(outDir, ".lr.txt", overwrite));
|
||||
usedExtensions.Add(".lr.txt");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.Listrom, CreateOutFileNamesHelper(outDir, ".txt", overwrite));
|
||||
usedExtensions.Add(".txt");
|
||||
}
|
||||
}
|
||||
|
||||
// Missfile
|
||||
if (DatFormat.HasFlag(DatFormat.MissFile))
|
||||
{
|
||||
if (usedExtensions.Contains(".txt"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.MissFile, CreateOutFileNamesHelper(outDir, ".miss.txt", overwrite));
|
||||
usedExtensions.Add(".miss.txt");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.MissFile, CreateOutFileNamesHelper(outDir, ".txt", overwrite));
|
||||
usedExtensions.Add(".txt");
|
||||
}
|
||||
}
|
||||
|
||||
// Everdrive SMDB
|
||||
if (DatFormat.HasFlag(DatFormat.EverdriveSMDB))
|
||||
{
|
||||
if (usedExtensions.Contains(".txt"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.EverdriveSMDB, CreateOutFileNamesHelper(outDir, ".smdb.txt", overwrite));
|
||||
usedExtensions.Add(".smdb.txt");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.EverdriveSMDB, CreateOutFileNamesHelper(outDir, ".txt", overwrite));
|
||||
usedExtensions.Add(".txt");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region .xml
|
||||
|
||||
// Logiqx XML
|
||||
if (DatFormat.HasFlag(DatFormat.Logiqx))
|
||||
{
|
||||
outfileNames.Add(DatFormat.Logiqx, CreateOutFileNamesHelper(outDir, ".xml", overwrite));
|
||||
}
|
||||
if (DatFormat.HasFlag(DatFormat.LogiqxDeprecated))
|
||||
{
|
||||
outfileNames.Add(DatFormat.LogiqxDeprecated, CreateOutFileNamesHelper(outDir, ".xml", overwrite));
|
||||
}
|
||||
|
||||
// SabreDAT
|
||||
if (DatFormat.HasFlag(DatFormat.SabreDat))
|
||||
{
|
||||
if (usedExtensions.Contains(".xml"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.SabreDat, CreateOutFileNamesHelper(outDir, ".sd.xml", overwrite));
|
||||
usedExtensions.Add(".sd.xml");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.SabreDat, CreateOutFileNamesHelper(outDir, ".xml", overwrite));
|
||||
usedExtensions.Add(".xml");
|
||||
}
|
||||
}
|
||||
|
||||
// Software List
|
||||
if (DatFormat.HasFlag(DatFormat.SoftwareList))
|
||||
{
|
||||
if (usedExtensions.Contains(".xml"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.SoftwareList, CreateOutFileNamesHelper(outDir, ".sl.xml", overwrite));
|
||||
usedExtensions.Add(".sl.xml");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.SoftwareList, CreateOutFileNamesHelper(outDir, ".xml", overwrite));
|
||||
usedExtensions.Add(".xml");
|
||||
}
|
||||
}
|
||||
|
||||
// MAME Listxml
|
||||
if (DatFormat.HasFlag(DatFormat.Listxml))
|
||||
{
|
||||
if (usedExtensions.Contains(".xml"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.Listxml, CreateOutFileNamesHelper(outDir, ".mame.xml", overwrite));
|
||||
usedExtensions.Add(".mame.xml");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.Listxml, CreateOutFileNamesHelper(outDir, ".xml", overwrite));
|
||||
usedExtensions.Add(".xml");
|
||||
}
|
||||
}
|
||||
|
||||
// OfflineList
|
||||
if (DatFormat.HasFlag(DatFormat.OfflineList))
|
||||
{
|
||||
if (usedExtensions.Contains(".xml"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.OfflineList, CreateOutFileNamesHelper(outDir, ".ol.xml", overwrite));
|
||||
usedExtensions.Add(".ol.xml");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.OfflineList, CreateOutFileNamesHelper(outDir, ".xml", overwrite));
|
||||
usedExtensions.Add(".xml");
|
||||
}
|
||||
}
|
||||
|
||||
// openMSX
|
||||
if (DatFormat.HasFlag(DatFormat.OpenMSX))
|
||||
{
|
||||
if (usedExtensions.Contains(".xml"))
|
||||
{
|
||||
outfileNames.Add(DatFormat.OpenMSX, CreateOutFileNamesHelper(outDir, ".msx.xml", overwrite));
|
||||
usedExtensions.Add(".msx.xml");
|
||||
}
|
||||
else
|
||||
{
|
||||
outfileNames.Add(DatFormat.OpenMSX, CreateOutFileNamesHelper(outDir, ".xml", overwrite));
|
||||
usedExtensions.Add(".xml");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return outfileNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Help generating the outfile name
|
||||
/// </summary>
|
||||
/// <param name="outDir">Output directory</param>
|
||||
/// <param name="extension">Extension to use for the file</param>
|
||||
/// <param name="overwrite">True if we ignore existing files, false otherwise</param>
|
||||
/// <returns>String containing the new filename</returns>
|
||||
private string CreateOutFileNamesHelper(string outDir, string extension, bool overwrite)
|
||||
{
|
||||
string filename = (string.IsNullOrWhiteSpace(FileName) ? Description : FileName);
|
||||
filename = Path.GetFileNameWithoutExtension(filename);
|
||||
string outfile = $"{outDir}{filename}{extension}";
|
||||
outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString());
|
||||
|
||||
if (!overwrite)
|
||||
{
|
||||
int i = 1;
|
||||
while (File.Exists(outfile))
|
||||
{
|
||||
outfile = $"{outDir}{filename}_{i}{extension}";
|
||||
outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return outfile;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion // Instance Methods
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
using SabreTools.Library.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
using SabreTools.Library.Reports;
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
@@ -24,6 +31,11 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
public StatReportFormat ReportFormat { get; set; } = StatReportFormat.None;
|
||||
|
||||
/// <summary>
|
||||
/// Statistics output file name
|
||||
/// </summary>
|
||||
public string ReportName { get; set; } = "report";
|
||||
|
||||
/// <summary>
|
||||
/// Overall item count
|
||||
/// </summary>
|
||||
@@ -80,10 +92,12 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
public long MD5Count { get; set; } = 0;
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
/// <summary>
|
||||
/// Number of items with a RIPEMD160 hash
|
||||
/// </summary>
|
||||
public long RIPEMD160Count { get; set; } = 0;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Number of items with a SHA-1 hash
|
||||
@@ -129,6 +143,8 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
#region Instance Methods
|
||||
|
||||
#region Updates
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics given a DatItem
|
||||
/// </summary>
|
||||
@@ -155,7 +171,9 @@ namespace SabreTools.Library.DatFiles
|
||||
if (((Disk)item).ItemStatus != ItemStatus.Nodump)
|
||||
{
|
||||
this.MD5Count += (string.IsNullOrWhiteSpace(((Disk)item).MD5) ? 0 : 1);
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160Count += (string.IsNullOrWhiteSpace(((Disk)item).RIPEMD160) ? 0 : 1);
|
||||
#endif
|
||||
this.SHA1Count += (string.IsNullOrWhiteSpace(((Disk)item).SHA1) ? 0 : 1);
|
||||
this.SHA256Count += (string.IsNullOrWhiteSpace(((Disk)item).SHA256) ? 0 : 1);
|
||||
this.SHA384Count += (string.IsNullOrWhiteSpace(((Disk)item).SHA384) ? 0 : 1);
|
||||
@@ -177,7 +195,9 @@ namespace SabreTools.Library.DatFiles
|
||||
this.TotalSize += ((Rom)item).Size;
|
||||
this.CRCCount += (string.IsNullOrWhiteSpace(((Rom)item).CRC) ? 0 : 1);
|
||||
this.MD5Count += (string.IsNullOrWhiteSpace(((Rom)item).MD5) ? 0 : 1);
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160Count += (string.IsNullOrWhiteSpace(((Rom)item).RIPEMD160) ? 0 : 1);
|
||||
#endif
|
||||
this.SHA1Count += (string.IsNullOrWhiteSpace(((Rom)item).SHA1) ? 0 : 1);
|
||||
this.SHA256Count += (string.IsNullOrWhiteSpace(((Rom)item).SHA256) ? 0 : 1);
|
||||
this.SHA384Count += (string.IsNullOrWhiteSpace(((Rom)item).SHA384) ? 0 : 1);
|
||||
@@ -218,7 +238,9 @@ namespace SabreTools.Library.DatFiles
|
||||
// Individual hash counts
|
||||
this.CRCCount += stats.CRCCount;
|
||||
this.MD5Count += stats.MD5Count;
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160Count += stats.RIPEMD160Count;
|
||||
#endif
|
||||
this.SHA1Count += stats.SHA1Count;
|
||||
this.SHA256Count += stats.SHA256Count;
|
||||
this.SHA384Count += stats.SHA384Count;
|
||||
@@ -229,7 +251,43 @@ namespace SabreTools.Library.DatFiles
|
||||
this.GoodCount += stats.GoodCount;
|
||||
this.NodumpCount += stats.NodumpCount;
|
||||
this.VerifiedCount += stats.VerifiedCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the highest-order BucketedBy value that represents the statistics
|
||||
/// </summary>
|
||||
public BucketedBy GetBestAvailable()
|
||||
{
|
||||
// If all items are supposed to have a SHA-512, we sort by that
|
||||
if (RomCount + DiskCount - NodumpCount == SHA512Count)
|
||||
return BucketedBy.SHA512;
|
||||
|
||||
// If all items are supposed to have a SHA-384, we sort by that
|
||||
else if (RomCount + DiskCount - NodumpCount == SHA384Count)
|
||||
return BucketedBy.SHA384;
|
||||
|
||||
// If all items are supposed to have a SHA-256, we sort by that
|
||||
else if (RomCount + DiskCount - NodumpCount == SHA256Count)
|
||||
return BucketedBy.SHA256;
|
||||
|
||||
// If all items are supposed to have a SHA-1, we sort by that
|
||||
else if (RomCount + DiskCount - NodumpCount == SHA1Count)
|
||||
return BucketedBy.SHA1;
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
// If all items are supposed to have a RIPEMD160, we sort by that
|
||||
else if (RomCount + DiskCount - NodumpCount == RIPEMD160Count)
|
||||
return BucketedBy.RIPEMD160;
|
||||
#endif
|
||||
|
||||
// If all items are supposed to have a MD5, we sort by that
|
||||
else if (RomCount + DiskCount - NodumpCount == MD5Count)
|
||||
return BucketedBy.MD5;
|
||||
|
||||
// Otherwise, we sort by CRC
|
||||
else
|
||||
return BucketedBy.CRC;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a DatItem
|
||||
@@ -257,7 +315,9 @@ namespace SabreTools.Library.DatFiles
|
||||
if (((Disk)item).ItemStatus != ItemStatus.Nodump)
|
||||
{
|
||||
this.MD5Count -= (string.IsNullOrWhiteSpace(((Disk)item).MD5) ? 0 : 1);
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160Count -= (string.IsNullOrWhiteSpace(((Disk)item).RIPEMD160) ? 0 : 1);
|
||||
#endif
|
||||
this.SHA1Count -= (string.IsNullOrWhiteSpace(((Disk)item).SHA1) ? 0 : 1);
|
||||
this.SHA256Count -= (string.IsNullOrWhiteSpace(((Disk)item).SHA256) ? 0 : 1);
|
||||
this.SHA384Count -= (string.IsNullOrWhiteSpace(((Disk)item).SHA384) ? 0 : 1);
|
||||
@@ -279,7 +339,9 @@ namespace SabreTools.Library.DatFiles
|
||||
this.TotalSize -= ((Rom)item).Size;
|
||||
this.CRCCount -= (string.IsNullOrWhiteSpace(((Rom)item).CRC) ? 0 : 1);
|
||||
this.MD5Count -= (string.IsNullOrWhiteSpace(((Rom)item).MD5) ? 0 : 1);
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160Count -= (string.IsNullOrWhiteSpace(((Rom)item).RIPEMD160) ? 0 : 1);
|
||||
#endif
|
||||
this.SHA1Count -= (string.IsNullOrWhiteSpace(((Rom)item).SHA1) ? 0 : 1);
|
||||
this.SHA256Count -= (string.IsNullOrWhiteSpace(((Rom)item).SHA256) ? 0 : 1);
|
||||
this.SHA384Count -= (string.IsNullOrWhiteSpace(((Rom)item).SHA384) ? 0 : 1);
|
||||
@@ -318,7 +380,9 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
this.CRCCount = 0;
|
||||
this.MD5Count = 0;
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160Count = 0;
|
||||
#endif
|
||||
this.SHA1Count = 0;
|
||||
this.SHA256Count = 0;
|
||||
this.SHA384Count = 0;
|
||||
@@ -330,6 +394,210 @@ namespace SabreTools.Library.DatFiles
|
||||
this.VerifiedCount = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Writing
|
||||
|
||||
/// <summary>
|
||||
/// Output the stats for a list of input dats as files in a human-readable format
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of input files and folders</param>
|
||||
/// <param name="reportName">Name of the output file</param>
|
||||
/// <param name="single">True if single DAT stats are output, false otherwise</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
/// <param name="statDatFormat" > Set the statistics output format to use</param>
|
||||
public static void OutputStats(List<string> inputs, string reportName, string outDir, bool single,
|
||||
bool baddumpCol, bool nodumpCol, StatReportFormat statDatFormat)
|
||||
{
|
||||
// If there's no output format, set the default
|
||||
if (statDatFormat == StatReportFormat.None)
|
||||
statDatFormat = StatReportFormat.Textfile;
|
||||
|
||||
// Get the proper output file name
|
||||
if (string.IsNullOrWhiteSpace(reportName))
|
||||
reportName = "report";
|
||||
|
||||
// Get the proper output directory name
|
||||
outDir = DirectoryExtensions.Ensure(outDir);
|
||||
|
||||
// Get the dictionary of desired output report names
|
||||
Dictionary<StatReportFormat, string> outputs = DatStats.CreateOutStatsNames(outDir, statDatFormat, reportName);
|
||||
|
||||
// Make sure we have all files and then order them
|
||||
List<string> files = DirectoryExtensions.GetFilesOnly(inputs);
|
||||
files = files
|
||||
.OrderBy(i => Path.GetDirectoryName(i))
|
||||
.ThenBy(i => Path.GetFileName(i))
|
||||
.ToList();
|
||||
|
||||
// Get all of the writers that we need
|
||||
List<BaseReport> reports = outputs.Select(kvp => BaseReport.Create(kvp.Key, kvp.Value, baddumpCol, nodumpCol)).ToList();
|
||||
|
||||
// Write the header, if any
|
||||
reports.ForEach(report => report.WriteHeader());
|
||||
|
||||
// Init all total variables
|
||||
DatStats totalStats = new DatStats();
|
||||
|
||||
// Init directory-level variables
|
||||
string lastdir = null;
|
||||
string basepath = null;
|
||||
DatStats dirStats = new DatStats();
|
||||
|
||||
// Now process each of the input files
|
||||
foreach (string file in files)
|
||||
{
|
||||
// Get the directory for the current file
|
||||
string thisdir = Path.GetDirectoryName(file);
|
||||
basepath = Path.GetDirectoryName(Path.GetDirectoryName(file));
|
||||
|
||||
// If we don't have the first file and the directory has changed, show the previous directory stats and reset
|
||||
if (lastdir != null && thisdir != lastdir)
|
||||
{
|
||||
// Output separator if needed
|
||||
reports.ForEach(report => report.WriteMidSeparator());
|
||||
|
||||
DatFile lastdirdat = DatFile.Create();
|
||||
|
||||
reports.ForEach(report => report.ReplaceStatistics($"DIR: {WebUtility.HtmlEncode(lastdir)}", dirStats.GameCount, dirStats));
|
||||
reports.ForEach(report => report.Write());
|
||||
|
||||
// Write the mid-footer, if any
|
||||
reports.ForEach(report => report.WriteFooterSeparator());
|
||||
|
||||
// Write the header, if any
|
||||
reports.ForEach(report => report.WriteMidHeader());
|
||||
|
||||
// Reset the directory stats
|
||||
dirStats.Reset();
|
||||
}
|
||||
|
||||
Globals.Logger.Verbose($"Beginning stat collection for '{file}'", false);
|
||||
List<string> games = new List<string>();
|
||||
DatFile datdata = DatFile.CreateAndParse(file);
|
||||
datdata.BucketBy(BucketedBy.Game, DedupeType.None, norename: true);
|
||||
|
||||
// Output single DAT stats (if asked)
|
||||
Globals.Logger.User($"Adding stats for file '{file}'\n", false);
|
||||
if (single)
|
||||
{
|
||||
reports.ForEach(report => report.ReplaceStatistics(datdata.DatHeader.FileName, datdata.Keys.Count, datdata.DatStats));
|
||||
reports.ForEach(report => report.Write());
|
||||
}
|
||||
|
||||
// Add single DAT stats to dir
|
||||
dirStats.AddStats(datdata.DatStats);
|
||||
dirStats.GameCount += datdata.Keys.Count();
|
||||
|
||||
// Add single DAT stats to totals
|
||||
totalStats.AddStats(datdata.DatStats);
|
||||
totalStats.GameCount += datdata.Keys.Count();
|
||||
|
||||
// Make sure to assign the new directory
|
||||
lastdir = thisdir;
|
||||
}
|
||||
|
||||
// Output the directory stats one last time
|
||||
reports.ForEach(report => report.WriteMidSeparator());
|
||||
|
||||
if (single)
|
||||
{
|
||||
reports.ForEach(report => report.ReplaceStatistics($"DIR: {WebUtility.HtmlEncode(lastdir)}", dirStats.GameCount, dirStats));
|
||||
reports.ForEach(report => report.Write());
|
||||
}
|
||||
|
||||
// Write the mid-footer, if any
|
||||
reports.ForEach(report => report.WriteFooterSeparator());
|
||||
|
||||
// Write the header, if any
|
||||
reports.ForEach(report => report.WriteMidHeader());
|
||||
|
||||
// Reset the directory stats
|
||||
dirStats.Reset();
|
||||
|
||||
// Output total DAT stats
|
||||
reports.ForEach(report => report.ReplaceStatistics("DIR: All DATs", totalStats.GameCount, totalStats));
|
||||
reports.ForEach(report => report.Write());
|
||||
|
||||
// Output footer if needed
|
||||
reports.ForEach(report => report.WriteFooter());
|
||||
|
||||
Globals.Logger.User(@"
|
||||
Please check the log folder if the stats scrolled offscreen", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the proper extension for the stat output format
|
||||
/// </summary>
|
||||
/// <param name="outDir">Output path to use</param>
|
||||
/// <param name="statDatFormat">StatDatFormat to get the extension for</param>
|
||||
/// <param name="reportName">Name of the input file to use</param>
|
||||
/// <returns>Dictionary of output formats mapped to file names</returns>
|
||||
private static Dictionary<StatReportFormat, string> CreateOutStatsNames(string outDir, StatReportFormat statDatFormat, string reportName, bool overwrite = true)
|
||||
{
|
||||
Dictionary<StatReportFormat, string> output = new Dictionary<StatReportFormat, string>();
|
||||
|
||||
// First try to create the output directory if we need to
|
||||
if (!Directory.Exists(outDir))
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
// Double check the outDir for the end delim
|
||||
if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
outDir += Path.DirectorySeparatorChar;
|
||||
|
||||
// For each output format, get the appropriate stream writer
|
||||
if (statDatFormat.HasFlag(StatReportFormat.None))
|
||||
output.Add(StatReportFormat.None, CreateOutStatsNamesHelper(outDir, ".null", reportName, overwrite));
|
||||
|
||||
if (statDatFormat.HasFlag(StatReportFormat.Textfile))
|
||||
output.Add(StatReportFormat.Textfile, CreateOutStatsNamesHelper(outDir, ".txt", reportName, overwrite));
|
||||
|
||||
if (statDatFormat.HasFlag(StatReportFormat.CSV))
|
||||
output.Add(StatReportFormat.CSV, CreateOutStatsNamesHelper(outDir, ".csv", reportName, overwrite));
|
||||
|
||||
if (statDatFormat.HasFlag(StatReportFormat.HTML))
|
||||
output.Add(StatReportFormat.HTML, CreateOutStatsNamesHelper(outDir, ".html", reportName, overwrite));
|
||||
|
||||
if (statDatFormat.HasFlag(StatReportFormat.SSV))
|
||||
output.Add(StatReportFormat.SSV, CreateOutStatsNamesHelper(outDir, ".ssv", reportName, overwrite));
|
||||
|
||||
if (statDatFormat.HasFlag(StatReportFormat.TSV))
|
||||
output.Add(StatReportFormat.TSV, CreateOutStatsNamesHelper(outDir, ".tsv", reportName, overwrite));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Help generating the outstats name
|
||||
/// </summary>
|
||||
/// <param name="outDir">Output directory</param>
|
||||
/// <param name="extension">Extension to use for the file</param>
|
||||
/// <param name="reportName">Name of the input file to use</param>
|
||||
/// <param name="overwrite">True if we ignore existing files, false otherwise</param>
|
||||
/// <returns>String containing the new filename</returns>
|
||||
private static string CreateOutStatsNamesHelper(string outDir, string extension, string reportName, bool overwrite)
|
||||
{
|
||||
string outfile = outDir + reportName + extension;
|
||||
outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString());
|
||||
|
||||
if (!overwrite)
|
||||
{
|
||||
int i = 1;
|
||||
while (File.Exists(outfile))
|
||||
{
|
||||
outfile = $"{outDir}{reportName}_{i}{extension}";
|
||||
outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return outfile;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion // Instance Methods
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
@@ -24,7 +23,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public DosCenter(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -32,26 +31,22 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a DOSCenter DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Open a file reader
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
ClrMameProReader cmpr = new ClrMameProReader(Utilities.TryOpenRead(filename), enc);
|
||||
cmpr.DosCenter = true;
|
||||
Encoding enc = FileExtensions.GetEncoding(filename);
|
||||
ClrMameProReader cmpr = new ClrMameProReader(FileExtensions.TryOpenRead(filename), enc)
|
||||
{
|
||||
DosCenter = true
|
||||
};
|
||||
|
||||
while (!cmpr.EndOfStream)
|
||||
{
|
||||
@@ -71,7 +66,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Sets
|
||||
case "game":
|
||||
ReadGame(cmpr, filename, sysid, srcid, clean, remUnicode);
|
||||
ReadGame(cmpr, filename, indexId);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -116,25 +111,25 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (itemKey)
|
||||
{
|
||||
case "name":
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? itemVal : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? itemVal : DatHeader.Name);
|
||||
break;
|
||||
case "description":
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? itemVal : Description);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? itemVal : DatHeader.Description);
|
||||
break;
|
||||
case "dersion":
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? itemVal : Version);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? itemVal : DatHeader.Version);
|
||||
break;
|
||||
case "date":
|
||||
Date = (string.IsNullOrWhiteSpace(Date) ? itemVal : Date);
|
||||
DatHeader.Date = (string.IsNullOrWhiteSpace(DatHeader.Date) ? itemVal : DatHeader.Date);
|
||||
break;
|
||||
case "author":
|
||||
Author = (string.IsNullOrWhiteSpace(Author) ? itemVal : Author);
|
||||
DatHeader.Author = (string.IsNullOrWhiteSpace(DatHeader.Author) ? itemVal : DatHeader.Author);
|
||||
break;
|
||||
case "homepage":
|
||||
Homepage = (string.IsNullOrWhiteSpace(Homepage) ? itemVal : Homepage);
|
||||
DatHeader.Homepage = (string.IsNullOrWhiteSpace(DatHeader.Homepage) ? itemVal : DatHeader.Homepage);
|
||||
break;
|
||||
case "comment":
|
||||
Comment = (string.IsNullOrWhiteSpace(Comment) ? itemVal : Comment);
|
||||
DatHeader.Comment = (string.IsNullOrWhiteSpace(DatHeader.Comment) ? itemVal : DatHeader.Comment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -145,21 +140,13 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="cmpr">ClrMameProReader to use to parse the header</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadGame(
|
||||
ClrMameProReader cmpr,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
bool containsItems = false;
|
||||
@@ -208,14 +195,13 @@ namespace SabreTools.Library.DatFiles
|
||||
containsItems = true;
|
||||
|
||||
// Create the proper DatItem based on the type
|
||||
Rom item = Utilities.GetDatItem(ItemType.Rom) as Rom;
|
||||
Rom item = DatItem.Create(ItemType.Rom) as Rom;
|
||||
|
||||
// Then populate it with information
|
||||
item.CopyMachineInformation(machine);
|
||||
|
||||
item.SystemID = sysid;
|
||||
item.System = filename;
|
||||
item.SourceID = srcid;
|
||||
item.IndexId = indexId;
|
||||
item.IndexSource = filename;
|
||||
|
||||
// Loop through all of the attributes
|
||||
foreach (var kvp in cmpr.Internal)
|
||||
@@ -243,7 +229,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "crc":
|
||||
item.CRC = Utilities.CleanHashData(attrVal, Constants.CRCLength);
|
||||
item.CRC = attrVal;
|
||||
break;
|
||||
case "date":
|
||||
item.Date = attrVal;
|
||||
@@ -252,7 +238,7 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(item, clean, remUnicode);
|
||||
ParseAddHelper(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,15 +247,14 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Blank blank = new Blank()
|
||||
{
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
blank.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(blank, clean, remUnicode);
|
||||
ParseAddHelper(blank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +270,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -294,8 +279,10 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
ClrMameProWriter cmpw = new ClrMameProWriter(fs, new UTF8Encoding(false));
|
||||
cmpw.Quotes = false;
|
||||
ClrMameProWriter cmpw = new ClrMameProWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Quotes = false
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(cmpw);
|
||||
@@ -329,7 +316,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// If we have a different game and we're not at the start of the list, output the end of last item
|
||||
if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant())
|
||||
WriteEndGame(cmpw, rom);
|
||||
WriteEndGame(cmpw);
|
||||
|
||||
// If we have a new game, output the beginning of the new item
|
||||
if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant())
|
||||
@@ -346,7 +333,9 @@ namespace SabreTools.Library.DatFiles
|
||||
((Rom)rom).Size = Constants.SizeZero;
|
||||
((Rom)rom).CRC = ((Rom)rom).CRC == "null" ? Constants.CRCZero : null;
|
||||
((Rom)rom).MD5 = ((Rom)rom).MD5 == "null" ? Constants.MD5Zero : null;
|
||||
#if NET_FRAMEWORK
|
||||
((Rom)rom).RIPEMD160 = ((Rom)rom).RIPEMD160 == "null" ? Constants.RIPEMD160Zero : null;
|
||||
#endif
|
||||
((Rom)rom).SHA1 = ((Rom)rom).SHA1 == "null" ? Constants.SHA1Zero : null;
|
||||
((Rom)rom).SHA256 = ((Rom)rom).SHA256 == "null" ? Constants.SHA256Zero : null;
|
||||
((Rom)rom).SHA384 = ((Rom)rom).SHA384 == "null" ? Constants.SHA384Zero : null;
|
||||
@@ -387,13 +376,13 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
cmpw.WriteStartElement("DOSCenter");
|
||||
cmpw.WriteStandalone("Name:", Name, false);
|
||||
cmpw.WriteStandalone("Description:", Description, false);
|
||||
cmpw.WriteStandalone("Version:", Version, false);
|
||||
cmpw.WriteStandalone("Date:", Date, false);
|
||||
cmpw.WriteStandalone("Author:", Author, false);
|
||||
cmpw.WriteStandalone("Homepage:", Homepage, false);
|
||||
cmpw.WriteStandalone("Comment:", Comment, false);
|
||||
cmpw.WriteStandalone("Name:", DatHeader.Name, false);
|
||||
cmpw.WriteStandalone("Description:", DatHeader.Description, false);
|
||||
cmpw.WriteStandalone("Version:", DatHeader.Version, false);
|
||||
cmpw.WriteStandalone("Date:", DatHeader.Date, false);
|
||||
cmpw.WriteStandalone("Author:", DatHeader.Author, false);
|
||||
cmpw.WriteStandalone("Homepage:", DatHeader.Homepage, false);
|
||||
cmpw.WriteStandalone("Comment:", DatHeader.Comment, false);
|
||||
cmpw.WriteEndElement();
|
||||
|
||||
cmpw.Flush();
|
||||
@@ -422,7 +411,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Build the state based on excluded fields
|
||||
cmpw.WriteStartElement("game");
|
||||
cmpw.WriteStandalone("name", $"{datItem.GetField(Field.MachineName, ExcludeFields)}.zip", true);
|
||||
cmpw.WriteStandalone("name", $"{datItem.GetField(Field.MachineName, DatHeader.ExcludeFields)}.zip", true);
|
||||
|
||||
cmpw.Flush();
|
||||
}
|
||||
@@ -439,9 +428,8 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Write out Game end using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="cmpw">ClrMameProWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
/// <returns>True if the data was written, false on error</returns>
|
||||
private bool WriteEndGame(ClrMameProWriter cmpw, DatItem datItem)
|
||||
private bool WriteEndGame(ClrMameProWriter cmpw)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -483,12 +471,12 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
cmpw.WriteStartElement("file");
|
||||
cmpw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
cmpw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
cmpw.WriteAttributeString("size", rom.Size.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("date", rom.Date);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
cmpw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant());
|
||||
cmpw.WriteEndElement();
|
||||
break;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public EverdriveSMDB(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,25 +29,19 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse an Everdrive SMDB file and return all found games within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Open a file reader
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc);
|
||||
Encoding enc = FileExtensions.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(FileExtensions.TryOpenRead(filename), enc);
|
||||
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
@@ -69,18 +63,21 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = gameinfo[1].Substring(fullname[0].Length + 1),
|
||||
Size = -1, // No size provided, but we don't want the size being 0
|
||||
CRC = Utilities.CleanHashData(gameinfo[4], Constants.CRCLength),
|
||||
MD5 = Utilities.CleanHashData(gameinfo[3], Constants.MD5Length),
|
||||
SHA1 = Utilities.CleanHashData(gameinfo[2], Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(gameinfo[0], Constants.SHA256Length),
|
||||
CRC = gameinfo[4],
|
||||
MD5 = gameinfo[3],
|
||||
SHA1 = gameinfo[2],
|
||||
SHA256 = gameinfo[0],
|
||||
ItemStatus = ItemStatus.None,
|
||||
|
||||
MachineName = fullname[0],
|
||||
MachineDescription = fullname[0],
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
sr.Dispose();
|
||||
@@ -97,7 +94,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -106,10 +103,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false));
|
||||
svw.Quotes = false;
|
||||
svw.Separator = '\t';
|
||||
svw.VerifyFieldCount = true;
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Quotes = false,
|
||||
Separator = '\t',
|
||||
VerifyFieldCount = true
|
||||
};
|
||||
|
||||
// Get a properly sorted set of keys
|
||||
List<string> keys = Keys;
|
||||
@@ -189,12 +188,12 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
string[] fields = new string[]
|
||||
{
|
||||
rom.GetField(Field.SHA256, ExcludeFields),
|
||||
$"{rom.GetField(Field.MachineName, ExcludeFields)}/",
|
||||
rom.GetField(Field.Name, ExcludeFields),
|
||||
rom.GetField(Field.SHA1, ExcludeFields),
|
||||
rom.GetField(Field.MD5, ExcludeFields),
|
||||
rom.GetField(Field.CRC, ExcludeFields),
|
||||
rom.GetField(Field.SHA256, DatHeader.ExcludeFields),
|
||||
$"{rom.GetField(Field.MachineName, DatHeader.ExcludeFields)}/",
|
||||
rom.GetField(Field.Name, DatHeader.ExcludeFields),
|
||||
rom.GetField(Field.SHA1, DatHeader.ExcludeFields),
|
||||
rom.GetField(Field.MD5, DatHeader.ExcludeFields),
|
||||
rom.GetField(Field.CRC, DatHeader.ExcludeFields),
|
||||
};
|
||||
|
||||
svw.WriteValues(fields);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
@@ -15,16 +20,37 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
#region Pubically facing variables
|
||||
|
||||
#region Machine Filters
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude machine names
|
||||
/// </summary>
|
||||
public FilterItem<string> MachineName { get; set; } = new FilterItem<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Include romof and cloneof when filtering machine names
|
||||
/// </summary>
|
||||
public FilterItem<bool> IncludeOfInGame { get; set; } = new FilterItem<bool>() { Neutral = false };
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude machine descriptions
|
||||
/// </summary>
|
||||
public FilterItem<string> MachineDescription { get; set; } = new FilterItem<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude machine types
|
||||
/// </summary>
|
||||
public FilterItem<MachineType> MachineTypes { get; set; } = new FilterItem<MachineType>() { Positive = MachineType.NULL, Negative = MachineType.NULL };
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude items with the "Runnable" tag
|
||||
/// </summary>
|
||||
public FilterItem<bool?> Runnable { get; set; } = new FilterItem<bool?>() { Neutral = null };
|
||||
|
||||
#endregion
|
||||
|
||||
#region DatItem Filters
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude item names
|
||||
/// </summary>
|
||||
@@ -35,6 +61,12 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
public FilterItem<string> ItemTypes { get; set; } = new FilterItem<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude item sizes
|
||||
/// </summary>
|
||||
/// <remarks>Positive means "Greater than or equal", Negative means "Less than or equal", Neutral means "Equal"</remarks>
|
||||
public FilterItem<long> Size { get; set; } = new FilterItem<long>() { Positive = -1, Negative = -1, Neutral = -1 };
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude CRC32 hashes
|
||||
/// </summary>
|
||||
@@ -45,10 +77,12 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
public FilterItem<string> MD5 { get; set; } = new FilterItem<string>();
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
/// <summary>
|
||||
/// Include or exclude RIPEMD160 hashes
|
||||
/// </summary>
|
||||
public FilterItem<string> RIPEMD160 { get; set; } = new FilterItem<string>();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude SHA-1 hashes
|
||||
@@ -75,26 +109,29 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
public FilterItem<ItemStatus> ItemStatuses { get; set; } = new FilterItem<ItemStatus>() { Positive = ItemStatus.NULL, Negative = ItemStatus.NULL };
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude machine types
|
||||
/// </summary>
|
||||
public FilterItem<MachineType> MachineTypes { get; set; } = new FilterItem<MachineType>() { Positive = MachineType.NULL, Negative = MachineType.NULL };
|
||||
#endregion
|
||||
|
||||
#region Manipulation Filters
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude item sizes
|
||||
/// Clean all names to WoD standards
|
||||
/// </summary>
|
||||
/// <remarks>Positive means "Greater than or equal", Negative means "Less than or equal", Neutral means "Equal"</remarks>
|
||||
public FilterItem<long> Size { get; set; } = new FilterItem<long>() { Positive = -1, Negative = -1, Neutral = -1 };
|
||||
public FilterItem<bool> Clean { get; set; } = new FilterItem<bool>() { Neutral = false };
|
||||
|
||||
/// <summary>
|
||||
/// Include romof and cloneof when filtering machine names
|
||||
/// Set Machine Description from Machine Name
|
||||
/// </summary>
|
||||
public FilterItem<bool> IncludeOfInGame { get; set; } = new FilterItem<bool>() { Neutral = false };
|
||||
public FilterItem<bool> DescriptionAsName { get; set; } = new FilterItem<bool>() { Neutral = false };
|
||||
|
||||
/// <summary>
|
||||
/// Include or exclude items with the "Runnable" tag
|
||||
/// Internally split a DatFile
|
||||
/// </summary>
|
||||
public FilterItem<bool?> Runnable { get; set; } = new FilterItem<bool?>() { Neutral = null };
|
||||
public FilterItem<SplitType> InternalSplit { get; set; } = new FilterItem<SplitType>() { Neutral = SplitType.None };
|
||||
|
||||
/// <summary>
|
||||
/// Remove all unicode characters
|
||||
/// </summary>
|
||||
public FilterItem<bool> RemoveUnicode { get; set; } = new FilterItem<bool>() { Neutral = false };
|
||||
|
||||
/// <summary>
|
||||
/// Change all machine names to "!"
|
||||
@@ -111,6 +148,8 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
public FilterItem<string> Root { get; set; } = new FilterItem<string>() { Neutral = null };
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion // Pubically facing variables
|
||||
|
||||
#region Instance methods
|
||||
@@ -118,9 +157,10 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <summary>
|
||||
/// Filter a DatFile using the inputs
|
||||
/// </summary>
|
||||
/// <param name="datFile"></param>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="useTags">True if DatFile tags override splitting, false otherwise</param>
|
||||
/// <returns>True if the DatFile was filtered, false on error</returns>
|
||||
public bool FilterDatFile(DatFile datFile)
|
||||
public bool FilterDatFile(DatFile datFile, bool useTags)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -136,6 +176,21 @@ namespace SabreTools.Library.DatFiles
|
||||
// If the rom passes the filter, include it
|
||||
if (ItemPasses(item))
|
||||
{
|
||||
// If we're stripping unicode characters, do so from all relevant things
|
||||
if (this.RemoveUnicode.Neutral)
|
||||
{
|
||||
item.Name = Sanitizer.RemoveUnicodeCharacters(item.Name);
|
||||
item.MachineName = Sanitizer.RemoveUnicodeCharacters(item.MachineName);
|
||||
item.MachineDescription = Sanitizer.RemoveUnicodeCharacters(item.MachineDescription);
|
||||
}
|
||||
|
||||
// If we're in cleaning mode, do so from all relevant things
|
||||
if (this.Clean.Neutral)
|
||||
{
|
||||
item.MachineName = Sanitizer.CleanGameName(item.MachineName);
|
||||
item.MachineDescription = Sanitizer.CleanGameName(item.MachineDescription);
|
||||
}
|
||||
|
||||
// If we are in single game mode, rename all games
|
||||
if (this.Single.Neutral)
|
||||
item.MachineName = "!";
|
||||
@@ -164,6 +219,38 @@ namespace SabreTools.Library.DatFiles
|
||||
datFile.Remove(key);
|
||||
datFile.AddRange(key, newitems);
|
||||
}
|
||||
|
||||
// Process description to machine name
|
||||
if (this.DescriptionAsName.Neutral)
|
||||
MachineDescriptionToName(datFile);
|
||||
|
||||
// If we are using tags from the DAT, set the proper input for split type unless overridden
|
||||
if (useTags && this.InternalSplit.Neutral == SplitType.None)
|
||||
this.InternalSplit.Neutral = datFile.DatHeader.ForceMerging.AsSplitType();
|
||||
|
||||
// Run internal splitting
|
||||
ProcessSplitType(datFile, this.InternalSplit.Neutral);
|
||||
|
||||
// We remove any blanks, if we aren't supposed to have any
|
||||
if (!datFile.DatHeader.KeepEmptyGames)
|
||||
{
|
||||
foreach (string key in datFile.Keys)
|
||||
{
|
||||
List<DatItem> items = datFile[key];
|
||||
List<DatItem> newitems = items.Where(i => i.ItemType != ItemType.Blank).ToList();
|
||||
|
||||
datFile.Remove(key);
|
||||
datFile.AddRange(key, newitems);
|
||||
}
|
||||
}
|
||||
|
||||
// If we are removing scene dates, do that now
|
||||
if (datFile.DatHeader.SceneDateStrip)
|
||||
StripSceneDatesFromItems(datFile);
|
||||
|
||||
// Run the one rom per game logic, if required
|
||||
if (datFile.DatHeader.OneRom)
|
||||
OneRomPerGame(datFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -226,11 +313,13 @@ namespace SabreTools.Library.DatFiles
|
||||
if (this.MD5.MatchesNegativeSet(rom.MD5) == true)
|
||||
return false;
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
// Filter on RIPEMD160
|
||||
if (this.RIPEMD160.MatchesPositiveSet(rom.RIPEMD160) == false)
|
||||
return false;
|
||||
if (this.RIPEMD160.MatchesNegativeSet(rom.RIPEMD160) == true)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Filter on SHA-1
|
||||
if (this.SHA1.MatchesPositiveSet(rom.SHA1) == false)
|
||||
@@ -272,11 +361,13 @@ namespace SabreTools.Library.DatFiles
|
||||
if (this.MD5.MatchesNegativeSet(rom.MD5) == true)
|
||||
return false;
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
// Filter on RIPEMD160
|
||||
if (this.RIPEMD160.MatchesPositiveSet(rom.RIPEMD160) == false)
|
||||
return false;
|
||||
if (this.RIPEMD160.MatchesNegativeSet(rom.RIPEMD160) == true)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Filter on SHA-1
|
||||
if (this.SHA1.MatchesPositiveSet(rom.SHA1) == false)
|
||||
@@ -346,6 +437,642 @@ namespace SabreTools.Library.DatFiles
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Internal Splitting/Merging
|
||||
|
||||
/// <summary>
|
||||
/// Process items according to SplitType
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="splitType">SplitType to implement</param>
|
||||
private void ProcessSplitType(DatFile datFile, SplitType splitType)
|
||||
{
|
||||
// Now we pre-process the DAT with the splitting/merging mode
|
||||
switch (splitType)
|
||||
{
|
||||
case SplitType.None:
|
||||
// No-op
|
||||
break;
|
||||
case SplitType.DeviceNonMerged:
|
||||
CreateDeviceNonMergedSets(datFile, DedupeType.None);
|
||||
break;
|
||||
case SplitType.FullNonMerged:
|
||||
CreateFullyNonMergedSets(datFile, DedupeType.None);
|
||||
break;
|
||||
case SplitType.NonMerged:
|
||||
CreateNonMergedSets(datFile, DedupeType.None);
|
||||
break;
|
||||
case SplitType.Merged:
|
||||
CreateMergedSets(datFile, DedupeType.None);
|
||||
break;
|
||||
case SplitType.Split:
|
||||
CreateSplitSets(datFile, DedupeType.None);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cdevice_ref tags to get full non-merged sets and remove parenting tags
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="mergeroms">Dedupe type to be used</param>
|
||||
private void CreateDeviceNonMergedSets(DatFile datFile, DedupeType mergeroms)
|
||||
{
|
||||
Globals.Logger.User("Creating device non-merged sets from the DAT");
|
||||
|
||||
// For sake of ease, the first thing we want to do is bucket by game
|
||||
datFile.BucketBy(BucketedBy.Game, mergeroms, norename: true);
|
||||
|
||||
// Now we want to loop through all of the games and set the correct information
|
||||
while (AddRomsFromDevices(datFile, false, false)) ;
|
||||
while (AddRomsFromDevices(datFile, true, false)) ;
|
||||
|
||||
// Then, remove the romof and cloneof tags so it's not picked up by the manager
|
||||
RemoveTagsFromChild(datFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cloneof tags to create non-merged sets and remove the tags plus using the device_ref tags to get full sets
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="mergeroms">Dedupe type to be used</param>
|
||||
private void CreateFullyNonMergedSets(DatFile datFile, DedupeType mergeroms)
|
||||
{
|
||||
Globals.Logger.User("Creating fully non-merged sets from the DAT");
|
||||
|
||||
// For sake of ease, the first thing we want to do is sort by game
|
||||
datFile.BucketBy(BucketedBy.Game, mergeroms, norename: true);
|
||||
|
||||
// Now we want to loop through all of the games and set the correct information
|
||||
while (AddRomsFromDevices(datFile, true, true)) ;
|
||||
AddRomsFromDevices(datFile, false, true);
|
||||
AddRomsFromParent(datFile);
|
||||
|
||||
// Now that we have looped through the cloneof tags, we loop through the romof tags
|
||||
AddRomsFromBios(datFile);
|
||||
|
||||
// Then, remove the romof and cloneof tags so it's not picked up by the manager
|
||||
RemoveTagsFromChild(datFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cloneof tags to create merged sets and remove the tags
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="mergeroms">Dedupe type to be used</param>
|
||||
private void CreateMergedSets(DatFile datFile, DedupeType mergeroms)
|
||||
{
|
||||
Globals.Logger.User("Creating merged sets from the DAT");
|
||||
|
||||
// For sake of ease, the first thing we want to do is sort by game
|
||||
datFile.BucketBy(BucketedBy.Game, mergeroms, norename: true);
|
||||
|
||||
// Now we want to loop through all of the games and set the correct information
|
||||
AddRomsFromChildren(datFile);
|
||||
|
||||
// Now that we have looped through the cloneof tags, we loop through the romof tags
|
||||
RemoveBiosRomsFromChild(datFile, false);
|
||||
RemoveBiosRomsFromChild(datFile, true);
|
||||
|
||||
// Finally, remove the romof and cloneof tags so it's not picked up by the manager
|
||||
RemoveTagsFromChild(datFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cloneof tags to create non-merged sets and remove the tags
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="mergeroms">Dedupe type to be used</param>
|
||||
private void CreateNonMergedSets(DatFile datFile, DedupeType mergeroms)
|
||||
{
|
||||
Globals.Logger.User("Creating non-merged sets from the DAT");
|
||||
|
||||
// For sake of ease, the first thing we want to do is sort by game
|
||||
datFile.BucketBy(BucketedBy.Game, mergeroms, norename: true);
|
||||
|
||||
// Now we want to loop through all of the games and set the correct information
|
||||
AddRomsFromParent(datFile);
|
||||
|
||||
// Now that we have looped through the cloneof tags, we loop through the romof tags
|
||||
RemoveBiosRomsFromChild(datFile, false);
|
||||
RemoveBiosRomsFromChild(datFile, true);
|
||||
|
||||
// Finally, remove the romof and cloneof tags so it's not picked up by the manager
|
||||
RemoveTagsFromChild(datFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cloneof and romof tags to create split sets and remove the tags
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="mergeroms">Dedupe type to be used</param>
|
||||
private void CreateSplitSets(DatFile datFile, DedupeType mergeroms)
|
||||
{
|
||||
Globals.Logger.User("Creating split sets from the DAT");
|
||||
|
||||
// For sake of ease, the first thing we want to do is sort by game
|
||||
datFile.BucketBy(BucketedBy.Game, mergeroms, norename: true);
|
||||
|
||||
// Now we want to loop through all of the games and set the correct information
|
||||
RemoveRomsFromChild(datFile);
|
||||
|
||||
// Now that we have looped through the cloneof tags, we loop through the romof tags
|
||||
RemoveBiosRomsFromChild(datFile, false);
|
||||
RemoveBiosRomsFromChild(datFile, true);
|
||||
|
||||
// Finally, remove the romof and cloneof tags so it's not picked up by the manager
|
||||
RemoveTagsFromChild(datFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use romof tags to add roms to the children
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void AddRomsFromBios(DatFile datFile)
|
||||
{
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
// If the game has no items in it, we want to continue
|
||||
if (datFile[game].Count == 0)
|
||||
continue;
|
||||
|
||||
// Determine if the game has a parent or not
|
||||
string parent = null;
|
||||
if (!string.IsNullOrWhiteSpace(datFile[game][0].RomOf))
|
||||
parent = datFile[game][0].RomOf;
|
||||
|
||||
// If the parent doesnt exist, we want to continue
|
||||
if (string.IsNullOrWhiteSpace(parent))
|
||||
continue;
|
||||
|
||||
// If the parent doesn't have any items, we want to continue
|
||||
if (datFile[parent].Count == 0)
|
||||
continue;
|
||||
|
||||
// If the parent exists and has items, we copy the items from the parent to the current game
|
||||
DatItem copyFrom = datFile[game][0];
|
||||
List<DatItem> parentItems = datFile[parent];
|
||||
foreach (DatItem item in parentItems)
|
||||
{
|
||||
DatItem datItem = (DatItem)item.Clone();
|
||||
datItem.CopyMachineInformation(copyFrom);
|
||||
if (datFile[game].Where(i => i.Name == datItem.Name).Count() == 0 && !datFile[game].Contains(datItem))
|
||||
datFile.Add(game, datItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use device_ref and optionally slotoption tags to add roms to the children
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="dev">True if only child device sets are touched, false for non-device sets (default)</param>
|
||||
/// <param name="slotoptions">True if slotoptions tags are used as well, false otherwise</param>
|
||||
private bool AddRomsFromDevices(DatFile datFile, bool dev = false, bool slotoptions = false)
|
||||
{
|
||||
bool foundnew = false;
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
// If the game doesn't have items, we continue
|
||||
if (datFile[game] == null || datFile[game].Count == 0)
|
||||
continue;
|
||||
|
||||
// If the game (is/is not) a bios, we want to continue
|
||||
if (dev ^ (datFile[game][0].MachineType.HasFlag(MachineType.Device)))
|
||||
continue;
|
||||
|
||||
// If the game has no devices, we continue
|
||||
if (datFile[game][0].Devices == null
|
||||
|| datFile[game][0].Devices.Count == 0
|
||||
|| (slotoptions && datFile[game][0].SlotOptions == null)
|
||||
|| (slotoptions && datFile[game][0].SlotOptions.Count == 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine if the game has any devices or not
|
||||
List<string> devices = datFile[game][0].Devices;
|
||||
List<string> newdevs = new List<string>();
|
||||
foreach (string device in devices)
|
||||
{
|
||||
// If the device doesn't exist then we continue
|
||||
if (datFile[device].Count == 0)
|
||||
continue;
|
||||
|
||||
// Otherwise, copy the items from the device to the current game
|
||||
DatItem copyFrom = datFile[game][0];
|
||||
List<DatItem> devItems = datFile[device];
|
||||
foreach (DatItem item in devItems)
|
||||
{
|
||||
DatItem datItem = (DatItem)item.Clone();
|
||||
newdevs.AddRange(datItem.Devices ?? new List<string>());
|
||||
datItem.CopyMachineInformation(copyFrom);
|
||||
if (datFile[game].Where(i => i.Name.ToLowerInvariant() == datItem.Name.ToLowerInvariant()).Count() == 0)
|
||||
{
|
||||
foundnew = true;
|
||||
datFile.Add(game, datItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that every device is accounted for, add the new list of devices, if they don't already exist
|
||||
foreach (string device in newdevs)
|
||||
{
|
||||
if (!datFile[game][0].Devices.Contains(device))
|
||||
datFile[game][0].Devices.Add(device);
|
||||
}
|
||||
|
||||
// If we're checking slotoptions too
|
||||
if (slotoptions)
|
||||
{
|
||||
// Determine if the game has any slotoptions or not
|
||||
List<string> slotopts = datFile[game][0].SlotOptions;
|
||||
List<string> newslotopts = new List<string>();
|
||||
foreach (string slotopt in slotopts)
|
||||
{
|
||||
// If the slotoption doesn't exist then we continue
|
||||
if (datFile[slotopt].Count == 0)
|
||||
continue;
|
||||
|
||||
// Otherwise, copy the items from the slotoption to the current game
|
||||
DatItem copyFrom = datFile[game][0];
|
||||
List<DatItem> slotItems = datFile[slotopt];
|
||||
foreach (DatItem item in slotItems)
|
||||
{
|
||||
DatItem datItem = (DatItem)item.Clone();
|
||||
newslotopts.AddRange(datItem.SlotOptions ?? new List<string>());
|
||||
datItem.CopyMachineInformation(copyFrom);
|
||||
if (datFile[game].Where(i => i.Name.ToLowerInvariant() == datItem.Name.ToLowerInvariant()).Count() == 0)
|
||||
{
|
||||
foundnew = true;
|
||||
datFile.Add(game, datItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that every slotoption is accounted for, add the new list of slotoptions, if they don't already exist
|
||||
foreach (string slotopt in newslotopts)
|
||||
{
|
||||
if (!datFile[game][0].SlotOptions.Contains(slotopt))
|
||||
datFile[game][0].SlotOptions.Add(slotopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundnew;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cloneof tags to add roms to the children, setting the new romof tag in the process
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void AddRomsFromParent(DatFile datFile)
|
||||
{
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
// If the game has no items in it, we want to continue
|
||||
if (datFile[game].Count == 0)
|
||||
continue;
|
||||
|
||||
// Determine if the game has a parent or not
|
||||
string parent = null;
|
||||
if (!string.IsNullOrWhiteSpace(datFile[game][0].CloneOf))
|
||||
parent = datFile[game][0].CloneOf;
|
||||
|
||||
// If the parent doesnt exist, we want to continue
|
||||
if (string.IsNullOrWhiteSpace(parent))
|
||||
continue;
|
||||
|
||||
// If the parent doesn't have any items, we want to continue
|
||||
if (datFile[parent].Count == 0)
|
||||
continue;
|
||||
|
||||
// If the parent exists and has items, we copy the items from the parent to the current game
|
||||
DatItem copyFrom = datFile[game][0];
|
||||
List<DatItem> parentItems = datFile[parent];
|
||||
foreach (DatItem item in parentItems)
|
||||
{
|
||||
DatItem datItem = (DatItem)item.Clone();
|
||||
datItem.CopyMachineInformation(copyFrom);
|
||||
if (datFile[game].Where(i => i.Name.ToLowerInvariant() == datItem.Name.ToLowerInvariant()).Count() == 0
|
||||
&& !datFile[game].Contains(datItem))
|
||||
{
|
||||
datFile.Add(game, datItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we want to get the parent romof tag and put it in each of the items
|
||||
List<DatItem> items = datFile[game];
|
||||
string romof = datFile[parent][0].RomOf;
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
item.RomOf = romof;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cloneof tags to add roms to the parents, removing the child sets in the process
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void AddRomsFromChildren(DatFile datFile)
|
||||
{
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
// If the game has no items in it, we want to continue
|
||||
if (datFile[game].Count == 0)
|
||||
continue;
|
||||
|
||||
// Determine if the game has a parent or not
|
||||
string parent = null;
|
||||
if (!string.IsNullOrWhiteSpace(datFile[game][0].CloneOf))
|
||||
parent = datFile[game][0].CloneOf;
|
||||
|
||||
// If there is no parent, then we continue
|
||||
if (string.IsNullOrWhiteSpace(parent))
|
||||
continue;
|
||||
|
||||
// Otherwise, move the items from the current game to a subfolder of the parent game
|
||||
DatItem copyFrom = datFile[parent].Count == 0 ? new Rom { MachineName = parent, MachineDescription = parent } : datFile[parent][0];
|
||||
List<DatItem> items = datFile[game];
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
// If the disk doesn't have a valid merge tag OR the merged file doesn't exist in the parent, then add it
|
||||
if (item.ItemType == ItemType.Disk && (((Disk)item).MergeTag == null || !datFile[parent].Select(i => i.Name).Contains(((Disk)item).MergeTag)))
|
||||
{
|
||||
item.CopyMachineInformation(copyFrom);
|
||||
datFile.Add(parent, item);
|
||||
}
|
||||
|
||||
// Otherwise, if the parent doesn't already contain the non-disk (or a merge-equivalent), add it
|
||||
else if (item.ItemType != ItemType.Disk && !datFile[parent].Contains(item))
|
||||
{
|
||||
// Rename the child so it's in a subfolder
|
||||
item.Name = $"{item.MachineName}\\{item.Name}";
|
||||
|
||||
// Update the machine to be the new parent
|
||||
item.CopyMachineInformation(copyFrom);
|
||||
|
||||
// Add the rom to the parent set
|
||||
datFile.Add(parent, item);
|
||||
}
|
||||
}
|
||||
|
||||
// Then, remove the old game so it's not picked up by the writer
|
||||
datFile.Remove(game);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all BIOS and device sets
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void RemoveBiosAndDeviceSets(DatFile datFile)
|
||||
{
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
if (datFile[game].Count > 0
|
||||
&& (datFile[game][0].MachineType.HasFlag(MachineType.Bios)
|
||||
|| datFile[game][0].MachineType.HasFlag(MachineType.Device)))
|
||||
{
|
||||
datFile.Remove(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use romof tags to remove bios roms from children
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// <param name="bios">True if only child Bios sets are touched, false for non-bios sets (default)</param>
|
||||
private void RemoveBiosRomsFromChild(DatFile datFile, bool bios = false)
|
||||
{
|
||||
// Loop through the romof tags
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
// If the game has no items in it, we want to continue
|
||||
if (datFile[game].Count == 0)
|
||||
continue;
|
||||
|
||||
// If the game (is/is not) a bios, we want to continue
|
||||
if (bios ^ datFile[game][0].MachineType.HasFlag(MachineType.Bios))
|
||||
continue;
|
||||
|
||||
// Determine if the game has a parent or not
|
||||
string parent = null;
|
||||
if (!string.IsNullOrWhiteSpace(datFile[game][0].RomOf))
|
||||
parent = datFile[game][0].RomOf;
|
||||
|
||||
// If the parent doesnt exist, we want to continue
|
||||
if (string.IsNullOrWhiteSpace(parent))
|
||||
continue;
|
||||
|
||||
// If the parent doesn't have any items, we want to continue
|
||||
if (datFile[parent].Count == 0)
|
||||
continue;
|
||||
|
||||
// If the parent exists and has items, we remove the items that are in the parent from the current game
|
||||
List<DatItem> parentItems = datFile[parent];
|
||||
foreach (DatItem item in parentItems)
|
||||
{
|
||||
DatItem datItem = (DatItem)item.Clone();
|
||||
while (datFile[game].Contains(datItem))
|
||||
{
|
||||
datFile.Remove(game, datItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use cloneof tags to remove roms from the children
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void RemoveRomsFromChild(DatFile datFile)
|
||||
{
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
// If the game has no items in it, we want to continue
|
||||
if (datFile[game].Count == 0)
|
||||
continue;
|
||||
|
||||
// Determine if the game has a parent or not
|
||||
string parent = null;
|
||||
if (!string.IsNullOrWhiteSpace(datFile[game][0].CloneOf))
|
||||
parent = datFile[game][0].CloneOf;
|
||||
|
||||
// If the parent doesnt exist, we want to continue
|
||||
if (string.IsNullOrWhiteSpace(parent))
|
||||
continue;
|
||||
|
||||
// If the parent doesn't have any items, we want to continue
|
||||
if (datFile[parent].Count == 0)
|
||||
continue;
|
||||
|
||||
// If the parent exists and has items, we remove the parent items from the current game
|
||||
List<DatItem> parentItems = datFile[parent];
|
||||
foreach (DatItem item in parentItems)
|
||||
{
|
||||
DatItem datItem = (DatItem)item.Clone();
|
||||
while (datFile[game].Contains(datItem))
|
||||
{
|
||||
datFile.Remove(game, datItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we want to get the parent romof tag and put it in each of the remaining items
|
||||
List<DatItem> items = datFile[game];
|
||||
string romof = datFile[parent][0].RomOf;
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
item.RomOf = romof;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all romof and cloneof tags from all games
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void RemoveTagsFromChild(DatFile datFile)
|
||||
{
|
||||
List<string> games = datFile.Keys;
|
||||
foreach (string game in games)
|
||||
{
|
||||
List<DatItem> items = datFile[game];
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
item.CloneOf = null;
|
||||
item.RomOf = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Use game descriptions as names in the DAT, updating cloneof/romof/sampleof
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void MachineDescriptionToName(DatFile datFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
// First we want to get a mapping for all games to description
|
||||
ConcurrentDictionary<string, string> mapping = new ConcurrentDictionary<string, string>();
|
||||
List<string> keys = datFile.Keys;
|
||||
Parallel.ForEach(keys, Globals.ParallelOptions, key =>
|
||||
{
|
||||
List<DatItem> items = datFile[key];
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
// If the key mapping doesn't exist, add it
|
||||
mapping.TryAdd(item.MachineName, item.MachineDescription.Replace('/', '_').Replace("\"", "''").Replace(":", " -"));
|
||||
}
|
||||
});
|
||||
|
||||
// Now we loop through every item and update accordingly
|
||||
keys = datFile.Keys;
|
||||
Parallel.ForEach(keys, Globals.ParallelOptions, key =>
|
||||
{
|
||||
List<DatItem> items = datFile[key];
|
||||
List<DatItem> newItems = new List<DatItem>();
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
// Update machine name
|
||||
if (!string.IsNullOrWhiteSpace(item.MachineName) && mapping.ContainsKey(item.MachineName))
|
||||
item.MachineName = mapping[item.MachineName];
|
||||
|
||||
// Update cloneof
|
||||
if (!string.IsNullOrWhiteSpace(item.CloneOf) && mapping.ContainsKey(item.CloneOf))
|
||||
item.CloneOf = mapping[item.CloneOf];
|
||||
|
||||
// Update romof
|
||||
if (!string.IsNullOrWhiteSpace(item.RomOf) && mapping.ContainsKey(item.RomOf))
|
||||
item.RomOf = mapping[item.RomOf];
|
||||
|
||||
// Update sampleof
|
||||
if (!string.IsNullOrWhiteSpace(item.SampleOf) && mapping.ContainsKey(item.SampleOf))
|
||||
item.SampleOf = mapping[item.SampleOf];
|
||||
|
||||
// Add the new item to the output list
|
||||
newItems.Add(item);
|
||||
}
|
||||
|
||||
// Replace the old list of roms with the new one
|
||||
datFile.Remove(key);
|
||||
datFile.AddRange(key, newItems);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Globals.Logger.Warning(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that all roms are in their own game (or at least try to ensure)
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
/// TODO: This is incorrect for the actual 1G1R logic... this is actually just silly
|
||||
private void OneRomPerGame(DatFile datFile)
|
||||
{
|
||||
// For each rom, we want to update the game to be "<game name>/<rom name>"
|
||||
Parallel.ForEach(datFile.Keys, Globals.ParallelOptions, key =>
|
||||
{
|
||||
List<DatItem> items = datFile[key];
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
string[] splitname = items[i].Name.Split('.');
|
||||
items[i].MachineName += $"/{string.Join(".", splitname.Take(splitname.Length > 1 ? splitname.Length - 1 : 1))}";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strip the dates from the beginning of scene-style set names
|
||||
/// </summary>
|
||||
/// <param name="datFile">DatFile to filter</param>
|
||||
private void StripSceneDatesFromItems(DatFile datFile)
|
||||
{
|
||||
// Output the logging statement
|
||||
Globals.Logger.User("Stripping scene-style dates");
|
||||
|
||||
// Set the regex pattern to use
|
||||
string pattern = @"([0-9]{2}\.[0-9]{2}\.[0-9]{2}-)(.*?-.*?)";
|
||||
|
||||
// Now process all of the roms
|
||||
List<string> keys = datFile.Keys;
|
||||
Parallel.ForEach(keys, Globals.ParallelOptions, key =>
|
||||
{
|
||||
List<DatItem> items = datFile[key];
|
||||
for (int j = 0; j < items.Count; j++)
|
||||
{
|
||||
DatItem item = items[j];
|
||||
if (Regex.IsMatch(item.MachineName, pattern))
|
||||
item.MachineName = Regex.Replace(item.MachineName, pattern, "$2");
|
||||
|
||||
if (Regex.IsMatch(item.MachineDescription, pattern))
|
||||
item.MachineDescription = Regex.Replace(item.MachineDescription, pattern, "$2");
|
||||
|
||||
items[j] = item;
|
||||
}
|
||||
|
||||
datFile.Remove(key);
|
||||
datFile.AddRange(key, items);
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion // Instance Methods
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
/// <param name="hash">Type of hash that is associated with this DAT</param>
|
||||
public Hashfile(DatFile datFile, Hash hash)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
_hash = hash;
|
||||
}
|
||||
@@ -34,25 +34,19 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a hashfile or SFV and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Open a file reader
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc);
|
||||
Encoding enc = FileExtensions.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(FileExtensions.TryOpenRead(filename), enc);
|
||||
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
@@ -64,7 +58,7 @@ namespace SabreTools.Library.DatFiles
|
||||
string hash = string.Empty;
|
||||
|
||||
// If we have CRC, then it's an SFV file and the name is first are
|
||||
if ((_hash & Hash.CRC) != 0)
|
||||
if (_hash.HasFlag(Hash.CRC))
|
||||
{
|
||||
name = split[0].Replace("*", String.Empty);
|
||||
hash = split[1];
|
||||
@@ -80,23 +74,25 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = name,
|
||||
Size = -1,
|
||||
CRC = ((_hash & Hash.CRC) != 0 ? Utilities.CleanHashData(hash, Constants.CRCLength) : null),
|
||||
MD5 = ((_hash & Hash.MD5) != 0 ? Utilities.CleanHashData(hash, Constants.MD5Length) : null),
|
||||
RIPEMD160 = ((_hash & Hash.RIPEMD160) != 0 ? Utilities.CleanHashData(hash, Constants.RIPEMD160Length) : null),
|
||||
SHA1 = ((_hash & Hash.SHA1) != 0 ? Utilities.CleanHashData(hash, Constants.SHA1Length) : null),
|
||||
SHA256 = ((_hash & Hash.SHA256) != 0 ? Utilities.CleanHashData(hash, Constants.SHA256Length) : null),
|
||||
SHA384 = ((_hash & Hash.SHA384) != 0 ? Utilities.CleanHashData(hash, Constants.SHA384Length) : null),
|
||||
SHA512 = ((_hash & Hash.SHA512) != 0 ? Utilities.CleanHashData(hash, Constants.SHA512Length) : null),
|
||||
CRC = (_hash.HasFlag(Hash.CRC) ? hash : null),
|
||||
MD5 = (_hash.HasFlag(Hash.MD5) ? hash : null),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = (_hash.HasFlag(Hash.RIPEMD160) ? hash : null),
|
||||
#endif
|
||||
SHA1 = (_hash.HasFlag(Hash.SHA1) ? hash : null),
|
||||
SHA256 = (_hash.HasFlag(Hash.SHA256) ? hash : null),
|
||||
SHA384 = (_hash.HasFlag(Hash.SHA384) ? hash : null),
|
||||
SHA512 = (_hash.HasFlag(Hash.SHA512) ? hash : null),
|
||||
ItemStatus = ItemStatus.None,
|
||||
|
||||
MachineName = Path.GetFileNameWithoutExtension(filename),
|
||||
|
||||
SystemID = sysid,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
sr.Dispose();
|
||||
@@ -113,7 +109,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -122,10 +118,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false));
|
||||
svw.Quotes = false;
|
||||
svw.Separator = ' ';
|
||||
svw.VerifyFieldCount = true;
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Quotes = false,
|
||||
Separator = ' ',
|
||||
VerifyFieldCount = true
|
||||
};
|
||||
|
||||
// Get a properly sorted set of keys
|
||||
List<string> keys = Keys;
|
||||
@@ -200,40 +198,42 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
fields[0] = string.Empty;
|
||||
if (GameName)
|
||||
fields[0] = $"{rom.GetField(Field.MachineName, ExcludeFields)}{Path.DirectorySeparatorChar}";
|
||||
fields[0] += rom.GetField(Field.Name, ExcludeFields);
|
||||
fields[1] = rom.GetField(Field.CRC, ExcludeFields);
|
||||
if (DatHeader.GameName)
|
||||
fields[0] = $"{rom.GetField(Field.MachineName, DatHeader.ExcludeFields)}{Path.DirectorySeparatorChar}";
|
||||
fields[0] += rom.GetField(Field.Name, DatHeader.ExcludeFields);
|
||||
fields[1] = rom.GetField(Field.CRC, DatHeader.ExcludeFields);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
#if NET_FRAMEWORK
|
||||
case Hash.RIPEMD160:
|
||||
#endif
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
Field hashField = Utilities.GetFieldFromHash(_hash);
|
||||
Field hashField = _hash.AsField();
|
||||
|
||||
switch (datItem.ItemType)
|
||||
{
|
||||
case ItemType.Disk:
|
||||
var disk = datItem as Disk;
|
||||
fields[0] = disk.GetField(hashField, ExcludeFields);
|
||||
fields[0] = disk.GetField(hashField, DatHeader.ExcludeFields);
|
||||
fields[1] = string.Empty;
|
||||
if (GameName)
|
||||
fields[1] = $"{disk.GetField(Field.MachineName, ExcludeFields)}{Path.DirectorySeparatorChar}";
|
||||
fields[1] += disk.GetField(Field.Name, ExcludeFields);
|
||||
if (DatHeader.GameName)
|
||||
fields[1] = $"{disk.GetField(Field.MachineName, DatHeader.ExcludeFields)}{Path.DirectorySeparatorChar}";
|
||||
fields[1] += disk.GetField(Field.Name, DatHeader.ExcludeFields);
|
||||
break;
|
||||
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
fields[0] = rom.GetField(hashField, ExcludeFields);
|
||||
fields[0] = rom.GetField(hashField, DatHeader.ExcludeFields);
|
||||
fields[1] = string.Empty;
|
||||
if (GameName)
|
||||
fields[1] = $"{rom.GetField(Field.MachineName, ExcludeFields)}{Path.DirectorySeparatorChar}";
|
||||
fields[1] += rom.GetField(Field.Name, ExcludeFields);
|
||||
if (DatHeader.GameName)
|
||||
fields[1] = $"{rom.GetField(Field.MachineName, DatHeader.ExcludeFields)}{Path.DirectorySeparatorChar}";
|
||||
fields[1] += rom.GetField(Field.Name, DatHeader.ExcludeFields);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Json(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,25 +29,18 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a Logiqx XML DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), new UTF8Encoding(false));
|
||||
StreamReader sr = new StreamReader(FileExtensions.TryOpenRead(filename), new UTF8Encoding(false));
|
||||
JsonTextReader jtr = new JsonTextReader(sr);
|
||||
|
||||
// If we got a null reader, just return
|
||||
@@ -77,7 +70,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Machine array
|
||||
case "machines":
|
||||
ReadMachines(sr, jtr, clean, remUnicode);
|
||||
ReadMachines(sr, jtr, filename, indexId);
|
||||
jtr.Read();
|
||||
break;
|
||||
|
||||
@@ -124,96 +117,96 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all header items (ONLY OVERWRITE IF THERE'S NO DATA)
|
||||
string content = string.Empty;
|
||||
string content;
|
||||
switch (jtr.Value)
|
||||
{
|
||||
case "name":
|
||||
content = jtr.ReadAsString();
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? content : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? content : DatHeader.Name);
|
||||
superdat = superdat || content.Contains(" - SuperDAT");
|
||||
if (keep && superdat)
|
||||
{
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? "SuperDAT" : DatHeader.Type);
|
||||
}
|
||||
break;
|
||||
|
||||
case "description":
|
||||
content = jtr.ReadAsString();
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? content : Description);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? content : DatHeader.Description);
|
||||
break;
|
||||
|
||||
case "rootdir": // This is exclusive to TruRip XML
|
||||
content = jtr.ReadAsString();
|
||||
RootDir = (string.IsNullOrWhiteSpace(RootDir) ? content : RootDir);
|
||||
DatHeader.RootDir = (string.IsNullOrWhiteSpace(DatHeader.RootDir) ? content : DatHeader.RootDir);
|
||||
break;
|
||||
|
||||
case "category":
|
||||
content = jtr.ReadAsString();
|
||||
Category = (string.IsNullOrWhiteSpace(Category) ? content : Category);
|
||||
DatHeader.Category = (string.IsNullOrWhiteSpace(DatHeader.Category) ? content : DatHeader.Category);
|
||||
break;
|
||||
|
||||
case "version":
|
||||
content = jtr.ReadAsString();
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? content : Version);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? content : DatHeader.Version);
|
||||
break;
|
||||
|
||||
case "date":
|
||||
content = jtr.ReadAsString();
|
||||
Date = (string.IsNullOrWhiteSpace(Date) ? content.Replace(".", "/") : Date);
|
||||
DatHeader.Date = (string.IsNullOrWhiteSpace(DatHeader.Date) ? content.Replace(".", "/") : DatHeader.Date);
|
||||
break;
|
||||
|
||||
case "author":
|
||||
content = jtr.ReadAsString();
|
||||
Author = (string.IsNullOrWhiteSpace(Author) ? content : Author);
|
||||
DatHeader.Author = (string.IsNullOrWhiteSpace(DatHeader.Author) ? content : DatHeader.Author);
|
||||
break;
|
||||
|
||||
case "email":
|
||||
content = jtr.ReadAsString();
|
||||
Email = (string.IsNullOrWhiteSpace(Email) ? content : Email);
|
||||
DatHeader.Email = (string.IsNullOrWhiteSpace(DatHeader.Email) ? content : DatHeader.Email);
|
||||
break;
|
||||
|
||||
case "homepage":
|
||||
content = jtr.ReadAsString();
|
||||
Homepage = (string.IsNullOrWhiteSpace(Homepage) ? content : Homepage);
|
||||
DatHeader.Homepage = (string.IsNullOrWhiteSpace(DatHeader.Homepage) ? content : DatHeader.Homepage);
|
||||
break;
|
||||
|
||||
case "url":
|
||||
content = jtr.ReadAsString();
|
||||
Url = (string.IsNullOrWhiteSpace(Url) ? content : Url);
|
||||
DatHeader.Url = (string.IsNullOrWhiteSpace(DatHeader.Url) ? content : DatHeader.Url);
|
||||
break;
|
||||
|
||||
case "comment":
|
||||
content = jtr.ReadAsString();
|
||||
Comment = (string.IsNullOrWhiteSpace(Comment) ? content : Comment);
|
||||
DatHeader.Comment = (string.IsNullOrWhiteSpace(DatHeader.Comment) ? content : DatHeader.Comment);
|
||||
break;
|
||||
|
||||
case "type": // This is exclusive to TruRip XML
|
||||
content = jtr.ReadAsString();
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? content : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? content : DatHeader.Type);
|
||||
superdat = superdat || content.Contains("SuperDAT");
|
||||
break;
|
||||
|
||||
case "forcemerging":
|
||||
if (ForceMerging == ForceMerging.None)
|
||||
ForceMerging = Utilities.GetForceMerging(jtr.ReadAsString());
|
||||
if (DatHeader.ForceMerging == ForceMerging.None)
|
||||
DatHeader.ForceMerging = jtr.ReadAsString().AsForceMerging();
|
||||
|
||||
break;
|
||||
|
||||
case "forcepacking":
|
||||
if (ForcePacking == ForcePacking.None)
|
||||
ForcePacking = Utilities.GetForcePacking(jtr.ReadAsString());
|
||||
if (DatHeader.ForcePacking == ForcePacking.None)
|
||||
DatHeader.ForcePacking = jtr.ReadAsString().AsForcePacking();
|
||||
|
||||
break;
|
||||
|
||||
case "forcenodump":
|
||||
if (ForceNodump == ForceNodump.None)
|
||||
ForceNodump = Utilities.GetForceNodump(jtr.ReadAsString());
|
||||
if (DatHeader.ForceNodump == ForceNodump.None)
|
||||
DatHeader.ForceNodump = jtr.ReadAsString().AsForceNodump();
|
||||
|
||||
break;
|
||||
|
||||
case "header":
|
||||
content = jtr.ReadAsString();
|
||||
Header = (string.IsNullOrWhiteSpace(Header) ? content : Header);
|
||||
DatHeader.Header = (string.IsNullOrWhiteSpace(DatHeader.Header) ? content : DatHeader.Header);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -229,15 +222,15 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="sr">StreamReader to use to parse the header</param>
|
||||
/// <param name="itr">JsonTextReader to use to parse the machine</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadMachines(
|
||||
StreamReader sr,
|
||||
JsonTextReader jtr,
|
||||
|
||||
// Miscellaneous
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int indexId)
|
||||
{
|
||||
// If the reader is invalid, skip
|
||||
if (jtr == null)
|
||||
@@ -257,7 +250,7 @@ namespace SabreTools.Library.DatFiles
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadMachine(sr, jtr, clean, remUnicode);
|
||||
ReadMachine(sr, jtr, filename, indexId);
|
||||
jtr.Read();
|
||||
}
|
||||
}
|
||||
@@ -267,15 +260,15 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="sr">StreamReader to use to parse the header</param>
|
||||
/// <param name="itr">JsonTextReader to use to parse the machine</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadMachine(
|
||||
StreamReader sr,
|
||||
JsonTextReader jtr,
|
||||
|
||||
// Miscellaneous
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int indexId)
|
||||
{
|
||||
// If we have an empty machine, skip it
|
||||
if (jtr == null)
|
||||
@@ -337,19 +330,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "supported":
|
||||
string supported = jtr.ReadAsString();
|
||||
switch (supported)
|
||||
{
|
||||
case "yes":
|
||||
machine.Supported = true;
|
||||
break;
|
||||
case "no":
|
||||
machine.Supported = false;
|
||||
break;
|
||||
case "partial":
|
||||
machine.Supported = null;
|
||||
break;
|
||||
}
|
||||
machine.Supported = jtr.ReadAsString().AsYesNo();
|
||||
break;
|
||||
|
||||
case "sourcefile":
|
||||
@@ -357,16 +338,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "runnable":
|
||||
string runnable = jtr.ReadAsString();
|
||||
switch (runnable)
|
||||
{
|
||||
case "yes":
|
||||
machine.Runnable = true;
|
||||
break;
|
||||
case "no":
|
||||
machine.Runnable = false;
|
||||
break;
|
||||
}
|
||||
machine.Runnable = jtr.ReadAsString().AsYesNo();
|
||||
break;
|
||||
|
||||
case "board":
|
||||
@@ -438,7 +410,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "items":
|
||||
ReadItems(sr, jtr, clean, remUnicode, machine);
|
||||
ReadItems(sr, jtr, filename, indexId, machine);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -453,16 +425,19 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Read item array information
|
||||
/// </summary>
|
||||
/// <param name="sr">StreamReader to use to parse the header</param>
|
||||
/// <param name="itr">JsonTextReader to use to parse the machine</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="jtr">JsonTextReader to use to parse the machine</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="machine">Machine information to add to the parsed items</param>
|
||||
private void ReadItems(
|
||||
StreamReader sr,
|
||||
JsonTextReader jtr,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool clean,
|
||||
bool remUnicode,
|
||||
Machine machine)
|
||||
{
|
||||
// If the reader is invalid, skip
|
||||
@@ -483,7 +458,7 @@ namespace SabreTools.Library.DatFiles
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadItem(sr, jtr, clean, remUnicode, machine);
|
||||
ReadItem(sr, jtr, filename, indexId, machine);
|
||||
jtr.Read();
|
||||
}
|
||||
}
|
||||
@@ -492,16 +467,19 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Read item information
|
||||
/// </summary>
|
||||
/// <param name="sr">StreamReader to use to parse the header</param>
|
||||
/// <param name="itr">JsonTextReader to use to parse the machine</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="jtr">JsonTextReader to use to parse the machine</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="machine">Machine information to add to the parsed items</param>
|
||||
private void ReadItem(
|
||||
StreamReader sr,
|
||||
JsonTextReader jtr,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool clean,
|
||||
bool remUnicode,
|
||||
Machine machine)
|
||||
{
|
||||
// If we have an empty machine, skip it
|
||||
@@ -547,8 +525,10 @@ namespace SabreTools.Library.DatFiles
|
||||
if (itemType == null)
|
||||
return;
|
||||
|
||||
DatItem datItem = Utilities.GetDatItem(itemType.Value);
|
||||
DatItem datItem = DatItem.Create(itemType.Value);
|
||||
datItem.CopyMachineInformation(machine);
|
||||
datItem.IndexId = indexId;
|
||||
datItem.IndexSource = filename;
|
||||
|
||||
datItem.Name = name;
|
||||
datItem.PartName = partName;
|
||||
@@ -565,7 +545,9 @@ namespace SabreTools.Library.DatFiles
|
||||
else if (itemType == ItemType.Disk)
|
||||
{
|
||||
(datItem as Disk).MD5 = md5;
|
||||
#if NET_FRAMEWORK
|
||||
(datItem as Disk).RIPEMD160 = ripemd160;
|
||||
#endif
|
||||
(datItem as Disk).SHA1 = sha1;
|
||||
(datItem as Disk).SHA256 = sha256;
|
||||
(datItem as Disk).SHA384 = sha384;
|
||||
@@ -590,7 +572,9 @@ namespace SabreTools.Library.DatFiles
|
||||
(datItem as Rom).Size = size;
|
||||
(datItem as Rom).CRC = crc;
|
||||
(datItem as Rom).MD5 = md5;
|
||||
#if NET_FRAMEWORK
|
||||
(datItem as Rom).RIPEMD160 = ripemd160;
|
||||
#endif
|
||||
(datItem as Rom).SHA1 = sha1;
|
||||
(datItem as Rom).SHA256 = sha256;
|
||||
(datItem as Rom).SHA384 = sha384;
|
||||
@@ -603,7 +587,7 @@ namespace SabreTools.Library.DatFiles
|
||||
(datItem as Rom).Optional = optional;
|
||||
}
|
||||
|
||||
ParseAddHelper(datItem, clean, remUnicode);
|
||||
ParseAddHelper(datItem);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -618,7 +602,7 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (jtr.Value)
|
||||
{
|
||||
case "type":
|
||||
itemType = Utilities.GetItemType(jtr.ReadAsString());
|
||||
itemType = jtr.ReadAsString().AsItemType();
|
||||
break;
|
||||
|
||||
case "name":
|
||||
@@ -731,7 +715,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "status":
|
||||
itemStatus = Utilities.GetItemStatus(jtr.ReadAsString());
|
||||
itemStatus = jtr.ReadAsString().AsItemStatus();
|
||||
break;
|
||||
|
||||
case "optional":
|
||||
@@ -765,7 +749,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -775,10 +759,12 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false));
|
||||
JsonTextWriter jtw = new JsonTextWriter(sw);
|
||||
jtw.Formatting = Formatting.Indented;
|
||||
jtw.IndentChar = '\t';
|
||||
jtw.Indentation = 1;
|
||||
JsonTextWriter jtw = new JsonTextWriter(sw)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(jtw);
|
||||
@@ -827,7 +813,9 @@ namespace SabreTools.Library.DatFiles
|
||||
((Rom)rom).Size = Constants.SizeZero;
|
||||
((Rom)rom).CRC = ((Rom)rom).CRC == "null" ? Constants.CRCZero : null;
|
||||
((Rom)rom).MD5 = ((Rom)rom).MD5 == "null" ? Constants.MD5Zero : null;
|
||||
#if NET_FRAMEWORK
|
||||
((Rom)rom).RIPEMD160 = ((Rom)rom).RIPEMD160 == "null" ? Constants.RIPEMD160Zero : null;
|
||||
#endif
|
||||
((Rom)rom).SHA1 = ((Rom)rom).SHA1 == "null" ? Constants.SHA1Zero : null;
|
||||
((Rom)rom).SHA256 = ((Rom)rom).SHA256 == "null" ? Constants.SHA256Zero : null;
|
||||
((Rom)rom).SHA384 = ((Rom)rom).SHA384 == "null" ? Constants.SHA384Zero : null;
|
||||
@@ -873,57 +861,57 @@ namespace SabreTools.Library.DatFiles
|
||||
jtw.WriteStartObject();
|
||||
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(Name);
|
||||
jtw.WriteValue(DatHeader.Name);
|
||||
jtw.WritePropertyName("description");
|
||||
jtw.WriteValue(Description);
|
||||
if (!string.IsNullOrWhiteSpace(RootDir))
|
||||
jtw.WriteValue(DatHeader.Description);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.RootDir))
|
||||
{
|
||||
jtw.WritePropertyName("rootdir");
|
||||
jtw.WriteValue(RootDir);
|
||||
jtw.WriteValue(DatHeader.RootDir);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(Category))
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Category))
|
||||
{
|
||||
jtw.WritePropertyName("category");
|
||||
jtw.WriteValue(Category);
|
||||
jtw.WriteValue(DatHeader.Category);
|
||||
}
|
||||
jtw.WritePropertyName("version");
|
||||
jtw.WriteValue(Version);
|
||||
if (!string.IsNullOrWhiteSpace(Date))
|
||||
jtw.WriteValue(DatHeader.Version);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Date))
|
||||
{
|
||||
jtw.WritePropertyName("date");
|
||||
jtw.WriteValue(Date);
|
||||
jtw.WriteValue(DatHeader.Date);
|
||||
}
|
||||
jtw.WritePropertyName("author");
|
||||
jtw.WriteValue(Author);
|
||||
if (!string.IsNullOrWhiteSpace(Email))
|
||||
jtw.WriteValue(DatHeader.Author);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Email))
|
||||
{
|
||||
jtw.WritePropertyName("email");
|
||||
jtw.WriteValue(Email);
|
||||
jtw.WriteValue(DatHeader.Email);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(Homepage))
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Homepage))
|
||||
{
|
||||
jtw.WritePropertyName("homepage");
|
||||
jtw.WriteValue(Homepage);
|
||||
jtw.WriteValue(DatHeader.Homepage);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(Url))
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Url))
|
||||
{
|
||||
jtw.WritePropertyName("date");
|
||||
jtw.WriteValue(Url);
|
||||
jtw.WriteValue(DatHeader.Url);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(Comment))
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Comment))
|
||||
{
|
||||
jtw.WritePropertyName("comment");
|
||||
jtw.WriteValue(Comment);
|
||||
jtw.WriteValue(DatHeader.Comment);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(Type))
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Type))
|
||||
{
|
||||
jtw.WritePropertyName("type");
|
||||
jtw.WriteValue(Type);
|
||||
jtw.WriteValue(DatHeader.Type);
|
||||
}
|
||||
if (ForceMerging != ForceMerging.None)
|
||||
if (DatHeader.ForceMerging != ForceMerging.None)
|
||||
{
|
||||
jtw.WritePropertyName("forcemerging");
|
||||
switch (ForceMerging)
|
||||
switch (DatHeader.ForceMerging)
|
||||
{
|
||||
case ForceMerging.Full:
|
||||
jtw.WriteValue("full");
|
||||
@@ -939,10 +927,10 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ForcePacking != ForcePacking.None)
|
||||
if (DatHeader.ForcePacking != ForcePacking.None)
|
||||
{
|
||||
jtw.WritePropertyName("forcepacking");
|
||||
switch (ForcePacking)
|
||||
switch (DatHeader.ForcePacking)
|
||||
{
|
||||
case ForcePacking.Unzip:
|
||||
jtw.WriteValue("unzip");
|
||||
@@ -952,10 +940,10 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ForceNodump != ForceNodump.None)
|
||||
if (DatHeader.ForceNodump != ForceNodump.None)
|
||||
{
|
||||
jtw.WritePropertyName("forcenodump");
|
||||
switch (ForceNodump)
|
||||
switch (DatHeader.ForceNodump)
|
||||
{
|
||||
case ForceNodump.Ignore:
|
||||
jtw.WriteValue("ignore");
|
||||
@@ -968,10 +956,10 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(Header))
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Header))
|
||||
{
|
||||
jtw.WritePropertyName("header");
|
||||
jtw.WriteValue(Header);
|
||||
jtw.WriteValue(DatHeader.Header);
|
||||
}
|
||||
|
||||
// End header
|
||||
@@ -1008,49 +996,49 @@ namespace SabreTools.Library.DatFiles
|
||||
jtw.WriteStartObject();
|
||||
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(datItem.GetField(Field.MachineName, ExcludeFields));
|
||||
jtw.WriteValue(datItem.GetField(Field.MachineName, DatHeader.ExcludeFields));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Comment, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Comment, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("comment");
|
||||
jtw.WriteValue(datItem.Comment);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("description");
|
||||
jtw.WriteValue(datItem.MachineDescription);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("year");
|
||||
jtw.WriteValue(datItem.Year);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("manufacturer");
|
||||
jtw.WriteValue(datItem.Manufacturer);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("publisher");
|
||||
jtw.WriteValue(datItem.Publisher);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
jtw.WritePropertyName("romof");
|
||||
jtw.WriteValue(datItem.RomOf);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
jtw.WritePropertyName("cloneof");
|
||||
jtw.WriteValue(datItem.CloneOf);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
jtw.WritePropertyName("sampleof");
|
||||
jtw.WriteValue(datItem.SampleOf);
|
||||
}
|
||||
if (!ExcludeFields[(int)Field.Supported] && datItem.Supported != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Supported] && datItem.Supported != null)
|
||||
{
|
||||
if (datItem.Supported == true)
|
||||
{
|
||||
@@ -1063,12 +1051,12 @@ namespace SabreTools.Library.DatFiles
|
||||
jtw.WriteValue("no");
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SourceFile, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SourceFile, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sourcefile");
|
||||
jtw.WriteValue(datItem.SourceFile);
|
||||
}
|
||||
if (!ExcludeFields[(int)Field.Runnable] && datItem.Runnable != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Runnable] && datItem.Runnable != null)
|
||||
{
|
||||
if (datItem.Runnable == true)
|
||||
{
|
||||
@@ -1081,17 +1069,17 @@ namespace SabreTools.Library.DatFiles
|
||||
jtw.WriteValue("no");
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Board, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Board, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("board");
|
||||
jtw.WriteValue(datItem.Board);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RebuildTo, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RebuildTo, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("rebuildto");
|
||||
jtw.WriteValue(datItem.RebuildTo);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Devices, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Devices, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("devices");
|
||||
jtw.WriteStartArray();
|
||||
@@ -1102,7 +1090,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
jtw.WriteEndArray();
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SlotOptions, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SlotOptions, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("slotoptions");
|
||||
jtw.WriteStartArray();
|
||||
@@ -1113,7 +1101,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
jtw.WriteEndArray();
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Infos, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Infos, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("infos");
|
||||
jtw.WriteStartArray();
|
||||
@@ -1127,19 +1115,19 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
jtw.WriteEndArray();
|
||||
}
|
||||
if (!ExcludeFields[(int)Field.MachineType])
|
||||
if (!DatHeader.ExcludeFields[(int)Field.MachineType])
|
||||
{
|
||||
if ((datItem.MachineType & MachineType.Bios) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Bios))
|
||||
{
|
||||
jtw.WritePropertyName("isbios");
|
||||
jtw.WriteValue("yes");
|
||||
}
|
||||
if ((datItem.MachineType & MachineType.Device) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Device))
|
||||
{
|
||||
jtw.WritePropertyName("isdevice");
|
||||
jtw.WriteValue("yes");
|
||||
}
|
||||
if ((datItem.MachineType & MachineType.Mechanical) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Mechanical))
|
||||
{
|
||||
jtw.WritePropertyName("ismechanical");
|
||||
jtw.WriteValue("yes");
|
||||
@@ -1217,20 +1205,20 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Archive:
|
||||
jtw.WriteValue("archive");
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(datItem.GetField(Field.Name, ExcludeFields));
|
||||
jtw.WriteValue(datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
break;
|
||||
|
||||
case ItemType.BiosSet:
|
||||
var biosSet = datItem as BiosSet;
|
||||
jtw.WriteValue("biosset");
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(biosSet.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields)))
|
||||
jtw.WriteValue(biosSet.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("description");
|
||||
jtw.WriteValue(biosSet.Description);
|
||||
}
|
||||
if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
{
|
||||
jtw.WritePropertyName("default");
|
||||
jtw.WriteValue(biosSet.Default);
|
||||
@@ -1241,63 +1229,65 @@ namespace SabreTools.Library.DatFiles
|
||||
var disk = datItem as Disk;
|
||||
jtw.WriteValue("disk");
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(disk.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.MD5, ExcludeFields)))
|
||||
jtw.WriteValue(disk.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("md5");
|
||||
jtw.WriteValue(disk.MD5.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.MD5, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("ripemd160");
|
||||
jtw.WriteValue(disk.RIPEMD160.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha1");
|
||||
jtw.WriteValue(disk.SHA1.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha256");
|
||||
jtw.WriteValue(disk.SHA256.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha384");
|
||||
jtw.WriteValue(disk.SHA384.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha512");
|
||||
jtw.WriteValue(disk.SHA512.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Merge, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Merge, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("merge");
|
||||
jtw.WriteValue(disk.MergeTag);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Region, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("region");
|
||||
jtw.WriteValue(disk.Region);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Index, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Index, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("index");
|
||||
jtw.WriteValue(disk.Index);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Writable, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Writable, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("writable");
|
||||
jtw.WriteValue(disk.Writable);
|
||||
}
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
{
|
||||
jtw.WritePropertyName("status");
|
||||
jtw.WriteValue(disk.ItemStatus.ToString().ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Optional, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Optional, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("optional");
|
||||
jtw.WriteValue(disk.Optional);
|
||||
@@ -1308,23 +1298,23 @@ namespace SabreTools.Library.DatFiles
|
||||
var release = datItem as Release;
|
||||
jtw.WriteValue("release");
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(release.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(release.GetField(Field.Region, ExcludeFields)))
|
||||
jtw.WriteValue(release.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(release.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("region");
|
||||
jtw.WriteValue(release.Region);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(release.GetField(Field.Language, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(release.GetField(Field.Language, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("language");
|
||||
jtw.WriteValue(release.Language);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(release.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(release.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("date");
|
||||
jtw.WriteValue(release.Date);
|
||||
}
|
||||
if (!ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
{
|
||||
jtw.WritePropertyName("default");
|
||||
jtw.WriteValue(release.Default);
|
||||
@@ -1335,78 +1325,80 @@ namespace SabreTools.Library.DatFiles
|
||||
var rom = datItem as Rom;
|
||||
jtw.WriteValue("rom");
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(rom.GetField(Field.Name, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
jtw.WriteValue(rom.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
{
|
||||
jtw.WritePropertyName("size");
|
||||
jtw.WriteValue(rom.Size);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Offset, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Offset, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("offset");
|
||||
jtw.WriteValue(rom.Offset);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("crc");
|
||||
jtw.WriteValue(rom.CRC.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.MD5, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("md5");
|
||||
jtw.WriteValue(rom.MD5.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.MD5, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("ripemd160");
|
||||
jtw.WriteValue(rom.RIPEMD160.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha1");
|
||||
jtw.WriteValue(rom.SHA1.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha256");
|
||||
jtw.WriteValue(rom.SHA256.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha384");
|
||||
jtw.WriteValue(rom.SHA384.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("sha512");
|
||||
jtw.WriteValue(rom.SHA512.ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Bios, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Bios, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("bios");
|
||||
jtw.WriteValue(rom.Bios);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Merge, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Merge, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("merge");
|
||||
jtw.WriteValue(rom.MergeTag);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Region, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("region");
|
||||
jtw.WriteValue(rom.Region);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("date");
|
||||
jtw.WriteValue(rom.Date);
|
||||
}
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
{
|
||||
jtw.WritePropertyName("status");
|
||||
jtw.WriteValue(rom.ItemStatus.ToString().ToLowerInvariant());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Optional, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Optional, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("optional");
|
||||
jtw.WriteValue(rom.Optional);
|
||||
@@ -1416,21 +1408,21 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Sample:
|
||||
jtw.WriteValue("sample");
|
||||
jtw.WritePropertyName("name");
|
||||
jtw.WriteValue(datItem.GetField(Field.Name, ExcludeFields));
|
||||
jtw.WriteValue(datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.PartName, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.PartName, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("partname");
|
||||
jtw.WriteValue(datItem.PartName);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.PartInterface, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.PartInterface, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("partinterface");
|
||||
jtw.WriteValue(datItem.PartInterface);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Features, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Features, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("features");
|
||||
jtw.WriteStartArray();
|
||||
@@ -1444,12 +1436,12 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
jtw.WriteEndArray();
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.AreaName, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.AreaName, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("areaname");
|
||||
jtw.WriteValue(datItem.AreaName);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.AreaSize, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.AreaSize, DatHeader.ExcludeFields)))
|
||||
{
|
||||
jtw.WritePropertyName("areasize");
|
||||
jtw.WriteValue(datItem.AreaSize);
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Listrom(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,11 +29,8 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a MAME Listrom DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <remarks>
|
||||
/// In a new style MAME listrom DAT, each game has the following format:
|
||||
///
|
||||
@@ -43,20 +40,17 @@ namespace SabreTools.Library.DatFiles
|
||||
/// 6331.sound-u8 32 BAD CRC(1d298cb0) SHA1(bb0bb62365402543e3154b9a77be9c75010e6abc) BAD_DUMP
|
||||
/// 16v8h-blue.u24 279 NO GOOD DUMP KNOWN
|
||||
/// </remarks>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Open a file reader
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc);
|
||||
Encoding enc = FileExtensions.GetEncoding(filename);
|
||||
StreamReader sr = new StreamReader(FileExtensions.TryOpenRead(filename), enc);
|
||||
|
||||
string gamename = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
@@ -91,7 +85,6 @@ namespace SabreTools.Library.DatFiles
|
||||
else
|
||||
{
|
||||
// First, we preprocess the line so that the rom name is consistently correct
|
||||
string romname = string.Empty;
|
||||
string[] split = line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// If the line doesn't have the 4 spaces of padding, check for 3
|
||||
@@ -102,7 +95,7 @@ namespace SabreTools.Library.DatFiles
|
||||
if (split.Length == 1)
|
||||
Globals.Logger.Warning($"Possibly malformed line: '{line}'");
|
||||
|
||||
romname = split[0];
|
||||
string romname = split[0];
|
||||
line = line.Substring(romname.Length);
|
||||
|
||||
// Next we separate the ROM into pieces
|
||||
@@ -114,12 +107,15 @@ namespace SabreTools.Library.DatFiles
|
||||
Disk disk = new Disk()
|
||||
{
|
||||
Name = romname,
|
||||
SHA1 = Utilities.CleanListromHashData(split[0]),
|
||||
SHA1 = Sanitizer.CleanListromHashData(split[0]),
|
||||
|
||||
MachineName = gamename,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
ParseAddHelper(disk, clean, remUnicode);
|
||||
ParseAddHelper(disk);
|
||||
}
|
||||
|
||||
// Baddump Disks have 4 pieces (name, BAD, sha1, BAD_DUMP)
|
||||
@@ -128,13 +124,16 @@ namespace SabreTools.Library.DatFiles
|
||||
Disk disk = new Disk()
|
||||
{
|
||||
Name = romname,
|
||||
SHA1 = Utilities.CleanListromHashData(split[1]),
|
||||
SHA1 = Sanitizer.CleanListromHashData(split[1]),
|
||||
ItemStatus = ItemStatus.BadDump,
|
||||
|
||||
MachineName = gamename,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
ParseAddHelper(disk, clean, remUnicode);
|
||||
ParseAddHelper(disk);
|
||||
}
|
||||
|
||||
// Standard ROMs have 4 pieces (name, size, crc, sha1)
|
||||
@@ -147,13 +146,16 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = romname,
|
||||
Size = size,
|
||||
CRC = Utilities.CleanListromHashData(split[1]),
|
||||
SHA1 = Utilities.CleanListromHashData(split[2]),
|
||||
CRC = Sanitizer.CleanListromHashData(split[1]),
|
||||
SHA1 = Sanitizer.CleanListromHashData(split[2]),
|
||||
|
||||
MachineName = gamename,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
// Nodump Disks have 5 pieces (name, NO, GOOD, DUMP, KNOWN)
|
||||
@@ -165,9 +167,12 @@ namespace SabreTools.Library.DatFiles
|
||||
ItemStatus = ItemStatus.Nodump,
|
||||
|
||||
MachineName = gamename,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
ParseAddHelper(disk, clean, remUnicode);
|
||||
ParseAddHelper(disk);
|
||||
}
|
||||
|
||||
// Baddump ROMs have 6 pieces (name, size, BAD, crc, sha1, BAD_DUMP)
|
||||
@@ -180,14 +185,17 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = romname,
|
||||
Size = size,
|
||||
CRC = Utilities.CleanListromHashData(split[2]),
|
||||
SHA1 = Utilities.CleanListromHashData(split[3]),
|
||||
CRC = Sanitizer.CleanListromHashData(split[2]),
|
||||
SHA1 = Sanitizer.CleanListromHashData(split[3]),
|
||||
ItemStatus = ItemStatus.BadDump,
|
||||
|
||||
MachineName = gamename,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
// Nodump ROMs have 6 pieces (name, size, NO, GOOD, DUMP, KNOWN)
|
||||
@@ -203,9 +211,12 @@ namespace SabreTools.Library.DatFiles
|
||||
ItemStatus = ItemStatus.Nodump,
|
||||
|
||||
MachineName = gamename,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
// If we have something else, it's invalid
|
||||
@@ -217,7 +228,6 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create and open an output file for writing direct from a dictionary
|
||||
/// </summary>
|
||||
@@ -229,7 +239,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -325,7 +335,7 @@ namespace SabreTools.Library.DatFiles
|
||||
rom.MachineName = rom.MachineName.TrimStart(Path.DirectorySeparatorChar);
|
||||
|
||||
// Build the state based on excluded fields
|
||||
sw.Write($"ROMs required for driver \"{rom.GetField(Field.MachineName, ExcludeFields)}\".\n");
|
||||
sw.Write($"ROMs required for driver \"{rom.GetField(Field.MachineName, DatHeader.ExcludeFields)}\".\n");
|
||||
sw.Write("Name Size Checksum\n");
|
||||
|
||||
sw.Flush();
|
||||
@@ -393,19 +403,19 @@ namespace SabreTools.Library.DatFiles
|
||||
sw.Write($"{disk.Name} ");
|
||||
|
||||
// If we have a baddump, put the first indicator
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.BadDump)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.BadDump)
|
||||
sw.Write(" BAD");
|
||||
|
||||
// If we have a nodump, write out the indicator
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.Nodump)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.Nodump)
|
||||
sw.Write(" NO GOOD DUMP KNOWN");
|
||||
|
||||
// Otherwise, write out the SHA-1 hash
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
sw.Write($" SHA1({disk.SHA1})");
|
||||
|
||||
// If we have a baddump, put the second indicator
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.BadDump)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.BadDump)
|
||||
sw.Write(" BAD_DUMP");
|
||||
|
||||
sw.Write("\n");
|
||||
@@ -425,25 +435,25 @@ namespace SabreTools.Library.DatFiles
|
||||
sw.Write(rom.Size);
|
||||
|
||||
// If we have a baddump, put the first indicator
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.BadDump)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.BadDump)
|
||||
sw.Write(" BAD");
|
||||
|
||||
// If we have a nodump, write out the indicator
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.Nodump)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.Nodump)
|
||||
{
|
||||
sw.Write(" NO GOOD DUMP KNOWN");
|
||||
}
|
||||
// Otherwise, write out the CRC and SHA-1 hashes
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
sw.Write($" CRC({rom.CRC})");
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
sw.Write($" SHA1({rom.SHA1})");
|
||||
}
|
||||
|
||||
// If we have a baddump, put the second indicator
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.BadDump)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.BadDump)
|
||||
sw.Write(" BAD_DUMP");
|
||||
|
||||
sw.Write("\n");
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of a MAME XML DAT
|
||||
/// </summary>
|
||||
/// TODO: Verify that all write for this DatFile type is correct
|
||||
internal class Listxml : DatFile
|
||||
{
|
||||
/// <summary>
|
||||
@@ -22,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Listxml(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,27 +29,20 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a MAME XML DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
XmlReader xtr = Utilities.GetXmlTextReader(filename);
|
||||
XmlReader xtr = filename.GetXmlTextReader();
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (xtr == null)
|
||||
@@ -72,8 +64,8 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (xtr.Name)
|
||||
{
|
||||
case "mame":
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? xtr.GetAttribute("build") : Name);
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? Name : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? xtr.GetAttribute("build") : DatHeader.Name);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? DatHeader.Name : DatHeader.Description);
|
||||
// string mame_debug = xtr.GetAttribute("debug"); // (yes|no) "no"
|
||||
// string mame_mameconfig = xtr.GetAttribute("mameconfig"); CDATA
|
||||
xtr.Read();
|
||||
@@ -81,16 +73,16 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Handle M1 DATs since they're 99% the same as a SL DAT
|
||||
case "m1":
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? "M1" : Name);
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? "M1" : Description);
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? xtr.GetAttribute("version") ?? string.Empty : Version);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? "M1" : DatHeader.Name);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? "M1" : DatHeader.Description);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? xtr.GetAttribute("version") ?? string.Empty : DatHeader.Version);
|
||||
xtr.Read();
|
||||
break;
|
||||
|
||||
// We want to process the entire subtree of the machine
|
||||
case "game": // Some older DATs still use "game"
|
||||
case "machine":
|
||||
ReadMachine(xtr.ReadSubtree(), filename, sysid, srcid, clean, remUnicode);
|
||||
ReadMachine(xtr.ReadSubtree(), filename, indexId);
|
||||
|
||||
// Skip the machine now that we've processed it
|
||||
xtr.Skip();
|
||||
@@ -118,21 +110,13 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="reader">XmlReader representing a machine block</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadMachine(
|
||||
XmlReader reader,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
// If we have an empty machine, skip it
|
||||
if (reader == null)
|
||||
@@ -147,13 +131,13 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Create a new machine
|
||||
MachineType machineType = MachineType.NULL;
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("isbios")) == true)
|
||||
if (reader.GetAttribute("isbios").AsYesNo() == true)
|
||||
machineType |= MachineType.Bios;
|
||||
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("isdevice")) == true)
|
||||
if (reader.GetAttribute("isdevice").AsYesNo() == true)
|
||||
machineType |= MachineType.Device;
|
||||
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("ismechanical")) == true)
|
||||
if (reader.GetAttribute("ismechanical").AsYesNo() == true)
|
||||
machineType |= MachineType.Mechanical;
|
||||
|
||||
Machine machine = new Machine
|
||||
@@ -161,7 +145,7 @@ namespace SabreTools.Library.DatFiles
|
||||
Name = reader.GetAttribute("name"),
|
||||
Description = reader.GetAttribute("name"),
|
||||
SourceFile = reader.GetAttribute("sourcefile"),
|
||||
Runnable = Utilities.GetYesNo(reader.GetAttribute("runnable")),
|
||||
Runnable = reader.GetAttribute("runnable").AsYesNo(),
|
||||
|
||||
Comment = string.Empty,
|
||||
|
||||
@@ -205,17 +189,16 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Description = reader.GetAttribute("description"),
|
||||
Default = Utilities.GetYesNo(reader.GetAttribute("default")),
|
||||
Default = reader.GetAttribute("default").AsYesNo(),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
biosset.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(biosset, clean, remUnicode);
|
||||
key = ParseAddHelper(biosset);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -227,29 +210,30 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Bios = reader.GetAttribute("bios"),
|
||||
Size = Utilities.GetSize(reader.GetAttribute("size")),
|
||||
CRC = Utilities.CleanHashData(reader.GetAttribute("crc"), Constants.CRCLength),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.SHA1Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
Size = Sanitizer.CleanSize(reader.GetAttribute("size")),
|
||||
CRC = reader.GetAttribute("crc"),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 = reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
MergeTag = reader.GetAttribute("merge"),
|
||||
Region = reader.GetAttribute("region"),
|
||||
Offset = reader.GetAttribute("offset"),
|
||||
ItemStatus = Utilities.GetItemStatus(reader.GetAttribute("status")),
|
||||
Optional = Utilities.GetYesNo(reader.GetAttribute("optional")),
|
||||
ItemStatus = reader.GetAttribute("status").AsItemStatus(),
|
||||
Optional = reader.GetAttribute("optional").AsYesNo(),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
rom.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(rom, clean, remUnicode);
|
||||
key = ParseAddHelper(rom);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -260,28 +244,29 @@ namespace SabreTools.Library.DatFiles
|
||||
DatItem disk = new Disk
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.SHA1Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 = reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
MergeTag = reader.GetAttribute("merge"),
|
||||
Region = reader.GetAttribute("region"),
|
||||
Index = reader.GetAttribute("index"),
|
||||
Writable = Utilities.GetYesNo(reader.GetAttribute("writable")),
|
||||
ItemStatus = Utilities.GetItemStatus(reader.GetAttribute("status")),
|
||||
Optional = Utilities.GetYesNo(reader.GetAttribute("optional")),
|
||||
Writable = reader.GetAttribute("writable").AsYesNo(),
|
||||
ItemStatus = reader.GetAttribute("status").AsItemStatus(),
|
||||
Optional = reader.GetAttribute("optional").AsYesNo(),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
disk.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(disk, clean, remUnicode);
|
||||
key = ParseAddHelper(disk);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -301,15 +286,14 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
samplerom.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(samplerom, clean, remUnicode);
|
||||
key = ParseAddHelper(samplerom);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -503,14 +487,13 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Blank blank = new Blank()
|
||||
{
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
blank.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(blank, clean, remUnicode);
|
||||
ParseAddHelper(blank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +502,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="reader">XmlReader representing a machine block</param>
|
||||
/// <param name="machine">Machine information to pass to contained items</param>
|
||||
private void ReadSlot(XmlReader reader, Machine machine)
|
||||
private void ReadSlot(XmlReader reader, Machine machine)
|
||||
{
|
||||
// If we have an empty machine, skip it
|
||||
if (reader == null)
|
||||
@@ -569,7 +552,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -578,10 +561,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false));
|
||||
xtw.Formatting = Formatting.Indented;
|
||||
xtw.IndentChar = '\t';
|
||||
xtw.Indentation = 1;
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
@@ -666,7 +651,7 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteStartDocument();
|
||||
|
||||
xtw.WriteStartElement("mame");
|
||||
xtw.WriteAttributeString("build", Name);
|
||||
xtw.WriteAttributeString("build", DatHeader.Name);
|
||||
//xtw.WriteAttributeString("debug", Debug);
|
||||
//xtw.WriteAttributeString("mameconfig", MameConfig);
|
||||
|
||||
@@ -696,21 +681,21 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Build the state based on excluded fields
|
||||
xtw.WriteStartElement("machine");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SourceFile, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SourceFile, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("sourcefile", datItem.SourceFile);
|
||||
|
||||
if (!ExcludeFields[(int)Field.MachineType])
|
||||
|
||||
if (!DatHeader.ExcludeFields[(int)Field.MachineType])
|
||||
{
|
||||
if ((datItem.MachineType & MachineType.Bios) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Bios))
|
||||
xtw.WriteAttributeString("isbios", "yes");
|
||||
if ((datItem.MachineType & MachineType.Device) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Device))
|
||||
xtw.WriteAttributeString("isdevice", "yes");
|
||||
if ((datItem.MachineType & MachineType.Mechanical) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Mechanical))
|
||||
xtw.WriteAttributeString("ismechanical", "yes");
|
||||
}
|
||||
|
||||
if (!ExcludeFields[(int)Field.Runnable])
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Runnable])
|
||||
{
|
||||
if (datItem.Runnable == true)
|
||||
xtw.WriteAttributeString("runnable", "yes");
|
||||
@@ -718,21 +703,21 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteAttributeString("runnable", "no");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
xtw.WriteAttributeString("cloneof", datItem.CloneOf);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase))
|
||||
xtw.WriteAttributeString("romof", datItem.RomOf);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase))
|
||||
xtw.WriteAttributeString("sampleof", datItem.SampleOf);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("description", datItem.MachineDescription);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("year", datItem.Year);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("publisher", datItem.Publisher);
|
||||
|
||||
if (!ExcludeFields[(int)Field.Infos] && datItem.Infos != null && datItem.Infos.Count > 0)
|
||||
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Infos] && datItem.Infos != null && datItem.Infos.Count > 0)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> kvp in datItem.Infos)
|
||||
{
|
||||
@@ -801,10 +786,10 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.BiosSet:
|
||||
var biosSet = datItem as BiosSet;
|
||||
xtw.WriteStartElement("biosset");
|
||||
xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("description", biosSet.Description);
|
||||
if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
xtw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant());
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
@@ -812,30 +797,32 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Disk:
|
||||
var disk = datItem as Disk;
|
||||
xtw.WriteStartElement("disk");
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Merge, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Merge, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("merge", disk.MergeTag);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("region", disk.Region);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Index, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Index, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("index", disk.Index);
|
||||
if (!ExcludeFields[(int)Field.Writable] && disk.Writable != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Writable] && disk.Writable != null)
|
||||
xtw.WriteAttributeString("writable", disk.Writable == true ? "yes" : "no");
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
xtw.WriteAttributeString("status", disk.ItemStatus.ToString());
|
||||
if (!ExcludeFields[(int)Field.Optional] && disk.Optional != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Optional] && disk.Optional != null)
|
||||
xtw.WriteAttributeString("optional", disk.Optional == true ? "yes" : "no");
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
@@ -843,41 +830,43 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
xtw.WriteStartElement("rom");
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("size", rom.Size.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Bios, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Bios, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("bios", rom.Bios);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Merge, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Merge, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("merge", rom.MergeTag);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("region", rom.Region);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("offset", rom.Offset);
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
xtw.WriteAttributeString("status", rom.ItemStatus.ToString().ToLowerInvariant());
|
||||
if (!ExcludeFields[(int)Field.Optional] && rom.Optional != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Optional] && rom.Optional != null)
|
||||
xtw.WriteAttributeString("optional", rom.Optional == true ? "yes" : "no");
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
|
||||
case ItemType.Sample:
|
||||
xtw.WriteStartElement("sample");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Represents parsing and writing of a Logiqx-derived DAT
|
||||
/// </summary>
|
||||
/// TODO: Add XSD validation for all XML DAT types (maybe?)
|
||||
/// TODO: Verify that all write for this DatFile type is correct
|
||||
internal class Logiqx : DatFile
|
||||
{
|
||||
// Private instance variables specific to Logiqx DATs
|
||||
@@ -29,7 +28,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
/// <param name="deprecated">True if the output uses "game", false if the output uses "machine"</param>
|
||||
public Logiqx(DatFile datFile, bool deprecated)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
_deprecated = deprecated;
|
||||
}
|
||||
@@ -38,25 +37,18 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a Logiqx XML DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
XmlReader xtr = Utilities.GetXmlTextReader(filename);
|
||||
XmlReader xtr = filename.GetXmlTextReader();
|
||||
List<string> dirs = new List<string>();
|
||||
|
||||
// If we got a null reader, just return
|
||||
@@ -99,7 +91,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Unique to RomVault-created DATs
|
||||
case "dir":
|
||||
Type = "SuperDAT";
|
||||
DatHeader.Type = "SuperDAT";
|
||||
dirs.Add(xtr.GetAttribute("name") ?? string.Empty);
|
||||
xtr.Read();
|
||||
break;
|
||||
@@ -107,7 +99,7 @@ namespace SabreTools.Library.DatFiles
|
||||
// We want to process the entire subtree of the game
|
||||
case "machine": // New-style Logiqx
|
||||
case "game": // Old-style Logiqx
|
||||
ReadMachine(xtr.ReadSubtree(), dirs, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
ReadMachine(xtr.ReadSubtree(), dirs, filename, indexId, keep);
|
||||
|
||||
// Skip the machine now that we've processed it
|
||||
xtr.Skip();
|
||||
@@ -156,87 +148,87 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all header items (ONLY OVERWRITE IF THERE'S NO DATA)
|
||||
string content = string.Empty;
|
||||
string content;
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "name":
|
||||
content = reader.ReadElementContentAsString(); ;
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? content : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? content : DatHeader.Name);
|
||||
superdat = superdat || content.Contains(" - SuperDAT");
|
||||
if (keep && superdat)
|
||||
{
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? "SuperDAT" : DatHeader.Type);
|
||||
}
|
||||
break;
|
||||
|
||||
case "description":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? content : Description);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? content : DatHeader.Description);
|
||||
break;
|
||||
|
||||
case "rootdir": // This is exclusive to TruRip XML
|
||||
content = reader.ReadElementContentAsString();
|
||||
RootDir = (string.IsNullOrWhiteSpace(RootDir) ? content : RootDir);
|
||||
DatHeader.RootDir = (string.IsNullOrWhiteSpace(DatHeader.RootDir) ? content : DatHeader.RootDir);
|
||||
break;
|
||||
|
||||
case "category":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Category = (string.IsNullOrWhiteSpace(Category) ? content : Category);
|
||||
DatHeader.Category = (string.IsNullOrWhiteSpace(DatHeader.Category) ? content : DatHeader.Category);
|
||||
break;
|
||||
|
||||
case "version":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? content : Version);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? content : DatHeader.Version);
|
||||
break;
|
||||
|
||||
case "date":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Date = (string.IsNullOrWhiteSpace(Date) ? content.Replace(".", "/") : Date);
|
||||
DatHeader.Date = (string.IsNullOrWhiteSpace(DatHeader.Date) ? content.Replace(".", "/") : DatHeader.Date);
|
||||
break;
|
||||
|
||||
case "author":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Author = (string.IsNullOrWhiteSpace(Author) ? content : Author);
|
||||
DatHeader.Author = (string.IsNullOrWhiteSpace(DatHeader.Author) ? content : DatHeader.Author);
|
||||
break;
|
||||
|
||||
case "email":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Email = (string.IsNullOrWhiteSpace(Email) ? content : Email);
|
||||
DatHeader.Email = (string.IsNullOrWhiteSpace(DatHeader.Email) ? content : DatHeader.Email);
|
||||
break;
|
||||
|
||||
case "homepage":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Homepage = (string.IsNullOrWhiteSpace(Homepage) ? content : Homepage);
|
||||
DatHeader.Homepage = (string.IsNullOrWhiteSpace(DatHeader.Homepage) ? content : DatHeader.Homepage);
|
||||
break;
|
||||
|
||||
case "url":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Url = (string.IsNullOrWhiteSpace(Url) ? content : Url);
|
||||
DatHeader.Url = (string.IsNullOrWhiteSpace(DatHeader.Url) ? content : DatHeader.Url);
|
||||
break;
|
||||
|
||||
case "comment":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Comment = (string.IsNullOrWhiteSpace(Comment) ? content : Comment);
|
||||
DatHeader.Comment = (string.IsNullOrWhiteSpace(DatHeader.Comment) ? content : DatHeader.Comment);
|
||||
break;
|
||||
|
||||
case "type": // This is exclusive to TruRip XML
|
||||
content = reader.ReadElementContentAsString();
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? content : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? content : DatHeader.Type);
|
||||
superdat = superdat || content.Contains("SuperDAT");
|
||||
break;
|
||||
|
||||
case "clrmamepro":
|
||||
if (string.IsNullOrWhiteSpace(Header))
|
||||
Header = reader.GetAttribute("header");
|
||||
if (string.IsNullOrWhiteSpace(DatHeader.Header))
|
||||
DatHeader.Header = reader.GetAttribute("header");
|
||||
|
||||
if (ForceMerging == ForceMerging.None)
|
||||
ForceMerging = Utilities.GetForceMerging(reader.GetAttribute("forcemerging"));
|
||||
if (DatHeader.ForceMerging == ForceMerging.None)
|
||||
DatHeader.ForceMerging = reader.GetAttribute("forcemerging").AsForceMerging();
|
||||
|
||||
if (ForceNodump == ForceNodump.None)
|
||||
ForceNodump = Utilities.GetForceNodump(reader.GetAttribute("forcenodump"));
|
||||
if (DatHeader.ForceNodump == ForceNodump.None)
|
||||
DatHeader.ForceNodump = reader.GetAttribute("forcenodump").AsForceNodump();
|
||||
|
||||
if (ForcePacking == ForcePacking.None)
|
||||
ForcePacking = Utilities.GetForcePacking(reader.GetAttribute("forcepacking"));
|
||||
if (DatHeader.ForcePacking == ForcePacking.None)
|
||||
DatHeader.ForcePacking = reader.GetAttribute("forcepacking").AsForcePacking();
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -292,24 +284,18 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="reader">XmlReader to use to parse the machine</param>
|
||||
/// <param name="dirs">List of dirs to prepend to the game name</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private void ReadMachine(
|
||||
XmlReader reader,
|
||||
List<string> dirs,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// If we have an empty machine, skip it
|
||||
if (reader == null)
|
||||
@@ -324,13 +310,13 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Create a new machine
|
||||
MachineType machineType = MachineType.NULL;
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("isbios")) == true)
|
||||
if (reader.GetAttribute("isbios").AsYesNo() == true)
|
||||
machineType |= MachineType.Bios;
|
||||
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("isdevice")) == true) // Listxml-specific, used by older DATs
|
||||
if (reader.GetAttribute("isdevice").AsYesNo() == true) // Listxml-specific, used by older DATs
|
||||
machineType |= MachineType.Device;
|
||||
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("ismechanical")) == true) // Listxml-specific, used by older DATs
|
||||
|
||||
if (reader.GetAttribute("ismechanical").AsYesNo() == true) // Listxml-specific, used by older DATs
|
||||
machineType |= MachineType.Mechanical;
|
||||
|
||||
string dirsString = (dirs != null && dirs.Count() > 0 ? string.Join("/", dirs) + "/" : string.Empty);
|
||||
@@ -341,7 +327,7 @@ namespace SabreTools.Library.DatFiles
|
||||
SourceFile = reader.GetAttribute("sourcefile"),
|
||||
Board = reader.GetAttribute("board"),
|
||||
RebuildTo = reader.GetAttribute("rebuildto"),
|
||||
Runnable = Utilities.GetYesNo(reader.GetAttribute("runnable")), // Listxml-specific, used by older DATs
|
||||
Runnable = reader.GetAttribute("runnable").AsYesNo(), // Listxml-specific, used by older DATs
|
||||
|
||||
Comment = string.Empty,
|
||||
|
||||
@@ -352,7 +338,7 @@ namespace SabreTools.Library.DatFiles
|
||||
MachineType = (machineType == MachineType.NULL ? MachineType.None : machineType),
|
||||
};
|
||||
|
||||
if (Type == "SuperDAT" && !keep)
|
||||
if (DatHeader.Type == "SuperDAT" && !keep)
|
||||
{
|
||||
string tempout = Regex.Match(machine.Name, @".*?\\(.*)").Groups[1].Value;
|
||||
if (!string.IsNullOrWhiteSpace(tempout))
|
||||
@@ -407,13 +393,13 @@ namespace SabreTools.Library.DatFiles
|
||||
Region = reader.GetAttribute("region"),
|
||||
Language = reader.GetAttribute("language"),
|
||||
Date = reader.GetAttribute("date"),
|
||||
Default = Utilities.GetYesNo(reader.GetAttribute("default")),
|
||||
Default = reader.GetAttribute("default").AsYesNo(),
|
||||
};
|
||||
|
||||
release.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(release, clean, remUnicode);
|
||||
key = ParseAddHelper(release);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -425,17 +411,16 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Description = reader.GetAttribute("description"),
|
||||
Default = Utilities.GetYesNo(reader.GetAttribute("default")),
|
||||
Default = reader.GetAttribute("default").AsYesNo(),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
biosset.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(biosset, clean, remUnicode);
|
||||
key = ParseAddHelper(biosset);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -446,27 +431,28 @@ namespace SabreTools.Library.DatFiles
|
||||
DatItem rom = new Rom
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Size = Utilities.GetSize(reader.GetAttribute("size")),
|
||||
CRC = Utilities.CleanHashData(reader.GetAttribute("crc"), Constants.CRCLength),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.RIPEMD160Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
Size = Sanitizer.CleanSize(reader.GetAttribute("size")),
|
||||
CRC = reader.GetAttribute("crc"),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 = reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
MergeTag = reader.GetAttribute("merge"),
|
||||
ItemStatus = Utilities.GetItemStatus(reader.GetAttribute("status")),
|
||||
Date = Utilities.GetDate(reader.GetAttribute("date")),
|
||||
ItemStatus = reader.GetAttribute("status").AsItemStatus(),
|
||||
Date = Sanitizer.CleanDate(reader.GetAttribute("date")),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
rom.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(rom, clean, remUnicode);
|
||||
key = ParseAddHelper(rom);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -477,24 +463,25 @@ namespace SabreTools.Library.DatFiles
|
||||
DatItem disk = new Disk
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.RIPEMD160Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 = reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
MergeTag = reader.GetAttribute("merge"),
|
||||
ItemStatus = Utilities.GetItemStatus(reader.GetAttribute("status")),
|
||||
ItemStatus = reader.GetAttribute("status").AsItemStatus(),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
disk.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(disk, clean, remUnicode);
|
||||
key = ParseAddHelper(disk);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -506,15 +493,14 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
samplerom.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(samplerom, clean, remUnicode);
|
||||
key = ParseAddHelper(samplerom);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -526,15 +512,14 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
archiverom.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(archiverom, clean, remUnicode);
|
||||
key = ParseAddHelper(archiverom);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -550,14 +535,13 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Blank blank = new Blank()
|
||||
{
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
blank.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(blank, clean, remUnicode);
|
||||
ParseAddHelper(blank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,12 +569,10 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get the information from the trurip
|
||||
string content = string.Empty;
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "titleid":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string titleid = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "publisher":
|
||||
@@ -606,38 +588,31 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "genre":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string genre = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "subgenre":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string subgenre = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "ratings":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string ratings = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "score":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string score = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "players":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string players = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "enabled":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string enabled = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "crc":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string crc = Utilities.GetYesNo(content);
|
||||
reader.ReadElementContentAsString().AsYesNo();
|
||||
break;
|
||||
|
||||
case "source":
|
||||
@@ -649,8 +624,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "relatedto":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string relatedto = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -671,7 +645,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -680,10 +654,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false));
|
||||
xtw.Formatting = Formatting.Indented;
|
||||
xtw.IndentChar = '\t';
|
||||
xtw.Indentation = 1;
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
@@ -732,7 +708,9 @@ namespace SabreTools.Library.DatFiles
|
||||
((Rom)rom).Size = Constants.SizeZero;
|
||||
((Rom)rom).CRC = ((Rom)rom).CRC == "null" ? Constants.CRCZero : null;
|
||||
((Rom)rom).MD5 = ((Rom)rom).MD5 == "null" ? Constants.MD5Zero : null;
|
||||
#if NET_FRAMEWORK
|
||||
((Rom)rom).RIPEMD160 = ((Rom)rom).RIPEMD160 == "null" ? Constants.RIPEMD160Zero : null;
|
||||
#endif
|
||||
((Rom)rom).SHA1 = ((Rom)rom).SHA1 == "null" ? Constants.SHA1Zero : null;
|
||||
((Rom)rom).SHA256 = ((Rom)rom).SHA256 == "null" ? Constants.SHA256Zero : null;
|
||||
((Rom)rom).SHA384 = ((Rom)rom).SHA384 == "null" ? Constants.SHA384Zero : null;
|
||||
@@ -774,38 +752,38 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
xtw.WriteStartDocument();
|
||||
xtw.WriteDocType("datafile", "-//Logiqx//DTD ROM Management Datafile//EN", "http://www.logiqx.com/Dats/datafile.dtd", null);
|
||||
|
||||
xtw.WriteStartElement("datafile");
|
||||
|
||||
xtw.WriteStartElement("header");
|
||||
xtw.WriteElementString("name", Name);
|
||||
xtw.WriteElementString("description", Description);
|
||||
if (!string.IsNullOrWhiteSpace(RootDir))
|
||||
xtw.WriteElementString("rootdir", RootDir);
|
||||
if (!string.IsNullOrWhiteSpace(Category))
|
||||
xtw.WriteElementString("category", Category);
|
||||
xtw.WriteElementString("version", Version);
|
||||
if (!string.IsNullOrWhiteSpace(Date))
|
||||
xtw.WriteElementString("date", Date);
|
||||
xtw.WriteElementString("author", Author);
|
||||
if (!string.IsNullOrWhiteSpace(Email))
|
||||
xtw.WriteElementString("email", Email);
|
||||
if (!string.IsNullOrWhiteSpace(Homepage))
|
||||
xtw.WriteElementString("homepage", Homepage);
|
||||
if (!string.IsNullOrWhiteSpace(Url))
|
||||
xtw.WriteElementString("url", Url);
|
||||
if (!string.IsNullOrWhiteSpace(Comment))
|
||||
xtw.WriteElementString("comment", Comment);
|
||||
if (!string.IsNullOrWhiteSpace(Type))
|
||||
xtw.WriteElementString("type", Type);
|
||||
|
||||
if (ForcePacking != ForcePacking.None
|
||||
|| ForceMerging != ForceMerging.None
|
||||
|| ForceNodump != ForceNodump.None
|
||||
|| !string.IsNullOrWhiteSpace(Header))
|
||||
xtw.WriteStartElement("datafile");
|
||||
|
||||
xtw.WriteStartElement("header");
|
||||
xtw.WriteElementString("name", DatHeader.Name);
|
||||
xtw.WriteElementString("description", DatHeader.Description);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.RootDir))
|
||||
xtw.WriteElementString("rootdir", DatHeader.RootDir);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Category))
|
||||
xtw.WriteElementString("category", DatHeader.Category);
|
||||
xtw.WriteElementString("version", DatHeader.Version);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Date))
|
||||
xtw.WriteElementString("date", DatHeader.Date);
|
||||
xtw.WriteElementString("author", DatHeader.Author);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Email))
|
||||
xtw.WriteElementString("email", DatHeader.Email);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Homepage))
|
||||
xtw.WriteElementString("homepage", DatHeader.Homepage);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Url))
|
||||
xtw.WriteElementString("url", DatHeader.Url);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Comment))
|
||||
xtw.WriteElementString("comment", DatHeader.Comment);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Type))
|
||||
xtw.WriteElementString("type", DatHeader.Type);
|
||||
|
||||
if (DatHeader.ForcePacking != ForcePacking.None
|
||||
|| DatHeader.ForceMerging != ForceMerging.None
|
||||
|| DatHeader.ForceNodump != ForceNodump.None
|
||||
|| !string.IsNullOrWhiteSpace(DatHeader.Header))
|
||||
{
|
||||
xtw.WriteStartElement("clrmamepro");
|
||||
switch (ForcePacking)
|
||||
switch (DatHeader.ForcePacking)
|
||||
{
|
||||
case ForcePacking.Unzip:
|
||||
xtw.WriteAttributeString("forcepacking", "unzip");
|
||||
@@ -815,7 +793,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ForceMerging)
|
||||
switch (DatHeader.ForceMerging)
|
||||
{
|
||||
case ForceMerging.Full:
|
||||
xtw.WriteAttributeString("forcemerging", "full");
|
||||
@@ -831,7 +809,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ForceNodump)
|
||||
switch (DatHeader.ForceNodump)
|
||||
{
|
||||
case ForceNodump.Ignore:
|
||||
xtw.WriteAttributeString("forcenodump", "ignore");
|
||||
@@ -844,8 +822,8 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Header))
|
||||
xtw.WriteAttributeString("header", Header);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Header))
|
||||
xtw.WriteAttributeString("header", DatHeader.Header);
|
||||
|
||||
// End clrmamepro
|
||||
xtw.WriteEndElement();
|
||||
@@ -880,18 +858,18 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Build the state based on excluded fields
|
||||
xtw.WriteStartElement(_deprecated ? "game" : "machine");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.MachineType])
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.MachineType])
|
||||
{
|
||||
if ((datItem.MachineType & MachineType.Bios) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Bios))
|
||||
xtw.WriteAttributeString("isbios", "yes");
|
||||
if ((datItem.MachineType & MachineType.Device) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Device))
|
||||
xtw.WriteAttributeString("isdevice", "yes");
|
||||
if ((datItem.MachineType & MachineType.Mechanical) != 0)
|
||||
if (datItem.MachineType.HasFlag(MachineType.Mechanical))
|
||||
xtw.WriteAttributeString("ismechanical", "yes");
|
||||
}
|
||||
|
||||
if (!ExcludeFields[(int)Field.Runnable] && datItem.Runnable != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Runnable] && datItem.Runnable != null)
|
||||
{
|
||||
if (datItem.Runnable == true)
|
||||
xtw.WriteAttributeString("runnable", "yes");
|
||||
@@ -899,22 +877,22 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteAttributeString("runnable", "no");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
xtw.WriteAttributeString("cloneof", datItem.CloneOf);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase))
|
||||
xtw.WriteAttributeString("romof", datItem.RomOf);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase))
|
||||
xtw.WriteAttributeString("sampleof", datItem.SampleOf);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Comment, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Comment, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("comment", datItem.Comment);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("description", datItem.MachineDescription);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("year", datItem.Year);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("publisher", datItem.Publisher);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("manufacturer", datItem.Manufacturer);
|
||||
|
||||
xtw.Flush();
|
||||
@@ -974,17 +952,17 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
case ItemType.Archive:
|
||||
xtw.WriteStartElement("archive");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
|
||||
case ItemType.BiosSet:
|
||||
var biosSet = datItem as BiosSet;
|
||||
xtw.WriteStartElement("biosset");
|
||||
xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("description", biosSet.Description);
|
||||
if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
xtw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant());
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
@@ -992,20 +970,22 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Disk:
|
||||
var disk = datItem as Disk;
|
||||
xtw.WriteStartElement("disk");
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant());
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
xtw.WriteAttributeString("status", disk.ItemStatus.ToString().ToLowerInvariant());
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
@@ -1013,14 +993,14 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Release:
|
||||
var release = datItem as Release;
|
||||
xtw.WriteStartElement("release");
|
||||
xtw.WriteAttributeString("name", release.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", release.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("region", release.Region);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("language", release.Language);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("date", release.Date);
|
||||
if (!ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
xtw.WriteAttributeString("default", release.Default.ToString().ToLowerInvariant());
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
@@ -1028,33 +1008,35 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
xtw.WriteStartElement("rom");
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("size", rom.Size.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("date", rom.Date);
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
xtw.WriteAttributeString("status", rom.ItemStatus.ToString().ToLowerInvariant());
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
|
||||
case ItemType.Sample:
|
||||
xtw.WriteStartElement("sample");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Missfile(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -28,21 +28,15 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a Missfile and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// There is no consistent way to parse a missfile...
|
||||
throw new NotImplementedException();
|
||||
@@ -59,7 +53,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -146,21 +140,21 @@ namespace SabreTools.Library.DatFiles
|
||||
ProcessItemName(datItem, false, forceRomName: false);
|
||||
|
||||
// If we're in Romba mode, the state is consistent
|
||||
if (Romba)
|
||||
if (DatHeader.Romba)
|
||||
{
|
||||
sw.Write($"{datItem.GetField(Field.SHA1, ExcludeFields)}\n");
|
||||
sw.Write($"{datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)}\n");
|
||||
}
|
||||
// Otherwise, use any flags
|
||||
else
|
||||
{
|
||||
if (!UseRomName && datItem.MachineName != lastgame)
|
||||
if (!DatHeader.UseRomName && datItem.MachineName != lastgame)
|
||||
{
|
||||
sw.Write($"{datItem.GetField(Field.MachineName, ExcludeFields)}\n");
|
||||
sw.Write($"{datItem.GetField(Field.MachineName, DatHeader.ExcludeFields)}\n");
|
||||
lastgame = datItem.MachineName;
|
||||
}
|
||||
else if (UseRomName)
|
||||
else if (DatHeader.UseRomName)
|
||||
{
|
||||
sw.Write($"{datItem.GetField(Field.Name, ExcludeFields)}\n");
|
||||
sw.Write($"{datItem.GetField(Field.Name, DatHeader.ExcludeFields)}\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of an OfflineList XML DAT
|
||||
/// </summary>
|
||||
/// TODO: Verify that all write for this DatFile type is correct
|
||||
internal class OfflineList : DatFile
|
||||
{
|
||||
/// <summary>
|
||||
@@ -22,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public OfflineList(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,26 +29,19 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse an OfflineList XML DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
XmlReader xtr = Utilities.GetXmlTextReader(filename);
|
||||
XmlReader xtr = filename.GetXmlTextReader();
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (xtr == null)
|
||||
@@ -78,7 +70,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "games":
|
||||
ReadGames(xtr.ReadSubtree(), keep, clean, remUnicode);
|
||||
ReadGames(xtr.ReadSubtree(), filename, indexId);
|
||||
|
||||
// Skip the games node now that we've processed it
|
||||
xtr.Skip();
|
||||
@@ -128,37 +120,34 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all configuration items (ONLY OVERWRITE IF THERE'S NO DATA)
|
||||
string content = string.Empty;
|
||||
string content;
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "datname":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? content : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? content : DatHeader.Name);
|
||||
superdat = superdat || content.Contains(" - SuperDAT");
|
||||
if (keep && superdat)
|
||||
{
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? "SuperDAT" : DatHeader.Type);
|
||||
}
|
||||
break;
|
||||
|
||||
case "datversion":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? content : Version);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? content : DatHeader.Version);
|
||||
break;
|
||||
|
||||
case "system":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string system = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "screenshotswidth":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string screenshotsWidth = content; // Int32?
|
||||
reader.ReadElementContentAsString(); // Int32?
|
||||
break;
|
||||
|
||||
case "screenshotsheight":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string screenshotsHeight = content; // Int32?
|
||||
reader.ReadElementContentAsString(); // Int32?
|
||||
break;
|
||||
|
||||
case "infos":
|
||||
@@ -190,9 +179,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "romtitle":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string romtitle = content;
|
||||
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -229,93 +216,93 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "title":
|
||||
// string title_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string title_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string title_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "location":
|
||||
// string location_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string location_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string location_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "publisher":
|
||||
// string publisher_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string publisher_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string publisher_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "sourcerom":
|
||||
// string sourceRom_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string sourceRom_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string sourceRom_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "savetype":
|
||||
// string saveType_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string saveType_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string saveType_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "romsize":
|
||||
// string romSize_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string romSize_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string romSize_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "releasenumber":
|
||||
// string releaseNumber_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string releaseNumber_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string releaseNumber_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "languagenumber":
|
||||
// string languageNumber_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string languageNumber_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string languageNumber_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "comment":
|
||||
// string comment_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string comment_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string comment_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "romcrc":
|
||||
// string romCRC_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string romCRC_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string romCRC_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "im1crc":
|
||||
// string im1CRC_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string im1CRC_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string im1CRC_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "im2crc":
|
||||
// string im2CRC_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string im2CRC_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string im2CRC_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
case "languages":
|
||||
// string languages_visible = reader.GetAttribute("visible"); // (true|false)
|
||||
// string languages_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
// string languages_default = reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("visible"); // (true|false)
|
||||
reader.GetAttribute("inNamingOption"); // (true|false)
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.Read();
|
||||
break;
|
||||
|
||||
@@ -390,23 +377,21 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all newdat items
|
||||
string content = string.Empty;
|
||||
string content;
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "datversionurl":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Url = (string.IsNullOrWhiteSpace(Name) ? content : Url);
|
||||
DatHeader.Url = (string.IsNullOrWhiteSpace(DatHeader.Url) ? content : DatHeader.Url);
|
||||
break;
|
||||
|
||||
case "daturl":
|
||||
// string fileName = reader.GetAttribute("fileName");
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string url = content;
|
||||
reader.GetAttribute("fileName");
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "imurl":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string url = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -440,13 +425,12 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all search items
|
||||
string content = string.Empty;
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "to":
|
||||
// string value = reader.GetAttribute("value");
|
||||
// string default = reader.GetAttribute("default"); (true|false)
|
||||
// string auto = reader.GetAttribute("auto"); (true|false)
|
||||
reader.GetAttribute("value");
|
||||
reader.GetAttribute("default"); // (true|false)
|
||||
reader.GetAttribute("auto"); // (true|false)
|
||||
|
||||
ReadTo(reader.ReadSubtree());
|
||||
|
||||
@@ -485,14 +469,12 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all search items
|
||||
string content = string.Empty;
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "find":
|
||||
// string operation = reader.GetAttribute("operation");
|
||||
// string value = reader.GetAttribute("value"); // Int32?
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string findValue = content;
|
||||
reader.GetAttribute("operation");
|
||||
reader.GetAttribute("value"); // Int32?
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -506,15 +488,14 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Read games information
|
||||
/// </summary>
|
||||
/// <param name="reader">XmlReader to use to parse the header</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private void ReadGames(XmlReader reader,
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadGames(
|
||||
XmlReader reader,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int indexId)
|
||||
{
|
||||
// If there's no subtree to the configuration, skip it
|
||||
if (reader == null)
|
||||
@@ -537,7 +518,7 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "game":
|
||||
ReadGame(reader.ReadSubtree(), keep, clean, remUnicode);
|
||||
ReadGame(reader.ReadSubtree(), filename, indexId);
|
||||
|
||||
// Skip the game node now that we've processed it
|
||||
reader.Skip();
|
||||
@@ -554,18 +535,17 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Read game information
|
||||
/// </summary>
|
||||
/// <param name="reader">XmlReader to use to parse the header</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private void ReadGame(XmlReader reader,
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadGame(
|
||||
XmlReader reader,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int indexId)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
string releaseNumber = string.Empty, key = string.Empty, publisher = string.Empty, duplicateid = string.Empty;
|
||||
string releaseNumber = string.Empty, publisher = string.Empty, duplicateid;
|
||||
long size = -1;
|
||||
List<Rom> roms = new List<Rom>();
|
||||
Machine machine = new Machine();
|
||||
@@ -588,12 +568,10 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all games items
|
||||
string content = string.Empty;
|
||||
switch (reader.Name.ToLowerInvariant())
|
||||
{
|
||||
case "imagenumber":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string imageNumber = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "releasenumber":
|
||||
@@ -601,13 +579,11 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "title":
|
||||
content = reader.ReadElementContentAsString();
|
||||
machine.Name = content;
|
||||
machine.Name = reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "savetype":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string saveType = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "romsize":
|
||||
@@ -621,34 +597,30 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "location":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string location = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "sourcerom":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string sourceRom = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "language":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string language = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "files":
|
||||
roms = ReadFiles(reader.ReadSubtree(), releaseNumber, machine.Name, keep, clean, remUnicode);
|
||||
roms = ReadFiles(reader.ReadSubtree(), releaseNumber, machine.Name, filename, indexId);
|
||||
|
||||
// Skip the files node now that we've processed it
|
||||
reader.Skip();
|
||||
break;
|
||||
|
||||
case "im1crc":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string im1crc = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "im2crc":
|
||||
content = reader.ReadElementContentAsString();
|
||||
// string im2crc = content;
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "comment":
|
||||
@@ -676,7 +648,7 @@ namespace SabreTools.Library.DatFiles
|
||||
roms[i].CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(roms[i], clean, remUnicode);
|
||||
ParseAddHelper(roms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,17 +658,16 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="reader">XmlReader to use to parse the header</param>
|
||||
/// <param name="releaseNumber">Release number from the parent game</param>
|
||||
/// <param name="machineName">Name of the parent game to use</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private List<Rom> ReadFiles(XmlReader reader,
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private List<Rom> ReadFiles(
|
||||
XmlReader reader,
|
||||
string releaseNumber,
|
||||
string machineName,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int indexId)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
var extensionToCrc = new List<KeyValuePair<string, string>>();
|
||||
@@ -741,9 +712,12 @@ namespace SabreTools.Library.DatFiles
|
||||
roms.Add(new Rom()
|
||||
{
|
||||
Name = (releaseNumber != "0" ? releaseNumber + " - " : string.Empty) + machineName + pair.Key,
|
||||
CRC = Utilities.CleanHashData(pair.Value, Constants.CRCLength),
|
||||
CRC = pair.Value,
|
||||
|
||||
ItemStatus = ItemStatus.None,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -761,7 +735,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -770,10 +744,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false));
|
||||
xtw.Formatting = Formatting.Indented;
|
||||
xtw.IndentChar = '\t';
|
||||
xtw.Indentation = 1;
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
@@ -814,7 +790,9 @@ namespace SabreTools.Library.DatFiles
|
||||
((Rom)rom).Size = Constants.SizeZero;
|
||||
((Rom)rom).CRC = ((Rom)rom).CRC == "null" ? Constants.CRCZero : null;
|
||||
((Rom)rom).MD5 = ((Rom)rom).MD5 == "null" ? Constants.MD5Zero : null;
|
||||
#if NET_FRAMEWORK
|
||||
((Rom)rom).RIPEMD160 = ((Rom)rom).RIPEMD160 == "null" ? Constants.RIPEMD160Zero : null;
|
||||
#endif
|
||||
((Rom)rom).SHA1 = ((Rom)rom).SHA1 == "null" ? Constants.SHA1Zero : null;
|
||||
((Rom)rom).SHA256 = ((Rom)rom).SHA256 == "null" ? Constants.SHA256Zero : null;
|
||||
((Rom)rom).SHA384 = ((Rom)rom).SHA384 == "null" ? Constants.SHA384Zero : null;
|
||||
@@ -861,8 +839,8 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteAttributeString("noNamespaceSchemaLocation", "xsi", "datas.xsd");
|
||||
|
||||
xtw.WriteStartElement("configuration");
|
||||
xtw.WriteElementString("datName", Name);
|
||||
xtw.WriteElementString("datVersion", Count.ToString());
|
||||
xtw.WriteElementString("datName", DatHeader.Name);
|
||||
xtw.WriteElementString("datVersion", DatStats.Count.ToString());
|
||||
xtw.WriteElementString("system", "none");
|
||||
xtw.WriteElementString("screenshotsWidth", "240");
|
||||
xtw.WriteElementString("screenshotsHeight", "160");
|
||||
@@ -955,14 +933,14 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteEndElement();
|
||||
|
||||
xtw.WriteStartElement("newDat");
|
||||
xtw.WriteElementString("datVersionURL", Url);
|
||||
xtw.WriteElementString("datVersionURL", DatHeader.Url);
|
||||
|
||||
xtw.WriteStartElement("datUrl");
|
||||
xtw.WriteAttributeString("fileName", $"{FileName}.zip");
|
||||
xtw.WriteString(Url);
|
||||
xtw.WriteAttributeString("fileName", $"{DatHeader.FileName}.zip");
|
||||
xtw.WriteString(DatHeader.Url);
|
||||
xtw.WriteEndElement();
|
||||
|
||||
xtw.WriteElementString("imURL", Url);
|
||||
xtw.WriteElementString("imURL", DatHeader.Url);
|
||||
|
||||
// End newDat
|
||||
xtw.WriteEndElement();
|
||||
@@ -1048,13 +1026,13 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteStartElement("game");
|
||||
xtw.WriteElementString("imageNumber", "1");
|
||||
xtw.WriteElementString("releaseNumber", "1");
|
||||
xtw.WriteElementString("title", datItem.GetField(Field.Name, ExcludeFields));
|
||||
xtw.WriteElementString("title", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
xtw.WriteElementString("saveType", "None");
|
||||
|
||||
if (datItem.ItemType == ItemType.Rom)
|
||||
{
|
||||
var rom = datItem as Rom;
|
||||
xtw.WriteElementString("romSize", datItem.GetField(Field.Size, ExcludeFields));
|
||||
xtw.WriteElementString("romSize", datItem.GetField(Field.Size, DatHeader.ExcludeFields));
|
||||
}
|
||||
|
||||
xtw.WriteElementString("publisher", "None");
|
||||
@@ -1066,14 +1044,14 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
var disk = datItem as Disk;
|
||||
xtw.WriteStartElement("files");
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
{
|
||||
xtw.WriteStartElement("romMD5");
|
||||
xtw.WriteAttributeString("extension", ".chd");
|
||||
xtw.WriteString(disk.MD5.ToUpperInvariant());
|
||||
xtw.WriteEndElement();
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
{
|
||||
xtw.WriteStartElement("romSHA1");
|
||||
xtw.WriteAttributeString("extension", ".chd");
|
||||
@@ -1087,24 +1065,24 @@ namespace SabreTools.Library.DatFiles
|
||||
else if (datItem.ItemType == ItemType.Rom)
|
||||
{
|
||||
var rom = datItem as Rom;
|
||||
string tempext = "." + Utilities.GetExtension(rom.Name);
|
||||
string tempext = "." + PathExtensions.GetNormalizedExtension(rom.Name);
|
||||
|
||||
xtw.WriteStartElement("files");
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
{
|
||||
xtw.WriteStartElement("romCRC");
|
||||
xtw.WriteAttributeString("extension", tempext);
|
||||
xtw.WriteString(rom.CRC.ToUpperInvariant());
|
||||
xtw.WriteEndElement();
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
{
|
||||
xtw.WriteStartElement("romMD5");
|
||||
xtw.WriteAttributeString("extension", tempext);
|
||||
xtw.WriteString(rom.MD5.ToUpperInvariant());
|
||||
xtw.WriteEndElement();
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
{
|
||||
xtw.WriteStartElement("romSHA1");
|
||||
xtw.WriteAttributeString("extension", tempext);
|
||||
@@ -1120,7 +1098,7 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteElementString("im2CRC", "00000000");
|
||||
xtw.WriteElementString("comment", "");
|
||||
xtw.WriteElementString("duplicateID", "0");
|
||||
|
||||
|
||||
// End game
|
||||
xtw.WriteEndElement();
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of a openMSX softawre list XML DAT
|
||||
/// </summary>
|
||||
/// TODO: Verify that all write for this DatFile type is correct
|
||||
internal class OpenMSX : DatFile
|
||||
{
|
||||
/// <summary>
|
||||
@@ -22,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public OpenMSX(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,27 +29,18 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a openMSX softawre list XML DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
XmlReader xtr = Utilities.GetXmlTextReader(filename);
|
||||
XmlReader xtr = filename.GetXmlTextReader();
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (xtr == null)
|
||||
@@ -72,15 +62,15 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (xtr.Name)
|
||||
{
|
||||
case "softwaredb":
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? "openMSX Software List" : Name);
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? Name : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? "openMSX Software List" : DatHeader.Name);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? DatHeader.Name : DatHeader.Description);
|
||||
// string timestamp = xtr.GetAttribute("timestamp"); // CDATA
|
||||
xtr.Read();
|
||||
break;
|
||||
|
||||
// We want to process the entire subtree of the software
|
||||
case "software":
|
||||
ReadSoftware(xtr.ReadSubtree(), filename, sysid, srcid, keep, clean, remUnicode);
|
||||
ReadSoftware(xtr.ReadSubtree(), filename, indexId);
|
||||
|
||||
// Skip the software now that we've processed it
|
||||
xtr.Skip();
|
||||
@@ -108,23 +98,13 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="reader">XmlReader representing a machine block</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadSoftware(
|
||||
XmlReader reader,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
// If we have an empty machine, skip it
|
||||
if (reader == null)
|
||||
@@ -179,7 +159,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "dump":
|
||||
containsItems = ReadDump(reader.ReadSubtree(), machine, diskno, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
containsItems = ReadDump(reader.ReadSubtree(), machine, diskno, filename, indexId);
|
||||
diskno++;
|
||||
|
||||
// Skip the dump now that we've processed it
|
||||
@@ -197,14 +177,13 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Blank blank = new Blank()
|
||||
{
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
blank.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(blank, clean, remUnicode);
|
||||
ParseAddHelper(blank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,11 +194,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="machine">Machine information to pass to contained items</param>
|
||||
/// <param name="diskno">Disk number to use when outputting to other DAT formats</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private bool ReadDump(
|
||||
XmlReader reader,
|
||||
Machine machine,
|
||||
@@ -227,13 +202,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
bool containsItems = false;
|
||||
|
||||
@@ -250,21 +219,21 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "rom":
|
||||
containsItems = ReadRom(reader.ReadSubtree(), machine, diskno, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
containsItems = ReadRom(reader.ReadSubtree(), machine, diskno, filename, indexId);
|
||||
|
||||
// Skip the rom now that we've processed it
|
||||
reader.Skip();
|
||||
break;
|
||||
|
||||
case "megarom":
|
||||
containsItems = ReadMegaRom(reader.ReadSubtree(), machine, diskno, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
containsItems = ReadMegaRom(reader.ReadSubtree(), machine, diskno, filename, indexId);
|
||||
|
||||
// Skip the megarom now that we've processed it
|
||||
reader.Skip();
|
||||
break;
|
||||
|
||||
case "sccpluscart":
|
||||
containsItems = ReadSccPlusCart(reader.ReadSubtree(), machine, diskno, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
containsItems = ReadSccPlusCart(reader.ReadSubtree(), machine, diskno, filename, indexId);
|
||||
|
||||
// Skip the sccpluscart now that we've processed it
|
||||
reader.Skip();
|
||||
@@ -292,11 +261,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="machine">Machine information to pass to contained items</param>
|
||||
/// <param name="diskno">Disk number to use when outputting to other DAT formats</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private bool ReadRom(
|
||||
XmlReader reader,
|
||||
Machine machine,
|
||||
@@ -304,15 +269,9 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
string hash = string.Empty, offset = string.Empty, type = string.Empty, remark = string.Empty;
|
||||
string hash = string.Empty, offset = string.Empty, remark = string.Empty;
|
||||
bool containsItems = false;
|
||||
|
||||
while (!reader.EOF)
|
||||
@@ -337,7 +296,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "type":
|
||||
type = reader.ReadElementContentAsString();
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "remark":
|
||||
@@ -356,11 +315,14 @@ namespace SabreTools.Library.DatFiles
|
||||
Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty),
|
||||
Offset = offset,
|
||||
Size = -1,
|
||||
SHA1 = Utilities.CleanHashData(hash, Constants.SHA1Length),
|
||||
SHA1 = hash,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
rom.CopyMachineInformation(machine);
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
|
||||
return containsItems;
|
||||
}
|
||||
@@ -372,11 +334,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="machine">Machine information to pass to contained items</param>
|
||||
/// <param name="diskno">Disk number to use when outputting to other DAT formats</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private bool ReadMegaRom(
|
||||
XmlReader reader,
|
||||
Machine machine,
|
||||
@@ -384,15 +342,9 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
string hash = string.Empty, offset = string.Empty, type = string.Empty, remark = string.Empty;
|
||||
string hash = string.Empty, offset = string.Empty, remark = string.Empty;
|
||||
bool containsItems = false;
|
||||
|
||||
while (!reader.EOF)
|
||||
@@ -417,7 +369,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "type":
|
||||
type = reader.ReadElementContentAsString();
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "remark":
|
||||
@@ -436,11 +388,14 @@ namespace SabreTools.Library.DatFiles
|
||||
Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty),
|
||||
Offset = offset,
|
||||
Size = -1,
|
||||
SHA1 = Utilities.CleanHashData(hash, Constants.SHA1Length),
|
||||
SHA1 = hash,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
rom.CopyMachineInformation(machine);
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
|
||||
return containsItems;
|
||||
}
|
||||
@@ -452,11 +407,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="machine">Machine information to pass to contained items</param>
|
||||
/// <param name="diskno">Disk number to use when outputting to other DAT formats</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private bool ReadSccPlusCart(
|
||||
XmlReader reader,
|
||||
Machine machine,
|
||||
@@ -464,15 +415,9 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
int indexId)
|
||||
{
|
||||
string hash = string.Empty, boot = string.Empty, remark = string.Empty;
|
||||
string hash = string.Empty, remark = string.Empty;
|
||||
bool containsItems = false;
|
||||
|
||||
while (!reader.EOF)
|
||||
@@ -488,7 +433,7 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "boot":
|
||||
boot = reader.ReadElementContentAsString();
|
||||
reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "hash":
|
||||
@@ -511,11 +456,14 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty),
|
||||
Size = -1,
|
||||
SHA1 = Utilities.CleanHashData(hash, Constants.SHA1Length),
|
||||
SHA1 = hash,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
rom.CopyMachineInformation(machine);
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
|
||||
return containsItems;
|
||||
}
|
||||
@@ -531,7 +479,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -540,10 +488,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false));
|
||||
xtw.Formatting = Formatting.Indented;
|
||||
xtw.IndentChar = '\t';
|
||||
xtw.Indentation = 1;
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
@@ -671,11 +621,11 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Build the state based on excluded fields
|
||||
xtw.WriteStartElement("software");
|
||||
xtw.WriteElementString("title", datItem.GetField(Field.MachineName, ExcludeFields));
|
||||
xtw.WriteElementString("title", datItem.GetField(Field.MachineName, DatHeader.ExcludeFields));
|
||||
//xtw.WriteElementString("genmsxid", msxid);
|
||||
//xtw.WriteElementString("system", system));
|
||||
xtw.WriteElementString("company", datItem.GetField(Field.Manufacturer, ExcludeFields));
|
||||
xtw.WriteElementString("year", datItem.GetField(Field.Year, ExcludeFields));
|
||||
xtw.WriteElementString("company", datItem.GetField(Field.Manufacturer, DatHeader.ExcludeFields));
|
||||
xtw.WriteElementString("year", datItem.GetField(Field.Year, DatHeader.ExcludeFields));
|
||||
//xtw.WriteElementString("country", country);
|
||||
|
||||
xtw.Flush();
|
||||
@@ -743,10 +693,10 @@ namespace SabreTools.Library.DatFiles
|
||||
//xtw.WriteEndElement();
|
||||
|
||||
xtw.WriteStartElement("rom");
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("start", rom.Offset);
|
||||
//xtw.WriteElementString("type", "Normal");
|
||||
xtw.WriteElementString("hash", rom.GetField(Field.SHA1, ExcludeFields).ToLowerInvariant());
|
||||
xtw.WriteElementString("hash", rom.GetField(Field.SHA1, DatHeader.ExcludeFields).ToLowerInvariant());
|
||||
//xtw.WriteElementString("remark", "");
|
||||
|
||||
// End rom
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public RomCenter(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,25 +30,18 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a RomCenter DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all intenral variables
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
IniReader ir = Utilities.GetIniReader(filename, false);
|
||||
IniReader ir = filename.GetIniReader(false);
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (ir == null)
|
||||
@@ -85,7 +78,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "games":
|
||||
ReadGamesSection(ir, sysid, srcid, clean, remUnicode);
|
||||
ReadGamesSection(ir, filename, indexId);
|
||||
break;
|
||||
|
||||
// Unknown section so we ignore it
|
||||
@@ -137,32 +130,37 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (kvp?.Key.ToLowerInvariant())
|
||||
{
|
||||
case "author":
|
||||
Author = string.IsNullOrWhiteSpace(Author) ? kvp?.Value : Author;
|
||||
DatHeader.Author = string.IsNullOrWhiteSpace(DatHeader.Author) ? kvp?.Value : DatHeader.Author;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "version":
|
||||
Version = string.IsNullOrWhiteSpace(Version) ? kvp?.Value : Version;
|
||||
DatHeader.Version = string.IsNullOrWhiteSpace(DatHeader.Version) ? kvp?.Value : DatHeader.Version;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "email":
|
||||
Email = string.IsNullOrWhiteSpace(Email) ? kvp?.Value : Email;
|
||||
DatHeader.Email = string.IsNullOrWhiteSpace(DatHeader.Email) ? kvp?.Value : DatHeader.Email;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "homepage":
|
||||
Homepage = string.IsNullOrWhiteSpace(Homepage) ? kvp?.Value : Homepage;
|
||||
DatHeader.Homepage = string.IsNullOrWhiteSpace(DatHeader.Homepage) ? kvp?.Value : DatHeader.Homepage;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "url":
|
||||
Url = string.IsNullOrWhiteSpace(Url) ? kvp?.Value : Url;
|
||||
DatHeader.Url = string.IsNullOrWhiteSpace(DatHeader.Url) ? kvp?.Value : DatHeader.Url;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "date":
|
||||
Date = string.IsNullOrWhiteSpace(Date) ? kvp?.Value : Date;
|
||||
DatHeader.Date = string.IsNullOrWhiteSpace(DatHeader.Date) ? kvp?.Value : DatHeader.Date;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "comment":
|
||||
DatHeader.Comment = string.IsNullOrWhiteSpace(DatHeader.Comment) ? kvp?.Value : DatHeader.Comment;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
@@ -207,25 +205,23 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (kvp?.Key.ToLowerInvariant())
|
||||
{
|
||||
case "version":
|
||||
string rcVersion = kvp?.Value;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "plugin":
|
||||
string plugin = kvp?.Value;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "split":
|
||||
if (ForceMerging == ForceMerging.None && kvp?.Value == "1")
|
||||
ForceMerging = ForceMerging.Split;
|
||||
if (DatHeader.ForceMerging == ForceMerging.None && kvp?.Value == "1")
|
||||
DatHeader.ForceMerging = ForceMerging.Split;
|
||||
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "merge":
|
||||
if (ForceMerging == ForceMerging.None && kvp?.Value == "1")
|
||||
ForceMerging = ForceMerging.Merged;
|
||||
if (DatHeader.ForceMerging == ForceMerging.None && kvp?.Value == "1")
|
||||
DatHeader.ForceMerging = ForceMerging.Merged;
|
||||
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
@@ -271,12 +267,12 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (kvp?.Key.ToLowerInvariant())
|
||||
{
|
||||
case "refname":
|
||||
Name = string.IsNullOrWhiteSpace(Name) ? kvp?.Value : Name;
|
||||
DatHeader.Name = string.IsNullOrWhiteSpace(DatHeader.Name) ? kvp?.Value : DatHeader.Name;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
case "version":
|
||||
Description = string.IsNullOrWhiteSpace(Description) ? kvp?.Value : Description;
|
||||
DatHeader.Description = string.IsNullOrWhiteSpace(DatHeader.Description) ? kvp?.Value : DatHeader.Description;
|
||||
reader.ReadNextLine();
|
||||
break;
|
||||
|
||||
@@ -292,11 +288,9 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Read games information
|
||||
/// </summary>
|
||||
/// <param name="reader">IniReader to use to parse the credits</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private void ReadGamesSection(IniReader reader, int sysid, int srcid, bool clean, bool remUnicode)
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
private void ReadGamesSection(IniReader reader, string filename, int indexId)
|
||||
{
|
||||
// If the reader is somehow null, skip it
|
||||
if (reader == null)
|
||||
@@ -349,7 +343,7 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = rominfo[5],
|
||||
Size = size,
|
||||
CRC = Utilities.CleanHashData(rominfo[6], Constants.CRCLength),
|
||||
CRC = rominfo[6],
|
||||
ItemStatus = ItemStatus.None,
|
||||
|
||||
MachineName = rominfo[3],
|
||||
@@ -358,12 +352,12 @@ namespace SabreTools.Library.DatFiles
|
||||
RomOf = rominfo[8],
|
||||
MergeTag = rominfo[9],
|
||||
|
||||
SystemID = sysid,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
|
||||
reader.ReadNextLine();
|
||||
}
|
||||
@@ -380,7 +374,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -431,7 +425,9 @@ namespace SabreTools.Library.DatFiles
|
||||
((Rom)rom).Size = Constants.SizeZero;
|
||||
((Rom)rom).CRC = ((Rom)rom).CRC == "null" ? Constants.CRCZero : null;
|
||||
((Rom)rom).MD5 = ((Rom)rom).MD5 == "null" ? Constants.MD5Zero : null;
|
||||
#if NET_FRAMEWORK
|
||||
((Rom)rom).RIPEMD160 = ((Rom)rom).RIPEMD160 == "null" ? Constants.RIPEMD160Zero : null;
|
||||
#endif
|
||||
((Rom)rom).SHA1 = ((Rom)rom).SHA1 == "null" ? Constants.SHA1Zero : null;
|
||||
((Rom)rom).SHA256 = ((Rom)rom).SHA256 == "null" ? Constants.SHA256Zero : null;
|
||||
((Rom)rom).SHA384 = ((Rom)rom).SHA384 == "null" ? Constants.SHA384Zero : null;
|
||||
@@ -469,18 +465,18 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
iw.WriteSection("CREDITS");
|
||||
iw.WriteKeyValuePair("author", Author);
|
||||
iw.WriteKeyValuePair("version", Version);
|
||||
iw.WriteKeyValuePair("comment", Comment);
|
||||
iw.WriteKeyValuePair("author", DatHeader.Author);
|
||||
iw.WriteKeyValuePair("version", DatHeader.Version);
|
||||
iw.WriteKeyValuePair("comment", DatHeader.Comment);
|
||||
|
||||
iw.WriteSection("DAT");
|
||||
iw.WriteKeyValuePair("version", "2.50");
|
||||
iw.WriteKeyValuePair("split", ForceMerging == ForceMerging.Split ? "1" : "0");
|
||||
iw.WriteKeyValuePair("merge", ForceMerging == ForceMerging.Full || ForceMerging == ForceMerging.Merged ? "1" : "0");
|
||||
iw.WriteKeyValuePair("split", DatHeader.ForceMerging == ForceMerging.Split ? "1" : "0");
|
||||
iw.WriteKeyValuePair("merge", DatHeader.ForceMerging == ForceMerging.Full || DatHeader.ForceMerging == ForceMerging.Merged ? "1" : "0");
|
||||
|
||||
iw.WriteSection("EMULATOR");
|
||||
iw.WriteKeyValuePair("refname", Name);
|
||||
iw.WriteKeyValuePair("version", Description);
|
||||
iw.WriteKeyValuePair("refname", DatHeader.Name);
|
||||
iw.WriteKeyValuePair("version", DatHeader.Description);
|
||||
|
||||
iw.WriteSection("GAMES");
|
||||
|
||||
@@ -527,18 +523,18 @@ namespace SabreTools.Library.DatFiles
|
||||
ProcessItemName(datItem, true);
|
||||
|
||||
// Build the state based on excluded fields
|
||||
iw.WriteString($"¬{datItem.GetField(Field.CloneOf, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.CloneOf, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.MachineName, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.MachineName, DatHeader.ExcludeFields)}");
|
||||
if (string.IsNullOrWhiteSpace(datItem.MachineDescription))
|
||||
iw.WriteString($"¬{datItem.GetField(Field.MachineName, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.MachineName, DatHeader.ExcludeFields)}");
|
||||
else
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Description, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Name, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.CRC, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Size, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.RomOf, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Merge, ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Description, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Name, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.CRC, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Size, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.RomOf, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString($"¬{datItem.GetField(Field.Merge, DatHeader.ExcludeFields)}");
|
||||
iw.WriteString("¬");
|
||||
iw.WriteLine();
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of an SabreDat XML DAT
|
||||
/// </summary>
|
||||
/// TODO: Verify that all write for this DatFile type is correct
|
||||
internal class SabreDat : DatFile
|
||||
{
|
||||
/// <summary>
|
||||
@@ -23,7 +22,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SabreDat(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -31,29 +30,22 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse an SabreDat XML DAT and return all found directories and files within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
bool empty = true;
|
||||
string key = string.Empty;
|
||||
string key;
|
||||
List<string> parent = new List<string>();
|
||||
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
XmlReader xtr = Utilities.GetXmlTextReader(filename);
|
||||
XmlReader xtr = filename.GetXmlTextReader();
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (xtr == null)
|
||||
@@ -72,10 +64,10 @@ namespace SabreTools.Library.DatFiles
|
||||
if (empty)
|
||||
{
|
||||
string tempgame = string.Join("\\", parent);
|
||||
Rom rom = new Rom("null", tempgame, omitFromScan: Hash.DeepHashes); // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually
|
||||
Rom rom = new Rom("null", tempgame);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(rom, clean, remUnicode);
|
||||
key = ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
// Regardless, end the current folder
|
||||
@@ -91,7 +83,7 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
parent.RemoveAt(parent.Count - 1);
|
||||
if (keep && parentcount > 1)
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? "SuperDAT" : DatHeader.Type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +106,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
case "dir":
|
||||
case "directory":
|
||||
empty = ReadDirectory(xtr.ReadSubtree(), parent, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
empty = ReadDirectory(xtr.ReadSubtree(), parent, filename, indexId, keep);
|
||||
|
||||
// Skip the directory node now that we've processed it
|
||||
xtr.Read();
|
||||
@@ -160,55 +152,55 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// Get all header items (ONLY OVERWRITE IF THERE'S NO DATA)
|
||||
string content = string.Empty;
|
||||
string content;
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "name":
|
||||
content = reader.ReadElementContentAsString(); ;
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? content : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? content : DatHeader.Name);
|
||||
superdat = superdat || content.Contains(" - SuperDAT");
|
||||
if (keep && superdat)
|
||||
{
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? "SuperDAT" : DatHeader.Type);
|
||||
}
|
||||
break;
|
||||
|
||||
case "description":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? content : Description);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? content : DatHeader.Description);
|
||||
break;
|
||||
|
||||
case "rootdir":
|
||||
content = reader.ReadElementContentAsString();
|
||||
RootDir = (string.IsNullOrWhiteSpace(RootDir) ? content : RootDir);
|
||||
DatHeader.RootDir = (string.IsNullOrWhiteSpace(DatHeader.RootDir) ? content : DatHeader.RootDir);
|
||||
break;
|
||||
|
||||
case "category":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Category = (string.IsNullOrWhiteSpace(Category) ? content : Category);
|
||||
DatHeader.Category = (string.IsNullOrWhiteSpace(DatHeader.Category) ? content : DatHeader.Category);
|
||||
break;
|
||||
|
||||
case "version":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? content : Version);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? content : DatHeader.Version);
|
||||
break;
|
||||
|
||||
case "date":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Date = (string.IsNullOrWhiteSpace(Date) ? content.Replace(".", "/") : Date);
|
||||
DatHeader.Date = (string.IsNullOrWhiteSpace(DatHeader.Date) ? content.Replace(".", "/") : DatHeader.Date);
|
||||
break;
|
||||
|
||||
case "author":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Author = (string.IsNullOrWhiteSpace(Author) ? content : Author);
|
||||
Email = (string.IsNullOrWhiteSpace(Email) ? reader.GetAttribute("email") : Email);
|
||||
Homepage = (string.IsNullOrWhiteSpace(Homepage) ? reader.GetAttribute("homepage") : Homepage);
|
||||
Url = (string.IsNullOrWhiteSpace(Url) ? reader.GetAttribute("url") : Url);
|
||||
DatHeader.Author = (string.IsNullOrWhiteSpace(DatHeader.Author) ? content : DatHeader.Author);
|
||||
DatHeader.Email = (string.IsNullOrWhiteSpace(DatHeader.Email) ? reader.GetAttribute("email") : DatHeader.Email);
|
||||
DatHeader.Homepage = (string.IsNullOrWhiteSpace(DatHeader.Homepage) ? reader.GetAttribute("homepage") : DatHeader.Homepage);
|
||||
DatHeader.Url = (string.IsNullOrWhiteSpace(DatHeader.Url) ? reader.GetAttribute("url") : DatHeader.Url);
|
||||
break;
|
||||
|
||||
case "comment":
|
||||
content = reader.ReadElementContentAsString();
|
||||
Comment = (string.IsNullOrWhiteSpace(Comment) ? content : Comment);
|
||||
DatHeader.Comment = (string.IsNullOrWhiteSpace(DatHeader.Comment) ? content : DatHeader.Comment);
|
||||
break;
|
||||
|
||||
case "flags":
|
||||
@@ -230,23 +222,17 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="reader">XmlReader to use to parse the header</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private bool ReadDirectory(XmlReader reader,
|
||||
List<string> parent,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
XmlReader flagreader;
|
||||
@@ -273,10 +259,10 @@ namespace SabreTools.Library.DatFiles
|
||||
if (empty)
|
||||
{
|
||||
string tempgame = string.Join("\\", parent);
|
||||
Rom rom = new Rom("null", tempgame, omitFromScan: Hash.DeepHashes); // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually
|
||||
Rom rom = new Rom("null", tempgame);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(rom, clean, remUnicode);
|
||||
key = ParseAddHelper(rom);
|
||||
}
|
||||
|
||||
// Regardless, end the current folder
|
||||
@@ -292,7 +278,7 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
parent.RemoveAt(parent.Count - 1);
|
||||
if (keep && parentcount > 1)
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? "SuperDAT" : DatHeader.Type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +296,7 @@ namespace SabreTools.Library.DatFiles
|
||||
// Directories can contain directories
|
||||
case "dir":
|
||||
case "directory":
|
||||
ReadDirectory(reader.ReadSubtree(), parent, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
ReadDirectory(reader.ReadSubtree(), parent, filename, indexId, keep);
|
||||
|
||||
// Skip the directory node now that we've processed it
|
||||
reader.Read();
|
||||
@@ -345,7 +331,7 @@ namespace SabreTools.Library.DatFiles
|
||||
if (flagreader.GetAttribute("name") != null && flagreader.GetAttribute("value") != null)
|
||||
{
|
||||
content = flagreader.GetAttribute("value");
|
||||
its = Utilities.GetItemStatus(flagreader.GetAttribute("name"));
|
||||
its = flagreader.GetAttribute("name").AsItemStatus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -354,16 +340,17 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
// If the rom has a Date attached, read it in and then sanitize it
|
||||
date = Utilities.GetDate(reader.GetAttribute("date"));
|
||||
date = Sanitizer.CleanDate(reader.GetAttribute("date"));
|
||||
|
||||
// Take care of hex-sized files
|
||||
size = Utilities.GetSize(reader.GetAttribute("size"));
|
||||
size = Sanitizer.CleanSize(reader.GetAttribute("size"));
|
||||
|
||||
Machine dir = new Machine();
|
||||
|
||||
// Get the name of the game from the parent
|
||||
dir.Name = string.Join("\\", parent);
|
||||
dir.Description = dir.Name;
|
||||
Machine dir = new Machine
|
||||
{
|
||||
// Get the name of the game from the parent
|
||||
Name = string.Join("\\", parent),
|
||||
Description = string.Join("\\", parent),
|
||||
};
|
||||
|
||||
DatItem datItem;
|
||||
switch (reader.GetAttribute("type").ToLowerInvariant())
|
||||
@@ -373,9 +360,8 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -384,11 +370,10 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Description = reader.GetAttribute("description"),
|
||||
Default = Utilities.GetYesNo(reader.GetAttribute("default")),
|
||||
Default = reader.GetAttribute("default").AsYesNo(),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -396,17 +381,18 @@ namespace SabreTools.Library.DatFiles
|
||||
datItem = new Disk
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.RIPEMD160Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 =reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
ItemStatus = its,
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -417,11 +403,10 @@ namespace SabreTools.Library.DatFiles
|
||||
Region = reader.GetAttribute("region"),
|
||||
Language = reader.GetAttribute("language"),
|
||||
Date = reader.GetAttribute("date"),
|
||||
Default = Utilities.GetYesNo(reader.GetAttribute("default")),
|
||||
Default = reader.GetAttribute("default").AsYesNo(),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -430,19 +415,20 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Size = size,
|
||||
CRC = Utilities.CleanHashData(reader.GetAttribute("crc"), Constants.CRCLength),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.RIPEMD160Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
CRC = reader.GetAttribute("crc"),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 = reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
ItemStatus = its,
|
||||
Date = date,
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -451,9 +437,8 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -466,7 +451,7 @@ namespace SabreTools.Library.DatFiles
|
||||
datItem?.CopyMachineInformation(dir);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(datItem, clean, remUnicode);
|
||||
key = ParseAddHelper(datItem);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -484,7 +469,7 @@ namespace SabreTools.Library.DatFiles
|
||||
private void ReadFlags(XmlReader reader, bool superdat)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
string content = string.Empty;
|
||||
string content;
|
||||
|
||||
// If we somehow have a null flag section, skip it
|
||||
if (reader == null)
|
||||
@@ -508,29 +493,26 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (reader.GetAttribute("name").ToLowerInvariant())
|
||||
{
|
||||
case "type":
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? content : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? content : DatHeader.Type);
|
||||
superdat = superdat || content.Contains("SuperDAT");
|
||||
break;
|
||||
|
||||
case "forcemerging":
|
||||
if (ForceMerging == ForceMerging.None)
|
||||
{
|
||||
ForceMerging = Utilities.GetForceMerging(content);
|
||||
}
|
||||
if (DatHeader.ForceMerging == ForceMerging.None)
|
||||
DatHeader.ForceMerging = content.AsForceMerging();
|
||||
|
||||
break;
|
||||
|
||||
case "forcenodump":
|
||||
if (ForceNodump == ForceNodump.None)
|
||||
{
|
||||
ForceNodump = Utilities.GetForceNodump(content);
|
||||
}
|
||||
if (DatHeader.ForceNodump == ForceNodump.None)
|
||||
DatHeader.ForceNodump = content.AsForceNodump();
|
||||
|
||||
break;
|
||||
|
||||
case "forcepacking":
|
||||
if (ForcePacking == ForcePacking.None)
|
||||
{
|
||||
ForcePacking = Utilities.GetForcePacking(content);
|
||||
}
|
||||
if (DatHeader.ForcePacking == ForcePacking.None)
|
||||
DatHeader.ForcePacking = content.AsForcePacking();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -557,7 +539,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -566,10 +548,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false));
|
||||
xtw.Formatting = Formatting.Indented;
|
||||
xtw.IndentChar = '\t';
|
||||
xtw.Indentation = 1;
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
@@ -609,7 +593,7 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// If we have a new game, output the beginning of the new item
|
||||
if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant())
|
||||
depth = WriteStartGame(xtw, rom, newsplit, lastgame, depth, last);
|
||||
depth = WriteStartGame(xtw, rom, newsplit, depth, last);
|
||||
|
||||
// If we have a "null" game (created by DATFromDir or something similar), log it to file
|
||||
if (rom.ItemType == ItemType.Rom
|
||||
@@ -664,31 +648,34 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
xtw.WriteStartElement("header");
|
||||
|
||||
xtw.WriteElementString("name", Name);
|
||||
xtw.WriteElementString("description", Description);
|
||||
if (!string.IsNullOrWhiteSpace(RootDir))
|
||||
xtw.WriteElementString("rootdir", RootDir);
|
||||
if (!string.IsNullOrWhiteSpace(Category))
|
||||
xtw.WriteElementString("category", Category);
|
||||
xtw.WriteElementString("version", Version);
|
||||
if (!string.IsNullOrWhiteSpace(Date))
|
||||
xtw.WriteElementString("date", Date);
|
||||
xtw.WriteElementString("author", Author);
|
||||
if (!string.IsNullOrWhiteSpace(Comment))
|
||||
xtw.WriteElementString("comment", Comment);
|
||||
if (!string.IsNullOrWhiteSpace(Type) || ForcePacking != ForcePacking.None || ForceMerging != ForceMerging.None || ForceNodump != ForceNodump.None)
|
||||
xtw.WriteElementString("name", DatHeader.Name);
|
||||
xtw.WriteElementString("description", DatHeader.Description);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.RootDir))
|
||||
xtw.WriteElementString("rootdir", DatHeader.RootDir);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Category))
|
||||
xtw.WriteElementString("category", DatHeader.Category);
|
||||
xtw.WriteElementString("version", DatHeader.Version);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Date))
|
||||
xtw.WriteElementString("date", DatHeader.Date);
|
||||
xtw.WriteElementString("author", DatHeader.Author);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Comment))
|
||||
xtw.WriteElementString("comment", DatHeader.Comment);
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Type)
|
||||
|| DatHeader.ForcePacking != ForcePacking.None
|
||||
|| DatHeader.ForceMerging != ForceMerging.None
|
||||
|| DatHeader.ForceNodump != ForceNodump.None)
|
||||
{
|
||||
xtw.WriteStartElement("flags");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Type))
|
||||
if (!string.IsNullOrWhiteSpace(DatHeader.Type))
|
||||
{
|
||||
xtw.WriteStartElement("flag");
|
||||
xtw.WriteAttributeString("name", "type");
|
||||
xtw.WriteAttributeString("value", Type);
|
||||
xtw.WriteAttributeString("value", DatHeader.Type);
|
||||
xtw.WriteEndElement();
|
||||
}
|
||||
|
||||
switch (ForcePacking)
|
||||
|
||||
switch (DatHeader.ForcePacking)
|
||||
{
|
||||
case ForcePacking.Unzip:
|
||||
xtw.WriteStartElement("flag");
|
||||
@@ -703,8 +690,8 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ForceMerging)
|
||||
|
||||
switch (DatHeader.ForceMerging)
|
||||
{
|
||||
case ForceMerging.Full:
|
||||
xtw.WriteStartElement("flag");
|
||||
@@ -731,8 +718,8 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ForceNodump)
|
||||
|
||||
switch (DatHeader.ForceNodump)
|
||||
{
|
||||
case ForceNodump.Ignore:
|
||||
xtw.WriteStartElement("flag");
|
||||
@@ -780,11 +767,10 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="xtw">XmlTextWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
/// <param name="newsplit">Split path representing the parent game (SabreDAT only)</param>
|
||||
/// <param name="lastgame">The name of the last game to be output</param>
|
||||
/// <param name="depth">Current depth to output file at (SabreDAT only)</param>
|
||||
/// <param name="last">Last known depth to cycle back from (SabreDAT only)</param>
|
||||
/// <returns>The new depth of the tag</returns>
|
||||
private int WriteStartGame(XmlTextWriter xtw, DatItem datItem, List<string> newsplit, string lastgame, int depth, int last)
|
||||
private int WriteStartGame(XmlTextWriter xtw, DatItem datItem, List<string> newsplit, int depth, int last)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -795,8 +781,8 @@ namespace SabreTools.Library.DatFiles
|
||||
for (int i = (last == -1 ? 0 : last); i < newsplit.Count; i++)
|
||||
{
|
||||
xtw.WriteStartElement("directory");
|
||||
xtw.WriteAttributeString("name", !ExcludeFields[(int)Field.MachineName] ? newsplit[i] : string.Empty);
|
||||
xtw.WriteAttributeString("description", !ExcludeFields[(int)Field.MachineName] ? newsplit[i] : string.Empty);
|
||||
xtw.WriteAttributeString("name", !DatHeader.ExcludeFields[(int)Field.MachineName] ? newsplit[i] : string.Empty);
|
||||
xtw.WriteAttributeString("description", !DatHeader.ExcludeFields[(int)Field.MachineName] ? newsplit[i] : string.Empty);
|
||||
}
|
||||
|
||||
depth = depth - (last == -1 ? 0 : last) + newsplit.Count;
|
||||
@@ -886,7 +872,7 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Archive:
|
||||
xtw.WriteStartElement("file");
|
||||
xtw.WriteAttributeString("type", "archive");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
|
||||
@@ -894,10 +880,10 @@ namespace SabreTools.Library.DatFiles
|
||||
var biosSet = datItem as BiosSet;
|
||||
xtw.WriteStartElement("file");
|
||||
xtw.WriteAttributeString("type", "biosset");
|
||||
xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("description", biosSet.Description);
|
||||
if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && biosSet.Default != null)
|
||||
xtw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant());
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
@@ -906,20 +892,22 @@ namespace SabreTools.Library.DatFiles
|
||||
var disk = datItem as Disk;
|
||||
xtw.WriteStartElement("file");
|
||||
xtw.WriteAttributeString("type", "disk");
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant());
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
{
|
||||
xtw.WriteStartElement("flags");
|
||||
|
||||
@@ -938,14 +926,14 @@ namespace SabreTools.Library.DatFiles
|
||||
var release = datItem as Release;
|
||||
xtw.WriteStartElement("file");
|
||||
xtw.WriteAttributeString("type", "release");
|
||||
xtw.WriteAttributeString("name", release.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", release.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("region", release.Region);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("language", release.Language);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("date", release.Date);
|
||||
if (!ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Default] && release.Default != null)
|
||||
xtw.WriteAttributeString("default", release.Default.ToString().ToLowerInvariant());
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
@@ -954,26 +942,28 @@ namespace SabreTools.Library.DatFiles
|
||||
var rom = datItem as Rom;
|
||||
xtw.WriteStartElement("file");
|
||||
xtw.WriteAttributeString("type", "rom");
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("size", rom.Size.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("date", rom.Date);
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
{
|
||||
xtw.WriteStartElement("flags");
|
||||
|
||||
@@ -991,7 +981,7 @@ namespace SabreTools.Library.DatFiles
|
||||
case ItemType.Sample:
|
||||
xtw.WriteStartElement("file");
|
||||
xtw.WriteAttributeString("type", "sample");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields));
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
xtw.WriteEndElement();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ using System.Text;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
using SabreTools.Library.Readers;
|
||||
using SabreTools.Library.Tools;
|
||||
using SabreTools.Library.Writers;
|
||||
using NaturalSort;
|
||||
using SabreTools.Library.Readers;
|
||||
|
||||
namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
@@ -26,7 +26,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
/// <param name="delim">Delimiter for parsing individual lines</param>
|
||||
public SeparatedValue(DatFile datFile, char delim)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
_delim = delim;
|
||||
}
|
||||
@@ -35,29 +35,25 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse a character-separated value DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Open a file reader
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
SeparatedValueReader svr = new SeparatedValueReader(Utilities.TryOpenRead(filename), enc);
|
||||
svr.Header = true;
|
||||
svr.Quotes = true;
|
||||
svr.Separator = _delim;
|
||||
svr.VerifyFieldCount = true;
|
||||
Encoding enc = FileExtensions.GetEncoding(filename);
|
||||
SeparatedValueReader svr = new SeparatedValueReader(FileExtensions.TryOpenRead(filename), enc)
|
||||
{
|
||||
Header = true,
|
||||
Quotes = true,
|
||||
Separator = _delim,
|
||||
VerifyFieldCount = true
|
||||
};
|
||||
|
||||
// If we're somehow at the end of the stream already, we can't do anything
|
||||
if (svr.EndOfStream)
|
||||
@@ -93,7 +89,9 @@ namespace SabreTools.Library.DatFiles
|
||||
biosDescription = null,
|
||||
crc = null,
|
||||
md5 = null,
|
||||
#if NET_FRAMEWORK
|
||||
ripemd160 = null,
|
||||
#endif
|
||||
sha1 = null,
|
||||
sha256 = null,
|
||||
sha384 = null,
|
||||
@@ -120,71 +118,71 @@ namespace SabreTools.Library.DatFiles
|
||||
#region DatFile
|
||||
|
||||
case "DatFile.FileName":
|
||||
FileName = (string.IsNullOrWhiteSpace(FileName) ? value : FileName);
|
||||
DatHeader.FileName = (string.IsNullOrWhiteSpace(DatHeader.FileName) ? value : DatHeader.FileName);
|
||||
break;
|
||||
|
||||
case "DatFile.Name":
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? value : Name);
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? value : DatHeader.Name);
|
||||
break;
|
||||
|
||||
case "DatFile.Description":
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? value : Description);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? value : DatHeader.Description);
|
||||
break;
|
||||
|
||||
case "DatFile.RootDir":
|
||||
RootDir = (string.IsNullOrWhiteSpace(RootDir) ? value : RootDir);
|
||||
DatHeader.RootDir = (string.IsNullOrWhiteSpace(DatHeader.RootDir) ? value : DatHeader.RootDir);
|
||||
break;
|
||||
|
||||
case "DatFile.Category":
|
||||
Category = (string.IsNullOrWhiteSpace(Category) ? value : Category);
|
||||
DatHeader.Category = (string.IsNullOrWhiteSpace(DatHeader.Category) ? value : DatHeader.Category);
|
||||
break;
|
||||
|
||||
case "DatFile.Version":
|
||||
Version = (string.IsNullOrWhiteSpace(Version) ? value : Version);
|
||||
DatHeader.Version = (string.IsNullOrWhiteSpace(DatHeader.Version) ? value : DatHeader.Version);
|
||||
break;
|
||||
|
||||
case "DatFile.Date":
|
||||
Date = (string.IsNullOrWhiteSpace(Date) ? value : Date);
|
||||
DatHeader.Date = (string.IsNullOrWhiteSpace(DatHeader.Date) ? value : DatHeader.Date);
|
||||
break;
|
||||
|
||||
case "DatFile.Author":
|
||||
Author = (string.IsNullOrWhiteSpace(Author) ? value : Author);
|
||||
DatHeader.Author = (string.IsNullOrWhiteSpace(DatHeader.Author) ? value : DatHeader.Author);
|
||||
break;
|
||||
|
||||
case "DatFile.Email":
|
||||
Email = (string.IsNullOrWhiteSpace(Email) ? value : Email);
|
||||
DatHeader.Email = (string.IsNullOrWhiteSpace(DatHeader.Email) ? value : DatHeader.Email);
|
||||
break;
|
||||
|
||||
case "DatFile.Homepage":
|
||||
Homepage = (string.IsNullOrWhiteSpace(Homepage) ? value : Homepage);
|
||||
DatHeader.Homepage = (string.IsNullOrWhiteSpace(DatHeader.Homepage) ? value : DatHeader.Homepage);
|
||||
break;
|
||||
|
||||
case "DatFile.Url":
|
||||
Url = (string.IsNullOrWhiteSpace(Url) ? value : Url);
|
||||
DatHeader.Url = (string.IsNullOrWhiteSpace(DatHeader.Url) ? value : DatHeader.Url);
|
||||
break;
|
||||
|
||||
case "DatFile.Comment":
|
||||
Comment = (string.IsNullOrWhiteSpace(Comment) ? value : Comment);
|
||||
DatHeader.Comment = (string.IsNullOrWhiteSpace(DatHeader.Comment) ? value : DatHeader.Comment);
|
||||
break;
|
||||
|
||||
case "DatFile.Header":
|
||||
Header = (string.IsNullOrWhiteSpace(Header) ? value : Header);
|
||||
DatHeader.Header = (string.IsNullOrWhiteSpace(DatHeader.Header) ? value : DatHeader.Header);
|
||||
break;
|
||||
|
||||
case "DatFile.Type":
|
||||
Type = (string.IsNullOrWhiteSpace(Type) ? value : Type);
|
||||
DatHeader.Type = (string.IsNullOrWhiteSpace(DatHeader.Type) ? value : DatHeader.Type);
|
||||
break;
|
||||
|
||||
case "DatFile.ForceMerging":
|
||||
ForceMerging = (ForceMerging == ForceMerging.None ? Utilities.GetForceMerging(value) : ForceMerging);
|
||||
DatHeader.ForceMerging = (DatHeader.ForceMerging == ForceMerging.None ? value.AsForceMerging() : DatHeader.ForceMerging);
|
||||
break;
|
||||
|
||||
case "DatFile.ForceNodump":
|
||||
ForceNodump = (ForceNodump == ForceNodump.None ? Utilities.GetForceNodump(value) : ForceNodump);
|
||||
DatHeader.ForceNodump = (DatHeader.ForceNodump == ForceNodump.None ? value.AsForceNodump() : DatHeader.ForceNodump);
|
||||
break;
|
||||
|
||||
case "DatFile.ForcePacking":
|
||||
ForcePacking = (ForcePacking == ForcePacking.None ? Utilities.GetForcePacking(value) : ForcePacking);
|
||||
DatHeader.ForcePacking = (DatHeader.ForcePacking == ForcePacking.None ? value.AsForcePacking() : DatHeader.ForcePacking);
|
||||
break;
|
||||
|
||||
#endregion
|
||||
@@ -228,20 +226,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "Machine.Supported":
|
||||
switch (value.ToLowerInvariant())
|
||||
{
|
||||
case "yes":
|
||||
machine.Supported = true;
|
||||
break;
|
||||
case "no":
|
||||
machine.Supported = false;
|
||||
break;
|
||||
case "partial":
|
||||
default:
|
||||
machine.Supported = null;
|
||||
break;
|
||||
}
|
||||
|
||||
machine.Supported = value.AsYesNo();
|
||||
break;
|
||||
|
||||
case "Machine.SourceFile":
|
||||
@@ -249,19 +234,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "Machine.Runnable":
|
||||
switch (value.ToLowerInvariant())
|
||||
{
|
||||
case "yes":
|
||||
machine.Runnable = true;
|
||||
break;
|
||||
case "no":
|
||||
machine.Runnable = false;
|
||||
break;
|
||||
default:
|
||||
machine.Runnable = null;
|
||||
break;
|
||||
}
|
||||
|
||||
machine.Runnable = value.AsYesNo();
|
||||
break;
|
||||
|
||||
case "Machine.Board":
|
||||
@@ -304,7 +277,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "Machine.MachineType":
|
||||
machine.MachineType = Utilities.GetMachineType(value);
|
||||
machine.MachineType = value.AsMachineType();
|
||||
break;
|
||||
|
||||
#endregion
|
||||
@@ -312,7 +285,7 @@ namespace SabreTools.Library.DatFiles
|
||||
#region DatItem
|
||||
|
||||
case "DatItem.Type":
|
||||
itemType = Utilities.GetItemType(value) ?? ItemType.Rom;
|
||||
itemType = value.AsItemType() ?? ItemType.Rom;
|
||||
break;
|
||||
|
||||
case "DatItem.Name":
|
||||
@@ -351,19 +324,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "DatItem.Default":
|
||||
switch (value.ToLowerInvariant())
|
||||
{
|
||||
case "yes":
|
||||
def = true;
|
||||
break;
|
||||
case "no":
|
||||
def = false;
|
||||
break;
|
||||
default:
|
||||
def = null;
|
||||
break;
|
||||
}
|
||||
|
||||
def = value.AsYesNo();
|
||||
break;
|
||||
|
||||
case "DatItem.Description":
|
||||
@@ -377,31 +338,33 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "DatItem.CRC":
|
||||
crc = Utilities.CleanHashData(value, Constants.CRCLength);
|
||||
crc = value;
|
||||
break;
|
||||
|
||||
case "DatItem.MD5":
|
||||
md5 = Utilities.CleanHashData(value, Constants.MD5Length);
|
||||
md5 = value;
|
||||
break;
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
case "DatItem.RIPEMD160":
|
||||
ripemd160 = Utilities.CleanHashData(value, Constants.RIPEMD160Length);
|
||||
ripemd160 = value;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case "DatItem.SHA1":
|
||||
sha1 = Utilities.CleanHashData(value, Constants.SHA1Length);
|
||||
sha1 = value;
|
||||
break;
|
||||
|
||||
case "DatItem.SHA256":
|
||||
sha256 = Utilities.CleanHashData(value, Constants.SHA256Length);
|
||||
sha256 = value;
|
||||
break;
|
||||
|
||||
case "DatItem.SHA384":
|
||||
sha384 = Utilities.CleanHashData(value, Constants.SHA384Length);
|
||||
sha384 = value;
|
||||
break;
|
||||
|
||||
case "DatItem.SHA512":
|
||||
sha512 = Utilities.CleanHashData(value, Constants.SHA512Length);
|
||||
sha512 = value;
|
||||
break;
|
||||
|
||||
case "DatItem.Merge":
|
||||
@@ -417,39 +380,15 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "DatItem.Writable":
|
||||
switch (value.ToLowerInvariant())
|
||||
{
|
||||
case "yes":
|
||||
writable = true;
|
||||
break;
|
||||
case "no":
|
||||
writable = false;
|
||||
break;
|
||||
default:
|
||||
writable = null;
|
||||
break;
|
||||
}
|
||||
|
||||
writable = value.AsYesNo();
|
||||
break;
|
||||
|
||||
case "DatItem.Optional":
|
||||
switch (value.ToLowerInvariant())
|
||||
{
|
||||
case "yes":
|
||||
optional = true;
|
||||
break;
|
||||
case "no":
|
||||
optional = false;
|
||||
break;
|
||||
default:
|
||||
optional = null;
|
||||
break;
|
||||
}
|
||||
|
||||
optional = value.AsYesNo();
|
||||
break;
|
||||
|
||||
case "DatItem.Status":
|
||||
status = Utilities.GetItemStatus(value);
|
||||
status = value.AsItemStatus();
|
||||
break;
|
||||
|
||||
case "DatItem.Language":
|
||||
@@ -489,10 +428,13 @@ namespace SabreTools.Library.DatFiles
|
||||
Features = features,
|
||||
AreaName = areaName,
|
||||
AreaSize = areaSize,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
archive.CopyMachineInformation(machine);
|
||||
ParseAddHelper(archive, clean, remUnicode);
|
||||
ParseAddHelper(archive);
|
||||
break;
|
||||
|
||||
case ItemType.BiosSet:
|
||||
@@ -507,10 +449,13 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
Description = biosDescription,
|
||||
Default = def,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
biosset.CopyMachineInformation(machine);
|
||||
ParseAddHelper(biosset, clean, remUnicode);
|
||||
ParseAddHelper(biosset);
|
||||
break;
|
||||
|
||||
case ItemType.Disk:
|
||||
@@ -524,7 +469,9 @@ namespace SabreTools.Library.DatFiles
|
||||
AreaSize = areaSize,
|
||||
|
||||
MD5 = md5,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = ripemd160,
|
||||
#endif
|
||||
SHA1 = sha1,
|
||||
SHA256 = sha256,
|
||||
SHA384 = sha384,
|
||||
@@ -535,10 +482,13 @@ namespace SabreTools.Library.DatFiles
|
||||
Writable = writable,
|
||||
ItemStatus = status,
|
||||
Optional = optional,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
disk.CopyMachineInformation(machine);
|
||||
ParseAddHelper(disk, clean, remUnicode);
|
||||
ParseAddHelper(disk);
|
||||
break;
|
||||
|
||||
case ItemType.Release:
|
||||
@@ -555,10 +505,13 @@ namespace SabreTools.Library.DatFiles
|
||||
Language = language,
|
||||
Date = date,
|
||||
Default = default,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
release.CopyMachineInformation(machine);
|
||||
ParseAddHelper(release, clean, remUnicode);
|
||||
ParseAddHelper(release);
|
||||
break;
|
||||
|
||||
case ItemType.Rom:
|
||||
@@ -575,7 +528,9 @@ namespace SabreTools.Library.DatFiles
|
||||
Size = size,
|
||||
CRC = crc,
|
||||
MD5 = md5,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = ripemd160,
|
||||
#endif
|
||||
SHA1 = sha1,
|
||||
SHA256 = sha256,
|
||||
SHA384 = sha384,
|
||||
@@ -586,10 +541,13 @@ namespace SabreTools.Library.DatFiles
|
||||
Date = date,
|
||||
ItemStatus = status,
|
||||
Optional = optional,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
rom.CopyMachineInformation(machine);
|
||||
ParseAddHelper(rom, clean, remUnicode);
|
||||
ParseAddHelper(rom);
|
||||
break;
|
||||
|
||||
case ItemType.Sample:
|
||||
@@ -601,10 +559,13 @@ namespace SabreTools.Library.DatFiles
|
||||
Features = features,
|
||||
AreaName = areaName,
|
||||
AreaSize = areaSize,
|
||||
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
sample.CopyMachineInformation(machine);
|
||||
ParseAddHelper(sample, clean, remUnicode);
|
||||
ParseAddHelper(sample);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -898,7 +859,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -907,10 +868,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false));
|
||||
svw.Quotes = true;
|
||||
svw.Separator = this._delim;
|
||||
svw.VerifyFieldCount = true;
|
||||
SeparatedValueWriter svw = new SeparatedValueWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Quotes = true,
|
||||
Separator = this._delim,
|
||||
VerifyFieldCount = true
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(svw);
|
||||
@@ -1028,11 +991,11 @@ namespace SabreTools.Library.DatFiles
|
||||
// Build the state based on excluded fields
|
||||
// TODO: Can we have some way of saying what fields to write out? Support for read extends to all fields now
|
||||
string[] fields = new string[14]; // 17;
|
||||
fields[0] = FileName;
|
||||
fields[1] = Name;
|
||||
fields[2] = Description;
|
||||
fields[3] = datItem.GetField(Field.MachineName, ExcludeFields);
|
||||
fields[4] = datItem.GetField(Field.Description, ExcludeFields);
|
||||
fields[0] = DatHeader.FileName;
|
||||
fields[1] = DatHeader.Name;
|
||||
fields[2] = DatHeader.Description;
|
||||
fields[3] = datItem.GetField(Field.MachineName, DatHeader.ExcludeFields);
|
||||
fields[4] = datItem.GetField(Field.Description, DatHeader.ExcludeFields);
|
||||
|
||||
switch (datItem.ItemType)
|
||||
{
|
||||
@@ -1040,32 +1003,32 @@ namespace SabreTools.Library.DatFiles
|
||||
var disk = datItem as Disk;
|
||||
fields[5] = "disk";
|
||||
fields[6] = string.Empty;
|
||||
fields[7] = disk.GetField(Field.Name, ExcludeFields);
|
||||
fields[7] = disk.GetField(Field.Name, DatHeader.ExcludeFields);
|
||||
fields[8] = string.Empty;
|
||||
fields[9] = string.Empty;
|
||||
fields[10] = disk.GetField(Field.MD5, ExcludeFields).ToLowerInvariant();
|
||||
//fields[11] = disk.GetField(Field.RIPEMD160, ExcludeFields).ToLowerInvariant();
|
||||
fields[11] = disk.GetField(Field.SHA1, ExcludeFields).ToLowerInvariant();
|
||||
fields[12] = disk.GetField(Field.SHA256, ExcludeFields).ToLowerInvariant();
|
||||
//fields[13] = disk.GetField(Field.SHA384, ExcludeFields).ToLowerInvariant();
|
||||
//fields[14] = disk.GetField(Field.SHA512, ExcludeFields).ToLowerInvariant();
|
||||
fields[13] = disk.GetField(Field.Status, ExcludeFields);
|
||||
fields[10] = disk.GetField(Field.MD5, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
//fields[11] = disk.GetField(Field.RIPEMD160, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
fields[11] = disk.GetField(Field.SHA1, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
fields[12] = disk.GetField(Field.SHA256, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
//fields[13] = disk.GetField(Field.SHA384, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
//fields[14] = disk.GetField(Field.SHA512, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
fields[13] = disk.GetField(Field.Status, DatHeader.ExcludeFields);
|
||||
break;
|
||||
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
fields[5] = "rom";
|
||||
fields[6] = rom.GetField(Field.Name, ExcludeFields);
|
||||
fields[6] = rom.GetField(Field.Name, DatHeader.ExcludeFields);
|
||||
fields[7] = string.Empty;
|
||||
fields[8] = rom.GetField(Field.Size, ExcludeFields);
|
||||
fields[9] = rom.GetField(Field.CRC, ExcludeFields).ToLowerInvariant();
|
||||
fields[10] = rom.GetField(Field.MD5, ExcludeFields).ToLowerInvariant();
|
||||
//fields[11] = rom.GetField(Field.RIPEMD160, ExcludeFields).ToLowerInvariant();
|
||||
fields[11] = rom.GetField(Field.SHA1, ExcludeFields).ToLowerInvariant();
|
||||
fields[12] = rom.GetField(Field.SHA256, ExcludeFields).ToLowerInvariant();
|
||||
//fields[13] = rom.GetField(Field.SHA384, ExcludeFields).ToLowerInvariant();
|
||||
//fields[14] = rom.GetField(Field.SHA512, ExcludeFields).ToLowerInvariant();
|
||||
fields[13] = rom.GetField(Field.Status, ExcludeFields);
|
||||
fields[8] = rom.GetField(Field.Size, DatHeader.ExcludeFields);
|
||||
fields[9] = rom.GetField(Field.CRC, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
fields[10] = rom.GetField(Field.MD5, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
//fields[11] = rom.GetField(Field.RIPEMD160, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
fields[11] = rom.GetField(Field.SHA1, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
fields[12] = rom.GetField(Field.SHA256, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
//fields[13] = rom.GetField(Field.SHA384, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
//fields[14] = rom.GetField(Field.SHA512, DatHeader.ExcludeFields).ToLowerInvariant();
|
||||
fields[13] = rom.GetField(Field.Status, DatHeader.ExcludeFields);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of a SofwareList, M1, or MAME XML DAT
|
||||
/// </summary>
|
||||
/// TODO: Verify that all write for this DatFile type is correct
|
||||
internal class SoftwareList : DatFile
|
||||
{
|
||||
/// <summary>
|
||||
@@ -22,7 +21,7 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SoftwareList(DatFile datFile)
|
||||
: base(datFile, cloneHeader: false)
|
||||
: base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,25 +29,18 @@ namespace SabreTools.Library.DatFiles
|
||||
/// Parse an SofwareList XML DAT and return all found games and roms within
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
public override void ParseFile(
|
||||
protected override void ParseFile(
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
Encoding enc = Utilities.GetEncoding(filename);
|
||||
XmlReader xtr = Utilities.GetXmlTextReader(filename);
|
||||
XmlReader xtr = filename.GetXmlTextReader();
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (xtr == null)
|
||||
@@ -70,23 +62,23 @@ namespace SabreTools.Library.DatFiles
|
||||
switch (xtr.Name)
|
||||
{
|
||||
case "softwarelist":
|
||||
Name = (string.IsNullOrWhiteSpace(Name) ? xtr.GetAttribute("name") ?? string.Empty : Name);
|
||||
Description = (string.IsNullOrWhiteSpace(Description) ? xtr.GetAttribute("description") ?? string.Empty : Description);
|
||||
if (ForceMerging == ForceMerging.None)
|
||||
ForceMerging = Utilities.GetForceMerging(xtr.GetAttribute("forcemerging"));
|
||||
DatHeader.Name = (string.IsNullOrWhiteSpace(DatHeader.Name) ? xtr.GetAttribute("name") ?? string.Empty : DatHeader.Name);
|
||||
DatHeader.Description = (string.IsNullOrWhiteSpace(DatHeader.Description) ? xtr.GetAttribute("description") ?? string.Empty : DatHeader.Description);
|
||||
if (DatHeader.ForceMerging == ForceMerging.None)
|
||||
DatHeader.ForceMerging = xtr.GetAttribute("forcemerging").AsForceMerging();
|
||||
|
||||
if (ForceNodump == ForceNodump.None)
|
||||
ForceNodump = Utilities.GetForceNodump(xtr.GetAttribute("forcenodump"));
|
||||
if (DatHeader.ForceNodump == ForceNodump.None)
|
||||
DatHeader.ForceNodump = xtr.GetAttribute("forcenodump").AsForceNodump();
|
||||
|
||||
if (ForcePacking == ForcePacking.None)
|
||||
ForcePacking = Utilities.GetForcePacking(xtr.GetAttribute("forcepacking"));
|
||||
if (DatHeader.ForcePacking == ForcePacking.None)
|
||||
DatHeader.ForcePacking = xtr.GetAttribute("forcepacking").AsForcePacking();
|
||||
|
||||
xtr.Read();
|
||||
break;
|
||||
|
||||
// We want to process the entire subtree of the machine
|
||||
case "software":
|
||||
ReadSoftware(xtr.ReadSubtree(), filename, sysid, srcid, keep, clean, remUnicode);
|
||||
ReadSoftware(xtr.ReadSubtree(), filename, indexId, keep);
|
||||
|
||||
// Skip the software now that we've processed it
|
||||
xtr.Skip();
|
||||
@@ -114,23 +106,17 @@ namespace SabreTools.Library.DatFiles
|
||||
/// </summary>
|
||||
/// <param name="reader">XmlReader representing a software block</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private void ReadSoftware(
|
||||
XmlReader reader,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
// If we have an empty software, skip it
|
||||
if (reader == null)
|
||||
@@ -139,26 +125,24 @@ namespace SabreTools.Library.DatFiles
|
||||
// Otherwise, add what is possible
|
||||
reader.MoveToContent();
|
||||
|
||||
string key = string.Empty;
|
||||
string temptype = reader.Name;
|
||||
bool containsItems = false;
|
||||
|
||||
// Create a new machine
|
||||
MachineType machineType = MachineType.NULL;
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("isbios")) == true)
|
||||
if (reader.GetAttribute("isbios").AsYesNo() == true)
|
||||
machineType |= MachineType.Bios;
|
||||
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("isdevice")) == true)
|
||||
if (reader.GetAttribute("isdevice").AsYesNo() == true)
|
||||
machineType |= MachineType.Device;
|
||||
|
||||
if (Utilities.GetYesNo(reader.GetAttribute("ismechanical")) == true)
|
||||
if (reader.GetAttribute("ismechanical").AsYesNo() == true)
|
||||
machineType |= MachineType.Mechanical;
|
||||
|
||||
Machine machine = new Machine
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Description = reader.GetAttribute("name"),
|
||||
Supported = Utilities.GetYesNo(reader.GetAttribute("supported")), // (yes|partial|no) "yes"
|
||||
Supported = reader.GetAttribute("supported").AsYesNo(), // (yes|partial|no) "yes"
|
||||
|
||||
CloneOf = reader.GetAttribute("cloneof") ?? string.Empty,
|
||||
Infos = new List<KeyValuePair<string, string>>(),
|
||||
@@ -202,7 +186,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
|
||||
case "part": // Contains all rom and disk information
|
||||
containsItems = ReadPart(reader.ReadSubtree(), machine, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
containsItems = ReadPart(reader.ReadSubtree(), machine, filename, indexId, keep);
|
||||
|
||||
// Skip the part now that we've processed it
|
||||
reader.Skip();
|
||||
@@ -219,14 +203,13 @@ namespace SabreTools.Library.DatFiles
|
||||
{
|
||||
Blank blank = new Blank()
|
||||
{
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
blank.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
ParseAddHelper(blank, clean, remUnicode);
|
||||
ParseAddHelper(blank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,27 +219,20 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="reader">XmlReader representing a part block</param>
|
||||
/// <param name="machine">Machine information to pass to contained items</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private bool ReadPart(
|
||||
XmlReader reader,
|
||||
Machine machine,
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
string key = string.Empty, areaname = string.Empty, partname = string.Empty, partinterface = string.Empty;
|
||||
string temptype = reader.Name;
|
||||
string key, areaname, partname = string.Empty, partinterface = string.Empty;
|
||||
long? areasize = null;
|
||||
var features = new List<KeyValuePair<string, string>>();
|
||||
bool containsItems = false;
|
||||
@@ -274,10 +250,7 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
|
||||
if (reader.NodeType == XmlNodeType.EndElement && (reader.Name == "dataarea" || reader.Name == "diskarea"))
|
||||
{
|
||||
areaname = string.Empty;
|
||||
areasize = null;
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
continue;
|
||||
@@ -308,8 +281,8 @@ namespace SabreTools.Library.DatFiles
|
||||
// string dataarea_width = reader.GetAttribute("width"); // (8|16|32|64) "8"
|
||||
// string dataarea_endianness = reader.GetAttribute("endianness"); // endianness (big|little) "little"
|
||||
|
||||
containsItems = ReadDataArea(reader.ReadSubtree(), machine, features, areaname, areasize,
|
||||
partname, partinterface, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
containsItems = ReadDataArea(reader.ReadSubtree(), machine, features, areaname, areasize,
|
||||
partname, partinterface, filename, indexId, keep);
|
||||
|
||||
// Skip the dataarea now that we've processed it
|
||||
reader.Skip();
|
||||
@@ -319,7 +292,7 @@ namespace SabreTools.Library.DatFiles
|
||||
areaname = reader.GetAttribute("name");
|
||||
|
||||
containsItems = ReadDiskArea(reader.ReadSubtree(), machine, features, areaname, areasize,
|
||||
partname, partinterface, filename, sysid, srcid, keep, clean, remUnicode);
|
||||
partname, partinterface, filename, indexId, keep);
|
||||
|
||||
// Skip the diskarea now that we've processed it
|
||||
reader.Skip();
|
||||
@@ -358,11 +331,8 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="partname">Name of the containing part</param>
|
||||
/// <param name="partinterface">Interface of the containing part</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private bool ReadDataArea(
|
||||
XmlReader reader,
|
||||
Machine machine,
|
||||
@@ -374,13 +344,10 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
string key = string.Empty;
|
||||
string temptype = reader.Name;
|
||||
@@ -408,7 +375,7 @@ namespace SabreTools.Library.DatFiles
|
||||
DatItem lastrom = this[key][index];
|
||||
if (lastrom.ItemType == ItemType.Rom)
|
||||
{
|
||||
((Rom)lastrom).Size += Utilities.GetSize(reader.GetAttribute("size"));
|
||||
((Rom)lastrom).Size += Sanitizer.CleanSize(reader.GetAttribute("size"));
|
||||
}
|
||||
this[key].RemoveAt(index);
|
||||
this[key].Add(lastrom);
|
||||
@@ -419,17 +386,19 @@ namespace SabreTools.Library.DatFiles
|
||||
DatItem rom = new Rom
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
Size = Utilities.GetSize(reader.GetAttribute("size")),
|
||||
CRC = Utilities.CleanHashData(reader.GetAttribute("crc"), Constants.CRCLength),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.RIPEMD160Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
Size = Sanitizer.CleanSize(reader.GetAttribute("size")),
|
||||
CRC = reader.GetAttribute("crc"),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 = reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
Offset = reader.GetAttribute("offset"),
|
||||
// Value = reader.GetAttribute("value");
|
||||
ItemStatus = Utilities.GetItemStatus(reader.GetAttribute("status")),
|
||||
ItemStatus = reader.GetAttribute("status").AsItemStatus(),
|
||||
// LoadFlag = reader.GetAttribute("loadflag"), // (load16_byte|load16_word|load16_word_swap|load32_byte|load32_word|load32_word_swap|load32_dword|load64_word|load64_word_swap|reload|fill|continue|reload_plain|ignore)
|
||||
|
||||
AreaName = areaname,
|
||||
@@ -438,15 +407,14 @@ namespace SabreTools.Library.DatFiles
|
||||
PartName = partname,
|
||||
PartInterface = partinterface,
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
rom.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(rom, clean, remUnicode);
|
||||
key = ParseAddHelper(rom);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -471,11 +439,8 @@ namespace SabreTools.Library.DatFiles
|
||||
/// <param name="partname">Name of the containing part</param>
|
||||
/// <param name="partinterface">Interface of the containing part</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="sysid">System ID for the DAT</param>
|
||||
/// <param name="srcid">Source ID for the DAT</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
|
||||
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
|
||||
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
|
||||
private bool ReadDiskArea(
|
||||
XmlReader reader,
|
||||
Machine machine,
|
||||
@@ -487,13 +452,10 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Standard Dat parsing
|
||||
string filename,
|
||||
int sysid,
|
||||
int srcid,
|
||||
int indexId,
|
||||
|
||||
// Miscellaneous
|
||||
bool keep,
|
||||
bool clean,
|
||||
bool remUnicode)
|
||||
bool keep)
|
||||
{
|
||||
string key = string.Empty;
|
||||
string temptype = reader.Name;
|
||||
@@ -517,14 +479,16 @@ namespace SabreTools.Library.DatFiles
|
||||
DatItem disk = new Disk
|
||||
{
|
||||
Name = reader.GetAttribute("name"),
|
||||
MD5 = Utilities.CleanHashData(reader.GetAttribute("md5"), Constants.MD5Length),
|
||||
RIPEMD160 = Utilities.CleanHashData(reader.GetAttribute("ripemd160"), Constants.RIPEMD160Length),
|
||||
SHA1 = Utilities.CleanHashData(reader.GetAttribute("sha1"), Constants.SHA1Length),
|
||||
SHA256 = Utilities.CleanHashData(reader.GetAttribute("sha256"), Constants.SHA256Length),
|
||||
SHA384 = Utilities.CleanHashData(reader.GetAttribute("sha384"), Constants.SHA384Length),
|
||||
SHA512 = Utilities.CleanHashData(reader.GetAttribute("sha512"), Constants.SHA512Length),
|
||||
ItemStatus = Utilities.GetItemStatus(reader.GetAttribute("status")),
|
||||
Writable = Utilities.GetYesNo(reader.GetAttribute("writable")),
|
||||
MD5 = reader.GetAttribute("md5"),
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = reader.GetAttribute("ripemd160"),
|
||||
#endif
|
||||
SHA1 = reader.GetAttribute("sha1"),
|
||||
SHA256 = reader.GetAttribute("sha256"),
|
||||
SHA384 = reader.GetAttribute("sha384"),
|
||||
SHA512 = reader.GetAttribute("sha512"),
|
||||
ItemStatus = reader.GetAttribute("status").AsItemStatus(),
|
||||
Writable = reader.GetAttribute("writable").AsYesNo(),
|
||||
|
||||
AreaName = areaname,
|
||||
AreaSize = areasize,
|
||||
@@ -532,15 +496,14 @@ namespace SabreTools.Library.DatFiles
|
||||
PartName = partname,
|
||||
PartInterface = partinterface,
|
||||
|
||||
SystemID = sysid,
|
||||
System = filename,
|
||||
SourceID = srcid,
|
||||
IndexId = indexId,
|
||||
IndexSource = filename,
|
||||
};
|
||||
|
||||
disk.CopyMachineInformation(machine);
|
||||
|
||||
// Now process and add the rom
|
||||
key = ParseAddHelper(disk, clean, remUnicode);
|
||||
key = ParseAddHelper(disk);
|
||||
|
||||
reader.Read();
|
||||
break;
|
||||
@@ -565,7 +528,7 @@ namespace SabreTools.Library.DatFiles
|
||||
try
|
||||
{
|
||||
Globals.Logger.User($"Opening file for writing: {outfile}");
|
||||
FileStream fs = Utilities.TryCreate(outfile);
|
||||
FileStream fs = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs == null)
|
||||
@@ -574,10 +537,12 @@ namespace SabreTools.Library.DatFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false));
|
||||
xtw.Formatting = Formatting.Indented;
|
||||
xtw.IndentChar = '\t';
|
||||
xtw.Indentation = 1;
|
||||
XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
@@ -663,10 +628,10 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteDocType("softwarelist", null, "softwarelist.dtd", null);
|
||||
|
||||
xtw.WriteStartElement("softwarelist");
|
||||
xtw.WriteAttributeString("name", Name);
|
||||
xtw.WriteAttributeString("description", Description);
|
||||
xtw.WriteAttributeString("name", DatHeader.Name);
|
||||
xtw.WriteAttributeString("description", DatHeader.Description);
|
||||
|
||||
switch (ForcePacking)
|
||||
switch (DatHeader.ForcePacking)
|
||||
{
|
||||
case ForcePacking.Unzip:
|
||||
xtw.WriteAttributeString("forcepacking", "unzip");
|
||||
@@ -676,7 +641,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ForceMerging)
|
||||
switch (DatHeader.ForceMerging)
|
||||
{
|
||||
case ForceMerging.Full:
|
||||
xtw.WriteAttributeString("forcemerging", "full");
|
||||
@@ -692,7 +657,7 @@ namespace SabreTools.Library.DatFiles
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ForceNodump)
|
||||
switch (DatHeader.ForceNodump)
|
||||
{
|
||||
case ForceNodump.Ignore:
|
||||
xtw.WriteAttributeString("forcenodump", "ignore");
|
||||
@@ -731,12 +696,12 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Build the state based on excluded fields
|
||||
xtw.WriteStartElement("software");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, ExcludeFields));
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, DatHeader.ExcludeFields));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, DatHeader.ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase))
|
||||
xtw.WriteAttributeString("cloneof", datItem.CloneOf);
|
||||
|
||||
if (!ExcludeFields[(int)Field.Supported])
|
||||
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Supported])
|
||||
{
|
||||
if (datItem.Supported == true)
|
||||
xtw.WriteAttributeString("supported", "yes");
|
||||
@@ -746,14 +711,14 @@ namespace SabreTools.Library.DatFiles
|
||||
xtw.WriteAttributeString("supported", "partial");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("description", datItem.MachineDescription);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("year", datItem.Year);
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, DatHeader.ExcludeFields)))
|
||||
xtw.WriteElementString("publisher", datItem.Publisher);
|
||||
|
||||
if (!ExcludeFields[(int)Field.Infos] && datItem.Infos != null && datItem.Infos.Count > 0)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Infos] && datItem.Infos != null && datItem.Infos.Count > 0)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> kvp in datItem.Infos)
|
||||
{
|
||||
@@ -818,10 +783,10 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
// Build the state based on excluded fields
|
||||
xtw.WriteStartElement("part");
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.PartName, ExcludeFields));
|
||||
xtw.WriteAttributeString("interface", datItem.GetField(Field.PartInterface, ExcludeFields));
|
||||
xtw.WriteAttributeString("name", datItem.GetField(Field.PartName, DatHeader.ExcludeFields));
|
||||
xtw.WriteAttributeString("interface", datItem.GetField(Field.PartInterface, DatHeader.ExcludeFields));
|
||||
|
||||
if (!ExcludeFields[(int)Field.Features] && datItem.Features != null && datItem.Features.Count > 0)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Features] && datItem.Features != null && datItem.Features.Count > 0)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> kvp in datItem.Features)
|
||||
{
|
||||
@@ -832,36 +797,38 @@ namespace SabreTools.Library.DatFiles
|
||||
}
|
||||
}
|
||||
|
||||
string areaName = datItem.GetField(Field.AreaName, ExcludeFields);
|
||||
string areaName = datItem.GetField(Field.AreaName, DatHeader.ExcludeFields);
|
||||
switch (datItem.ItemType)
|
||||
{
|
||||
case ItemType.Disk:
|
||||
var disk = datItem as Disk;
|
||||
if (!ExcludeFields[(int)Field.AreaName] && string.IsNullOrWhiteSpace(areaName))
|
||||
if (!DatHeader.ExcludeFields[(int)Field.AreaName] && string.IsNullOrWhiteSpace(areaName))
|
||||
areaName = "cdrom";
|
||||
|
||||
xtw.WriteStartElement("diskarea");
|
||||
xtw.WriteAttributeString("name", areaName);
|
||||
if (!ExcludeFields[(int)Field.AreaSize] && disk.AreaSize != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.AreaSize] && disk.AreaSize != null)
|
||||
xtw.WriteAttributeString("size", disk.AreaSize.ToString());
|
||||
|
||||
xtw.WriteStartElement("disk");
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
xtw.WriteAttributeString("name", disk.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant());
|
||||
if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None)
|
||||
xtw.WriteAttributeString("status", disk.ItemStatus.ToString().ToLowerInvariant());
|
||||
if (!ExcludeFields[(int)Field.Writable] && disk.Writable != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Writable] && disk.Writable != null)
|
||||
xtw.WriteAttributeString("writable", disk.Writable == true ? "yes" : "no");
|
||||
xtw.WriteEndElement();
|
||||
|
||||
@@ -871,39 +838,41 @@ namespace SabreTools.Library.DatFiles
|
||||
|
||||
case ItemType.Rom:
|
||||
var rom = datItem as Rom;
|
||||
if (!ExcludeFields[(int)Field.AreaName] && string.IsNullOrWhiteSpace(areaName))
|
||||
if (!DatHeader.ExcludeFields[(int)Field.AreaName] && string.IsNullOrWhiteSpace(areaName))
|
||||
areaName = "rom";
|
||||
|
||||
xtw.WriteStartElement("dataarea");
|
||||
xtw.WriteAttributeString("name", areaName);
|
||||
if (!ExcludeFields[(int)Field.AreaSize] && rom.AreaSize != null)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.AreaSize] && rom.AreaSize != null)
|
||||
xtw.WriteAttributeString("size", rom.AreaSize.ToString());
|
||||
|
||||
xtw.WriteStartElement("rom");
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields));
|
||||
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("name", rom.GetField(Field.Name, DatHeader.ExcludeFields));
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Size] && rom.Size != -1)
|
||||
xtw.WriteAttributeString("size", rom.Size.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields)))
|
||||
#if NET_FRAMEWORK
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields)))
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, ExcludeFields)))
|
||||
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, DatHeader.ExcludeFields)))
|
||||
xtw.WriteAttributeString("offset", rom.Offset);
|
||||
//if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Value, ExcludeFields)))
|
||||
//if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Value, DatHeader.ExcludeFields)))
|
||||
// xtw.WriteAttributeString("value", rom.Value);
|
||||
if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
if (!DatHeader.ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None)
|
||||
xtw.WriteAttributeString("status", rom.ItemStatus.ToString().ToLowerInvariant());
|
||||
//if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Loadflag, ExcludeFields)))
|
||||
//if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Loadflag, DatHeader.ExcludeFields)))
|
||||
// xtw.WriteAttributeString("loadflag", rom.Loadflag);
|
||||
xtw.WriteEndElement();
|
||||
|
||||
|
||||
@@ -54,10 +54,8 @@ namespace SabreTools.Library.DatItems
|
||||
Devices = this.Devices,
|
||||
MachineType = this.MachineType,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -71,10 +71,8 @@ namespace SabreTools.Library.DatItems
|
||||
Devices = this.Devices,
|
||||
MachineType = this.MachineType,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
|
||||
Description = this.Description,
|
||||
Default = this.Default,
|
||||
|
||||
@@ -54,10 +54,8 @@ namespace SabreTools.Library.DatItems
|
||||
Devices = this.Devices,
|
||||
MachineType = this.MachineType,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatFiles;
|
||||
using SabreTools.Library.FileTypes;
|
||||
using SabreTools.Library.Tools;
|
||||
using NaturalSort;
|
||||
using Newtonsoft.Json;
|
||||
@@ -558,28 +559,16 @@ namespace SabreTools.Library.DatItems
|
||||
#region Source metadata information
|
||||
|
||||
/// <summary>
|
||||
/// Internal system ID for organization
|
||||
/// Internal DatFile index for organization
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public int SystemID { get; set; }
|
||||
public int IndexId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal system name for organization
|
||||
/// Internal DatFile name for organization
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string System { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal source ID for organization
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public int SourceID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal source name for organization
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string Source { get; set; }
|
||||
public string IndexSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag if item should be removed
|
||||
@@ -698,12 +687,14 @@ namespace SabreTools.Library.DatItems
|
||||
else if (ItemType == ItemType.Rom)
|
||||
fieldValue = (this as Rom).MD5;
|
||||
break;
|
||||
#if NET_FRAMEWORK
|
||||
case Field.RIPEMD160:
|
||||
if (ItemType == ItemType.Disk)
|
||||
fieldValue = (this as Disk).RIPEMD160;
|
||||
else if (ItemType == ItemType.Rom)
|
||||
fieldValue = (this as Rom).RIPEMD160;
|
||||
break;
|
||||
#endif
|
||||
case Field.SHA1:
|
||||
if (ItemType == ItemType.Disk)
|
||||
fieldValue = (this as Disk).SHA1;
|
||||
@@ -805,6 +796,90 @@ namespace SabreTools.Library.DatItems
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a specific type of DatItem to be used based on an ItemType
|
||||
/// </summary>
|
||||
/// <param name="itemType">Type of the DatItem to be created</param>
|
||||
/// <returns>DatItem of the specific internal type that corresponds to the inputs</returns>
|
||||
public static DatItem Create(ItemType itemType)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (itemType)
|
||||
{
|
||||
case ItemType.Archive:
|
||||
return new Archive();
|
||||
|
||||
case ItemType.BiosSet:
|
||||
return new BiosSet();
|
||||
|
||||
case ItemType.Blank:
|
||||
return new Blank();
|
||||
|
||||
case ItemType.Disk:
|
||||
return new Disk();
|
||||
|
||||
case ItemType.Release:
|
||||
return new Release();
|
||||
|
||||
case ItemType.Rom:
|
||||
return new Rom();
|
||||
|
||||
case ItemType.Sample:
|
||||
return new Sample();
|
||||
|
||||
default:
|
||||
return new Rom();
|
||||
}
|
||||
#else
|
||||
return itemType switch
|
||||
{
|
||||
ItemType.Archive => new Archive(),
|
||||
ItemType.BiosSet => new BiosSet(),
|
||||
ItemType.Blank => new Blank(),
|
||||
ItemType.Disk => new Disk(),
|
||||
ItemType.Release => new Release(),
|
||||
ItemType.Rom => new Rom(),
|
||||
ItemType.Sample => new Sample(),
|
||||
_ => new Rom(),
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a specific type of DatItem to be used based on a BaseFile
|
||||
/// </summary>
|
||||
/// <param name="baseFile">BaseFile containing information to be created</param>
|
||||
/// <returns>DatItem of the specific internal type that corresponds to the inputs</returns>
|
||||
public static DatItem Create(BaseFile baseFile)
|
||||
{
|
||||
switch (baseFile.Type)
|
||||
{
|
||||
case FileType.CHD:
|
||||
return new Disk(baseFile);
|
||||
|
||||
case FileType.GZipArchive:
|
||||
case FileType.LRZipArchive:
|
||||
case FileType.LZ4Archive:
|
||||
case FileType.None:
|
||||
case FileType.RarArchive:
|
||||
case FileType.SevenZipArchive:
|
||||
case FileType.TapeArchive:
|
||||
case FileType.XZArchive:
|
||||
case FileType.ZipArchive:
|
||||
case FileType.ZPAQArchive:
|
||||
case FileType.ZstdArchive:
|
||||
return new Rom(baseFile);
|
||||
|
||||
case FileType.Folder:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <summary>
|
||||
@@ -837,21 +912,17 @@ namespace SabreTools.Library.DatItems
|
||||
|
||||
public int CompareTo(DatItem other)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (this.Name == other.Name)
|
||||
ret = (this.Equals(other) ? 0 : 1);
|
||||
return this.Equals(other) ? 0 : 1;
|
||||
|
||||
ret = String.Compare(this.Name, other.Name);
|
||||
return String.Compare(this.Name, other.Name);
|
||||
}
|
||||
catch
|
||||
{
|
||||
ret = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -875,7 +946,7 @@ namespace SabreTools.Library.DatItems
|
||||
return output;
|
||||
|
||||
// If the duplicate is external already or should be, set it
|
||||
if ((lastItem.DupeType & DupeType.External) != 0 || lastItem.SystemID != this.SystemID || lastItem.SourceID != this.SourceID)
|
||||
if (lastItem.DupeType.HasFlag(DupeType.External) || lastItem.IndexId != this.IndexId)
|
||||
{
|
||||
if (lastItem.MachineName == this.MachineName && lastItem.Name == this.Name)
|
||||
output = DupeType.External | DupeType.All;
|
||||
@@ -900,94 +971,96 @@ namespace SabreTools.Library.DatItems
|
||||
#region Sorting and Merging
|
||||
|
||||
/// <summary>
|
||||
/// Check if a DAT contains the given rom
|
||||
/// Get the dictionary key that should be used for a given item and sorting type
|
||||
/// </summary>
|
||||
/// <param name="datdata">Dat to match against</param>
|
||||
/// <param name="sorted">True if the DAT is already sorted accordingly, false otherwise (default)</param>
|
||||
/// <returns>True if it contains the rom, false otherwise</returns>
|
||||
public bool HasDuplicates(DatFile datdata, bool sorted = false)
|
||||
/// <param name="sortedBy">BucketedBy enum representing what key to get</param>
|
||||
/// <param name="lower">True if the key should be lowercased (default), false otherwise</param>
|
||||
/// <param name="norename">True if games should only be compared on game and file name, false if system and source are counted</param>
|
||||
/// <returns>String representing the key to be used for the DatItem</returns>
|
||||
public string GetKey(BucketedBy sortedBy, bool lower = true, bool norename = true)
|
||||
{
|
||||
// Check for an empty rom list first
|
||||
if (datdata.Count == 0)
|
||||
return false;
|
||||
// Set the output key as the default blank string
|
||||
string key = string.Empty;
|
||||
|
||||
// We want to get the proper key for the DatItem
|
||||
string key = SortAndGetKey(datdata, sorted);
|
||||
|
||||
// If the key doesn't exist, return the empty list
|
||||
if (!datdata.Contains(key))
|
||||
return false;
|
||||
|
||||
// Try to find duplicates
|
||||
List<DatItem> roms = datdata[key];
|
||||
return roms.Any(r => this.Equals(r));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all duplicates found in a DAT based on a rom
|
||||
/// </summary>
|
||||
/// <param name="datdata">Dat to match against</param>
|
||||
/// <param name="remove">True to mark matched roms for removal from the input, false otherwise (default)</param>
|
||||
/// <param name="sorted">True if the DAT is already sorted accordingly, false otherwise (default)</param>
|
||||
/// <returns>List of matched DatItem objects</returns>
|
||||
public List<DatItem> GetDuplicates(DatFile datdata, bool remove = false, bool sorted = false)
|
||||
{
|
||||
List<DatItem> output = new List<DatItem>();
|
||||
|
||||
// Check for an empty rom list first
|
||||
if (datdata.Count == 0)
|
||||
return output;
|
||||
|
||||
// We want to get the proper key for the DatItem
|
||||
string key = SortAndGetKey(datdata, sorted);
|
||||
|
||||
// If the key doesn't exist, return the empty list
|
||||
if (!datdata.Contains(key))
|
||||
return output;
|
||||
|
||||
// Try to find duplicates
|
||||
List<DatItem> roms = datdata[key];
|
||||
List<DatItem> left = new List<DatItem>();
|
||||
for (int i = 0; i < roms.Count; i++)
|
||||
// Now determine what the key should be based on the sortedBy value
|
||||
switch (sortedBy)
|
||||
{
|
||||
DatItem datItem = roms[i];
|
||||
case BucketedBy.CRC:
|
||||
key = (this.ItemType == ItemType.Rom ? ((Rom)this).CRC : Constants.CRCZero);
|
||||
break;
|
||||
|
||||
if (this.Equals(datItem))
|
||||
{
|
||||
datItem.Remove = true;
|
||||
output.Add(datItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
left.Add(datItem);
|
||||
}
|
||||
case BucketedBy.Game:
|
||||
key = (norename ? string.Empty
|
||||
: this.IndexId.ToString().PadLeft(10, '0')
|
||||
+ "-")
|
||||
+ (string.IsNullOrWhiteSpace(this.MachineName)
|
||||
? "Default"
|
||||
: this.MachineName);
|
||||
if (lower)
|
||||
key = key.ToLowerInvariant();
|
||||
|
||||
if (key == null)
|
||||
key = "null";
|
||||
|
||||
key = WebUtility.HtmlEncode(key);
|
||||
break;
|
||||
|
||||
case BucketedBy.MD5:
|
||||
key = (this.ItemType == ItemType.Rom
|
||||
? ((Rom)this).MD5
|
||||
: (this.ItemType == ItemType.Disk
|
||||
? ((Disk)this).MD5
|
||||
: Constants.MD5Zero));
|
||||
break;
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
case BucketedBy.RIPEMD160:
|
||||
key = (this.ItemType == ItemType.Rom
|
||||
? ((Rom)this).RIPEMD160
|
||||
: (this.ItemType == ItemType.Disk
|
||||
? ((Disk)this).RIPEMD160
|
||||
: Constants.RIPEMD160Zero));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case BucketedBy.SHA1:
|
||||
key = (this.ItemType == ItemType.Rom
|
||||
? ((Rom)this).SHA1
|
||||
: (this.ItemType == ItemType.Disk
|
||||
? ((Disk)this).SHA1
|
||||
: Constants.SHA1Zero));
|
||||
break;
|
||||
|
||||
case BucketedBy.SHA256:
|
||||
key = (this.ItemType == ItemType.Rom
|
||||
? ((Rom)this).SHA256
|
||||
: (this.ItemType == ItemType.Disk
|
||||
? ((Disk)this).SHA256
|
||||
: Constants.SHA256Zero));
|
||||
break;
|
||||
|
||||
case BucketedBy.SHA384:
|
||||
key = (this.ItemType == ItemType.Rom
|
||||
? ((Rom)this).SHA384
|
||||
: (this.ItemType == ItemType.Disk
|
||||
? ((Disk)this).SHA384
|
||||
: Constants.SHA384Zero));
|
||||
break;
|
||||
|
||||
case BucketedBy.SHA512:
|
||||
key = (this.ItemType == ItemType.Rom
|
||||
? ((Rom)this).SHA512
|
||||
: (this.ItemType == ItemType.Disk
|
||||
? ((Disk)this).SHA512
|
||||
: Constants.SHA512Zero));
|
||||
break;
|
||||
}
|
||||
|
||||
// If we're in removal mode, add back all roms with the proper flags
|
||||
if (remove)
|
||||
{
|
||||
datdata.Remove(key);
|
||||
datdata.AddRange(key, output);
|
||||
datdata.AddRange(key, left);
|
||||
}
|
||||
// Double and triple check the key for corner cases
|
||||
if (key == null)
|
||||
key = string.Empty;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sort the input DAT and get the key to be used by the item
|
||||
/// </summary>
|
||||
/// <param name="datdata">Dat to match against</param>
|
||||
/// <param name="sorted">True if the DAT is already sorted accordingly, false otherwise (default)</param>
|
||||
/// <returns>Key to try to use</returns>
|
||||
private string SortAndGetKey(DatFile datdata, bool sorted = false)
|
||||
{
|
||||
// If we're not already sorted, take care of it
|
||||
if (!sorted)
|
||||
datdata.BucketByBestAvailable();
|
||||
|
||||
// Now that we have the sorted type, we get the proper key
|
||||
return Utilities.GetKeyFromDatItem(this, datdata.SortedBy);
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1069,9 +1142,11 @@ namespace SabreTools.Library.DatItems
|
||||
((Rom)saveditem).MD5 = (string.IsNullOrWhiteSpace(((Rom)saveditem).MD5) && !string.IsNullOrWhiteSpace(((Rom)file).MD5)
|
||||
? ((Rom)file).MD5
|
||||
: ((Rom)saveditem).MD5);
|
||||
#if NET_FRAMEWORK
|
||||
((Rom)saveditem).RIPEMD160 = (string.IsNullOrWhiteSpace(((Rom)saveditem).RIPEMD160) && !string.IsNullOrWhiteSpace(((Rom)file).RIPEMD160)
|
||||
? ((Rom)file).RIPEMD160
|
||||
: ((Rom)saveditem).RIPEMD160);
|
||||
#endif
|
||||
((Rom)saveditem).SHA1 = (string.IsNullOrWhiteSpace(((Rom)saveditem).SHA1) && !string.IsNullOrWhiteSpace(((Rom)file).SHA1)
|
||||
? ((Rom)file).SHA1
|
||||
: ((Rom)saveditem).SHA1);
|
||||
@@ -1090,9 +1165,11 @@ namespace SabreTools.Library.DatItems
|
||||
((Disk)saveditem).MD5 = (string.IsNullOrWhiteSpace(((Disk)saveditem).MD5) && !string.IsNullOrWhiteSpace(((Disk)file).MD5)
|
||||
? ((Disk)file).MD5
|
||||
: ((Disk)saveditem).MD5);
|
||||
#if NET_FRAMEWORK
|
||||
((Disk)saveditem).RIPEMD160 = (string.IsNullOrWhiteSpace(((Disk)saveditem).RIPEMD160) && !string.IsNullOrWhiteSpace(((Disk)file).RIPEMD160)
|
||||
? ((Disk)file).RIPEMD160
|
||||
: ((Disk)saveditem).RIPEMD160);
|
||||
#endif
|
||||
((Disk)saveditem).SHA1 = (string.IsNullOrWhiteSpace(((Disk)saveditem).SHA1) && !string.IsNullOrWhiteSpace(((Disk)file).SHA1)
|
||||
? ((Disk)file).SHA1
|
||||
: ((Disk)saveditem).SHA1);
|
||||
@@ -1110,19 +1187,10 @@ namespace SabreTools.Library.DatItems
|
||||
saveditem.DupeType = dupetype;
|
||||
|
||||
// If the current system has a lower ID than the previous, set the system accordingly
|
||||
if (file.SystemID < saveditem.SystemID)
|
||||
if (file.IndexId < saveditem.IndexId)
|
||||
{
|
||||
saveditem.SystemID = file.SystemID;
|
||||
saveditem.System = file.System;
|
||||
saveditem.CopyMachineInformation(file);
|
||||
saveditem.Name = file.Name;
|
||||
}
|
||||
|
||||
// If the current source has a lower ID than the previous, set the source accordingly
|
||||
if (file.SourceID < saveditem.SourceID)
|
||||
{
|
||||
saveditem.SourceID = file.SourceID;
|
||||
saveditem.Source = file.Source;
|
||||
saveditem.IndexId = file.IndexId;
|
||||
saveditem.IndexSource = file.IndexSource;
|
||||
saveditem.CopyMachineInformation(file);
|
||||
saveditem.Name = file.Name;
|
||||
}
|
||||
@@ -1185,7 +1253,7 @@ namespace SabreTools.Library.DatItems
|
||||
}
|
||||
|
||||
// If the current item exactly matches the last item, then we don't add it
|
||||
if ((datItem.GetDuplicateStatus(lastItem) & DupeType.All) != 0)
|
||||
if (datItem.GetDuplicateStatus(lastItem).HasFlag(DupeType.All))
|
||||
{
|
||||
Globals.Logger.Verbose($"Exact duplicate found for '{datItem.Name}'");
|
||||
continue;
|
||||
@@ -1199,7 +1267,11 @@ namespace SabreTools.Library.DatItems
|
||||
if (datItem.ItemType == ItemType.Disk || datItem.ItemType == ItemType.Rom)
|
||||
{
|
||||
datItem.Name += GetDuplicateSuffix(datItem);
|
||||
#if NET_FRAMEWORK
|
||||
lastrenamed = lastrenamed ?? datItem.Name;
|
||||
#else
|
||||
lastrenamed ??= datItem.Name;
|
||||
#endif
|
||||
}
|
||||
|
||||
// If we have a conflict with the last renamed item, do the right thing
|
||||
@@ -1281,47 +1353,42 @@ namespace SabreTools.Library.DatItems
|
||||
try
|
||||
{
|
||||
NaturalComparer nc = new NaturalComparer();
|
||||
if (x.SystemID == y.SystemID)
|
||||
if (x.IndexId == y.IndexId)
|
||||
{
|
||||
if (x.SourceID == y.SourceID)
|
||||
if (x.MachineName == y.MachineName)
|
||||
{
|
||||
if (x.MachineName == y.MachineName)
|
||||
if ((x.ItemType == ItemType.Rom || x.ItemType == ItemType.Disk) && (y.ItemType == ItemType.Rom || y.ItemType == ItemType.Disk))
|
||||
{
|
||||
if ((x.ItemType == ItemType.Rom || x.ItemType == ItemType.Disk) && (y.ItemType == ItemType.Rom || y.ItemType == ItemType.Disk))
|
||||
if (Path.GetDirectoryName(Sanitizer.RemovePathUnsafeCharacters(x.Name)) == Path.GetDirectoryName(Sanitizer.RemovePathUnsafeCharacters(y.Name)))
|
||||
{
|
||||
if (Path.GetDirectoryName(Utilities.RemovePathUnsafeCharacters(x.Name)) == Path.GetDirectoryName(Utilities.RemovePathUnsafeCharacters(y.Name)))
|
||||
{
|
||||
return nc.Compare(Path.GetFileName(Utilities.RemovePathUnsafeCharacters(x.Name)), Path.GetFileName(Utilities.RemovePathUnsafeCharacters(y.Name)));
|
||||
}
|
||||
return nc.Compare(Path.GetFileName(Sanitizer.RemovePathUnsafeCharacters(x.Name)), Path.GetFileName(Sanitizer.RemovePathUnsafeCharacters(y.Name)));
|
||||
}
|
||||
|
||||
return nc.Compare(Path.GetDirectoryName(Utilities.RemovePathUnsafeCharacters(x.Name)), Path.GetDirectoryName(Utilities.RemovePathUnsafeCharacters(y.Name)));
|
||||
}
|
||||
else if ((x.ItemType == ItemType.Rom || x.ItemType == ItemType.Disk) && (y.ItemType != ItemType.Rom && y.ItemType != ItemType.Disk))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if ((x.ItemType != ItemType.Rom && x.ItemType != ItemType.Disk) && (y.ItemType == ItemType.Rom || y.ItemType == ItemType.Disk))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Path.GetDirectoryName(x.Name) == Path.GetDirectoryName(y.Name))
|
||||
{
|
||||
return nc.Compare(Path.GetFileName(x.Name), Path.GetFileName(y.Name));
|
||||
}
|
||||
|
||||
return nc.Compare(Path.GetDirectoryName(x.Name), Path.GetDirectoryName(y.Name));
|
||||
}
|
||||
return nc.Compare(Path.GetDirectoryName(Sanitizer.RemovePathUnsafeCharacters(x.Name)), Path.GetDirectoryName(Sanitizer.RemovePathUnsafeCharacters(y.Name)));
|
||||
}
|
||||
else if ((x.ItemType == ItemType.Rom || x.ItemType == ItemType.Disk) && (y.ItemType != ItemType.Rom && y.ItemType != ItemType.Disk))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if ((x.ItemType != ItemType.Rom && x.ItemType != ItemType.Disk) && (y.ItemType == ItemType.Rom || y.ItemType == ItemType.Disk))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Path.GetDirectoryName(x.Name) == Path.GetDirectoryName(y.Name))
|
||||
{
|
||||
return nc.Compare(Path.GetFileName(x.Name), Path.GetFileName(y.Name));
|
||||
}
|
||||
|
||||
return nc.Compare(x.MachineName, y.MachineName);
|
||||
return nc.Compare(Path.GetDirectoryName(x.Name), Path.GetDirectoryName(y.Name));
|
||||
}
|
||||
}
|
||||
|
||||
return (norename ? nc.Compare(x.MachineName, y.MachineName) : x.SourceID - y.SourceID);
|
||||
return nc.Compare(x.MachineName, y.MachineName);
|
||||
}
|
||||
|
||||
return (norename ? nc.Compare(x.MachineName, y.MachineName) : x.SystemID - y.SystemID);
|
||||
return (norename ? nc.Compare(x.MachineName, y.MachineName) : x.IndexId - y.IndexId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@@ -31,19 +31,21 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("md5")]
|
||||
public string MD5
|
||||
{
|
||||
get { return _md5.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_md5); }
|
||||
set { _md5 = Utilities.StringToByteArray(value); }
|
||||
get { return _md5.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_md5); }
|
||||
set { _md5 = Utilities.StringToByteArray(Sanitizer.CleanMD5(value)); }
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
/// <summary>
|
||||
/// Data RIPEMD160 hash
|
||||
/// </summary>
|
||||
[JsonProperty("ripemd160")]
|
||||
public string RIPEMD160
|
||||
{
|
||||
get { return _ripemd160.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_ripemd160); }
|
||||
set { _ripemd160 = Utilities.StringToByteArray(value); }
|
||||
get { return _ripemd160.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_ripemd160); }
|
||||
set { _ripemd160 = Utilities.StringToByteArray(Sanitizer.CleanRIPEMD160(value)); }
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Data SHA-1 hash
|
||||
@@ -51,8 +53,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha1")]
|
||||
public string SHA1
|
||||
{
|
||||
get { return _sha1.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha1); }
|
||||
set { _sha1 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha1.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha1); }
|
||||
set { _sha1 = Utilities.StringToByteArray(Sanitizer.CleanSHA1(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,8 +63,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha256")]
|
||||
public string SHA256
|
||||
{
|
||||
get { return _sha256.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha256); }
|
||||
set { _sha256 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha256.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha256); }
|
||||
set { _sha256 = Utilities.StringToByteArray(Sanitizer.CleanSHA256(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,8 +73,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha384")]
|
||||
public string SHA384
|
||||
{
|
||||
get { return _sha384.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha384); }
|
||||
set { _sha384 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha384.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha384); }
|
||||
set { _sha384 = Utilities.StringToByteArray(Sanitizer.CleanSHA384(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,8 +83,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha512")]
|
||||
public string SHA512
|
||||
{
|
||||
get { return _sha512.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha512); }
|
||||
set { _sha512 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha512.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha512); }
|
||||
set { _sha512 = Utilities.StringToByteArray(Sanitizer.CleanSHA512(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -144,7 +146,9 @@ namespace SabreTools.Library.DatItems
|
||||
{
|
||||
this.Name = baseFile.Filename;
|
||||
_md5 = baseFile.MD5;
|
||||
#if NET_FRAMEWORK
|
||||
_ripemd160 = baseFile.RIPEMD160;
|
||||
#endif
|
||||
_sha1 = baseFile.SHA1;
|
||||
_sha256 = baseFile.SHA256;
|
||||
_sha384 = baseFile.SHA384;
|
||||
@@ -191,10 +195,8 @@ namespace SabreTools.Library.DatItems
|
||||
Devices = this.Devices,
|
||||
MachineType = this.MachineType,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
|
||||
_md5 = this._md5,
|
||||
_ripemd160 = this._ripemd160,
|
||||
@@ -220,7 +222,9 @@ namespace SabreTools.Library.DatItems
|
||||
|
||||
CRC = null,
|
||||
MD5 = this.MD5,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = this.RIPEMD160,
|
||||
#endif
|
||||
SHA1 = this.SHA1,
|
||||
SHA256 = this.SHA256,
|
||||
SHA384 = this.SHA384,
|
||||
@@ -256,10 +260,8 @@ namespace SabreTools.Library.DatItems
|
||||
AreaName = this.AreaName,
|
||||
AreaSize = this.AreaSize,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
Remove = this.Remove,
|
||||
};
|
||||
|
||||
@@ -284,34 +286,34 @@ namespace SabreTools.Library.DatItems
|
||||
// If all hashes are empty but they're both nodump and the names match, then they're dupes
|
||||
if ((this.ItemStatus == ItemStatus.Nodump && newOther.ItemStatus == ItemStatus.Nodump)
|
||||
&& (this.Name == newOther.Name)
|
||||
&& (this._md5.IsNullOrWhiteSpace() && newOther._md5.IsNullOrWhiteSpace())
|
||||
&& (this._ripemd160.IsNullOrWhiteSpace() && newOther._ripemd160.IsNullOrWhiteSpace())
|
||||
&& (this._sha1.IsNullOrWhiteSpace() && newOther._sha1.IsNullOrWhiteSpace())
|
||||
&& (this._sha256.IsNullOrWhiteSpace() && newOther._sha256.IsNullOrWhiteSpace())
|
||||
&& (this._sha384.IsNullOrWhiteSpace() && newOther._sha384.IsNullOrWhiteSpace())
|
||||
&& (this._sha512.IsNullOrWhiteSpace() && newOther._sha512.IsNullOrWhiteSpace()))
|
||||
&& (this._md5.IsNullOrEmpty() && newOther._md5.IsNullOrEmpty())
|
||||
&& (this._ripemd160.IsNullOrEmpty() && newOther._ripemd160.IsNullOrEmpty())
|
||||
&& (this._sha1.IsNullOrEmpty() && newOther._sha1.IsNullOrEmpty())
|
||||
&& (this._sha256.IsNullOrEmpty() && newOther._sha256.IsNullOrEmpty())
|
||||
&& (this._sha384.IsNullOrEmpty() && newOther._sha384.IsNullOrEmpty())
|
||||
&& (this._sha512.IsNullOrEmpty() && newOther._sha512.IsNullOrEmpty()))
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
// If we can determine that the disks have no non-empty hashes in common, we return false
|
||||
else if ((this._md5.IsNullOrWhiteSpace() || newOther._md5.IsNullOrWhiteSpace())
|
||||
&& (this._ripemd160.IsNullOrWhiteSpace() || newOther._ripemd160.IsNullOrWhiteSpace())
|
||||
&& (this._sha1.IsNullOrWhiteSpace() || newOther._sha1.IsNullOrWhiteSpace())
|
||||
&& (this._sha256.IsNullOrWhiteSpace() || newOther._sha256.IsNullOrWhiteSpace())
|
||||
&& (this._sha384.IsNullOrWhiteSpace() || newOther._sha384.IsNullOrWhiteSpace())
|
||||
&& (this._sha512.IsNullOrWhiteSpace() || newOther._sha512.IsNullOrWhiteSpace()))
|
||||
else if ((this._md5.IsNullOrEmpty() || newOther._md5.IsNullOrEmpty())
|
||||
&& (this._ripemd160.IsNullOrEmpty() || newOther._ripemd160.IsNullOrEmpty())
|
||||
&& (this._sha1.IsNullOrEmpty() || newOther._sha1.IsNullOrEmpty())
|
||||
&& (this._sha256.IsNullOrEmpty() || newOther._sha256.IsNullOrEmpty())
|
||||
&& (this._sha384.IsNullOrEmpty() || newOther._sha384.IsNullOrEmpty())
|
||||
&& (this._sha512.IsNullOrEmpty() || newOther._sha512.IsNullOrEmpty()))
|
||||
{
|
||||
dupefound = false;
|
||||
}
|
||||
|
||||
// Otherwise if we get a partial match
|
||||
else if (((this._md5.IsNullOrWhiteSpace() || newOther._md5.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._md5, newOther._md5))
|
||||
&& ((this._ripemd160.IsNullOrWhiteSpace() || newOther._ripemd160.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._ripemd160, newOther._ripemd160))
|
||||
&& ((this._sha1.IsNullOrWhiteSpace() || newOther._sha1.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha1, newOther._sha1))
|
||||
&& ((this._sha256.IsNullOrWhiteSpace() || newOther._sha256.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha256, newOther._sha256))
|
||||
&& ((this._sha384.IsNullOrWhiteSpace() || newOther._sha384.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha384, newOther._sha384))
|
||||
&& ((this._sha512.IsNullOrWhiteSpace() || newOther._sha512.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha512, newOther._sha512)))
|
||||
else if (((this._md5.IsNullOrEmpty() || newOther._md5.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._md5, newOther._md5))
|
||||
&& ((this._ripemd160.IsNullOrEmpty() || newOther._ripemd160.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._ripemd160, newOther._ripemd160))
|
||||
&& ((this._sha1.IsNullOrEmpty() || newOther._sha1.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha1, newOther._sha1))
|
||||
&& ((this._sha256.IsNullOrEmpty() || newOther._sha256.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha256, newOther._sha256))
|
||||
&& ((this._sha384.IsNullOrEmpty() || newOther._sha384.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha384, newOther._sha384))
|
||||
&& ((this._sha512.IsNullOrEmpty() || newOther._sha512.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha512, newOther._sha512)))
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
@@ -87,10 +87,8 @@ namespace SabreTools.Library.DatItems
|
||||
Devices = this.Devices,
|
||||
MachineType = this.MachineType,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
|
||||
Region = this.Region,
|
||||
Language = this.Language,
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("crc")]
|
||||
public string CRC
|
||||
{
|
||||
get { return _crc.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_crc); }
|
||||
set { _crc = (value == "null" ? Constants.CRCZeroBytes : Utilities.StringToByteArray(value)); }
|
||||
get { return _crc.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_crc); }
|
||||
set { _crc = (value == "null" ? Constants.CRCZeroBytes : Utilities.StringToByteArray(Sanitizer.CleanCRC32(value))); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,19 +54,21 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("md5")]
|
||||
public string MD5
|
||||
{
|
||||
get { return _md5.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_md5); }
|
||||
set { _md5 = Utilities.StringToByteArray(value); }
|
||||
get { return _md5.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_md5); }
|
||||
set { _md5 = Utilities.StringToByteArray(Sanitizer.CleanMD5(value)); }
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
/// <summary>
|
||||
/// File RIPEMD160 hash
|
||||
/// </summary>
|
||||
[JsonProperty("ripemd160")]
|
||||
public string RIPEMD160
|
||||
{
|
||||
get { return _ripemd160.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_ripemd160); }
|
||||
set { _ripemd160 = Utilities.StringToByteArray(value); }
|
||||
get { return _ripemd160.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_ripemd160); }
|
||||
set { _ripemd160 = Utilities.StringToByteArray(Sanitizer.CleanRIPEMD160(value)); }
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// File SHA-1 hash
|
||||
@@ -74,8 +76,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha1")]
|
||||
public string SHA1
|
||||
{
|
||||
get { return _sha1.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha1); }
|
||||
set { _sha1 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha1.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha1); }
|
||||
set { _sha1 = Utilities.StringToByteArray(Sanitizer.CleanSHA1(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,8 +86,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha256")]
|
||||
public string SHA256
|
||||
{
|
||||
get { return _sha256.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha256); }
|
||||
set { _sha256 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha256.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha256); }
|
||||
set { _sha256 = Utilities.StringToByteArray(Sanitizer.CleanSHA256(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,8 +96,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha384")]
|
||||
public string SHA384
|
||||
{
|
||||
get { return _sha384.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha384); }
|
||||
set { _sha384 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha384.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha384); }
|
||||
set { _sha384 = Utilities.StringToByteArray(Sanitizer.CleanSHA384(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -104,8 +106,8 @@ namespace SabreTools.Library.DatItems
|
||||
[JsonProperty("sha512")]
|
||||
public string SHA512
|
||||
{
|
||||
get { return _sha512.IsNullOrWhiteSpace() ? null : Utilities.ByteArrayToString(_sha512); }
|
||||
set { _sha512 = Utilities.StringToByteArray(value); }
|
||||
get { return _sha512.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha512); }
|
||||
set { _sha512 = Utilities.StringToByteArray(Sanitizer.CleanSHA512(value)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -166,34 +168,11 @@ namespace SabreTools.Library.DatItems
|
||||
/// <param name="name"></param>
|
||||
/// <param name="machineName"></param>
|
||||
/// <param name="omitFromScan"></param>
|
||||
/// <remarks>TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually</remarks>
|
||||
public Rom(string name, string machineName, Hash omitFromScan = Hash.DeepHashes)
|
||||
public Rom(string name, string machineName)
|
||||
{
|
||||
this.Name = name;
|
||||
this.ItemType = ItemType.Rom;
|
||||
this.Size = -1;
|
||||
|
||||
if ((omitFromScan & Hash.CRC) == 0)
|
||||
_crc = null;
|
||||
|
||||
if ((omitFromScan & Hash.MD5) == 0)
|
||||
_md5 = null;
|
||||
|
||||
if ((omitFromScan & Hash.RIPEMD160) == 0)
|
||||
_ripemd160 = null;
|
||||
|
||||
if ((omitFromScan & Hash.SHA1) == 0)
|
||||
_sha1 = null;
|
||||
|
||||
if ((omitFromScan & Hash.SHA256) == 0)
|
||||
_sha256 = null;
|
||||
|
||||
if ((omitFromScan & Hash.SHA384) == 0)
|
||||
_sha384 = null;
|
||||
|
||||
if ((omitFromScan & Hash.SHA512) == 0)
|
||||
_sha512 = null;
|
||||
|
||||
this.ItemStatus = ItemStatus.None;
|
||||
|
||||
_machine = new Machine
|
||||
@@ -213,7 +192,9 @@ namespace SabreTools.Library.DatItems
|
||||
this.Size = baseFile.Size ?? -1;
|
||||
_crc = baseFile.CRC;
|
||||
_md5 = baseFile.MD5;
|
||||
#if NET_FRAMEWORK
|
||||
_ripemd160 = baseFile.RIPEMD160;
|
||||
#endif
|
||||
_sha1 = baseFile.SHA1;
|
||||
_sha256 = baseFile.SHA256;
|
||||
_sha384 = baseFile.SHA384;
|
||||
@@ -261,10 +242,8 @@ namespace SabreTools.Library.DatItems
|
||||
Devices = this.Devices,
|
||||
MachineType = this.MachineType,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
|
||||
Size = this.Size,
|
||||
_crc = this._crc,
|
||||
@@ -297,51 +276,51 @@ namespace SabreTools.Library.DatItems
|
||||
// If all hashes are empty but they're both nodump and the names match, then they're dupes
|
||||
if ((this.ItemStatus == ItemStatus.Nodump && newOther.ItemStatus == ItemStatus.Nodump)
|
||||
&& (this.Name == newOther.Name)
|
||||
&& (this._crc.IsNullOrWhiteSpace() && newOther._crc.IsNullOrWhiteSpace())
|
||||
&& (this._md5.IsNullOrWhiteSpace() && newOther._md5.IsNullOrWhiteSpace())
|
||||
&& (this._ripemd160.IsNullOrWhiteSpace() && newOther._ripemd160.IsNullOrWhiteSpace())
|
||||
&& (this._sha1.IsNullOrWhiteSpace() && newOther._sha1.IsNullOrWhiteSpace())
|
||||
&& (this._sha256.IsNullOrWhiteSpace() && newOther._sha256.IsNullOrWhiteSpace())
|
||||
&& (this._sha384.IsNullOrWhiteSpace() && newOther._sha384.IsNullOrWhiteSpace())
|
||||
&& (this._sha512.IsNullOrWhiteSpace() && newOther._sha512.IsNullOrWhiteSpace()))
|
||||
&& (this._crc.IsNullOrEmpty() && newOther._crc.IsNullOrEmpty())
|
||||
&& (this._md5.IsNullOrEmpty() && newOther._md5.IsNullOrEmpty())
|
||||
&& (this._ripemd160.IsNullOrEmpty() && newOther._ripemd160.IsNullOrEmpty())
|
||||
&& (this._sha1.IsNullOrEmpty() && newOther._sha1.IsNullOrEmpty())
|
||||
&& (this._sha256.IsNullOrEmpty() && newOther._sha256.IsNullOrEmpty())
|
||||
&& (this._sha384.IsNullOrEmpty() && newOther._sha384.IsNullOrEmpty())
|
||||
&& (this._sha512.IsNullOrEmpty() && newOther._sha512.IsNullOrEmpty()))
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
// If we can determine that the roms have no non-empty hashes in common, we return false
|
||||
else if ((this._crc.IsNullOrWhiteSpace() || newOther._crc.IsNullOrWhiteSpace())
|
||||
&& (this._md5.IsNullOrWhiteSpace() || newOther._md5.IsNullOrWhiteSpace())
|
||||
&& (this._ripemd160.IsNullOrWhiteSpace() || newOther._ripemd160.IsNullOrWhiteSpace())
|
||||
&& (this._sha1.IsNullOrWhiteSpace() || newOther._sha1.IsNullOrWhiteSpace())
|
||||
&& (this._sha256.IsNullOrWhiteSpace() || newOther._sha256.IsNullOrWhiteSpace())
|
||||
&& (this._sha384.IsNullOrWhiteSpace() || newOther._sha384.IsNullOrWhiteSpace())
|
||||
&& (this._sha512.IsNullOrWhiteSpace() || newOther._sha512.IsNullOrWhiteSpace()))
|
||||
else if ((this._crc.IsNullOrEmpty() || newOther._crc.IsNullOrEmpty())
|
||||
&& (this._md5.IsNullOrEmpty() || newOther._md5.IsNullOrEmpty())
|
||||
&& (this._ripemd160.IsNullOrEmpty() || newOther._ripemd160.IsNullOrEmpty())
|
||||
&& (this._sha1.IsNullOrEmpty() || newOther._sha1.IsNullOrEmpty())
|
||||
&& (this._sha256.IsNullOrEmpty() || newOther._sha256.IsNullOrEmpty())
|
||||
&& (this._sha384.IsNullOrEmpty() || newOther._sha384.IsNullOrEmpty())
|
||||
&& (this._sha512.IsNullOrEmpty() || newOther._sha512.IsNullOrEmpty()))
|
||||
{
|
||||
dupefound = false;
|
||||
}
|
||||
|
||||
// If we have a file that has no known size, rely on the hashes only
|
||||
else if ((this.Size == -1)
|
||||
&& ((this._crc.IsNullOrWhiteSpace() || newOther._crc.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._crc, newOther._crc))
|
||||
&& ((this._md5.IsNullOrWhiteSpace() || newOther._md5.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._md5, newOther._md5))
|
||||
&& ((this._ripemd160.IsNullOrWhiteSpace() || newOther._ripemd160.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._ripemd160, newOther._ripemd160))
|
||||
&& ((this._sha1.IsNullOrWhiteSpace() || newOther._sha1.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha1, newOther._sha1))
|
||||
&& ((this._sha256.IsNullOrWhiteSpace() || newOther._sha256.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha256, newOther._sha256))
|
||||
&& ((this._sha384.IsNullOrWhiteSpace() || newOther._sha384.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha384, newOther._sha384))
|
||||
&& ((this._sha512.IsNullOrWhiteSpace() || newOther._sha512.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha512, newOther._sha512)))
|
||||
&& ((this._crc.IsNullOrEmpty() || newOther._crc.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._crc, newOther._crc))
|
||||
&& ((this._md5.IsNullOrEmpty() || newOther._md5.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._md5, newOther._md5))
|
||||
&& ((this._ripemd160.IsNullOrEmpty() || newOther._ripemd160.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._ripemd160, newOther._ripemd160))
|
||||
&& ((this._sha1.IsNullOrEmpty() || newOther._sha1.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha1, newOther._sha1))
|
||||
&& ((this._sha256.IsNullOrEmpty() || newOther._sha256.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha256, newOther._sha256))
|
||||
&& ((this._sha384.IsNullOrEmpty() || newOther._sha384.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha384, newOther._sha384))
|
||||
&& ((this._sha512.IsNullOrEmpty() || newOther._sha512.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha512, newOther._sha512)))
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
// Otherwise if we get a partial match
|
||||
else if ((this.Size == newOther.Size)
|
||||
&& ((this._crc.IsNullOrWhiteSpace() || newOther._crc.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._crc, newOther._crc))
|
||||
&& ((this._md5.IsNullOrWhiteSpace() || newOther._md5.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._md5, newOther._md5))
|
||||
&& ((this._ripemd160.IsNullOrWhiteSpace() || newOther._ripemd160.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._ripemd160, newOther._ripemd160))
|
||||
&& ((this._sha1.IsNullOrWhiteSpace() || newOther._sha1.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha1, newOther._sha1))
|
||||
&& ((this._sha256.IsNullOrWhiteSpace() || newOther._sha256.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha256, newOther._sha256))
|
||||
&& ((this._sha384.IsNullOrWhiteSpace() || newOther._sha384.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha384, newOther._sha384))
|
||||
&& ((this._sha512.IsNullOrWhiteSpace() || newOther._sha512.IsNullOrWhiteSpace()) || Enumerable.SequenceEqual(this._sha512, newOther._sha512)))
|
||||
&& ((this._crc.IsNullOrEmpty() || newOther._crc.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._crc, newOther._crc))
|
||||
&& ((this._md5.IsNullOrEmpty() || newOther._md5.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._md5, newOther._md5))
|
||||
&& ((this._ripemd160.IsNullOrEmpty() || newOther._ripemd160.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._ripemd160, newOther._ripemd160))
|
||||
&& ((this._sha1.IsNullOrEmpty() || newOther._sha1.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha1, newOther._sha1))
|
||||
&& ((this._sha256.IsNullOrEmpty() || newOther._sha256.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha256, newOther._sha256))
|
||||
&& ((this._sha384.IsNullOrEmpty() || newOther._sha384.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha384, newOther._sha384))
|
||||
&& ((this._sha512.IsNullOrEmpty() || newOther._sha512.IsNullOrEmpty()) || Enumerable.SequenceEqual(this._sha512, newOther._sha512)))
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
@@ -54,10 +54,8 @@ namespace SabreTools.Library.DatItems
|
||||
Devices = this.Devices,
|
||||
MachineType = this.MachineType,
|
||||
|
||||
SystemID = this.SystemID,
|
||||
System = this.System,
|
||||
SourceID = this.SourceID,
|
||||
Source = this.Source,
|
||||
IndexId = this.IndexId,
|
||||
IndexSource = this.IndexSource,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,27 +12,29 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// The current toolset version to be used by all child applications
|
||||
/// </summary>
|
||||
public readonly static string Version = $"v1.0.0-{File.GetCreationTime(Assembly.GetExecutingAssembly().Location).ToString("yyyy-MM-dd HH:mm:ss")}";
|
||||
public readonly static string Version = $"v1.0.0-{File.GetCreationTime(Assembly.GetExecutingAssembly().Location):yyyy-MM-dd HH:mm:ss}";
|
||||
public const int HeaderHeight = 3;
|
||||
|
||||
#region 0-byte file constants
|
||||
|
||||
public const long SizeZero = 0;
|
||||
public const string CRCZero = "00000000";
|
||||
public static readonly byte[] CRCZeroBytes = { 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] CRCZeroBytes = { 0x00, 0x00, 0x00, 0x00 };
|
||||
public const string MD5Zero = "d41d8cd98f00b204e9800998ecf8427e";
|
||||
public static readonly byte[] MD5ZeroBytes = { 0xd4, 0x1d, 0x8c, 0xd9,
|
||||
public static readonly byte[] MD5ZeroBytes = { 0xd4, 0x1d, 0x8c, 0xd9,
|
||||
0x8f, 0x00, 0xb2, 0x04,
|
||||
0xe9, 0x80, 0x09, 0x98,
|
||||
0xec, 0xf8, 0x42, 0x7e };
|
||||
#if NET_FRAMEWORK
|
||||
public const string RIPEMD160Zero = "9c1185a5c5e9fc54612808977ee8f548b2258d31";
|
||||
public static readonly byte[] RIPEMD160ZeroBytes = { 0x9c, 0x11, 0x85, 0xa5,
|
||||
0xc5, 0xe9, 0xfc, 0x54,
|
||||
0x61, 0x28, 0x08, 0x97,
|
||||
0x7e, 0xe8, 0xf5, 0x48,
|
||||
0xb2, 0x25, 0x8d, 0x31 };
|
||||
#endif
|
||||
public const string SHA1Zero = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
|
||||
public static readonly byte[] SHA1ZeroBytes = { 0xda, 0x39, 0xa3, 0xee,
|
||||
public static readonly byte[] SHA1ZeroBytes = { 0xda, 0x39, 0xa3, 0xee,
|
||||
0x5e, 0x6b, 0x4b, 0x0d,
|
||||
0x32, 0x55, 0xbf, 0xef,
|
||||
0x95, 0x60, 0x18, 0x90,
|
||||
@@ -105,54 +107,6 @@ namespace SabreTools.Library.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region CHD header values
|
||||
|
||||
// Header versions and sizes
|
||||
public const int CHD_HEADER_VERSION = 5;
|
||||
public const int CHD_V1_HEADER_SIZE = 76;
|
||||
public const int CHD_V2_HEADER_SIZE = 80;
|
||||
public const int CHD_V3_HEADER_SIZE = 120;
|
||||
public const int CHD_V4_HEADER_SIZE = 108;
|
||||
public const int CHD_V5_HEADER_SIZE = 124;
|
||||
public const int CHD_MAX_HEADER_SIZE = CHD_V5_HEADER_SIZE;
|
||||
|
||||
// Key offsets within the header (V1)
|
||||
public const long CHDv1MapOffsetOffset = 0;
|
||||
public const long CHDv1MetaOffsetOffset = 0;
|
||||
public const long CHDv1MD5Offset = 44;
|
||||
public const long CHDv1RawMD5Offset = 0;
|
||||
public const long CHDv1ParentMD5Offset = 60;
|
||||
|
||||
// Key offsets within the header (V2)
|
||||
public const long CHDv2MapOffsetOffset = 0;
|
||||
public const long CHDv2MetaOffsetOffset = 0;
|
||||
public const long CHDv2MD5Offset = 44;
|
||||
public const long CHDv2RawMD5Offset = 0;
|
||||
public const long CHDv2ParentMD5Offset = 60;
|
||||
|
||||
// Key offsets within the header (V3)
|
||||
public const long CHDv3MapOffsetOffset = 0; // offset of map offset field
|
||||
public const long CHDv3MetaOffsetOffset = 36; // offset of metaoffset field
|
||||
public const long CHDv3SHA1Offset = 80; // offset of SHA1 field
|
||||
public const long CHDv3RawSHA1Offset = 0; // offset of raw SHA1 field
|
||||
public const long CHDv3ParentSHA1Offset = 100; // offset of parent SHA1 field
|
||||
|
||||
// Key offsets within the header (V4)
|
||||
public const long CHDv4MapOffsetOffset = 0; // offset of map offset field
|
||||
public const long CHDv4MetaOffsetOffset = 36; // offset of metaoffset field
|
||||
public const long CHDv4SHA1Offset = 48; // offset of SHA1 field
|
||||
public const long CHDv4RawSHA1Offset = 88; // offset of raw SHA1 field
|
||||
public const long CHDv4ParentSHA1Offset = 68; // offset of parent SHA1 field
|
||||
|
||||
// Key offsets within the header (V5)
|
||||
public const long CHDv5MapOffsetOffset = 40; // offset of map offset field
|
||||
public const long CHDv5MetaOffsetOffset = 48; // offset of metaoffset field
|
||||
public const long CHDv5SHA1Offset = 84; // offset of SHA1 field
|
||||
public const long CHDv5RawSHA1Offset = 64; // offset of raw SHA1 field
|
||||
public const long CHDv5ParentSHA1Offset = 104; // offset of parent SHA1 field
|
||||
|
||||
#endregion
|
||||
|
||||
#region Database schema
|
||||
|
||||
public const string HeadererDbSchema = "Headerer";
|
||||
@@ -507,7 +461,9 @@ namespace SabreTools.Library.Data
|
||||
|
||||
public const int CRCLength = 8;
|
||||
public const int MD5Length = 32;
|
||||
#if NET_FRAMEWORK
|
||||
public const int RIPEMD160Length = 40;
|
||||
#endif
|
||||
public const int SHA1Length = 40;
|
||||
public const int SHA256Length = 64;
|
||||
public const int SHA384Length = 96;
|
||||
@@ -517,48 +473,48 @@ namespace SabreTools.Library.Data
|
||||
|
||||
#region Magic numbers
|
||||
|
||||
public static readonly byte[] SevenZipSignature = { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c };
|
||||
public static readonly byte[] A7800SignatureV1 = { 0x41, 0x54, 0x41, 0x52, 0x49, 0x37, 0x38, 0x30, 0x30 }; // Offset 0x01
|
||||
public static readonly byte[] A7800SignatureV2 = { 0x41, 0x43, 0x54, 0x55, 0x41, 0x4c, 0x20, 0x43, 0x41, 0x52, 0x54, 0x20, 0x44, 0x41,
|
||||
public static readonly byte[] SevenZipSignature = { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c };
|
||||
public static readonly byte[] A7800SignatureV1 = { 0x41, 0x54, 0x41, 0x52, 0x49, 0x37, 0x38, 0x30, 0x30 }; // Offset 0x01
|
||||
public static readonly byte[] A7800SignatureV2 = { 0x41, 0x43, 0x54, 0x55, 0x41, 0x4c, 0x20, 0x43, 0x41, 0x52, 0x54, 0x20, 0x44, 0x41,
|
||||
0x54, 0x41, 0x20, 0x53, 0x54, 0x41, 0x52, 0x54, 0x53, 0x20, 0x48, 0x45, 0x52, 0x45 }; // Offset 0x64
|
||||
public static readonly byte[] BZ2Signature = { 0x42, 0x5a, 0x68 };
|
||||
public static readonly byte[] CabinetSignature = { 0x4d, 0x53, 0x43, 0x46 };
|
||||
public static readonly byte[] CHDSignature = { 0x4d, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x48, 0x44 };
|
||||
public static readonly byte[] ELFSignature = { 0x7f, 0x45, 0x4c, 0x46 };
|
||||
public static readonly byte[] FDSSignatureV1 = { 0x46, 0x44, 0x53, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FDSSignatureV2 = { 0x46, 0x44, 0x53, 0x1a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FDSSignatureV3 = { 0x46, 0x44, 0x53, 0x1a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FDSSignatureV4 = { 0x46, 0x44, 0x53, 0x1a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FreeArcSignature = { 0x41, 0x72, 0x43, 0x01 };
|
||||
public static readonly byte[] GzSignature = { 0x1f, 0x8b, 0x08 };
|
||||
public static readonly byte[] LRZipSignature = { 0x4c, 0x52, 0x5a, 0x49 };
|
||||
public static readonly byte[] LynxSignatureV1 = { 0x4c, 0x59, 0x4f, 0x58 };
|
||||
public static readonly byte[] LynxSignatureV2 = { 0x42, 0x53, 0x39 }; // Offset 0x06
|
||||
public static readonly byte[] LZ4Signature = { 0x18, 0x4d, 0x22, 0x04 };
|
||||
public static readonly byte[] LZ4SkippableMinSignature = { 0x18, 0x4d, 0x22, 0x04 };
|
||||
public static readonly byte[] LZ4SkippableMaxSignature = { 0x18, 0x4d, 0x2a, 0x5f };
|
||||
public static readonly byte[] N64Signature = { 0x40, 0x12, 0x37, 0x80 };
|
||||
public static readonly byte[] NESSignature = { 0x4e, 0x45, 0x53, 0x1a };
|
||||
public static readonly byte[] PCESignature = { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0x02 };
|
||||
public static readonly byte[] PESignature = { 0x4d, 0x5a };
|
||||
public static readonly byte[] PSIDSignatureV1 = { 0x50, 0x53, 0x49, 0x44, 0x00, 0x01, 0x00, 0x76 };
|
||||
public static readonly byte[] PSIDSignatureV2 = { 0x50, 0x53, 0x49, 0x44, 0x00, 0x02, 0x00, 0x7c };
|
||||
public static readonly byte[] PSIDSignatureV3 = { 0x50, 0x53, 0x49, 0x44, 0x00, 0x03, 0x00, 0x7c };
|
||||
public static readonly byte[] RarSignature = { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
|
||||
public static readonly byte[] RarFiveSignature = { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 };
|
||||
public static readonly byte[] SMCSignature = { 0xaa, 0xbb, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Offset 0x16
|
||||
public static readonly byte[] SPCSignature = { 0x53, 0x4e, 0x45, 0x53, 0x2d, 0x53, 0x50, 0x43 };
|
||||
public static readonly byte[] TarSignature = { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 };
|
||||
public static readonly byte[] TarZeroSignature = { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 };
|
||||
public static readonly byte[] UFOSignature = { 0x53, 0x55, 0x50, 0x45, 0x52, 0x55, 0x46, 0x4f }; // Offset 0x16
|
||||
public static readonly byte[] V64Signature = { 0x80, 0x37, 0x12, 0x40 };
|
||||
public static readonly byte[] XZSignature = { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00 };
|
||||
public static readonly byte[] Z64Signature = { 0x37, 0x80, 0x40, 0x12 };
|
||||
public static readonly byte[] ZipSignature = { 0x50, 0x4b, 0x03, 0x04 };
|
||||
public static readonly byte[] ZipSignatureEmpty = { 0x50, 0x4b, 0x05, 0x06 };
|
||||
public static readonly byte[] ZipSignatureSpanned = { 0x50, 0x4b, 0x07, 0x08 };
|
||||
public static readonly byte[] ZPAQSignature = { 0x7a, 0x50, 0x51 };
|
||||
public static readonly byte[] ZstdSignature = { 0xfd, 0x2f, 0xb5 };
|
||||
public static readonly byte[] BZ2Signature = { 0x42, 0x5a, 0x68 };
|
||||
public static readonly byte[] CabinetSignature = { 0x4d, 0x53, 0x43, 0x46 };
|
||||
public static readonly byte[] CHDSignature = { 0x4d, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x48, 0x44 };
|
||||
public static readonly byte[] ELFSignature = { 0x7f, 0x45, 0x4c, 0x46 };
|
||||
public static readonly byte[] FDSSignatureV1 = { 0x46, 0x44, 0x53, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FDSSignatureV2 = { 0x46, 0x44, 0x53, 0x1a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FDSSignatureV3 = { 0x46, 0x44, 0x53, 0x1a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FDSSignatureV4 = { 0x46, 0x44, 0x53, 0x1a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static readonly byte[] FreeArcSignature = { 0x41, 0x72, 0x43, 0x01 };
|
||||
public static readonly byte[] GzSignature = { 0x1f, 0x8b, 0x08 };
|
||||
public static readonly byte[] LRZipSignature = { 0x4c, 0x52, 0x5a, 0x49 };
|
||||
public static readonly byte[] LynxSignatureV1 = { 0x4c, 0x59, 0x4f, 0x58 };
|
||||
public static readonly byte[] LynxSignatureV2 = { 0x42, 0x53, 0x39 }; // Offset 0x06
|
||||
public static readonly byte[] LZ4Signature = { 0x18, 0x4d, 0x22, 0x04 };
|
||||
public static readonly byte[] LZ4SkippableMinSignature = { 0x18, 0x4d, 0x22, 0x04 };
|
||||
public static readonly byte[] LZ4SkippableMaxSignature = { 0x18, 0x4d, 0x2a, 0x5f };
|
||||
public static readonly byte[] N64Signature = { 0x40, 0x12, 0x37, 0x80 };
|
||||
public static readonly byte[] NESSignature = { 0x4e, 0x45, 0x53, 0x1a };
|
||||
public static readonly byte[] PCESignature = { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0x02 };
|
||||
public static readonly byte[] PESignature = { 0x4d, 0x5a };
|
||||
public static readonly byte[] PSIDSignatureV1 = { 0x50, 0x53, 0x49, 0x44, 0x00, 0x01, 0x00, 0x76 };
|
||||
public static readonly byte[] PSIDSignatureV2 = { 0x50, 0x53, 0x49, 0x44, 0x00, 0x02, 0x00, 0x7c };
|
||||
public static readonly byte[] PSIDSignatureV3 = { 0x50, 0x53, 0x49, 0x44, 0x00, 0x03, 0x00, 0x7c };
|
||||
public static readonly byte[] RarSignature = { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
|
||||
public static readonly byte[] RarFiveSignature = { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 };
|
||||
public static readonly byte[] SMCSignature = { 0xaa, 0xbb, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Offset 0x16
|
||||
public static readonly byte[] SPCSignature = { 0x53, 0x4e, 0x45, 0x53, 0x2d, 0x53, 0x50, 0x43 };
|
||||
public static readonly byte[] TarSignature = { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 };
|
||||
public static readonly byte[] TarZeroSignature = { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 };
|
||||
public static readonly byte[] UFOSignature = { 0x53, 0x55, 0x50, 0x45, 0x52, 0x55, 0x46, 0x4f }; // Offset 0x16
|
||||
public static readonly byte[] V64Signature = { 0x80, 0x37, 0x12, 0x40 };
|
||||
public static readonly byte[] XZSignature = { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00 };
|
||||
public static readonly byte[] Z64Signature = { 0x37, 0x80, 0x40, 0x12 };
|
||||
public static readonly byte[] ZipSignature = { 0x50, 0x4b, 0x03, 0x04 };
|
||||
public static readonly byte[] ZipSignatureEmpty = { 0x50, 0x4b, 0x05, 0x06 };
|
||||
public static readonly byte[] ZipSignatureSpanned = { 0x50, 0x4b, 0x07, 0x08 };
|
||||
public static readonly byte[] ZPAQSignature = { 0x7a, 0x50, 0x51 };
|
||||
public static readonly byte[] ZstdSignature = { 0xfd, 0x2f, 0xb5 };
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -617,6 +573,15 @@ namespace SabreTools.Library.Data
|
||||
*/
|
||||
public readonly static byte[] TorrentGZHeader = new byte[] { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00 };
|
||||
|
||||
/* (Torrent)XZ Header Format
|
||||
https://tukaani.org/xz/xz-file-format.txt
|
||||
|
||||
00-05 Identification (0xFD, '7', 'z', 'X', 'Z', 0x00) XzSignature
|
||||
06 Flags (0x01 - CRC32, 0x04 - CRC64, 0x0A - SHA-256)
|
||||
07-0A Flags CRC32 (uint, little-endian)
|
||||
*/
|
||||
public readonly static byte[] TorrentXZHeader = new byte[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x01, 0x69, 0x22, 0xde, 0x36 };
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZIP internal signatures
|
||||
|
||||
@@ -7,28 +7,39 @@
|
||||
/// </summary>
|
||||
public enum ArchiveVersion : ushort
|
||||
{
|
||||
MSDOSandOS2 = 0,
|
||||
Amiga = 1,
|
||||
OpenVMS = 2,
|
||||
UNIX = 3,
|
||||
VMCMS = 4,
|
||||
AtariST = 5,
|
||||
OS2HPFS = 6,
|
||||
Macintosh = 7,
|
||||
ZSystem = 8,
|
||||
CPM = 9,
|
||||
WindowsNTFS = 10,
|
||||
MVS = 11,
|
||||
VSE = 12,
|
||||
AcornRisc = 13,
|
||||
VFAT = 14,
|
||||
AlternateMVS = 15,
|
||||
BeOS = 16,
|
||||
Tandem = 17,
|
||||
OS400 = 18,
|
||||
OSXDarwin = 19,
|
||||
TorrentZip = 20,
|
||||
TorrentZip64 = 45,
|
||||
MSDOSandOS2 = 0,
|
||||
Amiga = 1,
|
||||
OpenVMS = 2,
|
||||
UNIX = 3,
|
||||
VMCMS = 4,
|
||||
AtariST = 5,
|
||||
OS2HPFS = 6,
|
||||
Macintosh = 7,
|
||||
ZSystem = 8,
|
||||
CPM = 9,
|
||||
WindowsNTFS = 10,
|
||||
MVS = 11,
|
||||
VSE = 12,
|
||||
AcornRisc = 13,
|
||||
VFAT = 14,
|
||||
AlternateMVS = 15,
|
||||
BeOS = 16,
|
||||
Tandem = 17,
|
||||
OS400 = 18,
|
||||
OSXDarwin = 19,
|
||||
TorrentZip = 20,
|
||||
TorrentZip64 = 45,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compression being used in CHD
|
||||
/// </summary>
|
||||
public enum CHDCompression : uint
|
||||
{
|
||||
CHDCOMPRESSION_NONE = 0,
|
||||
CHDCOMPRESSION_ZLIB = 1,
|
||||
CHDCOMPRESSION_ZLIB_PLUS = 2,
|
||||
CHDCOMPRESSION_AV = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,36 +47,36 @@
|
||||
/// </summary>
|
||||
public enum CHD_CODEC : uint
|
||||
{
|
||||
NONE = 0,
|
||||
NONE = 0,
|
||||
|
||||
#region General Codecs
|
||||
|
||||
ZLIB = 0x7a6c6962, // zlib
|
||||
LZMA = 0x6c7a6d61, // lzma
|
||||
HUFFMAN = 0x68756666, // huff
|
||||
FLAC = 0x666c6163, // flac
|
||||
ZLIB = 0x7a6c6962, // zlib
|
||||
LZMA = 0x6c7a6d61, // lzma
|
||||
HUFFMAN = 0x68756666, // huff
|
||||
FLAC = 0x666c6163, // flac
|
||||
|
||||
#endregion
|
||||
|
||||
#region General Codecs with CD Frontend
|
||||
|
||||
CD_ZLIB = 0x63647a6c, // cdzl
|
||||
CD_LZMA = 0x63646c7a, // cdlz
|
||||
CD_FLAC = 0x6364666c, // cdfl
|
||||
CD_ZLIB = 0x63647a6c, // cdzl
|
||||
CD_LZMA = 0x63646c7a, // cdlz
|
||||
CD_FLAC = 0x6364666c, // cdfl
|
||||
|
||||
#endregion
|
||||
|
||||
#region A/V Codecs
|
||||
|
||||
AVHUFF = 0x61766875, // avhu
|
||||
AVHUFF = 0x61766875, // avhu
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pseudo-Codecs Returned by hunk_info
|
||||
|
||||
SELF = 1, // copy of another hunk
|
||||
PARENT = 2, // copy of a parent's hunk
|
||||
MINI = 3, // legacy "mini" 8-byte repeat
|
||||
SELF = 1, // copy of another hunk
|
||||
PARENT = 2, // copy of a parent's hunk
|
||||
MINI = 3, // legacy "mini" 8-byte repeat
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -75,28 +86,28 @@
|
||||
/// </summary>
|
||||
public enum CompressionMethod : ushort
|
||||
{
|
||||
Stored = 0,
|
||||
Shrunk = 1,
|
||||
ReducedCompressionFactor1 = 2,
|
||||
ReducedCompressionFactor2 = 3,
|
||||
ReducedCompressionFactor3 = 4,
|
||||
ReducedCompressionFactor4 = 5,
|
||||
Imploded = 6,
|
||||
Tokenizing = 7,
|
||||
Deflated = 8,
|
||||
Delfate64 = 9,
|
||||
PKWAREDataCompressionLibrary = 10,
|
||||
Type11 = 11, // Reserved and unused (SHOULD NOT BE USED)
|
||||
BZIP2 = 12,
|
||||
Type13 = 13, // Reserved and unused (SHOULD NOT BE USED)
|
||||
LZMA = 14,
|
||||
Type15 = 15, // Reserved and unused (SHOULD NOT BE USED)
|
||||
Type16 = 16, // Reserved and unused (SHOULD NOT BE USED)
|
||||
Type17 = 17, // Reserved and unused (SHOULD NOT BE USED)
|
||||
IBMTERSE = 18,
|
||||
IBMLZ77 = 19,
|
||||
WavPak = 97,
|
||||
PPMdVersionIRev1 = 98,
|
||||
Stored = 0,
|
||||
Shrunk = 1,
|
||||
ReducedCompressionFactor1 = 2,
|
||||
ReducedCompressionFactor2 = 3,
|
||||
ReducedCompressionFactor3 = 4,
|
||||
ReducedCompressionFactor4 = 5,
|
||||
Imploded = 6,
|
||||
Tokenizing = 7,
|
||||
Deflated = 8,
|
||||
Delfate64 = 9,
|
||||
PKWAREDataCompressionLibrary = 10,
|
||||
Type11 = 11, // Reserved and unused (SHOULD NOT BE USED)
|
||||
BZIP2 = 12,
|
||||
Type13 = 13, // Reserved and unused (SHOULD NOT BE USED)
|
||||
LZMA = 14,
|
||||
Type15 = 15, // Reserved and unused (SHOULD NOT BE USED)
|
||||
Type16 = 16, // Reserved and unused (SHOULD NOT BE USED)
|
||||
Type17 = 17, // Reserved and unused (SHOULD NOT BE USED)
|
||||
IBMTERSE = 18,
|
||||
IBMLZ77 = 19,
|
||||
WavPak = 97,
|
||||
PPMdVersionIRev1 = 98,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -132,12 +143,13 @@
|
||||
TorrentZip,
|
||||
TorrentGzip,
|
||||
TorrentGzipRomba,
|
||||
TorrentXZ,
|
||||
TorrentXZRomba,
|
||||
TapeArchive,
|
||||
|
||||
// Currently unimplemented fully
|
||||
Torrent7Zip,
|
||||
TorrentRar,
|
||||
TorrentXZ,
|
||||
TorrentLRZip,
|
||||
TorrentLZ4,
|
||||
TorrentZstd,
|
||||
@@ -149,13 +161,13 @@
|
||||
/// </summary>
|
||||
public enum RarExtraAreaFlag : uint
|
||||
{
|
||||
FileEncryption = 0x01,
|
||||
FileHash = 0x02,
|
||||
FileTime = 0x03,
|
||||
FileVersion = 0x04,
|
||||
Redirection = 0x05,
|
||||
UnixOwner = 0x06,
|
||||
ServiceData = 0x07,
|
||||
FileEncryption = 0x01,
|
||||
FileHash = 0x02,
|
||||
FileTime = 0x03,
|
||||
FileVersion = 0x04,
|
||||
Redirection = 0x05,
|
||||
UnixOwner = 0x06,
|
||||
ServiceData = 0x07,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -163,11 +175,11 @@
|
||||
/// </summary>
|
||||
public enum RarHeaderType : uint
|
||||
{
|
||||
MainArchiveHeader = 1,
|
||||
File = 2,
|
||||
Service = 3,
|
||||
ArchiveEncryption = 4,
|
||||
EndOfArchive = 5,
|
||||
MainArchiveHeader = 1,
|
||||
File = 2,
|
||||
Service = 3,
|
||||
ArchiveEncryption = 4,
|
||||
EndOfArchive = 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -175,11 +187,11 @@
|
||||
/// </summary>
|
||||
public enum RarRedirectionType : uint
|
||||
{
|
||||
UnixSymlink = 0x0001,
|
||||
WindowsSymlink = 0x0002,
|
||||
WindowsJunction = 0x0003,
|
||||
HardLink = 0x0004,
|
||||
FileCopy = 0x0005,
|
||||
UnixSymlink = 0x0001,
|
||||
WindowsSymlink = 0x0002,
|
||||
WindowsJunction = 0x0003,
|
||||
HardLink = 0x0004,
|
||||
FileCopy = 0x0005,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -187,49 +199,69 @@
|
||||
/// </summary>
|
||||
public enum SevenZipProperties : uint
|
||||
{
|
||||
kEnd = 0x00,
|
||||
kEnd = 0x00,
|
||||
|
||||
kHeader = 0x01,
|
||||
kHeader = 0x01,
|
||||
|
||||
kArchiveProperties = 0x02,
|
||||
kArchiveProperties = 0x02,
|
||||
|
||||
kAdditionalStreamsInfo = 0x03,
|
||||
kMainStreamsInfo = 0x04,
|
||||
kFilesInfo = 0x05,
|
||||
kAdditionalStreamsInfo = 0x03,
|
||||
kMainStreamsInfo = 0x04,
|
||||
kFilesInfo = 0x05,
|
||||
|
||||
kPackInfo = 0x06,
|
||||
kUnPackInfo = 0x07,
|
||||
kSubStreamsInfo = 0x08,
|
||||
kPackInfo = 0x06,
|
||||
kUnPackInfo = 0x07,
|
||||
kSubStreamsInfo = 0x08,
|
||||
|
||||
kSize = 0x09,
|
||||
kCRC = 0x0A,
|
||||
kSize = 0x09,
|
||||
kCRC = 0x0A,
|
||||
|
||||
kFolder = 0x0B,
|
||||
kFolder = 0x0B,
|
||||
|
||||
kCodersUnPackSize = 0x0C,
|
||||
kNumUnPackStream = 0x0D,
|
||||
kCodersUnPackSize = 0x0C,
|
||||
kNumUnPackStream = 0x0D,
|
||||
|
||||
kEmptyStream = 0x0E,
|
||||
kEmptyFile = 0x0F,
|
||||
kAnti = 0x10,
|
||||
kEmptyStream = 0x0E,
|
||||
kEmptyFile = 0x0F,
|
||||
kAnti = 0x10,
|
||||
|
||||
kName = 0x11,
|
||||
kCTime = 0x12,
|
||||
kATime = 0x13,
|
||||
kMTime = 0x14,
|
||||
kWinAttributes = 0x15,
|
||||
kComment = 0x16,
|
||||
kName = 0x11,
|
||||
kCTime = 0x12,
|
||||
kATime = 0x13,
|
||||
kMTime = 0x14,
|
||||
kWinAttributes = 0x15,
|
||||
kComment = 0x16,
|
||||
|
||||
kEncodedHeader = 0x17,
|
||||
kEncodedHeader = 0x17,
|
||||
|
||||
kStartPos = 0x18,
|
||||
kDummy = 0x19,
|
||||
kStartPos = 0x18,
|
||||
kDummy = 0x19,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DatFile related
|
||||
|
||||
/// <summary>
|
||||
/// Determines how the current dictionary is bucketed by
|
||||
/// </summary>
|
||||
public enum BucketedBy
|
||||
{
|
||||
Default = 0,
|
||||
Size,
|
||||
CRC,
|
||||
MD5,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160,
|
||||
#endif
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512,
|
||||
Game,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines the DAT deduplication type
|
||||
/// </summary>
|
||||
@@ -242,7 +274,9 @@
|
||||
Game,
|
||||
CRC,
|
||||
MD5,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160,
|
||||
#endif
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
@@ -292,23 +326,6 @@
|
||||
File,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines how the current dictionary is sorted by
|
||||
/// </summary>
|
||||
public enum SortedBy
|
||||
{
|
||||
Default = 0,
|
||||
Size,
|
||||
CRC,
|
||||
MD5,
|
||||
RIPEMD160,
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512,
|
||||
Game,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines how a DAT will be split internally
|
||||
/// </summary>
|
||||
@@ -367,7 +384,9 @@
|
||||
|
||||
// Disk
|
||||
MD5,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160,
|
||||
#endif
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
@@ -395,14 +414,14 @@
|
||||
/// </summary>
|
||||
public enum ItemType
|
||||
{
|
||||
Rom = 0,
|
||||
Disk = 1,
|
||||
Sample = 2,
|
||||
Release = 3,
|
||||
BiosSet = 4,
|
||||
Archive = 5,
|
||||
Rom = 0,
|
||||
Disk = 1,
|
||||
Sample = 2,
|
||||
Release = 3,
|
||||
BiosSet = 4,
|
||||
Archive = 5,
|
||||
|
||||
Blank = 99, // This is not a real type, only used internally
|
||||
Blank = 99, // This is not a real type, only used internally
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -11,29 +11,29 @@ namespace SabreTools.Library.Data
|
||||
public enum ArchiveScanLevel
|
||||
{
|
||||
// 7zip
|
||||
SevenZipExternal = 1 << 0,
|
||||
SevenZipInternal = 1 << 1,
|
||||
SevenZipBoth = SevenZipExternal | SevenZipInternal,
|
||||
SevenZipExternal = 1 << 0,
|
||||
SevenZipInternal = 1 << 1,
|
||||
SevenZipBoth = SevenZipExternal | SevenZipInternal,
|
||||
|
||||
// GZip
|
||||
GZipExternal = 1 << 2,
|
||||
GZipInternal = 1 << 3,
|
||||
GZipBoth = GZipExternal | GZipInternal,
|
||||
GZipExternal = 1 << 2,
|
||||
GZipInternal = 1 << 3,
|
||||
GZipBoth = GZipExternal | GZipInternal,
|
||||
|
||||
// RAR
|
||||
RarExternal = 1 << 4,
|
||||
RarInternal = 1 << 5,
|
||||
RarBoth = RarExternal | RarInternal,
|
||||
RarExternal = 1 << 4,
|
||||
RarInternal = 1 << 5,
|
||||
RarBoth = RarExternal | RarInternal,
|
||||
|
||||
// Zip
|
||||
ZipExternal = 1 << 6,
|
||||
ZipInternal = 1 << 7,
|
||||
ZipBoth = ZipExternal | ZipInternal,
|
||||
ZipExternal = 1 << 6,
|
||||
ZipInternal = 1 << 7,
|
||||
ZipBoth = ZipExternal | ZipInternal,
|
||||
|
||||
// Tar
|
||||
TarExternal = 1 << 8,
|
||||
TarInternal = 1 << 9,
|
||||
TarBoth = TarExternal | TarInternal,
|
||||
TarExternal = 1 << 8,
|
||||
TarInternal = 1 << 9,
|
||||
TarBoth = TarExternal | TarInternal,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,34 +42,34 @@ namespace SabreTools.Library.Data
|
||||
[Flags]
|
||||
public enum GeneralPurposeBitFlag : ushort
|
||||
{
|
||||
Encrypted = 1 << 0,
|
||||
ZeroedCRCAndSize = 1 << 3,
|
||||
CompressedPatchedData = 1 << 5,
|
||||
StrongEncryption = 1 << 6,
|
||||
LanguageEncodingFlag = 1 << 11,
|
||||
EncryptedCentralDirectory = 1 << 13,
|
||||
Encrypted = 1 << 0,
|
||||
ZeroedCRCAndSize = 1 << 3,
|
||||
CompressedPatchedData = 1 << 5,
|
||||
StrongEncryption = 1 << 6,
|
||||
LanguageEncodingFlag = 1 << 11,
|
||||
EncryptedCentralDirectory = 1 << 13,
|
||||
|
||||
// For Method 6 - Imploding
|
||||
Imploding8KSlidingDictionary = 1 << 1,
|
||||
Imploding3ShannonFanoTrees = 1 << 2,
|
||||
Imploding8KSlidingDictionary = 1 << 1,
|
||||
Imploding3ShannonFanoTrees = 1 << 2,
|
||||
|
||||
// For Methods 8 and 9 - Deflating
|
||||
DeflatingMaximumCompression = 1 << 1,
|
||||
DeflatingFastCompression = 1 << 2,
|
||||
DeflatingMaximumCompression = 1 << 1,
|
||||
DeflatingFastCompression = 1 << 2,
|
||||
DeflatingSuperFastCompression = 1 << 1 | 1 << 2,
|
||||
EnhancedDeflating = 1 << 4,
|
||||
EnhancedDeflating = 1 << 4,
|
||||
|
||||
// For Method 14 - LZMA
|
||||
LZMAEOSMarkerUsed = 1 << 1,
|
||||
LZMAEOSMarkerUsed = 1 << 1,
|
||||
|
||||
// Reserved and unused (SHOULD NOT BE USED)
|
||||
Bit7 = 1 << 7,
|
||||
Bit8 = 1 << 8,
|
||||
Bit9 = 1 << 9,
|
||||
Bit10 = 1 << 10,
|
||||
Bit12 = 1 << 12, // Reserved by PKWARE for enhanced compression
|
||||
Bit14 = 1 << 14, // Reserved by PKWARE
|
||||
Bit15 = 1 << 15, // Reserved by PKWARE
|
||||
Bit7 = 1 << 7,
|
||||
Bit8 = 1 << 8,
|
||||
Bit9 = 1 << 9,
|
||||
Bit10 = 1 << 10,
|
||||
Bit12 = 1 << 12, // Reserved by PKWARE for enhanced compression
|
||||
Bit14 = 1 << 14, // Reserved by PKWARE
|
||||
Bit15 = 1 << 15, // Reserved by PKWARE
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,12 +78,12 @@ namespace SabreTools.Library.Data
|
||||
[Flags]
|
||||
public enum InternalFileAttributes : ushort
|
||||
{
|
||||
ASCIIOrTextFile = 1 << 0,
|
||||
RecordLengthControl = 1 << 1,
|
||||
ASCIIOrTextFile = 1 << 0,
|
||||
RecordLengthControl = 1 << 1,
|
||||
|
||||
// Reserved and unused (SHOULD NOT BE USED)
|
||||
Bit1 = 1 << 1,
|
||||
Bit2 = 1 << 2,
|
||||
Bit1 = 1 << 1,
|
||||
Bit2 = 1 << 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,17 +95,17 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// Volume. Archive is a part of multivolume set.
|
||||
/// </summary>
|
||||
Volume = 1 << 0,
|
||||
Volume = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Volume number field is present. This flag is present in all volumes except first.
|
||||
/// </summary>
|
||||
VolumeNumberField = 1 << 1,
|
||||
VolumeNumberField = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Solid archive.
|
||||
/// </summary>
|
||||
Solid = 1 << 2,
|
||||
Solid = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Recovery record is present.
|
||||
@@ -115,7 +115,7 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// Locked archive.
|
||||
/// </summary>
|
||||
Locked = 1 << 4,
|
||||
Locked = 1 << 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -124,8 +124,8 @@ namespace SabreTools.Library.Data
|
||||
[Flags]
|
||||
public enum RarEncryptionFlags : uint
|
||||
{
|
||||
PasswordCheckDataPresent = 1 << 0,
|
||||
UseTweakedChecksums = 1 << 1,
|
||||
PasswordCheckDataPresent = 1 << 0,
|
||||
UseTweakedChecksums = 1 << 1,
|
||||
|
||||
/*
|
||||
If flag 0x0002 is present, RAR transforms the checksum preserving file or service data integrity, so it becomes dependent on
|
||||
@@ -143,22 +143,22 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// Directory file system object (file header only)
|
||||
/// </summary>
|
||||
Directory = 1 << 0,
|
||||
Directory = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Time field in Unix format is present
|
||||
/// </summary>
|
||||
TimeInUnix = 1 << 1,
|
||||
TimeInUnix = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// CRC32 field is present
|
||||
/// </summary>
|
||||
CRCPresent = 1 << 2,
|
||||
CRCPresent = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Unpacked size is unknown
|
||||
/// </summary>
|
||||
UnpackedSizeUnknown = 1 << 3,
|
||||
UnpackedSizeUnknown = 1 << 3,
|
||||
|
||||
/*
|
||||
If flag 0x0008 is set, unpacked size field is still present, but must be ignored and extraction
|
||||
@@ -177,46 +177,46 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// Extra area is present in the end of header
|
||||
/// </summary>
|
||||
ExtraAreaPresent = 1 << 0,
|
||||
ExtraAreaPresent = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Data area is present in the end of header
|
||||
/// </summary>
|
||||
DataAreaPresent = 1 << 1,
|
||||
DataAreaPresent = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Blocks with unknown type and this flag must be skipped when updating an archive
|
||||
/// </summary>
|
||||
BlocksWithUnknownType = 1 << 2,
|
||||
BlocksWithUnknownType = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Data area is continuing from previous volume
|
||||
/// </summary>
|
||||
DataAreaContinuingFromPrevious = 1 << 3,
|
||||
DataAreaContinuingFromPrevious = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Data area is continuing in next volume
|
||||
/// </summary>
|
||||
DataAreaContinuingToNext = 1 << 4,
|
||||
DataAreaContinuingToNext = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// Block depends on preceding file block
|
||||
/// </summary>
|
||||
BlockDependsOnPreceding = 1 << 5,
|
||||
BlockDependsOnPreceding = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// Preserve a child block if host block is modified
|
||||
/// </summary>
|
||||
PreserveChildBlock = 1 << 6,
|
||||
PreserveChildBlock = 1 << 6,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum RarUnixOwnerRecordFlags : uint
|
||||
{
|
||||
UserNameStringIsPresent = 1 << 0,
|
||||
GroupNameStringIsPresent = 1 << 1,
|
||||
NumericUserIdIsPresent = 1 << 2,
|
||||
NumericGroupIdIsPresent = 1 << 3,
|
||||
UserNameStringIsPresent = 1 << 0,
|
||||
GroupNameStringIsPresent = 1 << 1,
|
||||
NumericUserIdIsPresent = 1 << 2,
|
||||
NumericGroupIdIsPresent = 1 << 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,10 +225,10 @@ namespace SabreTools.Library.Data
|
||||
[Flags]
|
||||
public enum RarTimeFlags : uint
|
||||
{
|
||||
TimeInUnixFormat = 1 << 0,
|
||||
ModificationTimePresent = 1 << 1,
|
||||
CreationTimePresent = 1 << 2,
|
||||
LastAccessTimePresent = 1 << 3,
|
||||
TimeInUnixFormat = 1 << 0,
|
||||
ModificationTimePresent = 1 << 1,
|
||||
CreationTimePresent = 1 << 2,
|
||||
LastAccessTimePresent = 1 << 3,
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -246,37 +246,37 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// Logiqx XML (using machine)
|
||||
/// </summary>
|
||||
Logiqx = 1 << 0,
|
||||
Logiqx = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Logiqx XML (using game)
|
||||
/// </summary>
|
||||
LogiqxDeprecated = 1 << 1,
|
||||
LogiqxDeprecated = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// MAME Softare List XML
|
||||
/// </summary>
|
||||
SoftwareList = 1 << 2,
|
||||
SoftwareList = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// MAME Listxml output
|
||||
/// </summary>
|
||||
Listxml = 1 << 3,
|
||||
Listxml = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// OfflineList XML
|
||||
/// </summary>
|
||||
OfflineList = 1 << 4,
|
||||
OfflineList = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// SabreDat XML
|
||||
/// </summary>
|
||||
SabreDat = 1 << 5,
|
||||
SabreDat = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// openMSX Software List XML
|
||||
/// </summary>
|
||||
OpenMSX = 1 << 6,
|
||||
OpenMSX = 1 << 6,
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -285,22 +285,22 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// ClrMamePro custom
|
||||
/// </summary>
|
||||
ClrMamePro = 1 << 7,
|
||||
ClrMamePro = 1 << 7,
|
||||
|
||||
/// <summary>
|
||||
/// RomCetner INI-based
|
||||
/// </summary>
|
||||
RomCenter = 1 << 8,
|
||||
RomCenter = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// DOSCenter custom
|
||||
/// </summary>
|
||||
DOSCenter = 1 << 9,
|
||||
DOSCenter = 1 << 9,
|
||||
|
||||
/// <summary>
|
||||
/// AttractMode custom
|
||||
/// </summary>
|
||||
AttractMode = 1 << 10,
|
||||
AttractMode = 1 << 10,
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -309,37 +309,37 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// ClrMamePro missfile
|
||||
/// </summary>
|
||||
MissFile = 1 << 11,
|
||||
MissFile = 1 << 11,
|
||||
|
||||
/// <summary>
|
||||
/// Comma-Separated Values (standardized)
|
||||
/// </summary>
|
||||
CSV = 1 << 12,
|
||||
CSV = 1 << 12,
|
||||
|
||||
/// <summary>
|
||||
/// Semicolon-Separated Values (standardized)
|
||||
/// </summary>
|
||||
SSV = 1 << 13,
|
||||
SSV = 1 << 13,
|
||||
|
||||
/// <summary>
|
||||
/// Tab-Separated Values (standardized)
|
||||
/// </summary>
|
||||
TSV = 1 << 14,
|
||||
TSV = 1 << 14,
|
||||
|
||||
/// <summary>
|
||||
/// MAME Listrom output
|
||||
/// </summary>
|
||||
Listrom = 1 << 15,
|
||||
Listrom = 1 << 15,
|
||||
|
||||
/// <summary>
|
||||
/// Everdrive Packs SMDB
|
||||
/// </summary>
|
||||
EverdriveSMDB = 1 << 16,
|
||||
EverdriveSMDB = 1 << 16,
|
||||
|
||||
/// <summary>
|
||||
/// JSON
|
||||
/// </summary>
|
||||
Json = 1 << 17,
|
||||
Json = 1 << 17,
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -348,37 +348,39 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// CRC32 hash list
|
||||
/// </summary>
|
||||
RedumpSFV = 1 << 18,
|
||||
RedumpSFV = 1 << 18,
|
||||
|
||||
/// <summary>
|
||||
/// MD5 hash list
|
||||
/// </summary>
|
||||
RedumpMD5 = 1 << 19,
|
||||
RedumpMD5 = 1 << 19,
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
/// <summary>
|
||||
/// RIPEMD160 hash list
|
||||
/// </summary>
|
||||
RedumpRIPEMD160 = 1 << 20,
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// SHA-1 hash list
|
||||
/// </summary>
|
||||
RedumpSHA1 = 1 << 21,
|
||||
RedumpSHA1 = 1 << 21,
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 hash list
|
||||
/// </summary>
|
||||
RedumpSHA256 = 1 << 22,
|
||||
RedumpSHA256 = 1 << 22,
|
||||
|
||||
/// <summary>
|
||||
/// SHA-384 hash list
|
||||
/// </summary>
|
||||
RedumpSHA384 = 1 << 23,
|
||||
RedumpSHA384 = 1 << 23,
|
||||
|
||||
/// <summary>
|
||||
/// SHA-512 hash list
|
||||
/// </summary>
|
||||
RedumpSHA512 = 1 << 24,
|
||||
RedumpSHA512 = 1 << 24,
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -392,18 +394,25 @@ namespace SabreTools.Library.Data
|
||||
[Flags]
|
||||
public enum Hash
|
||||
{
|
||||
CRC = 1 << 0,
|
||||
MD5 = 1 << 1,
|
||||
CRC = 1 << 0,
|
||||
MD5 = 1 << 1,
|
||||
#if NET_FRAMEWORK
|
||||
RIPEMD160 = 1 << 2,
|
||||
SHA1 = 1 << 3,
|
||||
SHA256 = 1 << 4,
|
||||
SHA384 = 1 << 5,
|
||||
SHA512 = 1 << 6,
|
||||
#endif
|
||||
SHA1 = 1 << 3,
|
||||
SHA256 = 1 << 4,
|
||||
SHA384 = 1 << 5,
|
||||
SHA512 = 1 << 6,
|
||||
|
||||
// Special combinations
|
||||
Standard = CRC | MD5 | SHA1,
|
||||
DeepHashes = SHA256 | SHA384 | SHA512 | RIPEMD160,
|
||||
SecureHashes = MD5 | SHA1 | SHA256 | SHA384 | SHA512 | RIPEMD160,
|
||||
#if NET_FRAMEWORK
|
||||
DeepHashes = SHA256 | SHA384 | SHA512,
|
||||
SecureHashes = MD5 | RIPEMD160 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
#else
|
||||
DeepHashes = SHA256 | SHA384 | SHA512,
|
||||
SecureHashes = MD5 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -415,32 +424,32 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// Only output to the console
|
||||
/// </summary>
|
||||
None = 0x00,
|
||||
None = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// Console-formatted
|
||||
/// </summary>
|
||||
Textfile = 1 << 0,
|
||||
Textfile = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// ClrMamePro HTML
|
||||
/// </summary>
|
||||
HTML = 1 << 1,
|
||||
HTML = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Comma-Separated Values (Standardized)
|
||||
/// </summary>
|
||||
CSV = 1 << 2,
|
||||
CSV = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Semicolon-Separated Values (Standardized)
|
||||
/// </summary>
|
||||
SSV = 1 << 3,
|
||||
SSV = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Tab-Separated Values (Standardized)
|
||||
/// </summary>
|
||||
TSV = 1 << 4,
|
||||
TSV = 1 << 4,
|
||||
|
||||
All = Int32.MaxValue,
|
||||
}
|
||||
@@ -451,13 +460,13 @@ namespace SabreTools.Library.Data
|
||||
[Flags]
|
||||
public enum SplittingMode
|
||||
{
|
||||
None = 0x00,
|
||||
None = 0x00,
|
||||
|
||||
Extension = 1 << 0,
|
||||
Hash = 1 << 2,
|
||||
Level = 1 << 3,
|
||||
Type = 1 << 4,
|
||||
Size = 1 << 5,
|
||||
Extension = 1 << 0,
|
||||
Hash = 1 << 2,
|
||||
Level = 1 << 3,
|
||||
Type = 1 << 4,
|
||||
Size = 1 << 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -469,21 +478,21 @@ namespace SabreTools.Library.Data
|
||||
None = 0x00,
|
||||
|
||||
// Standard diffs
|
||||
DiffDupesOnly = 1 << 0,
|
||||
DiffNoDupesOnly = 1 << 1,
|
||||
DiffIndividualsOnly = 1 << 2,
|
||||
DiffDupesOnly = 1 << 0,
|
||||
DiffNoDupesOnly = 1 << 1,
|
||||
DiffIndividualsOnly = 1 << 2,
|
||||
|
||||
// Cascaded diffs
|
||||
DiffCascade = 1 << 3,
|
||||
DiffReverseCascade = 1 << 4,
|
||||
DiffCascade = 1 << 3,
|
||||
DiffReverseCascade = 1 << 4,
|
||||
|
||||
// Base diffs
|
||||
DiffAgainst = 1 << 5,
|
||||
DiffAgainst = 1 << 5,
|
||||
|
||||
// Special update modes
|
||||
Merge = 1 << 6,
|
||||
BaseReplace = 1 << 7,
|
||||
ReverseBaseReplace = 1 << 8,
|
||||
Merge = 1 << 6,
|
||||
BaseReplace = 1 << 7,
|
||||
ReverseBaseReplace = 1 << 8,
|
||||
|
||||
// Combinations
|
||||
AllDiffs = DiffDupesOnly | DiffNoDupesOnly | DiffIndividualsOnly,
|
||||
@@ -500,12 +509,12 @@ namespace SabreTools.Library.Data
|
||||
public enum DupeType
|
||||
{
|
||||
// Type of match
|
||||
Hash = 1 << 0,
|
||||
All = 1 << 1,
|
||||
Hash = 1 << 0,
|
||||
All = 1 << 1,
|
||||
|
||||
// Location of match
|
||||
Internal = 1 << 2,
|
||||
External = 1 << 3,
|
||||
Internal = 1 << 2,
|
||||
External = 1 << 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -517,13 +526,13 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0x00,
|
||||
NULL = 0x00,
|
||||
|
||||
None = 1 << 0,
|
||||
Good = 1 << 1,
|
||||
BadDump = 1 << 2,
|
||||
Nodump = 1 << 3,
|
||||
Verified = 1 << 4,
|
||||
None = 1 << 0,
|
||||
Good = 1 << 1,
|
||||
BadDump = 1 << 2,
|
||||
Nodump = 1 << 3,
|
||||
Verified = 1 << 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -535,12 +544,12 @@ namespace SabreTools.Library.Data
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0x00,
|
||||
NULL = 0x00,
|
||||
|
||||
None = 1 << 0,
|
||||
Bios = 1 << 1,
|
||||
Device = 1 << 2,
|
||||
Mechanical = 1 << 3,
|
||||
None = 1 << 0,
|
||||
Bios = 1 << 1,
|
||||
Device = 1 << 2,
|
||||
Mechanical = 1 << 3,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace NaturalSort
|
||||
{
|
||||
public class NaturalComparer : Comparer<string>, IDisposable
|
||||
@@ -77,12 +76,12 @@ namespace NaturalSort
|
||||
{
|
||||
if (!long.TryParse(left, out long x))
|
||||
{
|
||||
return Utilities.CompareNumeric(left, right);
|
||||
return NaturalComparerUtil.CompareNumeric(left, right);
|
||||
}
|
||||
|
||||
if (!long.TryParse(right, out long y))
|
||||
{
|
||||
return Utilities.CompareNumeric(left, right);
|
||||
return NaturalComparerUtil.CompareNumeric(left, right);
|
||||
}
|
||||
|
||||
// If we have an equal part, then make sure that "longer" ones are taken into account
|
||||
|
||||
78
SabreTools.Library/External/NaturalSort/NaturalComparerUtil.cs
vendored
Normal file
78
SabreTools.Library/External/NaturalSort/NaturalComparerUtil.cs
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NaturalSort
|
||||
{
|
||||
public static class NaturalComparerUtil
|
||||
{
|
||||
public static int CompareNumeric(string s1, string s2)
|
||||
{
|
||||
// Save the orginal strings, for later comparison
|
||||
string s1orig = s1;
|
||||
string s2orig = s2;
|
||||
|
||||
// We want to normalize the strings, so we set both to lower case
|
||||
s1 = s1.ToLowerInvariant();
|
||||
s2 = s2.ToLowerInvariant();
|
||||
|
||||
// If the strings are the same exactly, return
|
||||
if (s1 == s2)
|
||||
return s1orig.CompareTo(s2orig);
|
||||
|
||||
// If one is null, then say that's less than
|
||||
if (s1 == null)
|
||||
return -1;
|
||||
if (s2 == null)
|
||||
return 1;
|
||||
|
||||
// Now split into path parts after converting AltDirSeparator to DirSeparator
|
||||
s1 = s1.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
s2 = s2.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
string[] s1parts = s1.Split(Path.DirectorySeparatorChar);
|
||||
string[] s2parts = s2.Split(Path.DirectorySeparatorChar);
|
||||
|
||||
// Then compare each part in turn
|
||||
for (int j = 0; j < s1parts.Length && j < s2parts.Length; j++)
|
||||
{
|
||||
int compared = CompareNumericPart(s1parts[j], s2parts[j]);
|
||||
if (compared != 0)
|
||||
return compared;
|
||||
}
|
||||
|
||||
// If we got out here, then it looped through at least one of the strings
|
||||
if (s1parts.Length > s2parts.Length)
|
||||
return 1;
|
||||
if (s1parts.Length < s2parts.Length)
|
||||
return -1;
|
||||
|
||||
return s1orig.CompareTo(s2orig);
|
||||
}
|
||||
|
||||
private static int CompareNumericPart(string s1, string s2)
|
||||
{
|
||||
// Otherwise, loop through until we have an answer
|
||||
for (int i = 0; i < s1.Length && i < s2.Length; i++)
|
||||
{
|
||||
int s1c = s1[i];
|
||||
int s2c = s2[i];
|
||||
|
||||
// If the characters are the same, continue
|
||||
if (s1c == s2c)
|
||||
continue;
|
||||
|
||||
// If they're different, check which one was larger
|
||||
if (s1c > s2c)
|
||||
return 1;
|
||||
if (s1c < s2c)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If we got out here, then it looped through at least one of the strings
|
||||
if (s1.Length > s2.Length)
|
||||
return 1;
|
||||
if (s1.Length < s2.Length)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace NaturalSort
|
||||
{
|
||||
public class NaturalReversedComparer : Comparer<string>, IDisposable
|
||||
@@ -77,12 +75,12 @@ namespace NaturalSort
|
||||
{
|
||||
if (!long.TryParse(left, out long x))
|
||||
{
|
||||
return Utilities.CompareNumeric(right, left);
|
||||
return NaturalComparerUtil.CompareNumeric(right, left);
|
||||
}
|
||||
|
||||
if (!long.TryParse(right, out long y))
|
||||
{
|
||||
return Utilities.CompareNumeric(right, left);
|
||||
return NaturalComparerUtil.CompareNumeric(right, left);
|
||||
}
|
||||
|
||||
// If we have an equal part, then make sure that "longer" ones are taken into account
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
@@ -34,6 +35,83 @@ namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
}
|
||||
|
||||
/// <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 = input.GetFileType();
|
||||
|
||||
// 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
|
||||
Globals.Logger.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)
|
||||
{
|
||||
switch (archiveType)
|
||||
{
|
||||
case FileType.GZipArchive:
|
||||
return new GZipArchive();
|
||||
|
||||
case FileType.RarArchive:
|
||||
return new RarArchive();
|
||||
|
||||
case FileType.SevenZipArchive:
|
||||
return new SevenZipArchive();
|
||||
|
||||
case FileType.TapeArchive:
|
||||
return new TapeArchive();
|
||||
|
||||
case FileType.ZipArchive:
|
||||
return new ZipArchive();
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
@@ -17,7 +17,9 @@ namespace SabreTools.Library.FileTypes
|
||||
public long? Size { get; set; }
|
||||
public byte[] CRC { get; set; }
|
||||
public byte[] MD5 { get; set; }
|
||||
#if NET_FRAMEWORK
|
||||
public byte[] RIPEMD160 { get; set; }
|
||||
#endif
|
||||
public byte[] SHA1 { get; set; }
|
||||
public byte[] SHA256 { get; set; }
|
||||
public byte[] SHA384 { get; set; }
|
||||
@@ -45,14 +47,16 @@ namespace SabreTools.Library.FileTypes
|
||||
|
||||
if (getHashes)
|
||||
{
|
||||
BaseFile temp = Utilities.GetFileInfo(this.Filename);
|
||||
BaseFile temp = FileExtensions.GetInfo(this.Filename);
|
||||
if (temp != null)
|
||||
{
|
||||
this.Parent = temp.Parent;
|
||||
this.Date = temp.Date;
|
||||
this.CRC = temp.CRC;
|
||||
this.MD5 = temp.MD5;
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160 = temp.RIPEMD160;
|
||||
#endif
|
||||
this.SHA1 = temp.SHA1;
|
||||
this.SHA256 = temp.SHA256;
|
||||
this.SHA384 = temp.SHA384;
|
||||
@@ -73,21 +77,23 @@ namespace SabreTools.Library.FileTypes
|
||||
|
||||
if (getHashes)
|
||||
{
|
||||
BaseFile temp = Utilities.GetStreamInfo(stream, stream.Length);
|
||||
if(temp != null)
|
||||
BaseFile temp = stream.GetInfo();
|
||||
if (temp != null)
|
||||
{
|
||||
this.Parent = temp.Parent;
|
||||
this.Date = temp.Date;
|
||||
this.CRC = temp.CRC;
|
||||
this.MD5 = temp.MD5;
|
||||
#if NET_FRAMEWORK
|
||||
this.RIPEMD160 = temp.RIPEMD160;
|
||||
#endif
|
||||
this.SHA1 = temp.SHA1;
|
||||
this.SHA256 = temp.SHA256;
|
||||
this.SHA384 = temp.SHA384;
|
||||
this.SHA512 = temp.SHA512;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.Tools;
|
||||
@@ -9,477 +11,137 @@ namespace SabreTools.Library.FileTypes
|
||||
/// This is code adapted from chd.h and chd.cpp in MAME
|
||||
/// Additional archival code from https://github.com/rtissera/libchdr/blob/master/src/chd.h
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ----------------------------------------------
|
||||
/// Common CHD Header:
|
||||
/// 0x00-0x07 - CHD signature
|
||||
/// 0x08-0x0B - Header size
|
||||
/// 0x0C-0x0F - CHD version
|
||||
/// ----------------------------------------------
|
||||
/// CHD v1 header layout:
|
||||
/// 0x10-0x13 - Flags (1: Has parent MD5, 2: Disallow writes)
|
||||
/// 0x14-0x17 - Compression
|
||||
/// 0x18-0x1B - 512-byte sectors per hunk
|
||||
/// 0x1C-0x1F - Hunk count
|
||||
/// 0x20-0x23 - Hard disk cylinder count
|
||||
/// 0x24-0x27 - Hard disk head count
|
||||
/// 0x28-0x2B - Hard disk sector count
|
||||
/// 0x2C-0x3B - MD5
|
||||
/// 0x3C-0x4B - Parent MD5
|
||||
/// ----------------------------------------------
|
||||
/// CHD v2 header layout:
|
||||
/// 0x10-0x13 - Flags (1: Has parent MD5, 2: Disallow writes)
|
||||
/// 0x14-0x17 - Compression
|
||||
/// 0x18-0x1B - seclen-byte sectors per hunk
|
||||
/// 0x1C-0x1F - Hunk count
|
||||
/// 0x20-0x23 - Hard disk cylinder count
|
||||
/// 0x24-0x27 - Hard disk head count
|
||||
/// 0x28-0x2B - Hard disk sector count
|
||||
/// 0x2C-0x3B - MD5
|
||||
/// 0x3C-0x4B - Parent MD5
|
||||
/// 0x4C-0x4F - Number of bytes per sector (seclen)
|
||||
/// ----------------------------------------------
|
||||
/// CHD v3 header layout:
|
||||
/// 0x10-0x13 - Flags (1: Has parent SHA-1, 2: Disallow writes)
|
||||
/// 0x14-0x17 - Compression
|
||||
/// 0x18-0x1B - Hunk count
|
||||
/// 0x1C-0x23 - Logical Bytes
|
||||
/// 0x24-0x2C - Metadata Offset
|
||||
/// ...
|
||||
/// 0x4C-0x4F - Hunk Bytes
|
||||
/// 0x50-0x63 - SHA-1
|
||||
/// 0x64-0x77 - Parent SHA-1
|
||||
/// 0x78-0x87 - Map
|
||||
/// ----------------------------------------------
|
||||
/// CHD v4 header layout:
|
||||
/// 0x10-0x13 - Flags (1: Has parent SHA-1, 2: Disallow writes)
|
||||
/// 0x14-0x17 - Compression
|
||||
/// 0x18-0x1B - Hunk count
|
||||
/// 0x1C-0x23 - Logical Bytes
|
||||
/// 0x24-0x2C - Metadata Offset
|
||||
/// ...
|
||||
/// 0x2C-0x2F - Hunk Bytes
|
||||
/// 0x30-0x43 - SHA-1
|
||||
/// 0x44-0x57 - Parent SHA-1
|
||||
/// 0x58-0x6b - Raw SHA-1
|
||||
/// 0x6c-0x7b - Map
|
||||
/// ----------------------------------------------
|
||||
/// CHD v5 header layout:
|
||||
/// 0x10-0x13 - Compression format 1
|
||||
/// 0x14-0x17 - Compression format 2
|
||||
/// 0x18-0x1B - Compression format 3
|
||||
/// 0x1C-0x1F - Compression format 4
|
||||
/// 0x20-0x27 - Logical Bytes
|
||||
/// 0x28-0x2F - Map Offset
|
||||
/// 0x30-0x37 - Metadata Offset
|
||||
/// 0x38-0x3B - Hunk Bytes
|
||||
/// 0x3C-0x3F - Unit Bytes
|
||||
/// 0x40-0x53 - Raw SHA-1
|
||||
/// 0x54-0x67 - SHA-1
|
||||
/// 0x68-0x7b - Parent SHA-1
|
||||
/// ----------------------------------------------
|
||||
/// </remarks>
|
||||
public class CHDFile : BaseFile
|
||||
public abstract class CHDFile : BaseFile
|
||||
{
|
||||
#region Private instance variables
|
||||
|
||||
// Core parameters from the header
|
||||
private byte[] m_signature; // signature
|
||||
private uint m_headersize; // size of the header
|
||||
private uint m_version; // version of the header
|
||||
private ulong m_logicalbytes; // logical size of the raw CHD data in bytes
|
||||
private ulong m_mapoffset; // offset of map
|
||||
private ulong m_metaoffset; // offset to first metadata bit
|
||||
private uint m_sectorsperhunk; // number of sectors per hunk
|
||||
private uint m_hunkbytes; // size of each raw hunk in bytes
|
||||
private ulong m_hunkcount; // number of hunks represented
|
||||
private uint m_unitbytes; // size of each unit in bytes
|
||||
private ulong m_unitcount; // number of units represented
|
||||
private CHD_CODEC[] m_compression = new CHD_CODEC[4]; // array of compression types used
|
||||
|
||||
// map information
|
||||
private uint m_mapentrybytes; // length of each entry in a map
|
||||
|
||||
// additional required vars
|
||||
private uint? _headerVersion;
|
||||
private BinaryReader m_br; // Binary reader representing the CHD stream
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pubically facing variables
|
||||
|
||||
public uint? Version
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_headerVersion == null)
|
||||
{
|
||||
_headerVersion = ValidateHeaderVersion();
|
||||
}
|
||||
|
||||
return _headerVersion;
|
||||
}
|
||||
}
|
||||
// Common header fields
|
||||
protected char[] tag = new char[8]; // 'MComprHD'
|
||||
protected uint length; // length of header (including tag and length fields)
|
||||
protected uint version; // drive format version
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, blank CHDFile
|
||||
/// </summary>
|
||||
public CHDFile()
|
||||
{
|
||||
this.Type = FileType.CHD;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new CHDFile from an input file
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
public CHDFile(string filename)
|
||||
: this(Utilities.TryOpenRead(filename))
|
||||
/// <param name="filename">Filename respresenting the CHD file</param>
|
||||
public static CHDFile Create(string filename)
|
||||
{
|
||||
using (FileStream fs = FileExtensions.TryOpenRead(filename))
|
||||
{
|
||||
return Create(fs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new CHDFile from an input stream
|
||||
/// </summary>
|
||||
/// <param name="chdstream">Stream representing the CHD file</param>
|
||||
public CHDFile(Stream chdstream)
|
||||
public static CHDFile Create(Stream chdstream)
|
||||
{
|
||||
this.Type = FileType.CHD;
|
||||
m_br = new BinaryReader(chdstream);
|
||||
// Read the standard CHD headers
|
||||
(char[] tag, uint length, uint version) = GetHeaderValues(chdstream);
|
||||
chdstream.Seek(-16, SeekOrigin.Current); // Seek back to start
|
||||
|
||||
_headerVersion = ValidateHeaderVersion();
|
||||
if (_headerVersion != null)
|
||||
{
|
||||
byte[] hash = GetHashFromHeader();
|
||||
if (hash != null)
|
||||
{
|
||||
if (hash.Length == Constants.MD5Length)
|
||||
this.MD5 = hash;
|
||||
else if (hash.Length == Constants.SHA1Length)
|
||||
this.SHA1 = hash;
|
||||
}
|
||||
}
|
||||
// Validate that this is actually a valid CHD
|
||||
uint validatedVersion = ValidateHeader(tag, length, version);
|
||||
if (validatedVersion == 0)
|
||||
return null;
|
||||
|
||||
// Read and retrun the current CHD
|
||||
CHDFile generated = ReadAsVersion(chdstream, version);
|
||||
if (generated != null)
|
||||
generated.Type = FileType.CHD;
|
||||
|
||||
return generated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abstract functionality
|
||||
|
||||
/// <summary>
|
||||
/// Return the best-available hash for a particular CHD version
|
||||
/// </summary>
|
||||
public abstract byte[] GetHash();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Header Parsing
|
||||
|
||||
/// <summary>
|
||||
/// Validate the initial signature, version, and header size
|
||||
/// Get the generic header values of a CHD, if possible
|
||||
/// </summary>
|
||||
/// <returns>Unsigned int containing the version number, null if invalid</returns>
|
||||
private uint? ValidateHeaderVersion()
|
||||
/// <param name="stream"></param>
|
||||
/// <returns></returns>
|
||||
private static (char[] tag, uint length, uint version) GetHeaderValues(Stream stream)
|
||||
{
|
||||
try
|
||||
char[] parsedTag = new char[8];
|
||||
uint parsedLength = 0;
|
||||
uint parsedVersion = 0;
|
||||
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
// Seek to the beginning to make sure we're reading the correct bytes
|
||||
m_br.BaseStream.Seek(0, SeekOrigin.Begin);
|
||||
parsedTag = br.ReadCharsBigEndian(8);
|
||||
parsedLength = br.ReadUInt32BigEndian();
|
||||
parsedVersion = br.ReadUInt32BigEndian();
|
||||
}
|
||||
|
||||
// Read and verify the CHD signature
|
||||
m_signature = m_br.ReadBytes(8);
|
||||
return (parsedTag, parsedLength, parsedVersion);
|
||||
}
|
||||
|
||||
// If no signature could be read, return null
|
||||
if (m_signature == null || m_signature.Length == 0)
|
||||
/// <summary>
|
||||
/// Validate the header values
|
||||
/// </summary>
|
||||
/// <returns>Matching version, 0 if none</returns>
|
||||
private static uint ValidateHeader(char[] tag, uint length, uint version)
|
||||
{
|
||||
if (!string.Equals(new string(tag), "MComprHD", StringComparison.Ordinal))
|
||||
return 0;
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
return length == CHDFileV1.HeaderSize ? version : 0;
|
||||
case 2:
|
||||
return length == CHDFileV2.HeaderSize ? version : 0;
|
||||
case 3:
|
||||
return length == CHDFileV3.HeaderSize ? version : 0;
|
||||
case 4:
|
||||
return length == CHDFileV4.HeaderSize ? version : 0;
|
||||
case 5:
|
||||
return length == CHDFileV5.HeaderSize ? version : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a stream as a particular CHD version
|
||||
/// </summary>
|
||||
/// <param name="stream">CHD file as a stream</param>
|
||||
/// <param name="version">CHD version to parse</param>
|
||||
/// <returns>Populated CHD file, null on failure</returns>
|
||||
private static CHDFile ReadAsVersion(Stream stream, uint version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
return CHDFileV1.Deserialize(stream);
|
||||
case 2:
|
||||
return CHDFileV2.Deserialize(stream);
|
||||
case 3:
|
||||
return CHDFileV3.Deserialize(stream);
|
||||
case 4:
|
||||
return CHDFileV4.Deserialize(stream);
|
||||
case 5:
|
||||
return CHDFileV5.Deserialize(stream);
|
||||
default:
|
||||
return null;
|
||||
|
||||
if (!m_signature.StartsWith(Constants.CHDSignature, exact: true))
|
||||
{
|
||||
// throw CHDERR_INVALID_FILE;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the header size and version
|
||||
m_headersize = m_br.ReadUInt32Reverse();
|
||||
m_version = m_br.ReadUInt32Reverse();
|
||||
|
||||
// If we have an invalid combination of size and version
|
||||
if ((m_version == 1 && m_headersize != Constants.CHD_V1_HEADER_SIZE)
|
||||
|| (m_version == 2 && m_headersize != Constants.CHD_V2_HEADER_SIZE)
|
||||
|| (m_version == 3 && m_headersize != Constants.CHD_V3_HEADER_SIZE)
|
||||
|| (m_version == 4 && m_headersize != Constants.CHD_V4_HEADER_SIZE)
|
||||
|| (m_version == 5 && m_headersize != Constants.CHD_V5_HEADER_SIZE)
|
||||
|| (m_version < 1 || m_version > 5))
|
||||
{
|
||||
// throw CHDERR_UNSUPPORTED_VERSION;
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_version;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal MD5 (v1, v2) or SHA-1 (v3, v4, v5) from the CHD
|
||||
/// </summary>
|
||||
/// <returns>MD5 as a byte array, null on error</returns>
|
||||
private byte[] GetHashFromHeader()
|
||||
{
|
||||
// Validate the header by default just in case
|
||||
uint? version = ValidateHeaderVersion();
|
||||
|
||||
// Now get the hash, if possible
|
||||
byte[] hash;
|
||||
|
||||
// Now parse the rest of the header according to the version
|
||||
try
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
hash = ParseCHDv1Header();
|
||||
break;
|
||||
case 2:
|
||||
hash = ParseCHDv2Header();
|
||||
break;
|
||||
case 3:
|
||||
hash = ParseCHDv3Header();
|
||||
break;
|
||||
case 4:
|
||||
hash = ParseCHDv4Header();
|
||||
break;
|
||||
case 5:
|
||||
hash = ParseCHDv5Header();
|
||||
break;
|
||||
case null:
|
||||
default:
|
||||
// throw CHDERR_INVALID_FILE;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// throw CHDERR_INVALID_FILE;
|
||||
return null;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a CHD v1 header
|
||||
/// </summary>
|
||||
/// <returns>The extracted MD5 on success, null otherwise</returns>
|
||||
private byte[] ParseCHDv1Header()
|
||||
{
|
||||
// Seek to after the signature to make sure we're reading the correct bytes
|
||||
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
|
||||
|
||||
// Set the blank MD5 hash
|
||||
byte[] md5 = new byte[16];
|
||||
|
||||
// Set offsets and defaults
|
||||
m_mapoffset = 0;
|
||||
m_mapentrybytes = 0;
|
||||
|
||||
// Read the CHD flags
|
||||
uint flags = m_br.ReadUInt32Reverse();
|
||||
|
||||
// Determine compression
|
||||
switch (m_br.ReadUInt32())
|
||||
{
|
||||
case 0: m_compression[0] = CHD_CODEC.NONE; break;
|
||||
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
|
||||
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
|
||||
}
|
||||
|
||||
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
|
||||
|
||||
m_sectorsperhunk = m_br.ReadUInt32Reverse();
|
||||
m_hunkcount = m_br.ReadUInt32Reverse();
|
||||
m_br.ReadUInt32Reverse(); // Cylinder count
|
||||
m_br.ReadUInt32Reverse(); // Head count
|
||||
m_br.ReadUInt32Reverse(); // Sector count
|
||||
|
||||
md5 = m_br.ReadBytes(16);
|
||||
m_br.ReadBytes(16); // Parent MD5
|
||||
|
||||
return md5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a CHD v2 header
|
||||
/// </summary>
|
||||
/// <returns>The extracted MD5 on success, null otherwise</returns>
|
||||
private byte[] ParseCHDv2Header()
|
||||
{
|
||||
// Seek to after the signature to make sure we're reading the correct bytes
|
||||
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
|
||||
|
||||
// Set the blank MD5 hash
|
||||
byte[] md5 = new byte[16];
|
||||
|
||||
// Set offsets and defaults
|
||||
m_mapoffset = 0;
|
||||
m_mapentrybytes = 0;
|
||||
|
||||
// Read the CHD flags
|
||||
uint flags = m_br.ReadUInt32Reverse();
|
||||
|
||||
// Determine compression
|
||||
switch (m_br.ReadUInt32())
|
||||
{
|
||||
case 0: m_compression[0] = CHD_CODEC.NONE; break;
|
||||
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
|
||||
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
|
||||
}
|
||||
|
||||
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
|
||||
|
||||
m_sectorsperhunk = m_br.ReadUInt32Reverse();
|
||||
m_hunkcount = m_br.ReadUInt32Reverse();
|
||||
m_br.ReadUInt32Reverse(); // Cylinder count
|
||||
m_br.ReadUInt32Reverse(); // Head count
|
||||
m_br.ReadUInt32Reverse(); // Sector count
|
||||
|
||||
md5 = m_br.ReadBytes(16);
|
||||
m_br.ReadBytes(16); // Parent MD5
|
||||
m_br.ReadUInt32Reverse(); // Sector size
|
||||
|
||||
return md5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a CHD v3 header
|
||||
/// </summary>
|
||||
/// <returns>The extracted SHA-1 on success, null otherwise</returns>
|
||||
private byte[] ParseCHDv3Header()
|
||||
{
|
||||
// Seek to after the signature to make sure we're reading the correct bytes
|
||||
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
|
||||
|
||||
// Set the blank SHA-1 hash
|
||||
byte[] sha1 = new byte[20];
|
||||
|
||||
// Set offsets and defaults
|
||||
m_mapoffset = 120;
|
||||
m_mapentrybytes = 16;
|
||||
|
||||
// Read the CHD flags
|
||||
uint flags = m_br.ReadUInt32Reverse();
|
||||
|
||||
// Determine compression
|
||||
switch (m_br.ReadUInt32())
|
||||
{
|
||||
case 0: m_compression[0] = CHD_CODEC.NONE; break;
|
||||
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
|
||||
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
|
||||
}
|
||||
|
||||
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
|
||||
|
||||
m_hunkcount = m_br.ReadUInt32Reverse();
|
||||
m_logicalbytes = m_br.ReadUInt64Reverse();
|
||||
m_metaoffset = m_br.ReadUInt32Reverse();
|
||||
|
||||
m_br.BaseStream.Seek(76, SeekOrigin.Begin);
|
||||
m_hunkbytes = m_br.ReadUInt32Reverse();
|
||||
|
||||
m_br.BaseStream.Seek(Constants.CHDv3SHA1Offset, SeekOrigin.Begin);
|
||||
sha1 = m_br.ReadBytes(20);
|
||||
|
||||
// guess at the units based on snooping the metadata
|
||||
// m_unitbytes = guess_unitbytes();
|
||||
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
|
||||
|
||||
return sha1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a CHD v4 header
|
||||
/// </summary>
|
||||
/// <returns>The extracted SHA-1 on success, null otherwise</returns>
|
||||
private byte[] ParseCHDv4Header()
|
||||
{
|
||||
// Seek to after the signature to make sure we're reading the correct bytes
|
||||
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
|
||||
|
||||
// Set the blank SHA-1 hash
|
||||
byte[] sha1 = new byte[20];
|
||||
|
||||
// Set offsets and defaults
|
||||
m_mapoffset = 108;
|
||||
m_mapentrybytes = 16;
|
||||
|
||||
// Read the CHD flags
|
||||
uint flags = m_br.ReadUInt32Reverse();
|
||||
|
||||
// Determine compression
|
||||
switch (m_br.ReadUInt32())
|
||||
{
|
||||
case 0: m_compression[0] = CHD_CODEC.NONE; break;
|
||||
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
|
||||
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
|
||||
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
|
||||
}
|
||||
|
||||
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
|
||||
|
||||
m_hunkcount = m_br.ReadUInt32Reverse();
|
||||
m_logicalbytes = m_br.ReadUInt64Reverse();
|
||||
m_metaoffset = m_br.ReadUInt32Reverse();
|
||||
|
||||
m_br.BaseStream.Seek(44, SeekOrigin.Begin);
|
||||
m_hunkbytes = m_br.ReadUInt32Reverse();
|
||||
|
||||
m_br.BaseStream.Seek(Constants.CHDv4SHA1Offset, SeekOrigin.Begin);
|
||||
sha1 = m_br.ReadBytes(20);
|
||||
|
||||
// guess at the units based on snooping the metadata
|
||||
// m_unitbytes = guess_unitbytes();
|
||||
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
|
||||
return sha1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a CHD v5 header
|
||||
/// </summary>
|
||||
/// <returns>The extracted SHA-1 on success, null otherwise</returns>
|
||||
private byte[] ParseCHDv5Header()
|
||||
{
|
||||
// Seek to after the signature to make sure we're reading the correct bytes
|
||||
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
|
||||
|
||||
// Set the blank SHA-1 hash
|
||||
byte[] sha1 = new byte[20];
|
||||
|
||||
// Determine compression
|
||||
m_compression[0] = (CHD_CODEC)m_br.ReadUInt32Reverse();
|
||||
m_compression[1] = (CHD_CODEC)m_br.ReadUInt32Reverse();
|
||||
m_compression[2] = (CHD_CODEC)m_br.ReadUInt32Reverse();
|
||||
m_compression[3] = (CHD_CODEC)m_br.ReadUInt32Reverse();
|
||||
|
||||
m_logicalbytes = m_br.ReadUInt64Reverse();
|
||||
m_mapoffset = m_br.ReadUInt64Reverse();
|
||||
m_metaoffset = m_br.ReadUInt64Reverse();
|
||||
m_hunkbytes = m_br.ReadUInt32Reverse();
|
||||
m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes;
|
||||
m_unitbytes = m_br.ReadUInt32Reverse();
|
||||
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
|
||||
|
||||
// m_allow_writes = !compressed();
|
||||
|
||||
// determine properties of map entries
|
||||
// m_mapentrybytes = compressed() ? 12 : 4;
|
||||
|
||||
m_br.BaseStream.Seek(Constants.CHDv5SHA1Offset, SeekOrigin.Begin);
|
||||
sha1 = m_br.ReadBytes(20);
|
||||
return sha1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
90
SabreTools.Library/FileTypes/CHDFileV1.cs
Normal file
90
SabreTools.Library/FileTypes/CHDFileV1.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD V1 File
|
||||
/// </summary>
|
||||
public class CHDFileV1 : CHDFile
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD flags
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Flags : uint
|
||||
{
|
||||
DriveHasParent = 0x00000001,
|
||||
DriveAllowsWrites = 0x00000002,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compression being used in CHD
|
||||
/// </summary>
|
||||
public enum Compression : uint
|
||||
{
|
||||
CHDCOMPRESSION_NONE = 0,
|
||||
CHDCOMPRESSION_ZLIB = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map format
|
||||
/// </summary>
|
||||
public class Map
|
||||
{
|
||||
public ulong offset; // 44; starting offset within the file
|
||||
public ulong length; // 20; length of data; if == hunksize, data is uncompressed
|
||||
}
|
||||
|
||||
public const int HeaderSize = 76;
|
||||
public const uint Version = 1;
|
||||
|
||||
// V1-specific header values
|
||||
public Flags flags; // flags (see above)
|
||||
public Compression compression; // compression type
|
||||
public uint hunksize; // 512-byte sectors per hunk
|
||||
public uint totalhunks; // total # of hunks represented
|
||||
public uint cylinders; // number of cylinders on hard disk
|
||||
public uint heads; // number of heads on hard disk
|
||||
public uint sectors; // number of sectors on hard disk
|
||||
public byte[] md5 = new byte[16]; // MD5 checksum of raw data
|
||||
public byte[] parentmd5 = new byte[16]; // MD5 checksum of parent file
|
||||
|
||||
/// <summary>
|
||||
/// Parse and validate the header as if it's V1
|
||||
/// </summary>
|
||||
public static CHDFileV1 Deserialize(Stream stream)
|
||||
{
|
||||
CHDFileV1 chd = new CHDFileV1();
|
||||
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
chd.tag = br.ReadCharsBigEndian(8);
|
||||
chd.length = br.ReadUInt32BigEndian();
|
||||
chd.version = br.ReadUInt32BigEndian();
|
||||
chd.flags = (Flags)br.ReadUInt32BigEndian();
|
||||
chd.compression = (Compression)br.ReadUInt32BigEndian();
|
||||
chd.hunksize = br.ReadUInt32BigEndian();
|
||||
chd.totalhunks = br.ReadUInt32BigEndian();
|
||||
chd.cylinders = br.ReadUInt32BigEndian();
|
||||
chd.heads = br.ReadUInt32BigEndian();
|
||||
chd.sectors = br.ReadUInt32BigEndian();
|
||||
chd.md5 = br.ReadBytesBigEndian(16);
|
||||
chd.parentmd5 = br.ReadBytesBigEndian(16);
|
||||
}
|
||||
|
||||
return chd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return internal MD5 hash
|
||||
/// </summary>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
return md5;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
SabreTools.Library/FileTypes/CHDFileV2.cs
Normal file
92
SabreTools.Library/FileTypes/CHDFileV2.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD V2 File
|
||||
/// </summary>
|
||||
public class CHDFileV2 : CHDFile
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD flags
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Flags : uint
|
||||
{
|
||||
DriveHasParent = 0x00000001,
|
||||
DriveAllowsWrites = 0x00000002,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compression being used in CHD
|
||||
/// </summary>
|
||||
public enum Compression : uint
|
||||
{
|
||||
CHDCOMPRESSION_NONE = 0,
|
||||
CHDCOMPRESSION_ZLIB = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map format
|
||||
/// </summary>
|
||||
public class Map
|
||||
{
|
||||
public ulong offset; // 44; starting offset within the file
|
||||
public ulong length; // 20; length of data; if == hunksize, data is uncompressed
|
||||
}
|
||||
|
||||
public const int HeaderSize = 80;
|
||||
public const uint Version = 2;
|
||||
|
||||
// V2-specific header values
|
||||
public Flags flags; // flags (see above)
|
||||
public Compression compression; // compression type
|
||||
public uint hunksize; // 512-byte sectors per hunk
|
||||
public uint totalhunks; // total # of hunks represented
|
||||
public uint cylinders; // number of cylinders on hard disk
|
||||
public uint heads; // number of heads on hard disk
|
||||
public uint sectors; // number of sectors on hard disk
|
||||
public byte[] md5 = new byte[16]; // MD5 checksum of raw data
|
||||
public byte[] parentmd5 = new byte[16]; // MD5 checksum of parent file
|
||||
public uint seclen; // number of bytes per sector
|
||||
|
||||
/// <summary>
|
||||
/// Parse and validate the header as if it's V2
|
||||
/// </summary>
|
||||
public static CHDFileV2 Deserialize(Stream stream)
|
||||
{
|
||||
CHDFileV2 chd = new CHDFileV2();
|
||||
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
chd.tag = br.ReadCharsBigEndian(8);
|
||||
chd.length = br.ReadUInt32BigEndian();
|
||||
chd.version = br.ReadUInt32BigEndian();
|
||||
chd.flags = (Flags)br.ReadUInt32BigEndian();
|
||||
chd.compression = (Compression)br.ReadUInt32BigEndian();
|
||||
chd.hunksize = br.ReadUInt32BigEndian();
|
||||
chd.totalhunks = br.ReadUInt32BigEndian();
|
||||
chd.cylinders = br.ReadUInt32BigEndian();
|
||||
chd.heads = br.ReadUInt32BigEndian();
|
||||
chd.sectors = br.ReadUInt32BigEndian();
|
||||
chd.md5 = br.ReadBytesBigEndian(16);
|
||||
chd.parentmd5 = br.ReadBytesBigEndian(16);
|
||||
chd.seclen = br.ReadUInt32BigEndian();
|
||||
}
|
||||
|
||||
return chd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return internal MD5 hash
|
||||
/// </summary>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
return md5;
|
||||
}
|
||||
}
|
||||
}
|
||||
96
SabreTools.Library/FileTypes/CHDFileV3.cs
Normal file
96
SabreTools.Library/FileTypes/CHDFileV3.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD V3 File
|
||||
/// </summary>
|
||||
public class CHDFileV3 : CHDFile
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD flags
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Flags : uint
|
||||
{
|
||||
DriveHasParent = 0x00000001,
|
||||
DriveAllowsWrites = 0x00000002,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compression being used in CHD
|
||||
/// </summary>
|
||||
public enum Compression : uint
|
||||
{
|
||||
CHDCOMPRESSION_NONE = 0,
|
||||
CHDCOMPRESSION_ZLIB = 1,
|
||||
CHDCOMPRESSION_ZLIB_PLUS = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map format
|
||||
/// </summary>
|
||||
public class Map
|
||||
{
|
||||
public ulong offset; // starting offset within the file
|
||||
public uint crc32; // 32-bit CRC of the uncompressed data
|
||||
public ushort length_lo; // lower 16 bits of length
|
||||
public byte length_hi; // upper 8 bits of length
|
||||
public byte flags; // flags, indicating compression info
|
||||
}
|
||||
|
||||
public const int HeaderSize = 120;
|
||||
public const uint Version = 3;
|
||||
|
||||
// V3-specific header values
|
||||
public Flags flags; // flags (see above)
|
||||
public Compression compression; // compression type
|
||||
public uint totalhunks; // total # of hunks represented
|
||||
public ulong logicalbytes; // logical size of the data (in bytes)
|
||||
public ulong metaoffset; // offset to the first blob of metadata
|
||||
public byte[] md5 = new byte[16]; // MD5 checksum of raw data
|
||||
public byte[] parentmd5 = new byte[16]; // MD5 checksum of parent file
|
||||
public uint hunkbytes; // number of bytes per hunk
|
||||
public byte[] sha1 = new byte[20]; // SHA1 checksum of raw data
|
||||
public byte[] parentsha1 = new byte[20]; // SHA1 checksum of parent file
|
||||
|
||||
/// <summary>
|
||||
/// Parse and validate the header as if it's V3
|
||||
/// </summary>
|
||||
public static CHDFileV3 Deserialize(Stream stream)
|
||||
{
|
||||
CHDFileV3 chd = new CHDFileV3();
|
||||
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
chd.tag = br.ReadCharsBigEndian(8);
|
||||
chd.length = br.ReadUInt32BigEndian();
|
||||
chd.version = br.ReadUInt32BigEndian();
|
||||
chd.flags = (Flags)br.ReadUInt32BigEndian();
|
||||
chd.compression = (Compression)br.ReadUInt32BigEndian();
|
||||
chd.totalhunks = br.ReadUInt32BigEndian();
|
||||
chd.logicalbytes = br.ReadUInt64BigEndian();
|
||||
chd.metaoffset = br.ReadUInt64BigEndian();
|
||||
chd.md5 = br.ReadBytesBigEndian(16);
|
||||
chd.parentmd5 = br.ReadBytesBigEndian(16);
|
||||
chd.hunkbytes = br.ReadUInt32BigEndian();
|
||||
chd.sha1 = br.ReadBytesBigEndian(20);
|
||||
chd.parentsha1 = br.ReadBytesBigEndian(20);
|
||||
}
|
||||
|
||||
return chd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return internal SHA1 hash
|
||||
/// </summary>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
return sha1;
|
||||
}
|
||||
}
|
||||
}
|
||||
95
SabreTools.Library/FileTypes/CHDFileV4.cs
Normal file
95
SabreTools.Library/FileTypes/CHDFileV4.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD V4 File
|
||||
/// </summary>
|
||||
public class CHDFileV4 : CHDFile
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD flags
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Flags : uint
|
||||
{
|
||||
DriveHasParent = 0x00000001,
|
||||
DriveAllowsWrites = 0x00000002,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compression being used in CHD
|
||||
/// </summary>
|
||||
public enum Compression : uint
|
||||
{
|
||||
CHDCOMPRESSION_NONE = 0,
|
||||
CHDCOMPRESSION_ZLIB = 1,
|
||||
CHDCOMPRESSION_ZLIB_PLUS = 2,
|
||||
CHDCOMPRESSION_AV = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map format
|
||||
/// </summary>
|
||||
public class Map
|
||||
{
|
||||
public ulong offset; // starting offset within the file
|
||||
public uint crc32; // 32-bit CRC of the uncompressed data
|
||||
public ushort length_lo; // lower 16 bits of length
|
||||
public byte length_hi; // upper 8 bits of length
|
||||
public byte flags; // flags, indicating compression info
|
||||
}
|
||||
|
||||
public const int HeaderSize = 108;
|
||||
public const uint Version = 4;
|
||||
|
||||
// V4-specific header values
|
||||
public Flags flags; // flags (see above)
|
||||
public Compression compression; // compression type
|
||||
public uint totalhunks; // total # of hunks represented
|
||||
public ulong logicalbytes; // logical size of the data (in bytes)
|
||||
public ulong metaoffset; // offset to the first blob of metadata
|
||||
public uint hunkbytes; // number of bytes per hunk
|
||||
public byte[] sha1 = new byte[20]; // combined raw+meta SHA1
|
||||
public byte[] parentsha1 = new byte[20]; // combined raw+meta SHA1 of parent
|
||||
public byte[] rawsha1 = new byte[20]; // raw data SHA1
|
||||
|
||||
/// <summary>
|
||||
/// Parse and validate the header as if it's V4
|
||||
/// </summary>
|
||||
public static CHDFileV4 Deserialize(Stream stream)
|
||||
{
|
||||
CHDFileV4 chd = new CHDFileV4();
|
||||
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
chd.tag = br.ReadCharsBigEndian(8);
|
||||
chd.length = br.ReadUInt32BigEndian();
|
||||
chd.version = br.ReadUInt32BigEndian();
|
||||
chd.flags = (Flags)br.ReadUInt32BigEndian();
|
||||
chd.compression = (Compression)br.ReadUInt32BigEndian();
|
||||
chd.totalhunks = br.ReadUInt32BigEndian();
|
||||
chd.logicalbytes = br.ReadUInt64BigEndian();
|
||||
chd.metaoffset = br.ReadUInt64BigEndian();
|
||||
chd.hunkbytes = br.ReadUInt32BigEndian();
|
||||
chd.sha1 = br.ReadBytesBigEndian(20);
|
||||
chd.parentsha1 = br.ReadBytesBigEndian(20);
|
||||
chd.rawsha1 = br.ReadBytesBigEndian(20);
|
||||
}
|
||||
|
||||
return chd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return internal SHA1 hash
|
||||
/// </summary>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
return sha1;
|
||||
}
|
||||
}
|
||||
}
|
||||
98
SabreTools.Library/FileTypes/CHDFileV5.cs
Normal file
98
SabreTools.Library/FileTypes/CHDFileV5.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// CHD V5 File
|
||||
/// </summary>
|
||||
public class CHDFileV5 : CHDFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Uncompressed map format
|
||||
/// </summary>
|
||||
private class UncompressedMap
|
||||
{
|
||||
public uint offset; // starting offset within the file
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compressed map header format
|
||||
/// </summary>
|
||||
private class CompressedMapHeader
|
||||
{
|
||||
public uint length; // length of compressed map
|
||||
public byte[] datastart = new byte[12]; // UINT48; offset of first block
|
||||
public ushort crc; // crc-16 of the map
|
||||
public byte lengthbits; // bits used to encode complength
|
||||
public byte hunkbits; // bits used to encode self-refs
|
||||
public byte parentunitbits; // bits used to encode parent unit refs
|
||||
public byte reserved; // future use
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compressed map entry format
|
||||
/// </summary>
|
||||
private class CompressedMapEntry
|
||||
{
|
||||
public byte compression; // compression type
|
||||
public byte[] complength = new byte[6]; // UINT24; compressed length
|
||||
public byte[] offset = new byte[12]; // UINT48; offset
|
||||
public ushort crc; // crc-16 of the data
|
||||
}
|
||||
|
||||
public const int HeaderSize = 124;
|
||||
public const uint Version = 5;
|
||||
|
||||
// V5-specific header values
|
||||
public uint[] compressors = new uint[4]; // which custom compressors are used?
|
||||
public ulong logicalbytes; // logical size of the data (in bytes)
|
||||
public ulong mapoffset; // offset to the map
|
||||
public ulong metaoffset; // offset to the first blob of metadata
|
||||
public uint hunkbytes; // number of bytes per hunk
|
||||
public uint unitbytes; // number of bytes per unit within each hunk
|
||||
public byte[] rawsha1 = new byte[20]; // raw data SHA1
|
||||
public byte[] sha1 = new byte[20]; // combined raw+meta SHA1
|
||||
public byte[] parentsha1 = new byte[20]; // combined raw+meta SHA1 of parent
|
||||
|
||||
/// <summary>
|
||||
/// Parse and validate the header as if it's V5
|
||||
/// </summary>
|
||||
public static CHDFileV5 Deserialize(Stream stream)
|
||||
{
|
||||
CHDFileV5 chd = new CHDFileV5();
|
||||
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
chd.tag = br.ReadCharsBigEndian(8);
|
||||
chd.length = br.ReadUInt32BigEndian();
|
||||
chd.version = br.ReadUInt32BigEndian();
|
||||
chd.compressors = new uint[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
chd.compressors[i] = br.ReadUInt32BigEndian();
|
||||
}
|
||||
chd.logicalbytes = br.ReadUInt64BigEndian();
|
||||
chd.mapoffset = br.ReadUInt64BigEndian();
|
||||
chd.metaoffset = br.ReadUInt64BigEndian();
|
||||
chd.hunkbytes = br.ReadUInt32BigEndian();
|
||||
chd.unitbytes = br.ReadUInt32BigEndian();
|
||||
chd.rawsha1 = br.ReadBytesBigEndian(20);
|
||||
chd.sha1 = br.ReadBytesBigEndian(20);
|
||||
chd.parentsha1 = br.ReadBytesBigEndian(20);
|
||||
}
|
||||
|
||||
return chd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return internal SHA1 hash
|
||||
/// </summary>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
return sha1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
|
||||
/// <summary>
|
||||
/// This code is based on the header format described at http://www.rarlab.com/technote.htm#srvheaders
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ---------------------------------------------
|
||||
/// vint
|
||||
///
|
||||
/// Variable length integer. Can include one or more bytes, where lower 7 bits of every byte contain integer data
|
||||
/// and highest bit in every byte is the continuation flag.If highest bit is 0, this is the last byte in sequence.
|
||||
/// So first byte contains 7 least significant bits of integer and continuation flag. Second byte, if present,
|
||||
/// contains next 7 bits and so on.
|
||||
///
|
||||
/// Currently RAR format uses vint to store up to 64 bit integers, resulting in 10 bytes maximum. This value may
|
||||
/// be increased in the future if necessary for some reason.
|
||||
///
|
||||
/// Sometimes RAR needs to pre-allocate space for vint before knowing its exact value. In such situation it can
|
||||
/// allocate more space than really necessary and then fill several leading bytes with 0x80 hexadecimal, which means
|
||||
/// 0 with continuation flag set.
|
||||
/// ----------------------------------------------
|
||||
/// General archive layout:
|
||||
///
|
||||
/// Self-extracting module(optional) (RAR assumes the maximum SFX module size to not exceed 1 MB, but this value
|
||||
/// can be increased in the future.
|
||||
/// RAR 5.0 signature (RAR 5.0 signature consists of 8 bytes: 0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00.
|
||||
/// You need to search for this signature in supposed archive from beginning and up to maximum SFX
|
||||
/// module size. Just for comparison this is RAR 4.x 7 byte length signature: 0x52 0x61 0x72 0x21 0x1A 0x07 0x00.)
|
||||
/// Archive encryption header(optional)
|
||||
/// Main archive header
|
||||
/// Archive comment service header(optional)
|
||||
/// File header 1
|
||||
/// Service headers(NTFS ACL, streams, etc.) for preceding file(optional).
|
||||
/// ...
|
||||
/// File header N
|
||||
/// Service headers(NTFS ACL, streams, etc.) for preceding file(optional).
|
||||
/// Recovery record(optional).
|
||||
/// End of archive header.
|
||||
/// ----------------------------------------------
|
||||
/// General archive block format:
|
||||
///
|
||||
/// Header CRC32: uint32 (CRC32 of header data starting from Header size field and up to and including the optional extra area.)
|
||||
/// Header size: vint (Size of header data starting from Header type field and up to and including the optional extra area.
|
||||
/// This field must not be longer than 3 bytes in current implementation, resulting in 2 MB maximum header size.)
|
||||
/// Header type: vint (Type of archive header. Possible values are: )
|
||||
/// 1 Main archive header.
|
||||
/// 2 File header.
|
||||
/// 3 Service header.
|
||||
/// 4 Archive encryption header.
|
||||
/// 5 End of archive header.
|
||||
/// Header flags: vint (Flags common for all headers:)
|
||||
/// 0x0001 Extra area is present in the end of header.
|
||||
/// 0x0002 Data area is present in the end of header.
|
||||
/// 0x0004 Blocks with unknown type and this flag must be skipped when updating an archive.
|
||||
/// 0x0008 Data area is continuing from previous volume.
|
||||
/// 0x0010 Data area is continuing in next volume.
|
||||
/// 0x0020 Block depends on preceding file block.
|
||||
/// 0x0040 Preserve a child block if host block is modified.
|
||||
/// Extra area size: vint (Size of extra area. Optional field, present only if 0x0001 header flag is set.)
|
||||
/// Data size: vint (Size of data area. Optional field, present only if 0x0002 header flag is set.)
|
||||
/// ...: ... (Fields specific for current block type. See concrete block type descriptions for details)
|
||||
/// Extra data: ... (Optional area containing additional header fields, present only if 0x0001 header flag is set.)
|
||||
/// Data area: vint (Optional data area, present only if 0x0002 header flag is set. Used to store large data amounts, such as
|
||||
/// compressed file data. Not counted in Header CRC and Header size fields.
|
||||
/// ----------------------------------------------
|
||||
/// General extra area format
|
||||
///
|
||||
/// Size: vint (Size of record data starting from Type.)
|
||||
/// Type: vint (Record type. Different archive blocks have different associated extra area record types. Read the
|
||||
/// concrete archive block description for details. New record types can be added in the future, so unknown
|
||||
/// record types need to be skipped without interrupting an operation.)
|
||||
/// Data: ... (Record dependent data. May be missing if record consists only from size and type.)
|
||||
/// ----------------------------------------------
|
||||
/// Archive encryption header:
|
||||
///
|
||||
/// Header CRC32: uint32
|
||||
/// Header size: vint
|
||||
/// Header type: vint (4)
|
||||
/// Header flags: vint
|
||||
/// Encryption version: vint (Version of encryption algorithm. Now only 0 version(AES-256) is supported.)
|
||||
/// Encryption flags: vint
|
||||
/// 0x0001 Password check data is present.
|
||||
/// KDF count: 1 byte (Binary logarithm of iteration number for PBKDF2 function.RAR can refuse to process
|
||||
/// KDF count exceeding some threshold. Concrete value of threshold is a version dependent.)
|
||||
/// Salt: 16 bytes (Salt value used globally for all encrypted archive headers.)
|
||||
/// Check value: 12 bytes (Value used to verify the password validity. Present only if 0x0001 encryption
|
||||
/// flag is set.First 8 bytes are calculated using additional PBKDF2 rounds, 4 last bytes is the additional
|
||||
/// checksum. Together with the standard header CRC32 we have 64 bit checksum to reliably verify this field
|
||||
/// integrity and distinguish invalid password and damaged data. Further details can be found in UnRAR source code.)
|
||||
/// ----------------------------------------------
|
||||
/// Main archive header:
|
||||
///
|
||||
/// Header CRC32: uint32 (CRC32 of header data starting from Header size field and up to and including the optional extra area.)
|
||||
/// Header size: vint (Size of header data starting from Header type field and up to and including the optional extra area. This field must not be longer than 3 bytes in current implementation, resulting in 2 MB maximum header size.)
|
||||
/// Header type: vint (1)
|
||||
/// Header flags: vint (Flags common for all headers)
|
||||
/// Extra area size: vint (Size of extra area. Optional field, present only if 0x0001 header flag is set.)
|
||||
/// Archive flags: vint
|
||||
/// 0x0001 Volume.Archive is a part of multivolume set.
|
||||
/// 0x0002 Volume number field is present.This flag is present in all volumes except first.
|
||||
/// 0x0004 Solid archive.
|
||||
/// 0x0008 Recovery record is present.
|
||||
/// 0x0010 Locked archive.
|
||||
/// Volume number: vint (Optional field, present only if 0x0002 archive flag is set. Not present for first volume,
|
||||
/// 1 for second volume, 2 for third and so on.)
|
||||
/// Extra area: ... (Optional area containing additional header fields, present only if 0x0001 header flag is set.)
|
||||
/// [Extra area of main archive header can contain following record types
|
||||
/// Type Name Description
|
||||
/// 0x01 Locator Contains positions of different service blocks, so they can be accessed quickly, without scanning
|
||||
/// the entire archive.This record is optional.If it is missing, it is still necessary to scan the entire archive
|
||||
/// to verify presence of service blocks.]
|
||||
/// ----------------------------------------------
|
||||
/// </remarks>
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
public class CoreRarArchive : BaseArchive
|
||||
{
|
||||
// SFX Module Information
|
||||
public byte[] SFX;
|
||||
|
||||
// Standard Header Information
|
||||
public uint HeaderCRC32;
|
||||
public uint HeaderSize; // vint
|
||||
public RarHeaderFlags HeaderFlags; // vint
|
||||
public uint ExtraAreaSize; // vint
|
||||
public RarArchiveFlags ArchiveFlags; // vint
|
||||
public uint VolumeNumber; // vint
|
||||
public byte[] ExtraArea;
|
||||
|
||||
// Encryption Header Information
|
||||
public uint EncryptionHeaderCRC32;
|
||||
public uint EncryptionHeaderSize; // vint
|
||||
public RarHeaderFlags EncryptionHeaderFlags; // vint
|
||||
public uint EncryptionVersion; // vint
|
||||
public uint EncryptionFlags; // vint
|
||||
public byte KDFCount;
|
||||
public byte[] Salt = new byte[16];
|
||||
public byte[] CheckValue = new byte[12];
|
||||
|
||||
// Locator Information
|
||||
public uint LocatorSize; // vint
|
||||
public uint LocatorFlags; // vint
|
||||
public uint QuickOpenOffset; // vint
|
||||
public uint RecoveryRecordOffset; // vint
|
||||
|
||||
// Entry Information
|
||||
public List<CoreRarArchiveEntry> Entries = new List<CoreRarArchiveEntry>();
|
||||
|
||||
#region Unimplemented methods
|
||||
|
||||
public override bool CopyAll(string outDir)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string CopyToFile(string entryName, string outDir)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override (MemoryStream, string) CopyToStream(string entryName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override List<BaseFile> GetChildren(Hash omitFromScan = Hash.DeepHashes, bool date = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override List<string> GetEmptyFolders()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool IsTorrent()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool Write(List<string> inputFiles, string outDir, List<Rom> roms, bool date = false, bool romba = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class CoreRarArchiveEntry
|
||||
{
|
||||
// Standard Entry Information
|
||||
public uint HeaderCRC32;
|
||||
public uint HeaderSize; // vint
|
||||
public RarHeaderType HeaderType; // vint
|
||||
public RarHeaderFlags HeaderFlags; // vint
|
||||
public uint ExtraAreaSize; // vint
|
||||
public uint DataAreaSize; // vint
|
||||
public RarFileFlags FileFlags; // vint
|
||||
public uint UnpackedSize; // vint
|
||||
public uint Attributes; // vint
|
||||
public uint mtime;
|
||||
public uint DataCRC32;
|
||||
public uint CompressionInformation; // vint
|
||||
public uint HostOS; // vint
|
||||
public uint NameLength; // vint
|
||||
public byte[] Name;
|
||||
public byte[] DataArea;
|
||||
|
||||
// File Encryption Information
|
||||
public uint EncryptionSize; // vint
|
||||
public RarEncryptionFlags EncryptionFlags; // vint
|
||||
public byte KDFCount;
|
||||
public byte[] Salt = new byte[16];
|
||||
public byte[] IV = new byte[16];
|
||||
public byte[] CheckValue = new byte[12];
|
||||
|
||||
// File Hash Information
|
||||
public uint HashSize; // vint
|
||||
public uint HashType; // vint
|
||||
public byte[] HashData = new byte[32];
|
||||
|
||||
// File Time Information
|
||||
public uint TimeSize; // vint
|
||||
public RarTimeFlags TimeFlags; // vint
|
||||
public uint TimeMtime;
|
||||
public ulong TimeMtime64;
|
||||
public uint TimeCtime;
|
||||
public ulong TimeCtime64;
|
||||
public uint TimeLtime;
|
||||
public ulong TimeLtime64;
|
||||
|
||||
// File Version Information
|
||||
public uint VersionSize; // vint
|
||||
public const uint VersionFlags = 0; // vint
|
||||
public uint VersionNumber; // vint
|
||||
|
||||
// File System Redirection Record
|
||||
public uint RedirectionSize; // vint
|
||||
public RarRedirectionType RedirectionType; // vint
|
||||
public uint RedirectionFlags; // vint
|
||||
public uint RedirectionNameLength; // vint
|
||||
public byte[] RedirectionName;
|
||||
|
||||
// Unix Owner Record
|
||||
public uint UnixOwnerSize; // vint
|
||||
public RarUnixOwnerRecordFlags UnixOwnerFlags; // vint
|
||||
public uint UnixOwnerUserNameLength; // vint
|
||||
public byte[] UnixOwnerUserName;
|
||||
public uint UnixOwnerGroupNameLength; // vint
|
||||
public byte[] UnixOwnerGroupName;
|
||||
public uint UnixOwnerUserId; // vint
|
||||
public uint UnixOwnerGroupId; // vint
|
||||
|
||||
// Service Data Information
|
||||
public uint ServiceSize; // vint
|
||||
public byte[] ServiceData;
|
||||
}
|
||||
|
||||
// BELOW ARE CONCRETE IMPLEMENTATIONS OF HEADER DETAILS
|
||||
|
||||
/// <summary>
|
||||
/// General archive block format used by all RAR block types
|
||||
/// </summary>
|
||||
public class GeneralArchiveBlockFormat
|
||||
{
|
||||
public uint HeaderCRC32;
|
||||
public uint HeaderSize; // vint
|
||||
public HeaderType HeaderType;
|
||||
public HeaderFlags HeaderFlags;
|
||||
public ulong ExtraAreaSize; // vint
|
||||
public ulong DataAreaSize; // vint
|
||||
public byte[] ExtraArea;
|
||||
public byte[] DataArea;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General extra area format used by all RAR extra area records
|
||||
/// </summary>
|
||||
public class GeneralExtraAreaFormat
|
||||
{
|
||||
public ulong Size; // vint
|
||||
public ulong Type; // vint
|
||||
public byte[] Data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encryption header only present in encrypted archives
|
||||
///
|
||||
/// Every proceeding header is started from 16 byte AES-256
|
||||
/// initialization vectors followed by encrypted header data
|
||||
/// </summary>
|
||||
public class ArchiveEncryptionHeader : GeneralArchiveBlockFormat
|
||||
{
|
||||
public new HeaderType HeaderType = HeaderType.ArchiveEncryptionHeader;
|
||||
public ulong EncryptionVersion; // vint
|
||||
public ulong EncryptionFlags; // vint
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Types of archive header
|
||||
/// </summary>
|
||||
public enum HeaderType : ulong // vint
|
||||
{
|
||||
MainArchiveHeader = 1,
|
||||
FileHeader = 2,
|
||||
ServiceHeader = 3,
|
||||
ArchiveEncryptionHeader = 4,
|
||||
EndOfArchiveHeader = 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags common for all headers
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum HeaderFlags : ulong // vint
|
||||
{
|
||||
ExtraAreaIsPresentInEndOfHeader = 0x0001,
|
||||
DataAreaIsPresentInEndOfHeader = 0x0002,
|
||||
BlocksWithUnknownType = 0x0004, // this flag must be skipped when updating an archive
|
||||
DataAreaIsContinuingFromPreviousVolume = 0x0008,
|
||||
DataAreaIsContinuingInNextVolume = 0x0010,
|
||||
BlockDependsOnPrecedingFileBlock = 0x0020,
|
||||
PreserveChildBlockIfHostBlockIsModified = 0x0040,
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,55 @@ namespace SabreTools.Library.FileTypes
|
||||
this.Type = FileType.Folder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an folder object of the specified type, if possible
|
||||
/// </summary>
|
||||
/// <param name="outputFormat">SabreTools.Library.Data.OutputFormat representing the archive to create</param>
|
||||
/// <returns>Archive object representing the inputs</returns>
|
||||
public static Folder Create(OutputFormat outputFormat)
|
||||
{
|
||||
switch (outputFormat)
|
||||
{
|
||||
case OutputFormat.Folder:
|
||||
return new Folder();
|
||||
|
||||
case OutputFormat.TapeArchive:
|
||||
return new TapeArchive();
|
||||
|
||||
case OutputFormat.Torrent7Zip:
|
||||
return new SevenZipArchive();
|
||||
|
||||
case OutputFormat.TorrentGzip:
|
||||
case OutputFormat.TorrentGzipRomba:
|
||||
return new GZipArchive();
|
||||
|
||||
case OutputFormat.TorrentLRZip:
|
||||
return new LRZipArchive();
|
||||
|
||||
case OutputFormat.TorrentLZ4:
|
||||
return new LZ4Archive();
|
||||
|
||||
case OutputFormat.TorrentRar:
|
||||
return new RarArchive();
|
||||
|
||||
case OutputFormat.TorrentXZ:
|
||||
case OutputFormat.TorrentXZRomba:
|
||||
return new XZArchive();
|
||||
|
||||
case OutputFormat.TorrentZip:
|
||||
return new ZipArchive();
|
||||
|
||||
case OutputFormat.TorrentZPAQ:
|
||||
return new ZPAQArchive();
|
||||
|
||||
case OutputFormat.TorrentZstd:
|
||||
return new ZstdArchive();
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
@@ -129,7 +178,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
// Get all files from the input directory
|
||||
List<string> files = Utilities.RetrieveFiles(this.Filename, new List<string>());
|
||||
List<string> files = DirectoryExtensions.GetFilesOrdered(this.Filename);
|
||||
|
||||
// Now sort through to find the first file that matches
|
||||
string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault();
|
||||
@@ -168,7 +217,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(this.Filename);
|
||||
|
||||
// Get all files from the input directory
|
||||
List<string> files = Utilities.RetrieveFiles(this.Filename, new List<string>());
|
||||
List<string> files = DirectoryExtensions.GetFilesOrdered(this.Filename);
|
||||
|
||||
// Now sort through to find the first file that matches
|
||||
string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault();
|
||||
@@ -176,7 +225,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// If we had a file, copy that over to the new name
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
{
|
||||
Utilities.TryOpenRead(match).CopyTo(ms);
|
||||
FileExtensions.TryOpenRead(match).CopyTo(ms);
|
||||
realentry = match;
|
||||
}
|
||||
}
|
||||
@@ -207,7 +256,7 @@ namespace SabreTools.Library.FileTypes
|
||||
_children = new List<BaseFile>();
|
||||
foreach (string file in Directory.EnumerateFiles(this.Filename, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
BaseFile nf = Utilities.GetFileInfo(file, omitFromScan: omitFromScan, date: date);
|
||||
BaseFile nf = FileExtensions.GetInfo(file, omitFromScan: omitFromScan, date: date);
|
||||
_children.Add(nf);
|
||||
}
|
||||
|
||||
@@ -228,7 +277,7 @@ namespace SabreTools.Library.FileTypes
|
||||
/// <returns>List of empty folders in the folder</returns>
|
||||
public virtual List<string> GetEmptyFolders()
|
||||
{
|
||||
return Utilities.GetEmptyDirectories(this.Filename).ToList();
|
||||
return DirectoryExtensions.ListEmpty(this.Filename);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -247,7 +296,7 @@ namespace SabreTools.Library.FileTypes
|
||||
/// <remarks>This works for now, but it can be sped up by using Ionic.Zip or another zlib wrapper that allows for header values built-in. See edc's code.</remarks>
|
||||
public virtual bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
FileStream fs = Utilities.TryOpenRead(inputFile);
|
||||
FileStream fs = FileExtensions.TryOpenRead(inputFile);
|
||||
return Write(fs, outDir, rom, date, romba);
|
||||
}
|
||||
|
||||
@@ -281,7 +330,7 @@ namespace SabreTools.Library.FileTypes
|
||||
FileStream outputStream = null;
|
||||
|
||||
// Get the output folder name from the first rebuild rom
|
||||
string fileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName), Utilities.RemovePathUnsafeCharacters(rom.Name));
|
||||
string fileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName), Sanitizer.RemovePathUnsafeCharacters(rom.Name));
|
||||
|
||||
try
|
||||
{
|
||||
@@ -292,7 +341,7 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Overwrite output files by default
|
||||
outputStream = Utilities.TryCreate(fileName);
|
||||
outputStream = FileExtensions.TryCreate(fileName);
|
||||
|
||||
// If the output stream isn't null
|
||||
if (outputStream != null)
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
// Decompress the _filename stream
|
||||
FileStream outstream = Utilities.TryCreate(Path.Combine(outDir, Path.GetFileNameWithoutExtension(this.Filename)));
|
||||
FileStream outstream = FileExtensions.TryCreate(Path.Combine(outDir, Path.GetFileNameWithoutExtension(this.Filename)));
|
||||
var gz = new gZip();
|
||||
ZipReturn ret = gz.ZipFileOpen(this.Filename);
|
||||
ret = gz.ZipFileOpenReadStream(0, out Stream gzstream, out ulong streamSize);
|
||||
@@ -110,7 +110,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
|
||||
|
||||
// Now open and write the file if possible
|
||||
FileStream fs = Utilities.TryCreate(realEntry);
|
||||
FileStream fs = FileExtensions.TryCreate(realEntry);
|
||||
if (fs != null)
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
@@ -145,7 +145,7 @@ namespace SabreTools.Library.FileTypes
|
||||
public override (MemoryStream, string) CopyToStream(string entryName)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
string realEntry = null;
|
||||
string realEntry;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -215,11 +215,10 @@ namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
Filename = gamename,
|
||||
};
|
||||
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
|
||||
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
|
||||
br.BaseStream.Seek(-8, SeekOrigin.End);
|
||||
byte[] headercrc = br.ReadBytesReverse(4);
|
||||
tempRom.CRC = headercrc;
|
||||
tempRom.Size = br.ReadInt32Reverse();
|
||||
tempRom.CRC = br.ReadBytesBigEndian(4);
|
||||
tempRom.Size = br.ReadInt32BigEndian();
|
||||
br.Dispose();
|
||||
|
||||
_children.Add(tempRom);
|
||||
@@ -230,7 +229,7 @@ namespace SabreTools.Library.FileTypes
|
||||
var gz = new gZip();
|
||||
ZipReturn ret = gz.ZipFileOpen(this.Filename);
|
||||
ret = gz.ZipFileOpenReadStream(0, out Stream gzstream, out ulong streamSize);
|
||||
BaseFile gzipEntryRom = Utilities.GetStreamInfo(gzstream, gzstream.Length, omitFromScan);
|
||||
BaseFile gzipEntryRom = gzstream.GetInfo(omitFromScan: omitFromScan);
|
||||
gzipEntryRom.Filename = gz.Filename(0);
|
||||
gzipEntryRom.Parent = gamename;
|
||||
gzipEntryRom.Date = (date && gz.TimeStamp > 0 ? gz.TimeStamp.ToString() : null);
|
||||
@@ -267,9 +266,7 @@ namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
// Check for the file existing first
|
||||
if (!File.Exists(this.Filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string datum = Path.GetFileName(this.Filename).ToLowerInvariant();
|
||||
long filesize = new FileInfo(this.Filename).Length;
|
||||
@@ -296,15 +293,11 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Get the Romba-specific header data
|
||||
byte[] header; // Get preamble header for checking
|
||||
byte[] headermd5; // MD5
|
||||
byte[] headercrc; // CRC
|
||||
ulong headersz; // Int64 size
|
||||
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
|
||||
header = br.ReadBytes(12);
|
||||
headermd5 = br.ReadBytes(16);
|
||||
headercrc = br.ReadBytes(4);
|
||||
headersz = br.ReadUInt64();
|
||||
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
|
||||
byte[] header = br.ReadBytes(12); // Get preamble header for checking
|
||||
br.ReadBytes(16); // headermd5
|
||||
br.ReadBytes(4); // headercrc
|
||||
br.ReadUInt64(); // headersz
|
||||
br.Dispose();
|
||||
|
||||
// If the header is not correct, return a blank rom
|
||||
@@ -313,15 +306,13 @@ namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
// This is a temp fix to ignore the modification time and OS until romba can be fixed
|
||||
if (i == 4 || i == 5 || i == 6 || i == 7 || i == 9)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
correct &= (header[i] == Constants.TorrentGZHeader[i]);
|
||||
}
|
||||
|
||||
if (!correct)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -367,7 +358,7 @@ namespace SabreTools.Library.FileTypes
|
||||
byte[] headermd5; // MD5
|
||||
byte[] headercrc; // CRC
|
||||
ulong headersz; // Int64 size
|
||||
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
|
||||
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
|
||||
header = br.ReadBytes(12);
|
||||
headermd5 = br.ReadBytes(16);
|
||||
headercrc = br.ReadBytes(4);
|
||||
@@ -429,10 +420,11 @@ namespace SabreTools.Library.FileTypes
|
||||
Globals.Logger.Warning($"File '{inputFile}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
inputFile = Path.GetFullPath(inputFile);
|
||||
|
||||
// Get the file stream for the file and write out
|
||||
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date, romba);
|
||||
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date, romba);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -451,27 +443,24 @@ namespace SabreTools.Library.FileTypes
|
||||
|
||||
// If the stream is not readable, return
|
||||
if (!inputStream.CanRead)
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
// Make sure the output directory exists
|
||||
if (!Directory.Exists(outDir))
|
||||
{
|
||||
Directory.CreateDirectory(outDir);
|
||||
}
|
||||
|
||||
outDir = Path.GetFullPath(outDir);
|
||||
|
||||
// Now get the Rom info for the file so we have hashes and size
|
||||
rom = new Rom(Utilities.GetStreamInfo(inputStream, inputStream.Length, keepReadOpen: true));
|
||||
rom = new Rom(inputStream.GetInfo(keepReadOpen: true));
|
||||
|
||||
// Get the output file name
|
||||
string outfile = null;
|
||||
string outfile;
|
||||
|
||||
// If we have a romba output, add the romba path
|
||||
if (romba)
|
||||
{
|
||||
outfile = Path.Combine(outDir, Utilities.GetRombaPath(rom.SHA1)); // TODO: When updating to SHA-256, this needs to update to SHA256
|
||||
outfile = Path.Combine(outDir, PathExtensions.GetRombaPath(rom.SHA1)); // TODO: When updating to SHA-256, this needs to update to SHA256
|
||||
|
||||
// Check to see if the folder needs to be created
|
||||
if (!Directory.Exists(Path.GetDirectoryName(outfile)))
|
||||
@@ -489,7 +478,7 @@ namespace SabreTools.Library.FileTypes
|
||||
if (!File.Exists(outfile))
|
||||
{
|
||||
// Compress the input stream
|
||||
FileStream outputStream = Utilities.TryCreate(outfile);
|
||||
FileStream outputStream = FileExtensions.TryCreate(outfile);
|
||||
|
||||
// Open the output file for writing
|
||||
BinaryWriter sw = new BinaryWriter(outputStream);
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
|
||||
|
||||
// Now open and write the file if possible
|
||||
FileStream fs = Utilities.TryCreate(realEntry);
|
||||
FileStream fs = FileExtensions.TryCreate(realEntry);
|
||||
if (fs != null)
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
@@ -183,7 +183,7 @@ namespace SabreTools.Library.FileTypes
|
||||
|
||||
try
|
||||
{
|
||||
SharpCompress.Archives.Rar.RarArchive ra = SharpCompress.Archives.Rar.RarArchive.Open(Utilities.TryOpenRead(this.Filename));
|
||||
SharpCompress.Archives.Rar.RarArchive ra = SharpCompress.Archives.Rar.RarArchive.Open(FileExtensions.TryOpenRead(this.Filename));
|
||||
foreach (RarArchiveEntry entry in ra.Entries.Where(e => e != null && !e.IsDirectory))
|
||||
{
|
||||
// If secure hashes are disabled, do a quickscan
|
||||
@@ -203,7 +203,7 @@ namespace SabreTools.Library.FileTypes
|
||||
else
|
||||
{
|
||||
Stream entryStream = entry.OpenEntryStream();
|
||||
BaseFile rarEntryRom = Utilities.GetStreamInfo(entryStream, entry.Size, omitFromScan);
|
||||
BaseFile rarEntryRom = entryStream.GetInfo(entry.Size, omitFromScan);
|
||||
rarEntryRom.Filename = entry.Key;
|
||||
rarEntryRom.Parent = gamename;
|
||||
rarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
|
||||
@@ -224,223 +224,6 @@ namespace SabreTools.Library.FileTypes
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (INCOMPLETE) Retrieve file information for a RAR file
|
||||
/// </summary>
|
||||
/// TODO: Write the rest of this RAR file handling
|
||||
public void GetRarFileInfo()
|
||||
{
|
||||
if (!File.Exists(this.Filename))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
|
||||
|
||||
// Check for the signature first (Skipping the SFX Module)
|
||||
byte[] signature = br.ReadBytes(8);
|
||||
int startpos = 0;
|
||||
while (startpos < Constants.MibiByte && !signature.StartsWith(Constants.RarSignature, exact: true) && !signature.StartsWith(Constants.RarFiveSignature, exact: true))
|
||||
{
|
||||
startpos++;
|
||||
br.BaseStream.Position = startpos;
|
||||
signature = br.ReadBytes(8);
|
||||
}
|
||||
if (!signature.StartsWith(Constants.RarSignature, exact: true) && !signature.StartsWith(Constants.RarFiveSignature, exact: true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CoreRarArchive cra = new CoreRarArchive();
|
||||
if (startpos > 0)
|
||||
{
|
||||
br.BaseStream.Position = 0;
|
||||
cra.SFX = br.ReadBytes(startpos);
|
||||
}
|
||||
|
||||
// Get all archive header information
|
||||
cra.HeaderCRC32 = br.ReadUInt32();
|
||||
cra.HeaderSize = br.ReadUInt32();
|
||||
uint headerType = br.ReadUInt32();
|
||||
|
||||
// Special encryption information
|
||||
bool hasEncryptionHeader = false;
|
||||
|
||||
// If it's encrypted
|
||||
if (headerType == (uint)RarHeaderType.ArchiveEncryption)
|
||||
{
|
||||
hasEncryptionHeader = true;
|
||||
cra.EncryptionHeaderCRC32 = cra.HeaderCRC32;
|
||||
cra.EncryptionHeaderSize = cra.HeaderSize;
|
||||
cra.EncryptionHeaderFlags = (RarHeaderFlags)br.ReadUInt32();
|
||||
cra.EncryptionVersion = br.ReadUInt32();
|
||||
cra.EncryptionFlags = br.ReadUInt32();
|
||||
cra.KDFCount = br.ReadByte();
|
||||
cra.Salt = br.ReadBytes(16);
|
||||
cra.CheckValue = br.ReadBytes(12);
|
||||
|
||||
cra.HeaderCRC32 = br.ReadUInt32();
|
||||
cra.HeaderSize = br.ReadUInt32();
|
||||
headerType = br.ReadUInt32();
|
||||
}
|
||||
|
||||
cra.HeaderFlags = (RarHeaderFlags)br.ReadUInt32();
|
||||
if ((cra.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0)
|
||||
{
|
||||
cra.ExtraAreaSize = br.ReadUInt32();
|
||||
}
|
||||
cra.ArchiveFlags = (RarArchiveFlags)br.ReadUInt32();
|
||||
if ((cra.ArchiveFlags & RarArchiveFlags.VolumeNumberField) != 0)
|
||||
{
|
||||
cra.VolumeNumber = br.ReadUInt32();
|
||||
}
|
||||
if (((cra.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0) && cra.ExtraAreaSize != 0)
|
||||
{
|
||||
cra.ExtraArea = br.ReadBytes((int)cra.ExtraAreaSize);
|
||||
}
|
||||
|
||||
// Archive Comment Service Header
|
||||
|
||||
// Now for file headers
|
||||
for (; ; )
|
||||
{
|
||||
CoreRarArchiveEntry crae = new CoreRarArchiveEntry();
|
||||
crae.HeaderCRC32 = br.ReadUInt32();
|
||||
crae.HeaderSize = br.ReadUInt32();
|
||||
crae.HeaderType = (RarHeaderType)br.ReadUInt32();
|
||||
|
||||
if (crae.HeaderType == RarHeaderType.EndOfArchive)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
crae.HeaderFlags = (RarHeaderFlags)br.ReadUInt32();
|
||||
if ((crae.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0)
|
||||
{
|
||||
crae.ExtraAreaSize = br.ReadUInt32();
|
||||
}
|
||||
if ((crae.HeaderFlags & RarHeaderFlags.DataAreaPresent) != 0)
|
||||
{
|
||||
crae.DataAreaSize = br.ReadUInt32();
|
||||
}
|
||||
crae.FileFlags = (RarFileFlags)br.ReadUInt32();
|
||||
crae.UnpackedSize = br.ReadUInt32();
|
||||
if ((crae.FileFlags & RarFileFlags.UnpackedSizeUnknown) != 0)
|
||||
{
|
||||
crae.UnpackedSize = 0;
|
||||
}
|
||||
crae.Attributes = br.ReadUInt32();
|
||||
crae.mtime = br.ReadUInt32();
|
||||
crae.DataCRC32 = br.ReadUInt32();
|
||||
crae.CompressionInformation = br.ReadUInt32();
|
||||
crae.HostOS = br.ReadUInt32();
|
||||
crae.NameLength = br.ReadUInt32();
|
||||
crae.Name = br.ReadBytes((int)crae.NameLength);
|
||||
if ((crae.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0)
|
||||
{
|
||||
uint extraSize = br.ReadUInt32();
|
||||
switch (br.ReadUInt32()) // Extra Area Type
|
||||
{
|
||||
case 0x01: // File encryption information
|
||||
crae.EncryptionSize = extraSize;
|
||||
crae.EncryptionFlags = (RarEncryptionFlags)br.ReadUInt32();
|
||||
crae.KDFCount = br.ReadByte();
|
||||
crae.Salt = br.ReadBytes(16);
|
||||
crae.IV = br.ReadBytes(16);
|
||||
crae.CheckValue = br.ReadBytes(12);
|
||||
break;
|
||||
|
||||
case 0x02: // File data hash
|
||||
crae.HashSize = extraSize;
|
||||
crae.HashType = br.ReadUInt32();
|
||||
crae.HashData = br.ReadBytes(32);
|
||||
break;
|
||||
|
||||
case 0x03: // High precision file time
|
||||
crae.TimeSize = extraSize;
|
||||
crae.TimeFlags = (RarTimeFlags)br.ReadUInt32();
|
||||
if ((crae.TimeFlags & RarTimeFlags.TimeInUnixFormat) != 0)
|
||||
{
|
||||
if ((crae.TimeFlags & RarTimeFlags.ModificationTimePresent) != 0)
|
||||
{
|
||||
crae.TimeMtime64 = br.ReadUInt64();
|
||||
}
|
||||
if ((crae.TimeFlags & RarTimeFlags.CreationTimePresent) != 0)
|
||||
{
|
||||
crae.TimeCtime64 = br.ReadUInt64();
|
||||
}
|
||||
if ((crae.TimeFlags & RarTimeFlags.LastAccessTimePresent) != 0)
|
||||
{
|
||||
crae.TimeLtime64 = br.ReadUInt64();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((crae.TimeFlags & RarTimeFlags.ModificationTimePresent) != 0)
|
||||
{
|
||||
crae.TimeMtime = br.ReadUInt32();
|
||||
}
|
||||
if ((crae.TimeFlags & RarTimeFlags.CreationTimePresent) != 0)
|
||||
{
|
||||
crae.TimeCtime = br.ReadUInt32();
|
||||
}
|
||||
if ((crae.TimeFlags & RarTimeFlags.LastAccessTimePresent) != 0)
|
||||
{
|
||||
crae.TimeLtime = br.ReadUInt32();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04: // File version number
|
||||
crae.VersionSize = extraSize;
|
||||
/* crae.VersionFlags = */
|
||||
br.ReadUInt32();
|
||||
crae.VersionNumber = br.ReadUInt32();
|
||||
break;
|
||||
|
||||
case 0x05: // File system redirection
|
||||
crae.RedirectionSize = extraSize;
|
||||
crae.RedirectionType = (RarRedirectionType)br.ReadUInt32();
|
||||
crae.RedirectionFlags = br.ReadUInt32();
|
||||
crae.RedirectionNameLength = br.ReadUInt32();
|
||||
crae.RedirectionName = br.ReadBytes((int)crae.RedirectionNameLength);
|
||||
break;
|
||||
|
||||
case 0x06: // Unix owner and group information
|
||||
crae.UnixOwnerSize = extraSize;
|
||||
crae.UnixOwnerFlags = (RarUnixOwnerRecordFlags)br.ReadUInt32();
|
||||
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.UserNameStringIsPresent) != 0)
|
||||
{
|
||||
crae.UnixOwnerUserNameLength = br.ReadUInt32();
|
||||
crae.UnixOwnerUserName = br.ReadBytes((int)crae.UnixOwnerUserNameLength);
|
||||
}
|
||||
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.GroupNameStringIsPresent) != 0)
|
||||
{
|
||||
crae.UnixOwnerGroupNameLength = br.ReadUInt32();
|
||||
crae.UnixOwnerGroupName = br.ReadBytes((int)crae.UnixOwnerGroupNameLength);
|
||||
}
|
||||
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.NumericUserIdIsPresent) != 0)
|
||||
{
|
||||
crae.UnixOwnerUserId = br.ReadUInt32();
|
||||
}
|
||||
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.NumericGroupIdIsPresent) != 0)
|
||||
{
|
||||
crae.UnixOwnerGroupId = br.ReadUInt32();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x07: // Service header data array
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((crae.HeaderFlags & RarHeaderFlags.DataAreaPresent) != 0)
|
||||
{
|
||||
crae.DataArea = br.ReadBytes((int)crae.DataAreaSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of empty folders in an archive
|
||||
/// </summary>
|
||||
@@ -505,7 +288,7 @@ namespace SabreTools.Library.FileTypes
|
||||
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
// Get the file stream for the file and write out
|
||||
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date, romba);
|
||||
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date, romba);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace SabreTools.Library.FileTypes
|
||||
continue;
|
||||
}
|
||||
|
||||
FileStream writeStream = Utilities.TryCreate(Path.Combine(outDir, zf.Filename(i)));
|
||||
FileStream writeStream = FileExtensions.TryCreate(Path.Combine(outDir, zf.Filename(i)));
|
||||
|
||||
// If the stream is smaller than the buffer, just run one loop through to avoid issues
|
||||
if (streamsize < _bufferSize)
|
||||
@@ -152,7 +152,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
|
||||
|
||||
// Now open and write the file if possible
|
||||
FileStream fs = Utilities.TryCreate(realEntry);
|
||||
FileStream fs = FileExtensions.TryCreate(realEntry);
|
||||
if (fs != null)
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
@@ -314,7 +314,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// Otherwise, use the stream directly
|
||||
else
|
||||
{
|
||||
BaseFile zipEntryRom = Utilities.GetStreamInfo(readStream, (long)zf.UncompressedSize(i), omitFromScan, true);
|
||||
BaseFile zipEntryRom = readStream.GetInfo((long)zf.UncompressedSize(i), omitFromScan, true);
|
||||
zipEntryRom.Filename = zf.Filename(i);
|
||||
zipEntryRom.Parent = gamename;
|
||||
found.Add(zipEntryRom);
|
||||
@@ -417,7 +417,7 @@ namespace SabreTools.Library.FileTypes
|
||||
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
// Get the file stream for the file and write out
|
||||
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
|
||||
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date: date);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -450,7 +450,7 @@ namespace SabreTools.Library.FileTypes
|
||||
inputStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
|
||||
// Set internal variables
|
||||
Stream writeStream = null;
|
||||
@@ -615,7 +615,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
FileExtensions.TryDelete(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
@@ -658,7 +658,7 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
|
||||
// Set internal variables
|
||||
Stream writeStream = null;
|
||||
@@ -697,7 +697,7 @@ namespace SabreTools.Library.FileTypes
|
||||
int index = inputIndexMap[key];
|
||||
|
||||
// Open the input file for reading
|
||||
Stream freadStream = Utilities.TryOpenRead(inputFiles[index]);
|
||||
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[index]);
|
||||
ulong istreamSize = (ulong)(new FileInfo(inputFiles[index]).Length);
|
||||
|
||||
DateTime dt = DateTime.Now;
|
||||
@@ -780,7 +780,7 @@ namespace SabreTools.Library.FileTypes
|
||||
if (index < 0)
|
||||
{
|
||||
// Open the input file for reading
|
||||
Stream freadStream = Utilities.TryOpenRead(inputFiles[-index - 1]);
|
||||
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[-index - 1]);
|
||||
ulong istreamSize = (ulong)(new FileInfo(inputFiles[-index - 1]).Length);
|
||||
|
||||
DateTime dt = DateTime.Now;
|
||||
@@ -842,7 +842,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
FileExtensions.TryDelete(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
|
||||
|
||||
// Now open and write the file if possible
|
||||
FileStream fs = Utilities.TryCreate(realEntry);
|
||||
FileStream fs = FileExtensions.TryCreate(realEntry);
|
||||
if (fs != null)
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
@@ -187,7 +187,7 @@ namespace SabreTools.Library.FileTypes
|
||||
|
||||
try
|
||||
{
|
||||
TarArchive ta = TarArchive.Open(Utilities.TryOpenRead(this.Filename));
|
||||
TarArchive ta = TarArchive.Open(FileExtensions.TryOpenRead(this.Filename));
|
||||
foreach (TarArchiveEntry entry in ta.Entries.Where(e => e != null && !e.IsDirectory))
|
||||
{
|
||||
// If secure hashes are disabled, do a quickscan
|
||||
@@ -207,7 +207,7 @@ namespace SabreTools.Library.FileTypes
|
||||
else
|
||||
{
|
||||
Stream entryStream = entry.OpenEntryStream();
|
||||
BaseFile tarEntryRom = Utilities.GetStreamInfo(entryStream, entry.Size, omitFromScan);
|
||||
BaseFile tarEntryRom = entryStream.GetInfo(entry.Size, omitFromScan);
|
||||
tarEntryRom.Filename = entry.Key;
|
||||
tarEntryRom.Parent = gamename;
|
||||
tarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
|
||||
@@ -292,7 +292,7 @@ namespace SabreTools.Library.FileTypes
|
||||
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
// Get the file stream for the file and write out
|
||||
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
|
||||
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date: date);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -322,7 +322,7 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
|
||||
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
|
||||
|
||||
// Set internal variables
|
||||
TarArchive oldTarFile = TarArchive.Create();
|
||||
@@ -441,7 +441,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
FileExtensions.TryDelete(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
@@ -484,7 +484,7 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
|
||||
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
|
||||
|
||||
// Set internal variables
|
||||
TarArchive oldTarFile = TarArchive.Create();
|
||||
@@ -526,7 +526,7 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Copy the input stream to the output
|
||||
tarFile.AddEntry(roms[index].Name, Utilities.TryOpenRead(inputFiles[index]), size: roms[index].Size, modified: usableDate);
|
||||
tarFile.AddEntry(roms[index].Name, FileExtensions.TryOpenRead(inputFiles[index]), size: roms[index].Size, modified: usableDate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Copy the input file to the output
|
||||
tarFile.AddEntry(roms[-index - 1].Name, Utilities.TryOpenRead(inputFiles[-index - 1]), size: roms[-index - 1].Size, modified: usableDate);
|
||||
tarFile.AddEntry(roms[-index - 1].Name, FileExtensions.TryOpenRead(inputFiles[-index - 1]), size: roms[-index - 1].Size, modified: usableDate);
|
||||
}
|
||||
|
||||
// Otherwise, copy the file from the old archive
|
||||
@@ -622,7 +622,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
FileExtensions.TryDelete(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatItems;
|
||||
using SabreTools.Library.Tools;
|
||||
using Compress.ZipFile;
|
||||
using SevenZip;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
|
||||
namespace SabreTools.Library.FileTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a TorrentXZ archive for reading and writing
|
||||
/// </summary>
|
||||
/// TODO: Wait for XZ write to be enabled by SevenZipSharp library
|
||||
public class XZArchive : BaseArchive
|
||||
{
|
||||
#region Constructors
|
||||
@@ -61,14 +56,16 @@ namespace SabreTools.Library.FileTypes
|
||||
// Create the temp directory
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
// Extract all files to the temp directory
|
||||
SharpCompress.Archives.SevenZip.SevenZipArchive sza = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(Utilities.TryOpenRead(this.Filename));
|
||||
foreach (SevenZipArchiveEntry entry in sza.Entries)
|
||||
{
|
||||
entry.WriteToDirectory(outDir, new SharpCompress.Common.ExtractionOptions { PreserveFileTime = true, ExtractFullPath = true, Overwrite = true });
|
||||
}
|
||||
// Decompress the _filename stream
|
||||
FileStream outstream = FileExtensions.TryCreate(Path.Combine(outDir, Path.GetFileNameWithoutExtension(this.Filename)));
|
||||
var xz = new XZStream(File.OpenRead(this.Filename));
|
||||
xz.CopyTo(outstream);
|
||||
|
||||
// Dispose of the streams
|
||||
outstream.Dispose();
|
||||
xz.Dispose();
|
||||
|
||||
encounteredErrors = false;
|
||||
sza.Dispose();
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
@@ -107,7 +104,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
|
||||
|
||||
// Now open and write the file if possible
|
||||
FileStream fs = Utilities.TryCreate(realEntry);
|
||||
FileStream fs = FileExtensions.TryCreate(realEntry);
|
||||
if (fs != null)
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
@@ -142,22 +139,26 @@ namespace SabreTools.Library.FileTypes
|
||||
public override (MemoryStream, string) CopyToStream(string entryName)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
string realEntry = null;
|
||||
string realEntry;
|
||||
|
||||
try
|
||||
{
|
||||
SharpCompress.Archives.SevenZip.SevenZipArchive sza = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(this.Filename, new ReaderOptions { LeaveStreamOpen = false, });
|
||||
foreach (SevenZipArchiveEntry entry in sza.Entries)
|
||||
// Decompress the _filename stream
|
||||
realEntry = Path.GetFileNameWithoutExtension(this.Filename);
|
||||
var xz = new XZStream(File.OpenRead(this.Filename));
|
||||
|
||||
// Write the file out
|
||||
byte[] xbuffer = new byte[_bufferSize];
|
||||
int xlen;
|
||||
while ((xlen = xz.Read(xbuffer, 0, _bufferSize)) > 0)
|
||||
{
|
||||
if (entry != null && !entry.IsDirectory && entry.Key.Contains(entryName))
|
||||
{
|
||||
// Write the file out
|
||||
realEntry = entry.Key;
|
||||
entry.WriteTo(ms);
|
||||
break;
|
||||
}
|
||||
|
||||
ms.Write(xbuffer, 0, xlen);
|
||||
ms.Flush();
|
||||
}
|
||||
sza.Dispose();
|
||||
|
||||
// Dispose of the streams
|
||||
xz.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -182,7 +183,58 @@ namespace SabreTools.Library.FileTypes
|
||||
/// <remarks>TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually</remarks>
|
||||
public override List<BaseFile> GetChildren(Hash omitFromScan = Hash.DeepHashes, bool date = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (_children == null || _children.Count == 0)
|
||||
{
|
||||
_children = new List<BaseFile>();
|
||||
|
||||
string gamename = Path.GetFileNameWithoutExtension(this.Filename);
|
||||
|
||||
BaseFile possibleTxz = GetTorrentXZFileInfo();
|
||||
|
||||
// If it was, then add it to the outputs and continue
|
||||
if (possibleTxz != null && possibleTxz.Filename != null)
|
||||
{
|
||||
_children.Add(possibleTxz);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// If secure hashes are disabled, do a quickscan
|
||||
if (omitFromScan == Hash.SecureHashes)
|
||||
{
|
||||
BaseFile tempRom = new BaseFile()
|
||||
{
|
||||
Filename = gamename,
|
||||
};
|
||||
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
|
||||
br.BaseStream.Seek(-8, SeekOrigin.End);
|
||||
tempRom.CRC = br.ReadBytesBigEndian(4);
|
||||
tempRom.Size = br.ReadInt32BigEndian();
|
||||
br.Dispose();
|
||||
|
||||
_children.Add(tempRom);
|
||||
}
|
||||
// Otherwise, use the stream directly
|
||||
else
|
||||
{
|
||||
var xzStream = new XZStream(File.OpenRead(this.Filename));
|
||||
BaseFile xzEntryRom = xzStream.GetInfo(omitFromScan: omitFromScan);
|
||||
xzEntryRom.Filename = gamename;
|
||||
xzEntryRom.Parent = gamename;
|
||||
_children.Add(xzEntryRom);
|
||||
xzStream.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Globals.Logger.Error(ex.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _children;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,7 +244,8 @@ namespace SabreTools.Library.FileTypes
|
||||
/// <returns>List of empty folders in the archive</returns>
|
||||
public override List<string> GetEmptyFolders()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// XZ files don't contain directories
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -200,7 +253,50 @@ namespace SabreTools.Library.FileTypes
|
||||
/// </summary>
|
||||
public override bool IsTorrent()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Check for the file existing first
|
||||
if (!File.Exists(this.Filename))
|
||||
return false;
|
||||
|
||||
string datum = Path.GetFileName(this.Filename).ToLowerInvariant();
|
||||
|
||||
// Check if the name is the right length
|
||||
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz")) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length
|
||||
{
|
||||
Globals.Logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(this.Filename)}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single torrent XZ file
|
||||
/// </summary>
|
||||
/// <returns>Populated DatItem object if success, empty one on error</returns>
|
||||
public BaseFile GetTorrentXZFileInfo()
|
||||
{
|
||||
// Check for the file existing first
|
||||
if (!File.Exists(this.Filename))
|
||||
return null;
|
||||
|
||||
string datum = Path.GetFileName(this.Filename).ToLowerInvariant();
|
||||
|
||||
// Check if the name is the right length
|
||||
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz")) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length
|
||||
{
|
||||
Globals.Logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(this.Filename)}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
BaseFile baseFile = new BaseFile
|
||||
{
|
||||
Filename = Path.GetFileNameWithoutExtension(this.Filename).ToLowerInvariant(),
|
||||
SHA1 = Utilities.StringToByteArray(Path.GetFileNameWithoutExtension(this.Filename)), // TODO: When updating to SHA-256, this needs to update to SHA256
|
||||
|
||||
Parent = Path.GetFileNameWithoutExtension(this.Filename).ToLowerInvariant(),
|
||||
};
|
||||
|
||||
return baseFile;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -219,8 +315,17 @@ namespace SabreTools.Library.FileTypes
|
||||
/// <remarks>This works for now, but it can be sped up by using Ionic.Zip or another zlib wrapper that allows for header values built-in. See edc's code.</remarks>
|
||||
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
// Check that the input file exists
|
||||
if (!File.Exists(inputFile))
|
||||
{
|
||||
Globals.Logger.Warning($"File '{inputFile}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
inputFile = Path.GetFullPath(inputFile);
|
||||
|
||||
// Get the file stream for the file and write out
|
||||
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
|
||||
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date, romba);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -235,185 +340,48 @@ namespace SabreTools.Library.FileTypes
|
||||
public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
bool success = false;
|
||||
string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}");
|
||||
|
||||
// If either input is null or empty, return
|
||||
if (inputStream == null || rom == null || rom.Name == null)
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
// If the stream is not readable, return
|
||||
if (!inputStream.CanRead)
|
||||
{
|
||||
return success;
|
||||
|
||||
// Make sure the output directory exists
|
||||
if (!Directory.Exists(outDir))
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
outDir = Path.GetFullPath(outDir);
|
||||
|
||||
// Now get the Rom info for the file so we have hashes and size
|
||||
rom = new Rom(inputStream.GetInfo(keepReadOpen: true));
|
||||
|
||||
// Get the output file name
|
||||
string outfile;
|
||||
|
||||
// If we have a romba output, add the romba path
|
||||
if (romba)
|
||||
{
|
||||
outfile = Path.Combine(outDir, PathExtensions.GetRombaPath(rom.SHA1)); // TODO: When updating to SHA-256, this needs to update to SHA256
|
||||
|
||||
// Check to see if the folder needs to be created
|
||||
if (!Directory.Exists(Path.GetDirectoryName(outfile)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outfile));
|
||||
}
|
||||
// Otherwise, we're just rebuilding to the main directory
|
||||
else
|
||||
{
|
||||
outfile = Path.Combine(outDir, rom.SHA1 + ".xz"); // TODO: When updating to SHA-256, this needs to update to SHA256
|
||||
}
|
||||
|
||||
// Seek to the beginning of the stream
|
||||
inputStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".xz") ? string.Empty : ".xz"));
|
||||
|
||||
// Set internal variables
|
||||
SevenZipBase.SetLibraryPath("7za.dll");
|
||||
SevenZipExtractor oldZipFile = null;
|
||||
SevenZipCompressor zipFile;
|
||||
|
||||
try
|
||||
// If the output file exists, don't try to write again
|
||||
if (!File.Exists(outfile))
|
||||
{
|
||||
// If the full output path doesn't exist, create it
|
||||
if (!Directory.Exists(Path.GetDirectoryName(tempFile)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
|
||||
}
|
||||
// Compress the input stream
|
||||
XZStream outputStream = new XZStream(FileExtensions.TryCreate(outfile));
|
||||
inputStream.CopyTo(outputStream);
|
||||
|
||||
// If the archive doesn't exist, create it and put the single file
|
||||
if (!File.Exists(archiveFileName))
|
||||
{
|
||||
zipFile = new SevenZipCompressor()
|
||||
{
|
||||
ArchiveFormat = OutArchiveFormat.XZ,
|
||||
CompressionLevel = CompressionLevel.Normal,
|
||||
};
|
||||
|
||||
// Create the temp directory
|
||||
string tempPath = Path.Combine(outDir, Guid.NewGuid().ToString());
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
// Create a stream dictionary
|
||||
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
|
||||
dict.Add(rom.Name, inputStream);
|
||||
|
||||
// Now add the stream
|
||||
zipFile.CompressStreamDictionary(dict, tempFile);
|
||||
}
|
||||
|
||||
// Otherwise, sort the input files and write out in the correct order
|
||||
else
|
||||
{
|
||||
// Open the old archive for reading
|
||||
using (oldZipFile = new SevenZipExtractor(archiveFileName))
|
||||
{
|
||||
// Map all inputs to index
|
||||
Dictionary<string, int> inputIndexMap = new Dictionary<string, int>();
|
||||
|
||||
// If the old one doesn't contain the new file, then add it
|
||||
if (!oldZipFile.ArchiveFileNames.Contains(rom.Name.Replace('\\', '/')))
|
||||
{
|
||||
inputIndexMap.Add(rom.Name.Replace('\\', '/'), -1);
|
||||
}
|
||||
|
||||
// Then add all of the old entries to it too
|
||||
for (int i = 0; i < oldZipFile.FilesCount; i++)
|
||||
{
|
||||
inputIndexMap.Add(oldZipFile.ArchiveFileNames[i], i);
|
||||
}
|
||||
|
||||
// If the number of entries is the same as the old archive, skip out
|
||||
if (inputIndexMap.Keys.Count <= oldZipFile.FilesCount)
|
||||
{
|
||||
success = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
// Otherwise, process the old zipfile
|
||||
zipFile = new SevenZipCompressor()
|
||||
{
|
||||
ArchiveFormat = OutArchiveFormat.XZ,
|
||||
CompressionLevel = CompressionLevel.Normal,
|
||||
};
|
||||
|
||||
// Get the order for the entries with the new file
|
||||
List<string> keys = inputIndexMap.Keys.ToList();
|
||||
keys.Sort(ZipFile.TrrntZipStringCompare);
|
||||
|
||||
// Copy over all files to the new archive
|
||||
foreach (string key in keys)
|
||||
{
|
||||
// Get the index mapped to the key
|
||||
int index = inputIndexMap[key];
|
||||
|
||||
// If we have the input file, add it now
|
||||
if (index < 0)
|
||||
{
|
||||
// Create a stream dictionary
|
||||
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
|
||||
dict.Add(rom.Name, inputStream);
|
||||
|
||||
// Now add the stream
|
||||
zipFile.CompressStreamDictionary(dict, tempFile);
|
||||
}
|
||||
|
||||
// Otherwise, copy the file from the old archive
|
||||
else
|
||||
{
|
||||
Stream oldZipFileEntryStream = new MemoryStream();
|
||||
oldZipFile.ExtractFile(index, oldZipFileEntryStream);
|
||||
oldZipFileEntryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Create a stream dictionary
|
||||
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
|
||||
dict.Add(oldZipFile.ArchiveFileNames[index], oldZipFileEntryStream);
|
||||
|
||||
// Now add the stream
|
||||
zipFile.CompressStreamDictionary(dict, tempFile);
|
||||
oldZipFileEntryStream.Dispose();
|
||||
}
|
||||
|
||||
// After the first file, make sure we're in append mode
|
||||
zipFile.CompressionMode = CompressionMode.Append;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
success = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
inputStream?.Dispose();
|
||||
}
|
||||
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
// Now make the file T7Z
|
||||
// TODO: Add ACTUAL T7Z compatible code
|
||||
|
||||
BinaryWriter bw = new BinaryWriter(Utilities.TryOpenReadWrite(archiveFileName));
|
||||
bw.Seek(0, SeekOrigin.Begin);
|
||||
bw.Write(Constants.Torrent7ZipHeader);
|
||||
bw.Seek(0, SeekOrigin.End);
|
||||
|
||||
using (oldZipFile = new SevenZipExtractor(Utilities.TryOpenReadWrite(archiveFileName)))
|
||||
{
|
||||
|
||||
// Get the correct signature to use (Default 0, Unicode 1, SingleFile 2, StripFileNames 4)
|
||||
byte[] tempsig = Constants.Torrent7ZipSignature;
|
||||
if (oldZipFile.FilesCount > 1)
|
||||
{
|
||||
tempsig[16] = 0x2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempsig[16] = 0;
|
||||
}
|
||||
|
||||
bw.Write(tempsig);
|
||||
bw.Flush();
|
||||
bw.Dispose();
|
||||
// Dispose of everything
|
||||
outputStream.Dispose();
|
||||
inputStream.Dispose();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -430,216 +398,7 @@ namespace SabreTools.Library.FileTypes
|
||||
/// <returns>True if the archive was written properly, false otherwise</returns>
|
||||
public override bool Write(List<string> inputFiles, string outDir, List<Rom> roms, bool date = false, bool romba = false)
|
||||
{
|
||||
bool success = false;
|
||||
string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}");
|
||||
|
||||
// If either list of roms is null or empty, return
|
||||
if (inputFiles == null || roms == null || inputFiles.Count == 0 || roms.Count == 0)
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
// If the number of inputs is less than the number of available roms, return
|
||||
if (inputFiles.Count < roms.Count)
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
// If one of the files doesn't exist, return
|
||||
foreach (string file in inputFiles)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".xz") ? string.Empty : ".xz"));
|
||||
|
||||
// Set internal variables
|
||||
SevenZipBase.SetLibraryPath("7za.dll");
|
||||
SevenZipExtractor oldZipFile;
|
||||
SevenZipCompressor zipFile;
|
||||
|
||||
try
|
||||
{
|
||||
// If the full output path doesn't exist, create it
|
||||
if (!Directory.Exists(Path.GetDirectoryName(archiveFileName)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(archiveFileName));
|
||||
}
|
||||
|
||||
// If the archive doesn't exist, create it and put the single file
|
||||
if (!File.Exists(archiveFileName))
|
||||
{
|
||||
zipFile = new SevenZipCompressor()
|
||||
{
|
||||
ArchiveFormat = OutArchiveFormat.XZ,
|
||||
CompressionLevel = CompressionLevel.Normal,
|
||||
};
|
||||
|
||||
// Map all inputs to index
|
||||
Dictionary<string, int> inputIndexMap = new Dictionary<string, int>();
|
||||
for (int i = 0; i < inputFiles.Count; i++)
|
||||
{
|
||||
inputIndexMap.Add(roms[i].Name.Replace('\\', '/'), i);
|
||||
}
|
||||
|
||||
// Sort the keys in TZIP order
|
||||
List<string> keys = inputIndexMap.Keys.ToList();
|
||||
keys.Sort(ZipFile.TrrntZipStringCompare);
|
||||
|
||||
// Create the temp directory
|
||||
string tempPath = Path.Combine(outDir, Guid.NewGuid().ToString());
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
// Now add all of the files in order
|
||||
foreach (string key in keys)
|
||||
{
|
||||
string newkey = Path.Combine(tempPath, key);
|
||||
|
||||
File.Move(inputFiles[inputIndexMap[key]], newkey);
|
||||
zipFile.CompressFiles(tempFile, newkey);
|
||||
File.Move(newkey, inputFiles[inputIndexMap[key]]);
|
||||
|
||||
// After the first file, make sure we're in append mode
|
||||
zipFile.CompressionMode = CompressionMode.Append;
|
||||
}
|
||||
|
||||
Utilities.CleanDirectory(tempPath);
|
||||
Utilities.TryDeleteDirectory(tempPath);
|
||||
}
|
||||
|
||||
// Otherwise, sort the input files and write out in the correct order
|
||||
else
|
||||
{
|
||||
// Open the old archive for reading
|
||||
using (oldZipFile = new SevenZipExtractor(archiveFileName))
|
||||
{
|
||||
// Map all inputs to index
|
||||
Dictionary<string, int> inputIndexMap = new Dictionary<string, int>();
|
||||
for (int i = 0; i < inputFiles.Count; i++)
|
||||
{
|
||||
// If the old one contains the new file, then just skip out
|
||||
if (oldZipFile.ArchiveFileNames.Contains(roms[i].Name.Replace('\\', '/')))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
inputIndexMap.Add(roms[i].Name.Replace('\\', '/'), -(i + 1));
|
||||
}
|
||||
|
||||
// Then add all of the old entries to it too
|
||||
for (int i = 0; i < oldZipFile.FilesCount; i++)
|
||||
{
|
||||
inputIndexMap.Add(oldZipFile.ArchiveFileNames[i], i);
|
||||
}
|
||||
|
||||
// If the number of entries is the same as the old archive, skip out
|
||||
if (inputIndexMap.Keys.Count <= oldZipFile.FilesCount)
|
||||
{
|
||||
success = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
// Otherwise, process the old zipfile
|
||||
zipFile = new SevenZipCompressor()
|
||||
{
|
||||
ArchiveFormat = OutArchiveFormat.XZ,
|
||||
CompressionLevel = CompressionLevel.Normal,
|
||||
};
|
||||
|
||||
// Get the order for the entries with the new file
|
||||
List<string> keys = inputIndexMap.Keys.ToList();
|
||||
keys.Sort(ZipFile.TrrntZipStringCompare);
|
||||
|
||||
// Copy over all files to the new archive
|
||||
foreach (string key in keys)
|
||||
{
|
||||
// Get the index mapped to the key
|
||||
int index = inputIndexMap[key];
|
||||
|
||||
// If we have the input file, add it now
|
||||
if (index < 0)
|
||||
{
|
||||
FileStream inputStream = Utilities.TryOpenRead(inputFiles[-index - 1]);
|
||||
|
||||
// Create a stream dictionary
|
||||
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
|
||||
dict.Add(key, inputStream);
|
||||
|
||||
// Now add the stream
|
||||
zipFile.CompressStreamDictionary(dict, tempFile);
|
||||
}
|
||||
|
||||
// Otherwise, copy the file from the old archive
|
||||
else
|
||||
{
|
||||
Stream oldZipFileEntryStream = new MemoryStream();
|
||||
oldZipFile.ExtractFile(index, oldZipFileEntryStream);
|
||||
oldZipFileEntryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Create a stream dictionary
|
||||
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
|
||||
dict.Add(oldZipFile.ArchiveFileNames[index], oldZipFileEntryStream);
|
||||
|
||||
// Now add the stream
|
||||
zipFile.CompressStreamDictionary(dict, tempFile);
|
||||
oldZipFileEntryStream.Dispose();
|
||||
}
|
||||
|
||||
// After the first file, make sure we're in append mode
|
||||
zipFile.CompressionMode = CompressionMode.Append;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
success = false;
|
||||
}
|
||||
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
// Now make the file T7Z
|
||||
// TODO: Add ACTUAL T7Z compatible code
|
||||
|
||||
BinaryWriter bw = new BinaryWriter(Utilities.TryOpenReadWrite(archiveFileName));
|
||||
bw.Seek(0, SeekOrigin.Begin);
|
||||
bw.Write(Constants.Torrent7ZipHeader);
|
||||
bw.Seek(0, SeekOrigin.End);
|
||||
|
||||
using (oldZipFile = new SevenZipExtractor(Utilities.TryOpenReadWrite(archiveFileName)))
|
||||
{
|
||||
// Get the correct signature to use (Default 0, Unicode 1, SingleFile 2, StripFileNames 4)
|
||||
byte[] tempsig = Constants.Torrent7ZipSignature;
|
||||
if (oldZipFile.FilesCount > 1)
|
||||
{
|
||||
tempsig[16] = 0x2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempsig[16] = 0;
|
||||
}
|
||||
|
||||
bw.Write(tempsig);
|
||||
bw.Flush();
|
||||
bw.Dispose();
|
||||
}
|
||||
|
||||
return true;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace SabreTools.Library.FileTypes
|
||||
continue;
|
||||
}
|
||||
|
||||
FileStream writeStream = Utilities.TryCreate(Path.Combine(outDir, zf.Filename(i)));
|
||||
FileStream writeStream = FileExtensions.TryCreate(Path.Combine(outDir, zf.Filename(i)));
|
||||
|
||||
// If the stream is smaller than the buffer, just run one loop through to avoid issues
|
||||
if (streamsize < _bufferSize)
|
||||
@@ -153,7 +153,7 @@ namespace SabreTools.Library.FileTypes
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
|
||||
|
||||
// Now open and write the file if possible
|
||||
FileStream fs = Utilities.TryCreate(realEntry);
|
||||
FileStream fs = FileExtensions.TryCreate(realEntry);
|
||||
if (fs != null)
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
@@ -317,7 +317,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// Otherwise, use the stream directly
|
||||
else
|
||||
{
|
||||
BaseFile zipEntryRom = Utilities.GetStreamInfo(readStream, (long)zf.UncompressedSize(i), omitFromScan, true);
|
||||
BaseFile zipEntryRom = readStream.GetInfo((long)zf.UncompressedSize(i), omitFromScan, true);
|
||||
zipEntryRom.Filename = zf.Filename(i);
|
||||
zipEntryRom.Parent = gamename;
|
||||
string convertedDate = zf.LastModified(i).ToString("yyyy/MM/dd hh:mm:ss");
|
||||
@@ -422,7 +422,7 @@ namespace SabreTools.Library.FileTypes
|
||||
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
|
||||
{
|
||||
// Get the file stream for the file and write out
|
||||
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
|
||||
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date: date);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -455,7 +455,7 @@ namespace SabreTools.Library.FileTypes
|
||||
inputStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
|
||||
// Set internal variables
|
||||
Stream writeStream = null;
|
||||
@@ -621,7 +621,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
FileExtensions.TryDelete(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
@@ -664,7 +664,7 @@ namespace SabreTools.Library.FileTypes
|
||||
}
|
||||
|
||||
// Get the output archive name from the first rebuild rom
|
||||
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
|
||||
|
||||
// Set internal variables
|
||||
Stream writeStream = null;
|
||||
@@ -703,7 +703,7 @@ namespace SabreTools.Library.FileTypes
|
||||
int index = inputIndexMap[key];
|
||||
|
||||
// Open the input file for reading
|
||||
Stream freadStream = Utilities.TryOpenRead(inputFiles[index]);
|
||||
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[index]);
|
||||
ulong istreamSize = (ulong)(new FileInfo(inputFiles[index]).Length);
|
||||
|
||||
DateTime dt = DateTime.Now;
|
||||
@@ -786,7 +786,7 @@ namespace SabreTools.Library.FileTypes
|
||||
if (index < 0)
|
||||
{
|
||||
// Open the input file for reading
|
||||
Stream freadStream = Utilities.TryOpenRead(inputFiles[-index - 1]);
|
||||
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[-index - 1]);
|
||||
ulong istreamSize = (ulong)(new FileInfo(inputFiles[-index - 1]).Length);
|
||||
|
||||
DateTime dt = DateTime.Now;
|
||||
@@ -849,7 +849,7 @@ namespace SabreTools.Library.FileTypes
|
||||
// If the old file exists, delete it and replace
|
||||
if (File.Exists(archiveFileName))
|
||||
{
|
||||
Utilities.TryDeleteFile(archiveFileName);
|
||||
FileExtensions.TryDelete(archiveFileName);
|
||||
}
|
||||
File.Move(tempFile, archiveFileName);
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using SabreTools.Library.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
|
||||
namespace SabreTools.Library.Help
|
||||
{
|
||||
public class Feature
|
||||
@@ -42,8 +41,10 @@ namespace SabreTools.Library.Help
|
||||
public Feature(string name, string flag, string description, FeatureType featureType, string longDescription = null)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Flags = new List<string>();
|
||||
this.Flags.Add(flag);
|
||||
this.Flags = new List<string>
|
||||
{
|
||||
flag
|
||||
};
|
||||
this.Description = description;
|
||||
this.LongDescription = longDescription;
|
||||
this._featureType = featureType;
|
||||
@@ -91,7 +92,7 @@ namespace SabreTools.Library.Help
|
||||
if (this.Features == null)
|
||||
this.Features = new Dictionary<string, Feature>();
|
||||
|
||||
lock(this.Features)
|
||||
lock (this.Features)
|
||||
{
|
||||
this.Features[feature.Name] = feature;
|
||||
}
|
||||
@@ -239,7 +240,11 @@ namespace SabreTools.Library.Help
|
||||
output = CreatePadding(pre + 4);
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
output += subsplit[subsplit.Length - 1];
|
||||
#else
|
||||
output += subsplit[^1];
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -272,13 +277,7 @@ namespace SabreTools.Library.Help
|
||||
/// <returns>String with requested number of blank spaces</returns>
|
||||
private string CreatePadding(int spaces)
|
||||
{
|
||||
string blank = string.Empty;
|
||||
for (int i = 0; i < spaces; i++)
|
||||
{
|
||||
blank += " ";
|
||||
}
|
||||
|
||||
return blank;
|
||||
return string.Empty.PadRight(spaces);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -379,7 +378,11 @@ namespace SabreTools.Library.Help
|
||||
output = CreatePadding(preAdjusted + 4);
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
output += subsplit[subsplit.Length - 1];
|
||||
#else
|
||||
output += subsplit[^1];
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -527,7 +530,7 @@ namespace SabreTools.Library.Help
|
||||
if (_featureType != FeatureType.Flag)
|
||||
throw new ArgumentException("Feature is not a flag");
|
||||
|
||||
return (_value as bool?).HasValue ? (_value as bool?).Value : false;
|
||||
return (_value as bool?) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -549,7 +552,7 @@ namespace SabreTools.Library.Help
|
||||
if (_featureType != FeatureType.Int32)
|
||||
throw new ArgumentException("Feature is not an int");
|
||||
|
||||
return (_value as int?).HasValue ? (_value as int?).Value : int.MinValue;
|
||||
return (_value as int?) ?? int.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -560,7 +563,7 @@ namespace SabreTools.Library.Help
|
||||
if (_featureType != FeatureType.Int64)
|
||||
throw new ArgumentException("Feature is not a long");
|
||||
|
||||
return (_value as long?).HasValue ? (_value as long?).Value : long.MinValue;
|
||||
return (_value as long?) ?? long.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -580,6 +583,7 @@ namespace SabreTools.Library.Help
|
||||
/// <returns>True if the feature is enabled, false otherwise</returns>
|
||||
public bool IsEnabled()
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (_featureType)
|
||||
{
|
||||
case FeatureType.Flag:
|
||||
@@ -592,9 +596,20 @@ namespace SabreTools.Library.Help
|
||||
return (_value as long?).HasValue && (_value as long?).Value != long.MinValue;
|
||||
case FeatureType.List:
|
||||
return (_value as List<string>) != null;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return _featureType switch
|
||||
{
|
||||
FeatureType.Flag => (_value as bool?) == true,
|
||||
FeatureType.String => (_value as string) != null,
|
||||
FeatureType.Int32 => (_value as int?).HasValue && (_value as int?).Value != int.MinValue,
|
||||
FeatureType.Int64 => (_value as long?).HasValue && (_value as long?).Value != long.MinValue,
|
||||
FeatureType.List => (_value as List<string>) != null,
|
||||
_ => false,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -179,6 +179,7 @@ Options:
|
||||
-nr160, --skip-ripemd160 Don't include RIPEMD160 in output
|
||||
This allows the user to skip calculating the RIPEMD160 for each of
|
||||
the files which will speed up the creation of the DAT.
|
||||
.NET Framework 4.8 only.
|
||||
|
||||
-ns, --skip-sha1 Don't include SHA-1 in output
|
||||
This allows the user to skip calculating the SHA-1 for each of the
|
||||
@@ -227,7 +228,7 @@ Options:
|
||||
msx, openmsx - openMSX Software List
|
||||
ol, offlinelist - OfflineList XML
|
||||
rc, romcenter - RomCenter
|
||||
ripemd160 - RIPEMD160
|
||||
ripemd160 - RIPEMD160 (.NET Framework 4.8 only)
|
||||
sd, sabredat - SabreDat XML
|
||||
sfv - SFV
|
||||
sha1 - SHA1
|
||||
@@ -423,13 +424,13 @@ Options:
|
||||
Include only items with this RIPEMD160 hash in the output.
|
||||
Additionally, the user can specify an exact match or full C#-style
|
||||
regex for pattern matching. Multiple instances of this flag are
|
||||
allowed.
|
||||
allowed. .NET Framework 4.8 only.
|
||||
|
||||
-nripemd160=, --not-ripemd160= Filter by not RIPEMD160 hash
|
||||
Include only items without this RIPEMD160 hash in the output.
|
||||
Additionally, the user can specify an exact match or full C#-style
|
||||
regex for pattern matching. Multiple instances of this flag are
|
||||
allowed.
|
||||
allowed. .NET Framework 4.8 only
|
||||
|
||||
-sha1=, --sha1= Filter by SHA-1 hash
|
||||
Include only items with this SHA-1 hash in the output. Additionally,
|
||||
@@ -660,12 +661,11 @@ Options:
|
||||
format but with custom header information. This is currently unused
|
||||
by any major application.
|
||||
|
||||
-txz, --torrent-xz Enable Torrent XZ output [UNSUPPORTED]
|
||||
-txz, --torrent-xz Enable Torrent XZ output [UNIMPLEMENTED]
|
||||
Instead of outputting files to folder, files will be rebuilt to
|
||||
Torrent XZ (TXZ) files. This format is based on the LZMA container
|
||||
format XZ, but with custom header information. This is currently
|
||||
unused by any major application. Currently does not produce proper
|
||||
Torrent-compatible outputs.
|
||||
format XZ, but with a file name replaced by the SHA-1 of the file
|
||||
inside. This is currently unused by any major application.
|
||||
|
||||
-tzip, --torrent-zip Enable Torrent Zip output
|
||||
Instead of outputting files to folder, files will be rebuilt to
|
||||
@@ -694,21 +694,6 @@ Options:
|
||||
or specific copier headers by name (such as "fds.xml") to determine
|
||||
if a file matches or not.
|
||||
|
||||
-7z=, --7z= Set scanning level for 7zip archives (default 1)
|
||||
-gz=, --gz= Set scanning level for GZip archives (default 1)
|
||||
-rar=, --rar= Set scanning level for RAR archives (default 1)
|
||||
-zip=, --zip= Set scanning level for Zip archives (default 1)
|
||||
Scan archives in one of the following ways:
|
||||
0 - Hash both archive and its contents
|
||||
1 - Only hash contents of the archive
|
||||
2 - Only hash archive itself (treat like a regular file)
|
||||
|
||||
-sa, --scan-all Set scanning levels for all archives to 0
|
||||
This flag is the short equivalent to -7z=0 -gz=0 -rar=0 -zip=0
|
||||
wrapped up. Generally this will be helpful in all cases where the
|
||||
content of the rebuild folder is not entirely known or is known to be
|
||||
mixed.
|
||||
|
||||
-dm, --dat-merged Force creating merged sets
|
||||
Preprocess the DAT to have parent sets contain all items from the
|
||||
children based on the cloneof tag. This is incompatible with the
|
||||
@@ -766,7 +751,7 @@ Options:
|
||||
msx, openmsx - openMSX Software List
|
||||
ol, offlinelist - OfflineList XML
|
||||
rc, romcenter - RomCenter
|
||||
ripemd160 - RIPEMD160
|
||||
ripemd160 - RIPEMD160 (.NET Framework 4.8 only)
|
||||
sd, sabredat - SabreDat XML
|
||||
sfv - SFV
|
||||
sha1 - SHA1
|
||||
@@ -941,7 +926,7 @@ Options:
|
||||
msx, openmsx - openMSX Software List
|
||||
ol, offlinelist - OfflineList XML
|
||||
rc, romcenter - RomCenter
|
||||
ripemd160 - RIPEMD160
|
||||
ripemd160 - RIPEMD160 (.NET Framework 4.8 only)
|
||||
sd, sabredat - SabreDat XML
|
||||
sfv - SFV
|
||||
sha1 - SHA1
|
||||
@@ -967,7 +952,7 @@ Options:
|
||||
- %publisher% - Replaced with game Publisher
|
||||
- %crc% - Replaced with the CRC
|
||||
- %md5% - Replaced with the MD5
|
||||
- %ripemd160% - Replaced with the RIPEMD160
|
||||
- %ripemd160% - Replaced with the RIPEMD160 (.NET Framework 4.8 only)
|
||||
- %sha1% - Replaced with the SHA-1
|
||||
- %sha256% - Replaced with the SHA-256
|
||||
- %sha384% - Replaced with the SHA-384
|
||||
@@ -1381,13 +1366,13 @@ Options:
|
||||
Include only items with this RIPEMD160 hash in the output.
|
||||
Additionally, the user can specify an exact match or full C#-style
|
||||
regex for pattern matching. Multiple instances of this flag are
|
||||
allowed.
|
||||
allowed. .NET Framework 4.8 only.
|
||||
|
||||
-nripemd160=, --not-ripemd160= Filter by not RIPEMD160 hash
|
||||
Include only items without this RIPEMD160 hash in the output.
|
||||
Additionally, the user can specify an exact match or full C#-style
|
||||
regex for pattern matching. Multiple instances of this flag are
|
||||
allowed.
|
||||
allowed. .NET Framework 4.8 only.
|
||||
|
||||
-sha1=, --sha1= Filter by SHA-1 hash
|
||||
Include only items with this SHA-1 hash in the output. Additionally,
|
||||
@@ -1638,13 +1623,13 @@ Options:
|
||||
Include only items with this RIPEMD160 hash in the output.
|
||||
Additionally, the user can specify an exact match or full C#-style
|
||||
regex for pattern matching. Multiple instances of this flag are
|
||||
allowed.
|
||||
allowed. .NET Framework 4.8 only.
|
||||
|
||||
-nripemd160=, --not-ripemd160= Filter by not RIPEMD160 hash
|
||||
Include only items without this RIPEMD160 hash in the output.
|
||||
Additionally, the user can specify an exact match or full C#-style
|
||||
regex for pattern matching. Multiple instances of this flag are
|
||||
allowed.
|
||||
allowed. .NET Framework 4.8 only.
|
||||
|
||||
-sha1=, --sha1= Filter by SHA-1 hash
|
||||
Include only items with this SHA-1 hash in the output. Additionally,
|
||||
@@ -1802,9 +1787,9 @@ attributed to the site and/or person(s) that originally wrote the code. All
|
||||
code written by project members is licensed under GPL v3. See LICENSE for
|
||||
more details.
|
||||
|
||||
** Section 20.0 - TEMPORARY REMAPPINGS
|
||||
** Section 20.0 - REMAPPINGS
|
||||
|
||||
This section contains remappings from old flag names to new ones for the purposes of testing
|
||||
This section contains remappings from old flag names to new ones.
|
||||
|
||||
-ab, --add-blank -> -ab, --add-blank-files
|
||||
-ae, --add-ext -> -ae, --add-extension
|
||||
|
||||
@@ -498,7 +498,23 @@ Below are originally from SabreTools / DATabase -
|
||||
|
||||
system= Comma-separated list of system IDs
|
||||
source= Comma-separated list of source IDs
|
||||
|
||||
|
||||
(-ss, --sort - This feature flag is not removed, just internal flags)
|
||||
-7z=, --7z= Set scanning level for 7zip archives (default 1)
|
||||
-gz=, --gz= Set scanning level for GZip archives (default 1)
|
||||
-rar=, --rar= Set scanning level for RAR archives (default 1)
|
||||
-zip=, --zip= Set scanning level for Zip archives (default 1)
|
||||
Scan archives in one of the following ways:
|
||||
0 - Hash both archive and its contents
|
||||
1 - Only hash contents of the archive
|
||||
2 - Only hash archive itself (treat like a regular file)
|
||||
|
||||
-sa, --scan-all Set scanning levels for all archives to 0
|
||||
This flag is the short equivalent to -7z=0 -gz=0 -rar=0 -zip=0
|
||||
wrapped up. Generally this will be helpful in all cases where the
|
||||
content of the rebuild folder is not entirely known or is known to be
|
||||
mixed.
|
||||
|
||||
-tm, --trim-merge Consolidate DAT into a single game and trim entries
|
||||
In the cases where files will have too long a name, this allows for trimming
|
||||
the name of the files to the NTFS maximum length at most
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace SabreTools.Library.Readers
|
||||
{
|
||||
GroupCollection gc = Regex.Match(line, Constants.InternalPatternCMP).Groups;
|
||||
string normalizedValue = gc[1].Value.ToLowerInvariant();
|
||||
string[] linegc = Utilities.SplitLineAsCMP(gc[2].Value);
|
||||
string[] linegc = SplitLineAsCMP(gc[2].Value);
|
||||
|
||||
Internal = new Dictionary<string, string>();
|
||||
for (int i = 0; i < linegc.Length; i++)
|
||||
@@ -225,6 +225,32 @@ namespace SabreTools.Library.Readers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split a line as if it were a CMP rom line
|
||||
/// </summary>
|
||||
/// <param name="s">Line to split</param>
|
||||
/// <returns>Line split</returns>
|
||||
/// <remarks>Uses code from http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes</remarks>
|
||||
private string[] SplitLineAsCMP(string s)
|
||||
{
|
||||
// Get the opening and closing brace locations
|
||||
int openParenLoc = s.IndexOf('(');
|
||||
int closeParenLoc = s.LastIndexOf(')');
|
||||
|
||||
// Now remove anything outside of those braces, including the braces
|
||||
s = s.Substring(openParenLoc + 1, closeParenLoc - openParenLoc - 1);
|
||||
s = s.Trim();
|
||||
|
||||
// Now we get each string, divided up as cleanly as possible
|
||||
string[] matches = Regex
|
||||
.Matches(s, Constants.InternalPatternAttributesCMP)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Groups[0].Value)
|
||||
.ToArray();
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the underlying reader
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.DatFiles;
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
@@ -12,37 +13,35 @@ namespace SabreTools.Library.Reports
|
||||
/// TODO: Can this be overhauled to have all types write like DatFiles?
|
||||
public abstract class BaseReport
|
||||
{
|
||||
protected DatFile _datFile;
|
||||
protected string _name;
|
||||
protected long _machineCount;
|
||||
protected DatStats _stats;
|
||||
|
||||
protected StreamWriter _writer;
|
||||
protected bool _baddumpCol;
|
||||
protected bool _nodumpCol;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the filename
|
||||
/// Create a new report from the filename
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="filename">Name of the file to write out to</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public BaseReport(DatFile datfile, string filename, bool baddumpCol = false, bool nodumpCol = false)
|
||||
public BaseReport(string filename, bool baddumpCol = false, bool nodumpCol = false)
|
||||
{
|
||||
_datFile = datfile;
|
||||
_writer = new StreamWriter(Utilities.TryCreate(filename));
|
||||
_writer = new StreamWriter(FileExtensions.TryCreate(filename));
|
||||
_baddumpCol = baddumpCol;
|
||||
_nodumpCol = nodumpCol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the stream
|
||||
/// Create a new report from the stream
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="stream">Output stream to write to</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public BaseReport(DatFile datfile, Stream stream, bool baddumpCol = false, bool nodumpCol = false)
|
||||
public BaseReport(Stream stream, bool baddumpCol = false, bool nodumpCol = false)
|
||||
{
|
||||
_datFile = datfile;
|
||||
|
||||
if (!stream.CanWrite)
|
||||
throw new ArgumentException(nameof(stream));
|
||||
|
||||
@@ -52,19 +51,67 @@ namespace SabreTools.Library.Reports
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the DatFile that is being output
|
||||
/// Create a specific type of BaseReport to be used based on a format and user inputs
|
||||
/// </summary>
|
||||
/// <param name="datfile"></param>
|
||||
public void ReplaceDatFile(DatFile datfile)
|
||||
/// <param name="statReportFormat">Format of the Statistics Report to be created</param>
|
||||
/// <param name="filename">Name of the file to write out to</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
/// <returns>BaseReport of the specific internal type that corresponds to the inputs</returns>
|
||||
public static BaseReport Create(StatReportFormat statReportFormat, string filename, bool baddumpCol, bool nodumpCol)
|
||||
{
|
||||
_datFile = datfile;
|
||||
#if NET_FRAMEWORK
|
||||
switch (statReportFormat)
|
||||
{
|
||||
case StatReportFormat.None:
|
||||
return new Textfile(Console.OpenStandardOutput(), baddumpCol, nodumpCol);
|
||||
|
||||
case StatReportFormat.Textfile:
|
||||
return new Textfile(filename, baddumpCol, nodumpCol);
|
||||
|
||||
case StatReportFormat.CSV:
|
||||
return new SeparatedValue(filename, ',', baddumpCol, nodumpCol);
|
||||
|
||||
case StatReportFormat.HTML:
|
||||
return new Html(filename, baddumpCol, nodumpCol);
|
||||
|
||||
case StatReportFormat.SSV:
|
||||
return new SeparatedValue(filename, ';', baddumpCol, nodumpCol);
|
||||
|
||||
case StatReportFormat.TSV:
|
||||
return new SeparatedValue(filename, '\t', baddumpCol, nodumpCol);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
return statReportFormat switch
|
||||
{
|
||||
StatReportFormat.None => new Textfile(Console.OpenStandardOutput(), baddumpCol, nodumpCol),
|
||||
StatReportFormat.Textfile => new Textfile(filename, baddumpCol, nodumpCol),
|
||||
StatReportFormat.CSV => new SeparatedValue(filename, ',', baddumpCol, nodumpCol),
|
||||
StatReportFormat.HTML => new Html(filename, baddumpCol, nodumpCol),
|
||||
StatReportFormat.SSV => new SeparatedValue(filename, ';', baddumpCol, nodumpCol),
|
||||
StatReportFormat.TSV => new SeparatedValue(filename, '\t', baddumpCol, nodumpCol),
|
||||
_ => null,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the statistics that is being output
|
||||
/// </summary>
|
||||
public void ReplaceStatistics(string datName, long machineCount, DatStats datStats)
|
||||
{
|
||||
_name = datName;
|
||||
_machineCount = machineCount;
|
||||
_stats = datStats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the report to the output stream
|
||||
/// </summary>
|
||||
/// <param name="game">Number of games to use, -1 means use the number of keys</param>
|
||||
public abstract void Write(long game = -1);
|
||||
public abstract void Write();
|
||||
|
||||
/// <summary>
|
||||
/// Write out the header to the stream, if any exists
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
using SabreTools.Library.DatFiles;
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.Reports
|
||||
@@ -15,49 +13,49 @@ namespace SabreTools.Library.Reports
|
||||
internal class Html : BaseReport
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the filename
|
||||
/// Create a new report from the filename
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="filename">Name of the file to write out to</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public Html(DatFile datfile, string filename, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(datfile, filename, baddumpCol, nodumpCol)
|
||||
public Html(string filename, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(filename, baddumpCol, nodumpCol)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the stream
|
||||
/// Create a new report from the stream
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="stream">Output stream to write to</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public Html(DatFile datfile, Stream stream, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(datfile, stream, baddumpCol, nodumpCol)
|
||||
public Html(Stream stream, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(stream, baddumpCol, nodumpCol)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the report to file
|
||||
/// </summary>
|
||||
/// <param name="game">Number of games to use, -1 means use the number of keys</param>
|
||||
public override void Write(long game = -1)
|
||||
public override void Write()
|
||||
{
|
||||
string line = "\t\t\t<tr" + (_datFile.FileName.StartsWith("DIR: ")
|
||||
? $" class=\"dir\"><td>{WebUtility.HtmlEncode(_datFile.FileName.Remove(0, 5))}"
|
||||
: $"><td>{WebUtility.HtmlEncode(_datFile.FileName)}") + "</td>"
|
||||
+ $"<td align=\"right\">{Utilities.GetBytesReadable(_datFile.TotalSize)}</td>"
|
||||
+ $"<td align=\"right\">{(game == -1 ? _datFile.Keys.Count() : game)}</td>"
|
||||
+ $"<td align=\"right\">{_datFile.RomCount}</td>"
|
||||
+ $"<td align=\"right\">{_datFile.DiskCount}</td>"
|
||||
+ $"<td align=\"right\">{_datFile.CRCCount}</td>"
|
||||
+ $"<td align=\"right\">{_datFile.MD5Count}</td>"
|
||||
+ $"<td align=\"right\">{_datFile.RIPEMD160Count}</td>"
|
||||
+ $"<td align=\"right\">{_datFile.SHA1Count}</td>"
|
||||
+ $"<td align=\"right\">{_datFile.SHA256Count}</td>"
|
||||
+ (_baddumpCol ? $"<td align=\"right\">{_datFile.BaddumpCount}</td>" : string.Empty)
|
||||
+ (_nodumpCol ? $"<td align=\"right\">{_datFile.NodumpCount}</td>" : string.Empty)
|
||||
string line = "\t\t\t<tr" + (_name.StartsWith("DIR: ")
|
||||
? $" class=\"dir\"><td>{WebUtility.HtmlEncode(_name.Remove(0, 5))}"
|
||||
: $"><td>{WebUtility.HtmlEncode(_name)}") + "</td>"
|
||||
+ $"<td align=\"right\">{Utilities.GetBytesReadable(_stats.TotalSize)}</td>"
|
||||
+ $"<td align=\"right\">{_machineCount}</td>"
|
||||
+ $"<td align=\"right\">{_stats.RomCount}</td>"
|
||||
+ $"<td align=\"right\">{_stats.DiskCount}</td>"
|
||||
+ $"<td align=\"right\">{_stats.CRCCount}</td>"
|
||||
+ $"<td align=\"right\">{_stats.MD5Count}</td>"
|
||||
#if NET_FRAMEWORK
|
||||
+ $"<td align=\"right\">{_stats.RIPEMD160Count}</td>"
|
||||
#endif
|
||||
+ $"<td align=\"right\">{_stats.SHA1Count}</td>"
|
||||
+ $"<td align=\"right\">{_stats.SHA256Count}</td>"
|
||||
+ (_baddumpCol ? $"<td align=\"right\">{_stats.BaddumpCount}</td>" : string.Empty)
|
||||
+ (_nodumpCol ? $"<td align=\"right\">{_stats.NodumpCount}</td>" : string.Empty)
|
||||
+ "</tr>\n";
|
||||
_writer.Write(line);
|
||||
_writer.Flush();
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using SabreTools.Library.DatFiles;
|
||||
|
||||
namespace SabreTools.Library.Reports
|
||||
{
|
||||
@@ -13,15 +10,14 @@ namespace SabreTools.Library.Reports
|
||||
private char _separator;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the filename
|
||||
/// Create a new report from the filename
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="filename">Name of the file to write out to</param>
|
||||
/// <param name="separator">Separator character to use in output</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public SeparatedValue(DatFile datfile, string filename, char separator, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(datfile, filename, baddumpCol, nodumpCol)
|
||||
public SeparatedValue(string filename, char separator, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(filename, baddumpCol, nodumpCol)
|
||||
{
|
||||
_separator = separator;
|
||||
}
|
||||
@@ -29,13 +25,12 @@ namespace SabreTools.Library.Reports
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the stream
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="stream">Output stream to write to</param>
|
||||
/// <param name="separator">Separator character to use in output</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public SeparatedValue(DatFile datfile, Stream stream, char separator, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(datfile, stream, baddumpCol, nodumpCol)
|
||||
public SeparatedValue(Stream stream, char separator, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(stream, baddumpCol, nodumpCol)
|
||||
{
|
||||
_separator = separator;
|
||||
}
|
||||
@@ -43,25 +38,26 @@ namespace SabreTools.Library.Reports
|
||||
/// <summary>
|
||||
/// Write the report to file
|
||||
/// </summary>
|
||||
/// <param name="game">Number of games to use, -1 means use the number of keys</param>
|
||||
public override void Write(long game = -1)
|
||||
public override void Write()
|
||||
{
|
||||
string line = string.Format("\"" + _datFile.FileName + "\"{0}"
|
||||
+ "\"" + _datFile.TotalSize + "\"{0}"
|
||||
+ "\"" + (game == -1 ? _datFile.Keys.Count() : game) + "\"{0}"
|
||||
+ "\"" + _datFile.RomCount + "\"{0}"
|
||||
+ "\"" + _datFile.DiskCount + "\"{0}"
|
||||
+ "\"" + _datFile.CRCCount + "\"{0}"
|
||||
+ "\"" + _datFile.MD5Count + "\"{0}"
|
||||
+ "\"" + _datFile.RIPEMD160Count + "\"{0}"
|
||||
+ "\"" + _datFile.SHA1Count + "\"{0}"
|
||||
+ "\"" + _datFile.SHA256Count + "\"{0}"
|
||||
+ "\"" + _datFile.SHA384Count + "\"{0}"
|
||||
+ "\"" + _datFile.SHA512Count + "\""
|
||||
+ (_baddumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : string.Empty)
|
||||
+ (_nodumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : string.Empty)
|
||||
string line = string.Format("\"" + _name + "\"{0}"
|
||||
+ "\"" + _stats.TotalSize + "\"{0}"
|
||||
+ "\"" + _machineCount + "\"{0}"
|
||||
+ "\"" + _stats.RomCount + "\"{0}"
|
||||
+ "\"" + _stats.DiskCount + "\"{0}"
|
||||
+ "\"" + _stats.CRCCount + "\"{0}"
|
||||
+ "\"" + _stats.MD5Count + "\"{0}"
|
||||
#if NET_FRAMEWORK
|
||||
+ "\"" + _stats.RIPEMD160Count + "\"{0}"
|
||||
#endif
|
||||
+ "\"" + _stats.SHA1Count + "\"{0}"
|
||||
+ "\"" + _stats.SHA256Count + "\"{0}"
|
||||
+ "\"" + _stats.SHA384Count + "\"{0}"
|
||||
+ "\"" + _stats.SHA512Count + "\""
|
||||
+ (_baddumpCol ? "{0}\"" + _stats.BaddumpCount + "\"" : string.Empty)
|
||||
+ (_nodumpCol ? "{0}\"" + _stats.NodumpCount + "\"" : string.Empty)
|
||||
+ "\n", _separator);
|
||||
|
||||
|
||||
_writer.Write(line);
|
||||
_writer.Flush();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using SabreTools.Library.DatFiles;
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.Reports
|
||||
@@ -12,54 +10,55 @@ namespace SabreTools.Library.Reports
|
||||
internal class Textfile : BaseReport
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the filename
|
||||
/// Create a new report from the filename
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="filename">Name of the file to write out to</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public Textfile(DatFile datfile, string filename, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(datfile, filename, baddumpCol, nodumpCol)
|
||||
public Textfile(string filename, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(filename, baddumpCol, nodumpCol)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new report from the input DatFile and the stream
|
||||
/// Create a new report from the stream
|
||||
/// </summary>
|
||||
/// <param name="datfile">DatFile to write out statistics for</param>
|
||||
/// <param name="stream">Output stream to write to</param>
|
||||
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
|
||||
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
|
||||
public Textfile(DatFile datfile, Stream stream, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(datfile, stream, baddumpCol, nodumpCol)
|
||||
public Textfile(Stream stream, bool baddumpCol = false, bool nodumpCol = false)
|
||||
: base(stream, baddumpCol, nodumpCol)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the report to file
|
||||
/// </summary>
|
||||
/// <param name="game">Number of games to use, -1 means use the number of keys</param>
|
||||
public override void Write(long game = -1)
|
||||
public override void Write()
|
||||
{
|
||||
string line = @"'" + _datFile.FileName + @"':
|
||||
string line = @"'" + _name + @"':
|
||||
--------------------------------------------------
|
||||
Uncompressed size: " + Utilities.GetBytesReadable(_datFile.TotalSize) + @"
|
||||
Games found: " + (game == -1 ? _datFile.Keys.Count() : game) + @"
|
||||
Roms found: " + _datFile.RomCount + @"
|
||||
Disks found: " + _datFile.DiskCount + @"
|
||||
Roms with CRC: " + _datFile.CRCCount + @"
|
||||
Roms with MD5: " + _datFile.MD5Count + @"
|
||||
Roms with RIPEMD160: " + _datFile.RIPEMD160Count + @"
|
||||
Roms with SHA-1: " + _datFile.SHA1Count + @"
|
||||
Roms with SHA-256: " + _datFile.SHA256Count + @"
|
||||
Roms with SHA-384: " + _datFile.SHA384Count + @"
|
||||
Roms with SHA-512: " + _datFile.SHA512Count + "\n";
|
||||
Uncompressed size: " + Utilities.GetBytesReadable(_stats.TotalSize) + @"
|
||||
Games found: " + _machineCount + @"
|
||||
Roms found: " + _stats.RomCount + @"
|
||||
Disks found: " + _stats.DiskCount + @"
|
||||
Roms with CRC: " + _stats.CRCCount + @"
|
||||
Roms with MD5: " + _stats.MD5Count
|
||||
#if NET_FRAMEWORK
|
||||
+ @"
|
||||
Roms with RIPEMD160: " + _stats.RIPEMD160Count
|
||||
#endif
|
||||
+ @"
|
||||
Roms with SHA-1: " + _stats.SHA1Count + @"
|
||||
Roms with SHA-256: " + _stats.SHA256Count + @"
|
||||
Roms with SHA-384: " + _stats.SHA384Count + @"
|
||||
Roms with SHA-512: " + _stats.SHA512Count + "\n";
|
||||
|
||||
if (_baddumpCol)
|
||||
line += " Roms with BadDump status: " + _datFile.BaddumpCount + "\n";
|
||||
line += " Roms with BadDump status: " + _stats.BaddumpCount + "\n";
|
||||
|
||||
if (_nodumpCol)
|
||||
line += " Roms with Nodump status: " + _datFile.NodumpCount + "\n";
|
||||
line += " Roms with Nodump status: " + _stats.NodumpCount + "\n";
|
||||
|
||||
// For spacing between DATs
|
||||
line += "\n\n";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net462;net472;net48;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win10-x64;win7-x86</RuntimeIdentifiers>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
@@ -21,7 +21,6 @@
|
||||
<None Remove="Skippers\psid.xml" />
|
||||
<None Remove="Skippers\snes.xml" />
|
||||
<None Remove="Skippers\spc.xml" />
|
||||
<None Remove="sqlite3.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -52,15 +51,11 @@
|
||||
<Content Include="Skippers\spc.xml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="sqlite3.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Data.Sqlite.Portable" Version="1.0.3.5" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="3.1.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="SevenZipSharp.Net45" Version="1.0.19" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.1" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.FileTypes;
|
||||
using SabreTools.Library.Tools;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.Tools;
|
||||
|
||||
namespace SabreTools.Library.Skippers
|
||||
{
|
||||
public class Skipper
|
||||
@@ -38,22 +38,15 @@ namespace SabreTools.Library.Skippers
|
||||
/// </summary>
|
||||
public string SourceFile { get; set; }
|
||||
|
||||
// Local paths
|
||||
/// <summary>
|
||||
/// Local paths
|
||||
/// </summary>
|
||||
public static string LocalPath = Path.Combine(Globals.ExeDir, "Skippers") + Path.DirectorySeparatorChar;
|
||||
|
||||
// Header skippers represented by a list of skipper objects
|
||||
private static List<Skipper> _list;
|
||||
public static List<Skipper> List
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_list == null || _list.Count == 0)
|
||||
{
|
||||
PopulateSkippers();
|
||||
}
|
||||
return _list;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Header skippers represented by a list of skipper objects
|
||||
/// </summary>
|
||||
private static List<Skipper> List;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -80,8 +73,7 @@ namespace SabreTools.Library.Skippers
|
||||
Rules = new List<SkipperRule>();
|
||||
SourceFile = Path.GetFileNameWithoutExtension(filename);
|
||||
|
||||
Logger logger = new Logger();
|
||||
XmlReader xtr = Utilities.GetXmlTextReader(filename);
|
||||
XmlReader xtr = filename.GetXmlTextReader();
|
||||
|
||||
if (xtr == null)
|
||||
return;
|
||||
@@ -218,13 +210,9 @@ namespace SabreTools.Library.Skippers
|
||||
{
|
||||
string offset = subreader.GetAttribute("offset");
|
||||
if (offset.ToLowerInvariant() == "eof")
|
||||
{
|
||||
test.Offset = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
test.Offset = Convert.ToInt64(offset, 16);
|
||||
}
|
||||
}
|
||||
|
||||
if (subreader.GetAttribute("value") != null)
|
||||
@@ -243,16 +231,10 @@ namespace SabreTools.Library.Skippers
|
||||
if (subreader.GetAttribute("result") != null)
|
||||
{
|
||||
string result = subreader.GetAttribute("result");
|
||||
switch (result.ToLowerInvariant())
|
||||
{
|
||||
case "false":
|
||||
test.Result = false;
|
||||
break;
|
||||
case "true":
|
||||
default:
|
||||
test.Result = true;
|
||||
break;
|
||||
}
|
||||
if (!bool.TryParse(result, out bool resultBool))
|
||||
resultBool = true;
|
||||
|
||||
test.Result = resultBool;
|
||||
}
|
||||
|
||||
if (subreader.GetAttribute("mask") != null)
|
||||
@@ -272,18 +254,15 @@ namespace SabreTools.Library.Skippers
|
||||
{
|
||||
string size = subreader.GetAttribute("size");
|
||||
if (size.ToLowerInvariant() == "po2")
|
||||
{
|
||||
test.Size = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
test.Size = Convert.ToInt64(size, 16);
|
||||
}
|
||||
}
|
||||
|
||||
if (subreader.GetAttribute("operator") != null)
|
||||
{
|
||||
string oper = subreader.GetAttribute("operator");
|
||||
#if NET_FRAMEWORK
|
||||
switch (oper.ToLowerInvariant())
|
||||
{
|
||||
case "less":
|
||||
@@ -297,6 +276,15 @@ namespace SabreTools.Library.Skippers
|
||||
test.Operator = HeaderSkipTestFileOperator.Equal;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
test.Operator = oper.ToLowerInvariant() switch
|
||||
{
|
||||
"less" => HeaderSkipTestFileOperator.Less,
|
||||
"greater" => HeaderSkipTestFileOperator.Greater,
|
||||
"equal" => HeaderSkipTestFileOperator.Equal,
|
||||
_ => HeaderSkipTestFileOperator.Equal,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
// Add the created test to the rule
|
||||
@@ -330,6 +318,14 @@ namespace SabreTools.Library.Skippers
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initialize static fields
|
||||
/// </summary>
|
||||
public static void Init()
|
||||
{
|
||||
PopulateSkippers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the entire list of header Skippers
|
||||
/// </summary>
|
||||
@@ -339,15 +335,113 @@ namespace SabreTools.Library.Skippers
|
||||
/// </remarks>
|
||||
private static void PopulateSkippers()
|
||||
{
|
||||
if (_list == null)
|
||||
_list = new List<Skipper>();
|
||||
if (List == null)
|
||||
List = new List<Skipper>();
|
||||
|
||||
foreach (string skipperFile in Directory.EnumerateFiles(LocalPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
_list.Add(new Skipper(Path.GetFullPath(skipperFile)));
|
||||
List.Add(new Skipper(Path.GetFullPath(skipperFile)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect header skipper compliance and create an output file
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to be parsed</param>
|
||||
/// <param name="outDir">Output directory to write the file to, empty means the same directory as the input file</param>
|
||||
/// <param name="nostore">True if headers should not be stored in the database, false otherwise</param>
|
||||
/// <returns>True if the output file was created, false otherwise</returns>
|
||||
public static bool DetectTransformStore(string file, string outDir, bool nostore)
|
||||
{
|
||||
// Create the output directory if it doesn't exist
|
||||
DirectoryExtensions.Ensure(outDir, create: true);
|
||||
|
||||
Globals.Logger.User($"\nGetting skipper information for '{file}'");
|
||||
|
||||
// Get the skipper rule that matches the file, if any
|
||||
SkipperRule rule = GetMatchingRule(file, string.Empty);
|
||||
|
||||
// If we have an empty rule, return false
|
||||
if (rule.Tests == null || rule.Tests.Count == 0 || rule.Operation != HeaderSkipOperation.None)
|
||||
return false;
|
||||
|
||||
Globals.Logger.User("File has a valid copier header");
|
||||
|
||||
// Get the header bytes from the file first
|
||||
string hstr;
|
||||
try
|
||||
{
|
||||
// Extract the header as a string for the database
|
||||
#if NET_FRAMEWORK
|
||||
using (var fs = FileExtensions.TryOpenRead(file))
|
||||
{
|
||||
#else
|
||||
using var fs = FileExtensions.TryOpenRead(file);
|
||||
#endif
|
||||
byte[] hbin = new byte[(int)rule.StartOffset];
|
||||
fs.Read(hbin, 0, (int)rule.StartOffset);
|
||||
hstr = Utilities.ByteArrayToString(hbin);
|
||||
#if NET_FRAMEWORK
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply the rule to the file
|
||||
string newfile = (string.IsNullOrWhiteSpace(outDir) ? Path.GetFullPath(file) + ".new" : Path.Combine(outDir, Path.GetFileName(file)));
|
||||
rule.TransformFile(file, newfile);
|
||||
|
||||
// If the output file doesn't exist, return false
|
||||
if (!File.Exists(newfile))
|
||||
return false;
|
||||
|
||||
// Now add the information to the database if it's not already there
|
||||
if (!nostore)
|
||||
{
|
||||
BaseFile baseFile = FileExtensions.GetInfo(newfile, chdsAsFiles: true);
|
||||
DatabaseTools.AddHeaderToDatabase(hstr, Utilities.ByteArrayToString(baseFile.SHA1), rule.SourceFile);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect and replace header(s) to the given file
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to be parsed</param>
|
||||
/// <param name="outDir">Output directory to write the file to, empty means the same directory as the input file</param>
|
||||
/// <returns>True if a header was found and appended, false otherwise</returns>
|
||||
public static bool RestoreHeader(string file, string outDir)
|
||||
{
|
||||
// Create the output directory if it doesn't exist
|
||||
if (!string.IsNullOrWhiteSpace(outDir) && !Directory.Exists(outDir))
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
// First, get the SHA-1 hash of the file
|
||||
BaseFile baseFile = FileExtensions.GetInfo(file, chdsAsFiles: true);
|
||||
|
||||
// Retrieve a list of all related headers from the database
|
||||
List<string> headers = DatabaseTools.RetrieveHeadersFromDatabase(Utilities.ByteArrayToString(baseFile.SHA1));
|
||||
|
||||
// If we have nothing retrieved, we return false
|
||||
if (headers.Count == 0)
|
||||
return false;
|
||||
|
||||
// Now loop through and create the reheadered files, if possible
|
||||
for (int i = 0; i < headers.Count; i++)
|
||||
{
|
||||
string outputFile = (string.IsNullOrWhiteSpace(outDir) ? $"{Path.GetFullPath(file)}.new" : Path.Combine(outDir, Path.GetFileName(file))) + i;
|
||||
Globals.Logger.User($"Creating reheadered file: {outputFile}");
|
||||
FileExtensions.AppendBytes(file, outputFile, Utilities.StringToByteArray(headers[i]), null);
|
||||
Globals.Logger.User("Reheadered file created!");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the SkipperRule associated with a given file
|
||||
/// </summary>
|
||||
@@ -364,7 +458,7 @@ namespace SabreTools.Library.Skippers
|
||||
return new SkipperRule();
|
||||
}
|
||||
|
||||
return GetMatchingRule(Utilities.TryOpenRead(input), skipperName);
|
||||
return GetMatchingRule(FileExtensions.TryOpenRead(input), skipperName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -46,8 +46,6 @@ namespace SabreTools.Library.Skippers
|
||||
/// <returns>True if the file was transformed properly, false otherwise</returns>
|
||||
public bool TransformFile(string input, string output)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
// If the input file doesn't exist, fail
|
||||
if (!File.Exists(input))
|
||||
{
|
||||
@@ -56,15 +54,15 @@ namespace SabreTools.Library.Skippers
|
||||
}
|
||||
|
||||
// Create the output directory if it doesn't already
|
||||
Utilities.EnsureOutputDirectory(Path.GetDirectoryName(output));
|
||||
DirectoryExtensions.Ensure(Path.GetDirectoryName(output));
|
||||
|
||||
Globals.Logger.User($"Attempting to apply rule to '{input}'");
|
||||
success = TransformStream(Utilities.TryOpenRead(input), Utilities.TryCreate(output));
|
||||
bool success = TransformStream(FileExtensions.TryOpenRead(input), FileExtensions.TryCreate(output));
|
||||
|
||||
// If the output file has size 0, delete it
|
||||
if (new FileInfo(output).Length == 0)
|
||||
{
|
||||
Utilities.TryDeleteFile(output);
|
||||
FileExtensions.TryDelete(output);
|
||||
success = false;
|
||||
}
|
||||
|
||||
|
||||
174
SabreTools.Library/Tools/BinaryReaderExtensions.cs
Normal file
174
SabreTools.Library/Tools/BinaryReaderExtensions.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Big endian reading overloads for BinaryReader
|
||||
/// </summary>
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes from the stream, starting from a specified point in the byte array.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to read data into.</param>
|
||||
/// <param name="index">The starting point in the buffer at which to begin reading into the buffer.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, or it might be zero if the end of the stream is reached.</returns>
|
||||
public static int ReadBigEndian(this BinaryReader reader, byte[] buffer, int index, int count)
|
||||
{
|
||||
int retval = reader.Read(buffer, index, count);
|
||||
Array.Reverse(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of characters from the stream, starting from a specified point in the character array.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to read data into.</param>
|
||||
/// <param name="index">The starting point in the buffer at which to begin reading into the buffer.</param>
|
||||
/// <param name="count">The number of characters to read.</param>
|
||||
/// <returns>The total number of characters read into the buffer. This might be less than the number of characters requested if that many characters are not currently available, or it might be zero if the end of the stream is reached.</returns>
|
||||
public static int ReadBigEndian(this BinaryReader reader, char[] buffer, int index, int count)
|
||||
{
|
||||
int retval = reader.Read(buffer, index, count);
|
||||
Array.Reverse(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes from the current stream into a byte array and advances the current position by that number of bytes.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bytes to read. This value must be 0 or a non-negative number or an exception will occur.</param>
|
||||
/// <returns>A byte array containing data read from the underlying stream. This might be less than the number of bytes requested if the end of the stream is reached.</returns>
|
||||
public static byte[] ReadBytesBigEndian(this BinaryReader reader, int count)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(count);
|
||||
Array.Reverse(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of characters from the current stream, returns the data in a character array, and advances the current position in accordance with the Encoding used and the specific character being read from the stream.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of characters to read. This value must be 0 or a non-negative number or an exception will occur.</param>
|
||||
/// <returns>A character array containing data read from the underlying stream. This might be less than the number of bytes requested if the end of the stream is reached.</returns>
|
||||
public static char[] ReadCharsBigEndian(this BinaryReader reader, int count)
|
||||
{
|
||||
char[] retval = reader.ReadChars(count);
|
||||
Array.Reverse(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a decimal value from the current stream and advances the current position of the stream by sixteen bytes.
|
||||
/// </summary>
|
||||
/// <returns>A decimal value read from the current stream.</returns>
|
||||
public static decimal ReadDecimalBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(16);
|
||||
Array.Reverse(retval);
|
||||
|
||||
int i1 = BitConverter.ToInt32(retval, 0);
|
||||
int i2 = BitConverter.ToInt32(retval, 4);
|
||||
int i3 = BitConverter.ToInt32(retval, 8);
|
||||
int i4 = BitConverter.ToInt32(retval, 12);
|
||||
|
||||
return new decimal(new int[] { i1, i2, i3, i4 });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// eads an 8-byte floating point value from the current stream and advances the current position of the stream by eight bytes.
|
||||
/// </summary>
|
||||
/// <returns>An 8-byte floating point value read from the current stream.</returns>
|
||||
public static double ReadDoubleBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(8);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToDouble(retval, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 2-byte signed integer from the current stream and advances the current position of the stream by two bytes.
|
||||
/// </summary>
|
||||
/// <returns>A 2-byte signed integer read from the current stream.</returns>
|
||||
public static short ReadInt16BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(2);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToInt16(retval, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes.
|
||||
/// </summary>
|
||||
/// <returns>A 4-byte signed integer read from the current stream.</returns>
|
||||
public static int ReadInt32BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(4);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToInt32(retval, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an 8-byte signed integer from the current stream and advances the current position of the stream by eight bytes.
|
||||
/// </summary>
|
||||
/// <returns>An 8-byte signed integer read from the current stream.</returns>
|
||||
public static long ReadInt64BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(8);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToInt64(retval, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes.
|
||||
/// </summary>
|
||||
/// <returns>A 4-byte floating point value read from the current stream.</returns>
|
||||
public static float ReadSingleBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(4);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToSingle(retval, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 2-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by two bytes.
|
||||
///
|
||||
/// This API is not CLS-compliant.
|
||||
/// </summary>
|
||||
/// <returns>A 2-byte unsigned integer read from this stream.</returns>
|
||||
public static ushort ReadUInt16BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(2);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToUInt16(retval, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 4-byte unsigned integer from the current stream and advances the position of the stream by four bytes.
|
||||
///
|
||||
/// This API is not CLS-compliant.
|
||||
/// </summary>
|
||||
/// <returns>A 4-byte unsigned integer read from this stream.</returns>
|
||||
public static uint ReadUInt32BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(4);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToUInt32(retval, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an 8-byte unsigned integer from the current stream and advances the position of the stream by eight bytes.
|
||||
///
|
||||
/// This API is not CLS-compliant.
|
||||
/// </summary>
|
||||
/// <returns>An 8-byte unsigned integer read from this stream.</returns>
|
||||
public static ulong ReadUInt64BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(8);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToUInt64(retval, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
545
SabreTools.Library/Tools/Converters.cs
Normal file
545
SabreTools.Library/Tools/Converters.cs
Normal file
@@ -0,0 +1,545 @@
|
||||
using SabreTools.Library.Data;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Get DatFormat value from input string
|
||||
/// </summary>
|
||||
/// <param name="input">String to get value from</param>
|
||||
/// <returns>DatFormat value corresponding to the string</returns>
|
||||
public static DatFormat AsDatFormat(this string input)
|
||||
{
|
||||
switch (input?.Trim().ToLowerInvariant())
|
||||
{
|
||||
case "all":
|
||||
return DatFormat.ALL;
|
||||
case "am":
|
||||
case "attractmode":
|
||||
return DatFormat.AttractMode;
|
||||
case "cmp":
|
||||
case "clrmamepro":
|
||||
return DatFormat.ClrMamePro;
|
||||
case "csv":
|
||||
return DatFormat.CSV;
|
||||
case "dc":
|
||||
case "doscenter":
|
||||
return DatFormat.DOSCenter;
|
||||
case "json":
|
||||
return DatFormat.Json;
|
||||
case "lr":
|
||||
case "listrom":
|
||||
return DatFormat.Listrom;
|
||||
case "lx":
|
||||
case "listxml":
|
||||
return DatFormat.Listxml;
|
||||
case "md5":
|
||||
return DatFormat.RedumpMD5;
|
||||
case "miss":
|
||||
case "missfile":
|
||||
return DatFormat.MissFile;
|
||||
case "msx":
|
||||
case "openmsx":
|
||||
return DatFormat.OpenMSX;
|
||||
case "ol":
|
||||
case "offlinelist":
|
||||
return DatFormat.OfflineList;
|
||||
case "rc":
|
||||
case "romcenter":
|
||||
return DatFormat.RomCenter;
|
||||
#if NET_FRAMEWORK
|
||||
case "ripemd160":
|
||||
return DatFormat.RedumpRIPEMD160;
|
||||
#endif
|
||||
case "sd":
|
||||
case "sabredat":
|
||||
return DatFormat.SabreDat;
|
||||
case "sfv":
|
||||
return DatFormat.RedumpSFV;
|
||||
case "sha1":
|
||||
return DatFormat.RedumpSHA1;
|
||||
case "sha256":
|
||||
return DatFormat.RedumpSHA256;
|
||||
case "sha384":
|
||||
return DatFormat.RedumpSHA384;
|
||||
case "sha512":
|
||||
return DatFormat.RedumpSHA512;
|
||||
case "sl":
|
||||
case "softwarelist":
|
||||
return DatFormat.SoftwareList;
|
||||
case "smdb":
|
||||
case "everdrive":
|
||||
return DatFormat.EverdriveSMDB;
|
||||
case "ssv":
|
||||
return DatFormat.SSV;
|
||||
case "tsv":
|
||||
return DatFormat.TSV;
|
||||
case "xml":
|
||||
case "logiqx":
|
||||
return DatFormat.Logiqx;
|
||||
default:
|
||||
return 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the field associated with each hash type
|
||||
/// </summary>
|
||||
public static Field AsField(this Hash hash)
|
||||
{
|
||||
switch (hash)
|
||||
{
|
||||
case Hash.CRC:
|
||||
return Field.CRC;
|
||||
case Hash.MD5:
|
||||
return Field.MD5;
|
||||
#if NET_FRAMEWORK
|
||||
case Hash.RIPEMD160:
|
||||
return Field.RIPEMD160;
|
||||
#endif
|
||||
case Hash.SHA1:
|
||||
return Field.SHA1;
|
||||
case Hash.SHA256:
|
||||
return Field.SHA256;
|
||||
case Hash.SHA384:
|
||||
return Field.SHA384;
|
||||
case Hash.SHA512:
|
||||
return Field.SHA512;
|
||||
|
||||
default:
|
||||
return Field.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Field value from input string
|
||||
/// </summary>
|
||||
/// <param name="input">String to get value from</param>
|
||||
/// <returns>Field value corresponding to the string</returns>
|
||||
public static Field AsField(this string input)
|
||||
{
|
||||
switch (input?.ToLowerInvariant())
|
||||
{
|
||||
case "areaname":
|
||||
return Field.AreaName;
|
||||
case "areasize":
|
||||
return Field.AreaSize;
|
||||
case "bios":
|
||||
return Field.Bios;
|
||||
case "biosdescription":
|
||||
case "bios description":
|
||||
case "biossetdescription":
|
||||
case "biosset description":
|
||||
case "bios set description":
|
||||
return Field.BiosDescription;
|
||||
case "board":
|
||||
return Field.Board;
|
||||
case "cloneof":
|
||||
return Field.CloneOf;
|
||||
case "comment":
|
||||
return Field.Comment;
|
||||
case "crc":
|
||||
return Field.CRC;
|
||||
case "default":
|
||||
return Field.Default;
|
||||
case "date":
|
||||
return Field.Date;
|
||||
case "description":
|
||||
return Field.Description;
|
||||
case "devices":
|
||||
return Field.Devices;
|
||||
case "features":
|
||||
return Field.Features;
|
||||
case "gamename":
|
||||
case "machinename":
|
||||
return Field.MachineName;
|
||||
case "gametype":
|
||||
case "machinetype":
|
||||
return Field.MachineType;
|
||||
case "index":
|
||||
return Field.Index;
|
||||
case "infos":
|
||||
return Field.Infos;
|
||||
case "language":
|
||||
return Field.Language;
|
||||
case "manufacturer":
|
||||
return Field.Manufacturer;
|
||||
case "md5":
|
||||
return Field.MD5;
|
||||
case "merge":
|
||||
return Field.Merge;
|
||||
case "name":
|
||||
return Field.Name;
|
||||
case "offset":
|
||||
return Field.Offset;
|
||||
case "optional":
|
||||
return Field.Optional;
|
||||
case "partinterface":
|
||||
return Field.PartInterface;
|
||||
case "partname":
|
||||
return Field.PartName;
|
||||
case "publisher":
|
||||
return Field.Publisher;
|
||||
case "rebuildto":
|
||||
return Field.RebuildTo;
|
||||
case "region":
|
||||
return Field.Region;
|
||||
#if NET_FRAMEWORK
|
||||
case "ripemd160":
|
||||
return Field.RIPEMD160;
|
||||
#endif
|
||||
case "romof":
|
||||
return Field.RomOf;
|
||||
case "runnable":
|
||||
return Field.Runnable;
|
||||
case "sampleof":
|
||||
return Field.SampleOf;
|
||||
case "sha1":
|
||||
return Field.SHA1;
|
||||
case "sha256":
|
||||
return Field.SHA256;
|
||||
case "sha384":
|
||||
return Field.SHA384;
|
||||
case "sha512":
|
||||
return Field.SHA512;
|
||||
case "size":
|
||||
return Field.Size;
|
||||
case "slotoptions":
|
||||
return Field.SlotOptions;
|
||||
case "sourcefile":
|
||||
return Field.SourceFile;
|
||||
case "status":
|
||||
return Field.Status;
|
||||
case "supported":
|
||||
return Field.Supported;
|
||||
case "writable":
|
||||
return Field.Writable;
|
||||
case "year":
|
||||
return Field.Year;
|
||||
default:
|
||||
return Field.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ForceMerging value from input string
|
||||
/// </summary>
|
||||
/// <param name="forcemerge">String to get value from</param>
|
||||
/// <returns>ForceMerging value corresponding to the string</returns>
|
||||
public static ForceMerging AsForceMerging(this string forcemerge)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (forcemerge?.ToLowerInvariant())
|
||||
{
|
||||
case "split":
|
||||
return ForceMerging.Split;
|
||||
case "merged":
|
||||
return ForceMerging.Merged;
|
||||
case "nonmerged":
|
||||
return ForceMerging.NonMerged;
|
||||
case "full":
|
||||
return ForceMerging.Full;
|
||||
case "none":
|
||||
default:
|
||||
return ForceMerging.None;
|
||||
}
|
||||
#else
|
||||
return forcemerge?.ToLowerInvariant() switch
|
||||
{
|
||||
"split" => ForceMerging.Split,
|
||||
"merged" => ForceMerging.Merged,
|
||||
"nonmerged" => ForceMerging.NonMerged,
|
||||
"full" => ForceMerging.Full,
|
||||
"none" => ForceMerging.None,
|
||||
_ => ForceMerging.None,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ForceNodump value from input string
|
||||
/// </summary>
|
||||
/// <param name="forcend">String to get value from</param>
|
||||
/// <returns>ForceNodump value corresponding to the string</returns>
|
||||
public static ForceNodump AsForceNodump(this string forcend)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (forcend?.ToLowerInvariant())
|
||||
{
|
||||
case "obsolete":
|
||||
return ForceNodump.Obsolete;
|
||||
case "required":
|
||||
return ForceNodump.Required;
|
||||
case "ignore":
|
||||
return ForceNodump.Ignore;
|
||||
case "none":
|
||||
default:
|
||||
return ForceNodump.None;
|
||||
}
|
||||
#else
|
||||
return forcend?.ToLowerInvariant() switch
|
||||
{
|
||||
"obsolete" => ForceNodump.Obsolete,
|
||||
"required" => ForceNodump.Required,
|
||||
"ignore" => ForceNodump.Ignore,
|
||||
"none" => ForceNodump.None,
|
||||
_ => ForceNodump.None,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ForcePacking value from input string
|
||||
/// </summary>
|
||||
/// <param name="forcepack">String to get value from</param>
|
||||
/// <returns>ForcePacking value corresponding to the string</returns>
|
||||
public static ForcePacking AsForcePacking(this string forcepack)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (forcepack?.ToLowerInvariant())
|
||||
{
|
||||
case "yes":
|
||||
case "zip":
|
||||
return ForcePacking.Zip;
|
||||
case "no":
|
||||
case "unzip":
|
||||
return ForcePacking.Unzip;
|
||||
case "none":
|
||||
default:
|
||||
return ForcePacking.None;
|
||||
}
|
||||
#else
|
||||
return forcepack?.ToLowerInvariant() switch
|
||||
{
|
||||
"yes" => ForcePacking.Zip,
|
||||
"zip" => ForcePacking.Zip,
|
||||
"no" => ForcePacking.Unzip,
|
||||
"unzip" => ForcePacking.Unzip,
|
||||
"none" => ForcePacking.None,
|
||||
_ => ForcePacking.None,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ItemStatus value from input string
|
||||
/// </summary>
|
||||
/// <param name="status">String to get value from</param>
|
||||
/// <returns>ItemStatus value corresponding to the string</returns>
|
||||
public static ItemStatus AsItemStatus(this string status)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (status?.ToLowerInvariant())
|
||||
{
|
||||
case "good":
|
||||
return ItemStatus.Good;
|
||||
case "baddump":
|
||||
return ItemStatus.BadDump;
|
||||
case "nodump":
|
||||
case "yes":
|
||||
return ItemStatus.Nodump;
|
||||
case "verified":
|
||||
return ItemStatus.Verified;
|
||||
case "none":
|
||||
case "no":
|
||||
default:
|
||||
return ItemStatus.None;
|
||||
}
|
||||
#else
|
||||
return status?.ToLowerInvariant() switch
|
||||
{
|
||||
"good" => ItemStatus.Good,
|
||||
"baddump" => ItemStatus.BadDump,
|
||||
"nodump" => ItemStatus.Nodump,
|
||||
"yes" => ItemStatus.Nodump,
|
||||
"verified" => ItemStatus.Verified,
|
||||
"none" => ItemStatus.None,
|
||||
"no" => ItemStatus.None,
|
||||
_ => ItemStatus.None,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ItemType? value from input string
|
||||
/// </summary>
|
||||
/// <param name="itemType">String to get value from</param>
|
||||
/// <returns>ItemType? value corresponding to the string</returns>
|
||||
public static ItemType? AsItemType(this string itemType)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (itemType?.ToLowerInvariant())
|
||||
{
|
||||
case "archive":
|
||||
return ItemType.Archive;
|
||||
case "biosset":
|
||||
return ItemType.BiosSet;
|
||||
case "blank":
|
||||
return ItemType.Blank;
|
||||
case "disk":
|
||||
return ItemType.Disk;
|
||||
case "release":
|
||||
return ItemType.Release;
|
||||
case "rom":
|
||||
return ItemType.Rom;
|
||||
case "sample":
|
||||
return ItemType.Sample;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
return itemType?.ToLowerInvariant() switch
|
||||
{
|
||||
"archive" => ItemType.Archive,
|
||||
"biosset" => ItemType.BiosSet,
|
||||
"blank" => ItemType.Blank,
|
||||
"disk" => ItemType.Disk,
|
||||
"release" => ItemType.Release,
|
||||
"rom" => ItemType.Rom,
|
||||
"sample" => ItemType.Sample,
|
||||
_ => null,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get MachineType value from input string
|
||||
/// </summary>
|
||||
/// <param name="gametype">String to get value from</param>
|
||||
/// <returns>MachineType value corresponding to the string</returns>
|
||||
public static MachineType AsMachineType(this string gametype)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (gametype?.ToLowerInvariant())
|
||||
{
|
||||
case "bios":
|
||||
return MachineType.Bios;
|
||||
case "dev":
|
||||
case "device":
|
||||
return MachineType.Device;
|
||||
case "mech":
|
||||
case "mechanical":
|
||||
return MachineType.Mechanical;
|
||||
case "none":
|
||||
default:
|
||||
return MachineType.None;
|
||||
}
|
||||
#else
|
||||
return gametype?.ToLowerInvariant() switch
|
||||
{
|
||||
"bios" => MachineType.Bios,
|
||||
"dev" => MachineType.Device,
|
||||
"device" => MachineType.Device,
|
||||
"mech" => MachineType.Mechanical,
|
||||
"mechanical" => MachineType.Mechanical,
|
||||
"none" => MachineType.None,
|
||||
_ => MachineType.None,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get SplitType value from input ForceMerging
|
||||
/// </summary>
|
||||
/// <param name="forceMerging">ForceMerging to get value from</param>
|
||||
/// <returns>SplitType value corresponding to the string</returns>
|
||||
public static SplitType AsSplitType(this ForceMerging forceMerging)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (forceMerging)
|
||||
{
|
||||
case ForceMerging.Split:
|
||||
return SplitType.Split;
|
||||
case ForceMerging.Merged:
|
||||
return SplitType.Merged;
|
||||
case ForceMerging.NonMerged:
|
||||
return SplitType.NonMerged;
|
||||
case ForceMerging.Full:
|
||||
return SplitType.FullNonMerged;
|
||||
case ForceMerging.None:
|
||||
default:
|
||||
return SplitType.None;
|
||||
}
|
||||
#else
|
||||
return forceMerging switch
|
||||
{
|
||||
ForceMerging.Split => SplitType.Split,
|
||||
ForceMerging.Merged => SplitType.Merged,
|
||||
ForceMerging.NonMerged => SplitType.NonMerged,
|
||||
ForceMerging.Full => SplitType.FullNonMerged,
|
||||
ForceMerging.None => SplitType.None,
|
||||
_ => SplitType.None,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get StatReportFormat value from input string
|
||||
/// </summary>
|
||||
/// <param name="input">String to get value from</param>
|
||||
/// <returns>StatReportFormat value corresponding to the string</returns>
|
||||
public static StatReportFormat AsStatReportFormat(this string input)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (input?.Trim().ToLowerInvariant())
|
||||
{
|
||||
case "all":
|
||||
return StatReportFormat.All;
|
||||
case "csv":
|
||||
return StatReportFormat.CSV;
|
||||
case "html":
|
||||
return StatReportFormat.HTML;
|
||||
case "ssv":
|
||||
return StatReportFormat.SSV;
|
||||
case "text":
|
||||
return StatReportFormat.Textfile;
|
||||
case "tsv":
|
||||
return StatReportFormat.TSV;
|
||||
default:
|
||||
return 0x0;
|
||||
}
|
||||
#else
|
||||
return input?.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"all" => StatReportFormat.All,
|
||||
"csv" => StatReportFormat.CSV,
|
||||
"html" => StatReportFormat.HTML,
|
||||
"ssv" => StatReportFormat.SSV,
|
||||
"text" => StatReportFormat.Textfile,
|
||||
"tsv" => StatReportFormat.TSV,
|
||||
_ => 0x0,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get bool? value from input string
|
||||
/// </summary>
|
||||
/// <param name="yesno">String to get value from</param>
|
||||
/// <returns>bool? corresponding to the string</returns>
|
||||
public static bool? AsYesNo(this string yesno)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
switch (yesno?.ToLowerInvariant())
|
||||
{
|
||||
case "yes":
|
||||
return true;
|
||||
case "no":
|
||||
return false;
|
||||
case "partial":
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
return yesno?.ToLowerInvariant() switch
|
||||
{
|
||||
"yes" => true,
|
||||
"no" => false,
|
||||
"partial" => null,
|
||||
_ => null,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using Mono.Data.Sqlite;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
@@ -20,8 +20,6 @@ namespace SabreTools.Library.Tools
|
||||
/// <param name="type">Name of the source skipper file</param>
|
||||
public static void AddHeaderToDatabase(string header, string SHA1, string source)
|
||||
{
|
||||
bool exists = false;
|
||||
|
||||
// Ensure the database exists
|
||||
EnsureDatabase(Constants.HeadererDbSchema, Constants.HeadererFileName, Constants.HeadererConnectionString);
|
||||
|
||||
@@ -32,7 +30,7 @@ namespace SabreTools.Library.Tools
|
||||
string query = $"SELECT * FROM data WHERE sha1='{SHA1}' AND header='{header}'";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
exists = sldr.HasRows;
|
||||
bool exists = sldr.HasRows;
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
@@ -60,7 +58,7 @@ namespace SabreTools.Library.Tools
|
||||
|
||||
// Make sure the file exists
|
||||
if (!File.Exists(db))
|
||||
SqliteConnection.CreateFile(db);
|
||||
File.Create(db);
|
||||
|
||||
// Open the database connection
|
||||
SqliteConnection dbc = new SqliteConnection(connectionString);
|
||||
|
||||
286
SabreTools.Library/Tools/DirectoryExtensions.cs
Normal file
286
SabreTools.Library/Tools/DirectoryExtensions.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using NaturalSort;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions to Directory functionality
|
||||
/// </summary>
|
||||
public static class DirectoryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Cleans out the temporary directory
|
||||
/// </summary>
|
||||
/// <param name="dir">Name of the directory to clean out</param>
|
||||
public static void Clean(string dir)
|
||||
{
|
||||
foreach (string file in Directory.EnumerateFiles(dir, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
FileExtensions.TryDelete(file);
|
||||
}
|
||||
|
||||
foreach (string subdir in Directory.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
TryDelete(subdir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure the output directory is a proper format and can be created
|
||||
/// </summary>
|
||||
/// <param name="dir">Directory to check</param>
|
||||
/// <param name="create">True if the directory should be created, false otherwise (default)</param>
|
||||
/// <param name="temp">True if this is a temp directory, false otherwise</param>
|
||||
/// <returns>Full path to the directory</returns>
|
||||
public static string Ensure(string dir, bool create = false, bool temp = false)
|
||||
{
|
||||
// If the output directory is invalid
|
||||
if (string.IsNullOrWhiteSpace(dir))
|
||||
{
|
||||
if (temp)
|
||||
dir = Path.GetTempPath();
|
||||
else
|
||||
dir = Environment.CurrentDirectory;
|
||||
}
|
||||
|
||||
// Get the full path for the output directory
|
||||
dir = Path.GetFullPath(dir);
|
||||
|
||||
// If we're creating the output folder, do so
|
||||
if (create)
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of just directories from inputs
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of strings representing directories and files</param>
|
||||
/// <param name="appendparent">True if the parent name should be appended after the special character "¬", false otherwise (default)</param>
|
||||
/// <returns>List of strings representing just directories from the inputs</returns>
|
||||
public static List<string> GetDirectoriesOnly(List<string> inputs, bool appendparent = false)
|
||||
{
|
||||
List<string> outputs = new List<string>();
|
||||
foreach (string input in inputs)
|
||||
{
|
||||
if (Directory.Exists(input))
|
||||
{
|
||||
List<string> directories = GetDirectoriesOrdered(input);
|
||||
foreach (string dir in directories)
|
||||
{
|
||||
try
|
||||
{
|
||||
outputs.Add(Path.GetFullPath(dir) + (appendparent ? $"¬{Path.GetFullPath(input)}" : string.Empty));
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
Globals.Logger.Warning($"The path for '{dir}' was too long");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Globals.Logger.Error(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of directories from a directory recursively in proper order
|
||||
/// </summary>
|
||||
/// <param name="dir">Directory to parse</param>
|
||||
/// <returns>List with all new files</returns>
|
||||
private static List<string> GetDirectoriesOrdered(string dir)
|
||||
{
|
||||
return GetDirectoriesOrderedHelper(dir, new List<string>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of directories from a directory recursively in proper order
|
||||
/// </summary>
|
||||
/// <param name="dir">Directory to parse</param>
|
||||
/// <param name="infiles">List representing existing files</param>
|
||||
/// <returns>List with all new files</returns>
|
||||
private static List<string> GetDirectoriesOrderedHelper(string dir, List<string> infiles)
|
||||
{
|
||||
// Take care of the files in the top directory
|
||||
List<string> toadd = Directory.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
toadd.Sort(new NaturalComparer());
|
||||
infiles.AddRange(toadd);
|
||||
|
||||
// Then recurse through and add from the directories
|
||||
foreach (string subDir in toadd)
|
||||
{
|
||||
infiles = GetDirectoriesOrderedHelper(subDir, infiles);
|
||||
}
|
||||
|
||||
// Return the new list
|
||||
return infiles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of just files from inputs
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of strings representing directories and files</param>
|
||||
/// <param name="appendparent">True if the parent name should be appended after the special character "¬", false otherwise (default)</param>
|
||||
/// <returns>List of strings representing just files from the inputs</returns>
|
||||
public static List<string> GetFilesOnly(List<string> inputs, bool appendparent = false)
|
||||
{
|
||||
List<string> outputs = new List<string>();
|
||||
foreach (string input in inputs)
|
||||
{
|
||||
if (Directory.Exists(input))
|
||||
{
|
||||
List<string> files = GetFilesOrdered(input);
|
||||
foreach (string file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
outputs.Add(Path.GetFullPath(file) + (appendparent ? $"¬{Path.GetFullPath(input)}" : string.Empty));
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
Globals.Logger.Warning($"The path for '{file}' was too long");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Globals.Logger.Error(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (File.Exists(input))
|
||||
{
|
||||
try
|
||||
{
|
||||
outputs.Add(Path.GetFullPath(input) + (appendparent ? $"¬{Path.GetFullPath(input)}" : string.Empty));
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
Globals.Logger.Warning($"The path for '{input}' was too long");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Globals.Logger.Error(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of files from a directory recursively in proper order
|
||||
/// </summary>
|
||||
/// <param name="dir">Directory to parse</param>
|
||||
/// <param name="infiles">List representing existing files</param>
|
||||
/// <returns>List with all new files</returns>
|
||||
public static List<string> GetFilesOrdered(string dir)
|
||||
{
|
||||
return GetFilesOrderedHelper(dir, new List<string>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of files from a directory recursively in proper order
|
||||
/// </summary>
|
||||
/// <param name="dir">Directory to parse</param>
|
||||
/// <param name="infiles">List representing existing files</param>
|
||||
/// <returns>List with all new files</returns>
|
||||
private static List<string> GetFilesOrderedHelper(string dir, List<string> infiles)
|
||||
{
|
||||
// Take care of the files in the top directory
|
||||
List<string> toadd = Directory.EnumerateFiles(dir, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
toadd.Sort(new NaturalComparer());
|
||||
infiles.AddRange(toadd);
|
||||
|
||||
// Then recurse through and add from the directories
|
||||
List<string> subDirs = Directory.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
subDirs.Sort(new NaturalComparer());
|
||||
foreach (string subdir in subDirs)
|
||||
{
|
||||
infiles = GetFilesOrderedHelper(subdir, infiles);
|
||||
}
|
||||
|
||||
// Return the new list
|
||||
return infiles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all empty folders within a root folder
|
||||
/// </summary>
|
||||
/// <param name="root">Root directory to parse</param>
|
||||
/// <returns>IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise</returns>
|
||||
public static List<string> ListEmpty(string root)
|
||||
{
|
||||
// Check if the root exists first
|
||||
if (!Directory.Exists(root))
|
||||
return null;
|
||||
|
||||
// If it does and it is empty, return a blank enumerable
|
||||
if (Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories).Count() == 0)
|
||||
return new List<string>();
|
||||
|
||||
// Otherwise, get the complete list
|
||||
return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories)
|
||||
.Where(dir => Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Count() == 0)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to safely delete a directory, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the directory to delete</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
||||
public static bool TryCreateDirectory(string file, bool throwOnError = false)
|
||||
{
|
||||
// Now wrap creating the directory
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(file);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw ex;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to safely delete a directory, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the directory to delete</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
||||
public static bool TryDelete(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the directory exists first
|
||||
if (!Directory.Exists(file))
|
||||
return true;
|
||||
|
||||
// Now wrap deleting the directory
|
||||
try
|
||||
{
|
||||
Directory.Delete(file, true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw ex;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
550
SabreTools.Library/Tools/FileExtensions.cs
Normal file
550
SabreTools.Library/Tools/FileExtensions.cs
Normal file
@@ -0,0 +1,550 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.FileTypes;
|
||||
using SabreTools.Library.Readers;
|
||||
using SabreTools.Library.Skippers;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions to File functionality
|
||||
/// </summary>
|
||||
public static class FileExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Add an aribtrary number of bytes to the inputted file
|
||||
/// </summary>
|
||||
/// <param name="input">File to be appended to</param>
|
||||
/// <param name="output">Outputted file</param>
|
||||
/// <param name="bytesToAddToHead">Bytes to be added to head of file</param>
|
||||
/// <param name="bytesToAddToTail">Bytes to be added to tail of file</param>
|
||||
public static void AppendBytes(string input, string output, byte[] bytesToAddToHead, byte[] bytesToAddToTail)
|
||||
{
|
||||
// If any of the inputs are invalid, skip
|
||||
if (!File.Exists(input))
|
||||
return;
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
using (FileStream fsr = TryOpenRead(input))
|
||||
using (FileStream fsw = TryOpenWrite(output))
|
||||
{
|
||||
#else
|
||||
using FileStream fsr = TryOpenRead(input);
|
||||
using FileStream fsw = TryOpenWrite(output);
|
||||
#endif
|
||||
StreamExtensions.AppendBytes(fsr, fsw, bytesToAddToHead, bytesToAddToTail);
|
||||
#if NET_FRAMEWORK
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get what type of DAT the input file is
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <returns>The DatFormat corresponding to the DAT</returns>
|
||||
public static DatFormat GetDatFormat(this string filename)
|
||||
{
|
||||
// Limit the output formats based on extension
|
||||
if (!PathExtensions.HasValidDatExtension(filename))
|
||||
return 0;
|
||||
|
||||
// Get the extension from the filename
|
||||
string ext = PathExtensions.GetNormalizedExtension(filename);
|
||||
|
||||
// Read the input file, if possible
|
||||
Globals.Logger.Verbose($"Attempting to read file to get format: {filename}");
|
||||
|
||||
// Check if file exists
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Globals.Logger.Warning($"File '{filename}' could not read from!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some formats should only require the extension to know
|
||||
switch (ext)
|
||||
{
|
||||
case "csv":
|
||||
return DatFormat.CSV;
|
||||
case "json":
|
||||
return DatFormat.Json;
|
||||
case "md5":
|
||||
return DatFormat.RedumpMD5;
|
||||
#if NET_FRAMEWORK
|
||||
case "ripemd160":
|
||||
return DatFormat.RedumpRIPEMD160;
|
||||
#endif
|
||||
case "sfv":
|
||||
return DatFormat.RedumpSFV;
|
||||
case "sha1":
|
||||
return DatFormat.RedumpSHA1;
|
||||
case "sha256":
|
||||
return DatFormat.RedumpSHA256;
|
||||
case "sha384":
|
||||
return DatFormat.RedumpSHA384;
|
||||
case "sha512":
|
||||
return DatFormat.RedumpSHA512;
|
||||
case "ssv":
|
||||
return DatFormat.SSV;
|
||||
case "tsv":
|
||||
return DatFormat.TSV;
|
||||
}
|
||||
|
||||
// For everything else, we need to read it
|
||||
try
|
||||
{
|
||||
// Get the first two non-whitespace, non-comment lines to check
|
||||
StreamReader sr = File.OpenText(filename);
|
||||
string first = sr.ReadLine().ToLowerInvariant();
|
||||
while (string.IsNullOrWhiteSpace(first) || first.StartsWith("<!--"))
|
||||
first = sr.ReadLine().ToLowerInvariant();
|
||||
|
||||
string second = sr.ReadLine().ToLowerInvariant();
|
||||
while (string.IsNullOrWhiteSpace(second) || second.StartsWith("<!--"))
|
||||
second = sr.ReadLine().ToLowerInvariant();
|
||||
|
||||
sr.Dispose();
|
||||
|
||||
// If we have an XML-based DAT
|
||||
if (first.Contains("<?xml") && first.Contains("?>"))
|
||||
{
|
||||
if (second.StartsWith("<!doctype datafile"))
|
||||
return DatFormat.Logiqx;
|
||||
|
||||
else if (second.StartsWith("<!doctype mame")
|
||||
|| second.StartsWith("<!doctype m1")
|
||||
|| second.StartsWith("<mame")
|
||||
|| second.StartsWith("<m1"))
|
||||
return DatFormat.Listxml;
|
||||
|
||||
else if (second.StartsWith("<!doctype softwaredb"))
|
||||
return DatFormat.OpenMSX;
|
||||
|
||||
else if (second.StartsWith("<!doctype softwarelist"))
|
||||
return DatFormat.SoftwareList;
|
||||
|
||||
else if (second.StartsWith("<!doctype sabredat"))
|
||||
return DatFormat.SabreDat;
|
||||
|
||||
else if ((second.StartsWith("<dat") && !second.StartsWith("<datafile"))
|
||||
|| second.StartsWith("<?xml-stylesheet"))
|
||||
return DatFormat.OfflineList;
|
||||
|
||||
// Older and non-compliant DATs
|
||||
else
|
||||
return DatFormat.Logiqx;
|
||||
}
|
||||
|
||||
// If we have an SMDB (SHA-256, Filename, SHA-1, MD5, CRC32)
|
||||
else if (Regex.IsMatch(first, @"[0-9a-f]{64}\t.*?\t[0-9a-f]{40}\t[0-9a-f]{32}\t[0-9a-f]{8}"))
|
||||
return DatFormat.EverdriveSMDB;
|
||||
|
||||
// If we have an INI-based DAT
|
||||
else if (first.Contains("[") && first.Contains("]"))
|
||||
return DatFormat.RomCenter;
|
||||
|
||||
// If we have a listroms DAT
|
||||
else if (first.StartsWith("roms required for driver"))
|
||||
return DatFormat.Listrom;
|
||||
|
||||
// If we have a CMP-based DAT
|
||||
else if (first.Contains("clrmamepro"))
|
||||
return DatFormat.ClrMamePro;
|
||||
|
||||
else if (first.Contains("romvault"))
|
||||
return DatFormat.ClrMamePro;
|
||||
|
||||
else if (first.Contains("doscenter"))
|
||||
return DatFormat.DOSCenter;
|
||||
|
||||
else if (first.Contains("#Name;Title;Emulator;CloneOf;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra"))
|
||||
return DatFormat.AttractMode;
|
||||
|
||||
else
|
||||
return DatFormat.ClrMamePro;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a text file's encoding by analyzing its byte order mark (BOM).
|
||||
/// Defaults to ASCII when detection of the text file's endianness fails.
|
||||
/// </summary>
|
||||
/// <param name="filename">The text file to analyze.</param>
|
||||
/// <returns>The detected encoding.</returns>
|
||||
/// <link>http://stackoverflow.com/questions/3825390/effective-way-to-find-any-files-encoding</link>
|
||||
public static Encoding GetEncoding(string filename)
|
||||
{
|
||||
// Read the BOM
|
||||
var bom = new byte[4];
|
||||
FileStream file = FileExtensions.TryOpenRead(filename);
|
||||
file.Read(bom, 0, 4);
|
||||
file.Dispose();
|
||||
|
||||
// Analyze the BOM
|
||||
if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7;
|
||||
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
|
||||
if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
|
||||
if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
|
||||
if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32;
|
||||
return Encoding.Default;
|
||||
}
|
||||
|
||||
/// <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(this 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 (!PathExtensions.HasValidArchiveExtension(input))
|
||||
return outFileType;
|
||||
|
||||
// Read the first bytes of the file and get the magic number
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[8];
|
||||
BinaryReader br = new BinaryReader(TryOpenRead(input));
|
||||
magic = br.ReadBytes(8);
|
||||
br.Dispose();
|
||||
|
||||
// Now try to match it to a known signature
|
||||
if (magic.StartsWith(Constants.SevenZipSignature))
|
||||
{
|
||||
outFileType = FileType.SevenZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.CHDSignature))
|
||||
{
|
||||
outFileType = FileType.CHD;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.GzSignature))
|
||||
{
|
||||
outFileType = FileType.GZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.LRZipSignature))
|
||||
{
|
||||
outFileType = FileType.LRZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.LZ4Signature)
|
||||
|| magic.StartsWith(Constants.LZ4SkippableMinSignature)
|
||||
|| magic.StartsWith(Constants.LZ4SkippableMaxSignature))
|
||||
{
|
||||
outFileType = FileType.LZ4Archive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.RarSignature)
|
||||
|| magic.StartsWith(Constants.RarFiveSignature))
|
||||
{
|
||||
outFileType = FileType.RarArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.TarSignature)
|
||||
|| magic.StartsWith(Constants.TarZeroSignature))
|
||||
{
|
||||
outFileType = FileType.TapeArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.XZSignature))
|
||||
{
|
||||
outFileType = FileType.XZArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.ZipSignature)
|
||||
|| magic.StartsWith(Constants.ZipSignatureEmpty)
|
||||
|| magic.StartsWith(Constants.ZipSignatureSpanned))
|
||||
{
|
||||
outFileType = FileType.ZipArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.ZPAQSignature))
|
||||
{
|
||||
outFileType = FileType.ZPAQArchive;
|
||||
}
|
||||
else if (magic.StartsWith(Constants.ZstdSignature))
|
||||
{
|
||||
outFileType = FileType.ZstdArchive;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Don't log file open errors
|
||||
}
|
||||
|
||||
return outFileType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the first byte array starts with the second array
|
||||
/// </summary>
|
||||
/// <param name="arr1">First byte array to compare</param>
|
||||
/// <param name="arr2">Second byte array to compare</param>
|
||||
/// <param name="exact">True if the input arrays should match exactly, false otherwise (default)</param>
|
||||
/// <returns>True if the first byte array starts with the second, false otherwise</returns>
|
||||
private static bool StartsWith(this byte[] arr1, byte[] arr2, bool exact = false)
|
||||
{
|
||||
// If we have any invalid inputs, we return false
|
||||
if (arr1 == null || arr2 == null
|
||||
|| arr1.Length == 0 || arr2.Length == 0
|
||||
|| arr2.Length > arr1.Length
|
||||
|| (exact && arr1.Length != arr2.Length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, loop through and see
|
||||
for (int i = 0; i < arr2.Length; i++)
|
||||
{
|
||||
if (arr1[i] != arr2[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single file
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <param name="omitFromScan">Hash flag saying what hashes should not be calculated (defaults to none)</param>
|
||||
/// <param name="date">True if the file Date should be included, false otherwise (default)</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="chdsAsFiles">True if CHDs should be treated like regular files, false otherwise</param>
|
||||
/// <returns>Populated BaseFile object if success, empty one on error</returns>
|
||||
public static BaseFile GetInfo(string input, Hash omitFromScan = 0x0, bool date = false, string header = null, bool chdsAsFiles = true)
|
||||
{
|
||||
// Add safeguard if file doesn't exist
|
||||
if (!File.Exists(input))
|
||||
return null;
|
||||
|
||||
// Get the information from the file stream
|
||||
BaseFile baseFile;
|
||||
if (header != null)
|
||||
{
|
||||
SkipperRule rule = Skipper.GetMatchingRule(input, Path.GetFileNameWithoutExtension(header));
|
||||
|
||||
// If there's a match, get the new information from the stream
|
||||
if (rule.Tests != null && rule.Tests.Count != 0)
|
||||
{
|
||||
// Create the input and output streams
|
||||
MemoryStream outputStream = new MemoryStream();
|
||||
FileStream inputStream = TryOpenRead(input);
|
||||
|
||||
// Transform the stream and get the information from it
|
||||
rule.TransformStream(inputStream, outputStream, keepReadOpen: false, keepWriteOpen: true);
|
||||
baseFile = outputStream.GetInfo(omitFromScan: omitFromScan, keepReadOpen: false, chdsAsFiles: chdsAsFiles);
|
||||
|
||||
// Dispose of the streams
|
||||
outputStream.Dispose();
|
||||
inputStream.Dispose();
|
||||
}
|
||||
// Otherwise, just get the info
|
||||
else
|
||||
{
|
||||
baseFile = TryOpenRead(input).GetInfo(omitFromScan: omitFromScan, keepReadOpen: false, chdsAsFiles: chdsAsFiles);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
baseFile = TryOpenRead(input).GetInfo(omitFromScan: omitFromScan, keepReadOpen: false, chdsAsFiles: chdsAsFiles);
|
||||
}
|
||||
|
||||
// Add unique data from the file
|
||||
baseFile.Filename = Path.GetFileName(input);
|
||||
baseFile.Date = (date ? new FileInfo(input).LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss") : string.Empty);
|
||||
|
||||
return baseFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the IniReader associated with a file, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="validateRows">True if rows should be in a proper format, false if invalid is okay</param>
|
||||
/// <returns>The IniReader representing the (possibly converted) file, null otherwise</returns>
|
||||
public static IniReader GetIniReader(this string filename, bool validateRows)
|
||||
{
|
||||
Globals.Logger.Verbose($"Attempting to read file: {filename}");
|
||||
|
||||
// Check if file exists
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Globals.Logger.Warning($"File '{filename}' could not read from!");
|
||||
return null;
|
||||
}
|
||||
|
||||
IniReader ir = new IniReader(filename)
|
||||
{
|
||||
ValidateRows = validateRows
|
||||
};
|
||||
return ir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the XmlTextReader associated with a file, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <returns>The XmlTextReader representing the (possibly converted) file, null otherwise</returns>
|
||||
public static XmlReader GetXmlTextReader(this string filename)
|
||||
{
|
||||
Globals.Logger.Verbose($"Attempting to read file: {filename}");
|
||||
|
||||
// Check if file exists
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Globals.Logger.Warning($"File '{filename}' could not read from!");
|
||||
return null;
|
||||
}
|
||||
|
||||
XmlReader xtr = XmlReader.Create(filename, new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
IgnoreComments = true,
|
||||
IgnoreWhitespace = true,
|
||||
ValidationFlags = XmlSchemaValidationFlags.None,
|
||||
ValidationType = ValidationType.None,
|
||||
});
|
||||
|
||||
return xtr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to create a file for write, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to create</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryCreate(string file, bool throwOnError = false)
|
||||
{
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw ex;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to safely delete a file, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to delete</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
||||
public static bool TryDelete(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
return true;
|
||||
|
||||
// Now wrap deleting the file
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw ex;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to open a file for read, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to open</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryOpenRead(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw ex;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to open a file for read/write, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to open</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryOpenReadWrite(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw ex;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to open an existing file for write, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to open</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryOpenWrite(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Open, FileAccess.Write, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw ex;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace SabreTools.Library.Tools
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
Globals.Logger.User($"{_subject} completed in {DateTime.Now.Subtract(_startTime).ToString("hh:mm:ss.fffff")}");
|
||||
Globals.Logger.User($"{_subject} completed in {DateTime.Now.Subtract(_startTime):G}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,17 +13,17 @@ namespace SabreTools.Library.Tools
|
||||
public class Logger
|
||||
{
|
||||
// Private instance variables
|
||||
private bool _tofile;
|
||||
private readonly bool _tofile;
|
||||
private bool _warnings;
|
||||
private bool _errors;
|
||||
private string _filename;
|
||||
private LogLevel _filter;
|
||||
private readonly string _filename;
|
||||
private readonly LogLevel _filter;
|
||||
private DateTime _start;
|
||||
private StreamWriter _log;
|
||||
private object _lock = new object(); // This is used during multithreaded logging
|
||||
private readonly object _lock = new object(); // This is used during multithreaded logging
|
||||
|
||||
// Private required variables
|
||||
private string _basepath = Path.Combine(Globals.ExeDir, "logs") + Path.DirectorySeparatorChar;
|
||||
private readonly string _basepath = Path.Combine(Globals.ExeDir, "logs") + Path.DirectorySeparatorChar;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a console-only logger object
|
||||
@@ -50,7 +50,7 @@ namespace SabreTools.Library.Tools
|
||||
_tofile = tofile;
|
||||
_warnings = false;
|
||||
_errors = false;
|
||||
_filename = $"{Path.GetFileNameWithoutExtension(filename)} ({DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss")}).{Utilities.GetExtension(filename)}";
|
||||
_filename = $"{Path.GetFileNameWithoutExtension(filename)} ({DateTime.Now:yyyy-MM-dd HH-mm-ss}).{PathExtensions.GetNormalizedExtension(filename)}";
|
||||
_filter = filter;
|
||||
|
||||
if (!Directory.Exists(_basepath))
|
||||
@@ -71,11 +71,13 @@ namespace SabreTools.Library.Tools
|
||||
|
||||
try
|
||||
{
|
||||
FileStream logfile = Utilities.TryCreate(Path.Combine(_basepath, _filename));
|
||||
_log = new StreamWriter(logfile, Encoding.UTF8, (int)(4 * Constants.KibiByte), true);
|
||||
_log.AutoFlush = true;
|
||||
FileStream logfile = FileExtensions.TryCreate(Path.Combine(_basepath, _filename));
|
||||
_log = new StreamWriter(logfile, Encoding.UTF8, (int)(4 * Constants.KibiByte), true)
|
||||
{
|
||||
AutoFlush = true
|
||||
};
|
||||
|
||||
_log.WriteLine($"Logging started {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
_log.WriteLine($"Logging started {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
_log.WriteLine($"Command run: {Globals.CommandLineArgs}");
|
||||
}
|
||||
catch
|
||||
@@ -104,7 +106,7 @@ namespace SabreTools.Library.Tools
|
||||
TimeSpan span = DateTime.Now.Subtract(_start);
|
||||
|
||||
// Special case for multi-day runs
|
||||
string total = string.Empty;
|
||||
string total;
|
||||
if (span >= TimeSpan.FromDays(1))
|
||||
total = span.ToString(@"d\:hh\:mm\:ss");
|
||||
else
|
||||
@@ -118,7 +120,7 @@ namespace SabreTools.Library.Tools
|
||||
|
||||
try
|
||||
{
|
||||
_log.WriteLine($"Logging ended {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
_log.WriteLine($"Logging ended {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
_log.WriteLine($"Total runtime: {total}");
|
||||
Console.WriteLine($"Total runtime: {total}");
|
||||
_log.Close();
|
||||
|
||||
223
SabreTools.Library/Tools/PathExtensions.cs
Normal file
223
SabreTools.Library/Tools/PathExtensions.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions to Path functionality
|
||||
/// </summary>
|
||||
public static class PathExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the extension from the path, if possible
|
||||
/// </summary>
|
||||
/// <param name="path">Path to get extension from</param>
|
||||
/// <returns>Extension, if possible</returns>
|
||||
public static string GetNormalizedExtension(string path)
|
||||
{
|
||||
// Check null or empty first
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return null;
|
||||
|
||||
// Get the extension from the path, if possible
|
||||
string ext = Path.GetExtension(path)?.ToLowerInvariant();
|
||||
|
||||
// Check if the extension is null or empty
|
||||
if (string.IsNullOrWhiteSpace(ext))
|
||||
return null;
|
||||
|
||||
// Make sure that extensions are valid
|
||||
ext = ext.TrimStart('.');
|
||||
|
||||
return ext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the proper filename (with subpath) from the file and parent combination
|
||||
/// </summary>
|
||||
/// <param name="path">Input combined path to use</param>
|
||||
/// <param name="sanitize">True if path separators should be converted to '-', false otherwise</param>
|
||||
/// <returns>Subpath for the file</returns>
|
||||
public static string GetNormalizedFileName(string path, bool sanitize)
|
||||
{
|
||||
// Check that we have a combined path first
|
||||
if (!path.Contains("¬"))
|
||||
{
|
||||
string filename = Path.GetFileName(path);
|
||||
if (sanitize)
|
||||
filename.Replace(Path.DirectorySeparatorChar, '-').Replace(Path.AltDirectorySeparatorChar, '-');
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
// First separate out the parts
|
||||
string child = path.Split('¬')[0];
|
||||
string parent = path.Split('¬')[1];
|
||||
|
||||
// If the parts are the same, return the filename from the first part
|
||||
if (string.Equals(child, parent, StringComparison.Ordinal))
|
||||
{
|
||||
string filename = Path.GetFileName(child);
|
||||
if (sanitize)
|
||||
filename.Replace(Path.DirectorySeparatorChar, '-').Replace(Path.AltDirectorySeparatorChar, '-');
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
// Otherwise, remove the parent from the child and return the remainder
|
||||
else
|
||||
{
|
||||
string filename = child.Remove(0, parent.Length + 1);
|
||||
if (sanitize)
|
||||
filename.Replace(Path.DirectorySeparatorChar, '-').Replace(Path.AltDirectorySeparatorChar, '-');
|
||||
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the proper output path for a given input file and output directory
|
||||
/// </summary>
|
||||
/// <param name="outDir">Output directory to use</param>
|
||||
/// <param name="inputpath">Input path to create output for</param>
|
||||
/// <param name="inplace">True if the output file should go to the same input folder, false otherwise</param>
|
||||
/// <returns>Complete output path</returns>
|
||||
public static string GetOutputPath(string outDir, string inputpath, bool inplace)
|
||||
{
|
||||
// First, we need to ensure the output directory
|
||||
outDir = DirectoryExtensions.Ensure(outDir);
|
||||
|
||||
// Check if we have a split path or not
|
||||
bool splitpath = inputpath.Contains("¬");
|
||||
|
||||
// If we have a split path, we need to treat the input separately
|
||||
if (splitpath)
|
||||
{
|
||||
string[] split = inputpath.Split('¬');
|
||||
|
||||
// If we have an inplace output, use the directory name from the input path
|
||||
if (inplace)
|
||||
{
|
||||
outDir = Path.GetDirectoryName(split[0]);
|
||||
}
|
||||
|
||||
// TODO: Should this be the default? Always create a subfolder if a folder is found?
|
||||
// If we are processing a path that is coming from a directory and we are outputting to the current directory, we want to get the subfolder to write to
|
||||
else if (split[0].Length != split[1].Length && outDir == Environment.CurrentDirectory)
|
||||
{
|
||||
outDir = Path.GetDirectoryName(Path.Combine(outDir, split[0].Remove(0, Path.GetDirectoryName(split[1]).Length + 1)));
|
||||
}
|
||||
|
||||
// If we are processing a path that is coming from a directory, we want to get the subfolder to write to
|
||||
else if (split[0].Length != split[1].Length)
|
||||
{
|
||||
outDir = Path.GetDirectoryName(Path.Combine(outDir, split[0].Remove(0, split[1].Length + 1)));
|
||||
}
|
||||
|
||||
// If we are processing a single file from the root of a directory, we just use the output directory
|
||||
else
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
// Otherwise, assume the input path is just a filename
|
||||
else
|
||||
{
|
||||
// If we have an inplace output, use the directory name from the input path
|
||||
if (inplace)
|
||||
{
|
||||
outDir = Path.GetDirectoryName(inputpath);
|
||||
}
|
||||
|
||||
// Otherwise, just use the supplied output directory
|
||||
else
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, return the output directory
|
||||
return outDir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a proper romba sub path
|
||||
/// </summary>
|
||||
/// <param name="hash">SHA-1 hash to get the path for</param>
|
||||
/// <returns>Subfolder path for the given hash</returns>
|
||||
public static string GetRombaPath(string hash)
|
||||
{
|
||||
// If the hash isn't the right size, then we return null
|
||||
if (hash.Length != Constants.SHA1Length) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length
|
||||
return null;
|
||||
|
||||
return Path.Combine(hash.Substring(0, 2), hash.Substring(2, 2), hash.Substring(4, 2), hash.Substring(6, 2), hash + ".gz");
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static bool HasValidArchiveExtension(string path)
|
||||
{
|
||||
// Get the extension from the path, if possible
|
||||
string ext = PathExtensions.GetNormalizedExtension(path);
|
||||
|
||||
// Check against the list of known archive extensions
|
||||
switch (ext)
|
||||
{
|
||||
case "7z":
|
||||
case "gz":
|
||||
case "lzma":
|
||||
case "rar":
|
||||
case "rev":
|
||||
case "r00":
|
||||
case "r01":
|
||||
case "tar":
|
||||
case "tgz":
|
||||
case "tlz":
|
||||
case "zip":
|
||||
case "zipx":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static bool HasValidDatExtension(string path)
|
||||
{
|
||||
// Get the extension from the path, if possible
|
||||
string ext = GetNormalizedExtension(path);
|
||||
|
||||
// Check against the list of known DAT extensions
|
||||
switch (ext)
|
||||
{
|
||||
case "csv":
|
||||
case "dat":
|
||||
case "json":
|
||||
case "md5":
|
||||
case "ripemd160":
|
||||
case "sfv":
|
||||
case "sha1":
|
||||
case "sha256":
|
||||
case "sha384":
|
||||
case "sha512":
|
||||
case "ssv":
|
||||
case "tsv":
|
||||
case "txt":
|
||||
case "xml":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
413
SabreTools.Library/Tools/Sanitizer.cs
Normal file
413
SabreTools.Library/Tools/Sanitizer.cs
Normal file
@@ -0,0 +1,413 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
public static class Sanitizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a sanitized Date from an input string
|
||||
/// </summary>
|
||||
/// <param name="input">String to get value from</param>
|
||||
/// <returns>Date as a string, if possible</returns>
|
||||
public static string CleanDate(string input)
|
||||
{
|
||||
string date = string.Empty;
|
||||
if (input != null)
|
||||
{
|
||||
if (DateTime.TryParse(input, out DateTime dateTime))
|
||||
date = dateTime.ToString();
|
||||
else
|
||||
date = input;
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a game (or rom) name to the WoD standard
|
||||
/// </summary>
|
||||
/// <param name="game">Name of the game to be cleaned</param>
|
||||
/// <returns>The cleaned name</returns>
|
||||
public static string CleanGameName(string game)
|
||||
{
|
||||
///Run the name through the filters to make sure that it's correct
|
||||
game = NormalizeChars(game);
|
||||
game = RussianToLatin(game);
|
||||
game = SearchPattern(game);
|
||||
|
||||
game = new Regex(@"(([[(].*[\)\]] )?([^([]+))").Match(game).Groups[1].Value;
|
||||
game = game.TrimStart().TrimEnd();
|
||||
return game;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a game (or rom) name to the WoD standard
|
||||
/// </summary>
|
||||
/// <param name="game">Array representing the path to be cleaned</param>
|
||||
/// <returns>The cleaned name</returns>
|
||||
public static string CleanGameName(string[] game)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
game[game.Length - 1] = CleanGameName(game.Last());
|
||||
#else
|
||||
game[^1] = CleanGameName(game[^1]);
|
||||
#endif
|
||||
string outgame = string.Join(Path.DirectorySeparatorChar.ToString(), game);
|
||||
outgame = outgame.TrimStart().TrimEnd();
|
||||
return outgame;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a CRC32 string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanCRC32(string hash)
|
||||
{
|
||||
return CleanHashData(hash, Constants.CRCLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a MD5 string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanMD5(string hash)
|
||||
{
|
||||
return CleanHashData(hash, Constants.MD5Length);
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
/// <summary>
|
||||
/// Clean a RIPEMD160 string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanRIPEMD160(string hash)
|
||||
{
|
||||
return CleanHashData(hash, Constants.RIPEMD160Length);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Clean a SHA1 string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanSHA1(string hash)
|
||||
{
|
||||
return CleanHashData(hash, Constants.SHA1Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a SHA256 string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanSHA256(string hash)
|
||||
{
|
||||
return CleanHashData(hash, Constants.SHA256Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a SHA384 string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanSHA384(string hash)
|
||||
{
|
||||
return CleanHashData(hash, Constants.SHA384Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a SHA512 string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanSHA512(string hash)
|
||||
{
|
||||
return CleanHashData(hash, Constants.SHA512Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a hash string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <param name="padding">Amount of characters to pad to</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
private static string CleanHashData(string hash, int padding)
|
||||
{
|
||||
// If we have a known blank hash, return blank
|
||||
if (string.IsNullOrWhiteSpace(hash) || hash == "-" || hash == "_")
|
||||
return string.Empty;
|
||||
|
||||
// Check to see if it's a "hex" hash
|
||||
hash = hash.Trim().Replace("0x", string.Empty);
|
||||
|
||||
// If we have a blank hash now, return blank
|
||||
if (string.IsNullOrWhiteSpace(hash))
|
||||
return string.Empty;
|
||||
|
||||
// If the hash shorter than the required length, pad it
|
||||
if (hash.Length < padding)
|
||||
hash = hash.PadLeft(padding, '0');
|
||||
|
||||
// If the hash is longer than the required length, it's invalid
|
||||
else if (hash.Length > padding)
|
||||
return string.Empty;
|
||||
|
||||
// Now normalize the hash
|
||||
hash = hash.ToLowerInvariant();
|
||||
|
||||
// Otherwise, make sure that every character is a proper match
|
||||
for (int i = 0; i < hash.Length; i++)
|
||||
{
|
||||
if ((hash[i] < '0' || hash[i] > '9') && (hash[i] < 'a' || hash[i] > 'f'))
|
||||
{
|
||||
hash = string.Empty;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a hash string from a Listrom DAT
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanListromHashData(string hash)
|
||||
{
|
||||
if (hash.StartsWith("CRC"))
|
||||
return hash.Substring(4, 8).ToLowerInvariant();
|
||||
|
||||
else if (hash.StartsWith("SHA1"))
|
||||
return hash.Substring(5, 40).ToLowerInvariant();
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a sanitized size from an input string
|
||||
/// </summary>
|
||||
/// <param name="input">String to get value from</param>
|
||||
/// <returns>Size as a long, if possible</returns>
|
||||
public static long CleanSize(string input)
|
||||
{
|
||||
long size = -1;
|
||||
if (input != null && input.Contains("0x"))
|
||||
size = Convert.ToInt64(input, 16);
|
||||
|
||||
else if (input != null)
|
||||
Int64.TryParse(input, out size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all chars that are considered path unsafe
|
||||
/// </summary>
|
||||
/// <param name="s">Input string to clean</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string RemovePathUnsafeCharacters(string s)
|
||||
{
|
||||
List<char> invalidPath = Path.GetInvalidPathChars().ToList();
|
||||
return new string(s.Where(c => !invalidPath.Contains(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all unicode-specific chars from a string
|
||||
/// </summary>
|
||||
/// <param name="s">Input string to clean</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string RemoveUnicodeCharacters(string s)
|
||||
{
|
||||
return new string(s.Where(c => c <= 255).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace accented characters
|
||||
/// </summary>
|
||||
/// <param name="input">String to be parsed</param>
|
||||
/// <returns>String with characters replaced</returns>
|
||||
private static string NormalizeChars(string input)
|
||||
{
|
||||
string[,] charmap = {
|
||||
{ "Á", "A" }, { "á", "a" },
|
||||
{ "À", "A" }, { "à", "a" },
|
||||
{ "Â", "A" }, { "â", "a" },
|
||||
{ "Ä", "Ae" }, { "ä", "ae" },
|
||||
{ "Ã", "A" }, { "ã", "a" },
|
||||
{ "Å", "A" }, { "å", "a" },
|
||||
{ "Æ", "Ae" }, { "æ", "ae" },
|
||||
{ "Ç", "C" }, { "ç", "c" },
|
||||
{ "Ð", "D" }, { "ð", "d" },
|
||||
{ "É", "E" }, { "é", "e" },
|
||||
{ "È", "E" }, { "è", "e" },
|
||||
{ "Ê", "E" }, { "ê", "e" },
|
||||
{ "Ë", "E" }, { "ë", "e" },
|
||||
{ "ƒ", "f" },
|
||||
{ "Í", "I" }, { "í", "i" },
|
||||
{ "Ì", "I" }, { "ì", "i" },
|
||||
{ "Î", "I" }, { "î", "i" },
|
||||
{ "Ï", "I" }, { "ï", "i" },
|
||||
{ "Ñ", "N" }, { "ñ", "n" },
|
||||
{ "Ó", "O" }, { "ó", "o" },
|
||||
{ "Ò", "O" }, { "ò", "o" },
|
||||
{ "Ô", "O" }, { "ô", "o" },
|
||||
{ "Ö", "Oe" }, { "ö", "oe" },
|
||||
{ "Õ", "O" }, { "õ", "o" },
|
||||
{ "Ø", "O" }, { "ø", "o" },
|
||||
{ "Š", "S" }, { "š", "s" },
|
||||
{ "ß", "ss" },
|
||||
{ "Þ", "B" }, { "þ", "b" },
|
||||
{ "Ú", "U" }, { "ú", "u" },
|
||||
{ "Ù", "U" }, { "ù", "u" },
|
||||
{ "Û", "U" }, { "û", "u" },
|
||||
{ "Ü", "Ue" }, { "ü", "ue" },
|
||||
{ "ÿ", "y" },
|
||||
{ "Ý", "Y" }, { "ý", "y" },
|
||||
{ "Ž", "Z" }, { "ž", "z" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < charmap.GetLength(0); i++)
|
||||
{
|
||||
input = input.Replace(charmap[i, 0], charmap[i, 1]);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Cyrillic lettering to Latin lettering
|
||||
/// </summary>
|
||||
/// <param name="input">String to be parsed</param>
|
||||
/// <returns>String with characters replaced</returns>
|
||||
private static string RussianToLatin(string input)
|
||||
{
|
||||
string[,] charmap = {
|
||||
{ "А", "A" }, { "Б", "B" }, { "В", "V" }, { "Г", "G" }, { "Д", "D" },
|
||||
{ "Е", "E" }, { "Ё", "Yo" }, { "Ж", "Zh" }, { "З", "Z" }, { "И", "I" },
|
||||
{ "Й", "J" }, { "К", "K" }, { "Л", "L" }, { "М", "M" }, { "Н", "N" },
|
||||
{ "О", "O" }, { "П", "P" }, { "Р", "R" }, { "С", "S" }, { "Т", "T" },
|
||||
{ "У", "U" }, { "Ф", "f" }, { "Х", "Kh" }, { "Ц", "Ts" }, { "Ч", "Ch" },
|
||||
{ "Ш", "Sh" }, { "Щ", "Sch" }, { "Ъ", string.Empty }, { "Ы", "y" }, { "Ь", string.Empty },
|
||||
{ "Э", "e" }, { "Ю", "yu" }, { "Я", "ya" }, { "а", "a" }, { "б", "b" },
|
||||
{ "в", "v" }, { "г", "g" }, { "д", "d" }, { "е", "e" }, { "ё", "yo" },
|
||||
{ "ж", "zh" }, { "з", "z" }, { "и", "i" }, { "й", "j" }, { "к", "k" },
|
||||
{ "л", "l" }, { "м", "m" }, { "н", "n" }, { "о", "o" }, { "п", "p" },
|
||||
{ "р", "r" }, { "с", "s" }, { "т", "t" }, { "у", "u" }, { "ф", "f" },
|
||||
{ "х", "kh" }, { "ц", "ts" }, { "ч", "ch" }, { "ш", "sh" }, { "щ", "sch" },
|
||||
{ "ъ", string.Empty }, { "ы", "y" }, { "ь", string.Empty }, { "э", "e" }, { "ю", "yu" },
|
||||
{ "я", "ya" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < charmap.GetLength(0); i++)
|
||||
{
|
||||
input = input.Replace(charmap[i, 0], charmap[i, 1]);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace special characters and patterns
|
||||
/// </summary>
|
||||
/// <param name="input">String to be parsed</param>
|
||||
/// <returns>String with characters replaced</returns>
|
||||
private static string SearchPattern(string input)
|
||||
{
|
||||
string[,] charmap = {
|
||||
{ @"~", " - " },
|
||||
{ @"_", " " },
|
||||
{ @":", " " },
|
||||
{ @">", ")" },
|
||||
{ @"<", "(" },
|
||||
{ @"\|", "-" },
|
||||
{ "\"", "'" },
|
||||
{ @"\*", "." },
|
||||
{ @"\\", "-" },
|
||||
{ @"/", "-" },
|
||||
{ @"\?", " " },
|
||||
{ @"\(([^)(]*)\(([^)]*)\)([^)(]*)\)", " " },
|
||||
{ @"\(([^)]+)\)", " " },
|
||||
{ @"\[([^]]+)\]", " " },
|
||||
{ @"\{([^}]+)\}", " " },
|
||||
{ @"(ZZZJUNK|ZZZ-UNK-|ZZZ-UNK |zzz unknow |zzz unk |Copy of |[.][a-z]{3}[.][a-z]{3}[.]|[.][a-z]{3}[.])", " " },
|
||||
{ @" (r|rev|v|ver)\s*[\d\.]+[^\s]*", " " },
|
||||
{ @"(( )|(\A))(\d{6}|\d{8})(( )|(\Z))", " " },
|
||||
{ @"(( )|(\A))(\d{1,2})-(\d{1,2})-(\d{4}|\d{2})", " " },
|
||||
{ @"(( )|(\A))(\d{4}|\d{2})-(\d{1,2})-(\d{1,2})", " " },
|
||||
{ @"[-]+", "-" },
|
||||
{ @"\A\s*\)", " " },
|
||||
{ @"\A\s*(,|-)", " " },
|
||||
{ @"\s+", " " },
|
||||
{ @"\s+,", "," },
|
||||
{ @"\s*(,|-)\s*\Z", " " },
|
||||
};
|
||||
|
||||
for (int i = 0; i < charmap.GetLength(0); i++)
|
||||
{
|
||||
input = Regex.Replace(input, charmap[i, 0], charmap[i, 1]);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the multiplier to be used with the size given
|
||||
/// </summary>
|
||||
/// <param name="sizestring">String with possible size with extension</param>
|
||||
/// <returns>Tuple of multiplier to use on final size and fixed size string</returns>
|
||||
public static long ToSize(string sizestring)
|
||||
{
|
||||
// If the string is null or empty, we return -1
|
||||
if (string.IsNullOrWhiteSpace(sizestring))
|
||||
return -1;
|
||||
|
||||
// Make sure the string is in lower case
|
||||
sizestring = sizestring.ToLowerInvariant();
|
||||
|
||||
// Get any trailing size identifiers
|
||||
long multiplier = 1;
|
||||
if (sizestring.EndsWith("k") || sizestring.EndsWith("kb"))
|
||||
multiplier = Constants.KiloByte;
|
||||
else if (sizestring.EndsWith("ki") || sizestring.EndsWith("kib"))
|
||||
multiplier = Constants.KibiByte;
|
||||
else if (sizestring.EndsWith("m") || sizestring.EndsWith("mb"))
|
||||
multiplier = Constants.MegaByte;
|
||||
else if (sizestring.EndsWith("mi") || sizestring.EndsWith("mib"))
|
||||
multiplier = Constants.MibiByte;
|
||||
else if (sizestring.EndsWith("g") || sizestring.EndsWith("gb"))
|
||||
multiplier = Constants.GigaByte;
|
||||
else if (sizestring.EndsWith("gi") || sizestring.EndsWith("gib"))
|
||||
multiplier = Constants.GibiByte;
|
||||
else if (sizestring.EndsWith("t") || sizestring.EndsWith("tb"))
|
||||
multiplier = Constants.TeraByte;
|
||||
else if (sizestring.EndsWith("ti") || sizestring.EndsWith("tib"))
|
||||
multiplier = Constants.TibiByte;
|
||||
else if (sizestring.EndsWith("p") || sizestring.EndsWith("pb"))
|
||||
multiplier = Constants.PetaByte;
|
||||
else if (sizestring.EndsWith("pi") || sizestring.EndsWith("pib"))
|
||||
multiplier = Constants.PibiByte;
|
||||
|
||||
// Remove any trailing identifiers
|
||||
sizestring = sizestring.TrimEnd(new char[] { 'k', 'm', 'g', 't', 'p', 'i', 'b', ' ' });
|
||||
|
||||
// Now try to get the size from the string
|
||||
if (!Int64.TryParse(sizestring, out long size))
|
||||
size = -1;
|
||||
else
|
||||
size *= multiplier;
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
||||
218
SabreTools.Library/Tools/StreamExtensions.cs
Normal file
218
SabreTools.Library/Tools/StreamExtensions.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SabreTools.Library.Data;
|
||||
using SabreTools.Library.FileTypes;
|
||||
using Compress.ThreadReaders;
|
||||
|
||||
namespace SabreTools.Library.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions to Stream functionality
|
||||
/// </summary>
|
||||
public static class StreamExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Add an aribtrary number of bytes to the inputted stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to be appended to</param>
|
||||
/// <param name="output">Outputted stream</param>
|
||||
/// <param name="bytesToAddToHead">Bytes to be added to head of stream</param>
|
||||
/// <param name="bytesToAddToTail">Bytes to be added to tail of stream</param>
|
||||
public static void AppendBytes(Stream input, Stream output, byte[] bytesToAddToHead, byte[] bytesToAddToTail)
|
||||
{
|
||||
// Write out prepended bytes
|
||||
if (bytesToAddToHead != null && bytesToAddToHead.Count() > 0)
|
||||
output.Write(bytesToAddToHead, 0, bytesToAddToHead.Length);
|
||||
|
||||
// Now copy the existing file over
|
||||
input.CopyTo(output);
|
||||
|
||||
// Write out appended bytes
|
||||
if (bytesToAddToTail != null && bytesToAddToTail.Count() > 0)
|
||||
output.Write(bytesToAddToTail, 0, bytesToAddToTail.Length);
|
||||
}
|
||||
|
||||
/// <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="omitFromScan">Hash flag saying what hashes should not be calculated (defaults to none)</param>
|
||||
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
||||
/// <param name="chdsAsFiles">True if CHDs should be treated like regular files, false otherwise</param>
|
||||
/// <returns>Populated BaseFile object if success, empty one on error</returns>
|
||||
public static BaseFile GetInfo(this Stream input, long size = -1, Hash omitFromScan = 0x0, bool keepReadOpen = false, bool chdsAsFiles = true)
|
||||
{
|
||||
return GetInfoAsync(input, size, omitFromScan, keepReadOpen, chdsAsFiles).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <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="omitFromScan">Hash flag saying what hashes should not be calculated (defaults to none)</param>
|
||||
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
||||
/// <param name="chdsAsFiles">True if CHDs should be treated like regular files, false otherwise</param>
|
||||
/// <returns>Populated BaseFile object if success, empty one on error</returns>
|
||||
public static async Task<BaseFile> GetInfoAsync(Stream input, long size = -1, Hash omitFromScan = 0x0, bool keepReadOpen = false, bool chdsAsFiles = true)
|
||||
{
|
||||
// If we want to automatically set the size
|
||||
if (size == -1)
|
||||
size = input.Length;
|
||||
|
||||
// We first check to see if it's a CHD if we have to
|
||||
if (!chdsAsFiles)
|
||||
{
|
||||
var chd = CHDFile.Create(input);
|
||||
input.SeekIfPossible();
|
||||
|
||||
// If we found a valid CHD
|
||||
if (chd != null)
|
||||
{
|
||||
if (!keepReadOpen)
|
||||
input.Dispose();
|
||||
|
||||
return chd;
|
||||
}
|
||||
}
|
||||
|
||||
BaseFile rom = new BaseFile()
|
||||
{
|
||||
Size = size,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
// Get a list of hashers to run over the buffer
|
||||
List<Hasher> hashers = new List<Hasher>()
|
||||
{
|
||||
new Hasher(Hash.CRC),
|
||||
new Hasher(Hash.MD5),
|
||||
#if NET_FRAMEWORK
|
||||
new Hasher(Hash.RIPEMD160),
|
||||
#endif
|
||||
new Hasher(Hash.SHA1),
|
||||
new Hasher(Hash.SHA256),
|
||||
new Hasher(Hash.SHA384),
|
||||
new Hasher(Hash.SHA512),
|
||||
};
|
||||
|
||||
// Initialize the hashing helpers
|
||||
var loadBuffer = new ThreadLoadBuffer(input);
|
||||
int buffersize = 3 * 1024 * 1024;
|
||||
byte[] buffer0 = new byte[buffersize];
|
||||
byte[] buffer1 = new byte[buffersize];
|
||||
|
||||
/*
|
||||
Please note that some of the following code is adapted from
|
||||
RomVault. This is a modified version of how RomVault does
|
||||
threaded hashing. As such, some of the terminology and code
|
||||
is the same, though variable names and comments may have
|
||||
been tweaked to better fit this code base.
|
||||
*/
|
||||
|
||||
// Pre load the first buffer
|
||||
int next = size > buffersize ? buffersize : (int)size;
|
||||
input.Read(buffer0, 0, next);
|
||||
int current = next;
|
||||
size -= next;
|
||||
bool bufferSelect = true;
|
||||
|
||||
while (current > 0)
|
||||
{
|
||||
// Trigger the buffer load on the second buffer
|
||||
next = size > buffersize ? buffersize : (int)size;
|
||||
if (next > 0)
|
||||
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
|
||||
|
||||
byte[] buffer = bufferSelect ? buffer0 : buffer1;
|
||||
|
||||
// Run hashes in parallel
|
||||
await Task.WhenAll(hashers.Select(h => h.Process(buffer, current)));
|
||||
|
||||
// Wait for the load buffer worker, if needed
|
||||
if (next > 0)
|
||||
loadBuffer.Wait();
|
||||
|
||||
// Setup for the next hashing step
|
||||
current = next;
|
||||
size -= next;
|
||||
bufferSelect = !bufferSelect;
|
||||
}
|
||||
|
||||
// Finalize all hashing helpers
|
||||
loadBuffer.Finish();
|
||||
await Task.WhenAll(hashers.Select(h => h.Finalize()));
|
||||
|
||||
// Get the results
|
||||
if (!omitFromScan.HasFlag(Hash.CRC))
|
||||
rom.CRC = hashers.First(h => h.HashType == Hash.CRC).GetHash();
|
||||
if (!omitFromScan.HasFlag(Hash.MD5))
|
||||
rom.MD5 = hashers.First(h => h.HashType == Hash.MD5).GetHash();
|
||||
#if NET_FRAMEWORK
|
||||
if (!omitFromScan.HasFlag(Hash.RIPEMD160))
|
||||
rom.RIPEMD160 = hashers.First(h => h.HashType == Hash.RIPEMD160).GetHash();
|
||||
#endif
|
||||
if (!omitFromScan.HasFlag(Hash.SHA1))
|
||||
rom.SHA1 = hashers.First(h => h.HashType == Hash.SHA1).GetHash();
|
||||
if (!omitFromScan.HasFlag(Hash.SHA256))
|
||||
rom.SHA256 = hashers.First(h => h.HashType == Hash.SHA256).GetHash();
|
||||
if (!omitFromScan.HasFlag(Hash.SHA384))
|
||||
rom.SHA384 = hashers.First(h => h.HashType == Hash.SHA384).GetHash();
|
||||
if (!omitFromScan.HasFlag(Hash.SHA512))
|
||||
rom.SHA512 = hashers.First(h => h.HashType == Hash.SHA512).GetHash();
|
||||
|
||||
// Dispose of the hashers
|
||||
loadBuffer.Dispose();
|
||||
hashers.ForEach(h => h.Dispose());
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return new BaseFile();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!keepReadOpen)
|
||||
input.Dispose();
|
||||
else
|
||||
input.SeekIfPossible();
|
||||
}
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek to a specific point in the stream, if possible
|
||||
/// </summary>
|
||||
/// <param name="input">Input stream to try seeking on</param>
|
||||
/// <param name="offset">Optional offset to seek to</param>
|
||||
private static long SeekIfPossible(this Stream input, long offset = 0)
|
||||
{
|
||||
if (input.CanSeek)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (offset < 0)
|
||||
return input.Seek(offset, SeekOrigin.End);
|
||||
else if (offset >= 0)
|
||||
return input.Seek(offset, SeekOrigin.Begin);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
Globals.Logger.Verbose("Stream does not support seeking to starting offset. Stream position not changed");
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
Globals.Logger.Warning("Stream does not support seeking to starting offset. Stream position not changed");
|
||||
}
|
||||
}
|
||||
|
||||
return input.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Reference in New Issue
Block a user