2016-10-24 12:58:57 -07:00
using System ;
2016-04-19 01:11:23 -07:00
using System.Collections.Generic ;
2016-04-19 15:41:06 -07:00
using System.Linq ;
2016-09-01 20:38:41 -07:00
using System.Text ;
2016-04-19 01:11:23 -07:00
using System.Text.RegularExpressions ;
2016-09-07 10:58:21 -07:00
using System.Threading.Tasks ;
2016-09-01 20:38:41 -07:00
using System.Web ;
2016-04-19 01:11:23 -07:00
using System.Xml ;
2016-10-24 13:51:39 -07:00
2016-10-24 12:58:57 -07:00
using SabreTools.Helper.Data ;
2016-10-25 10:38:26 -07:00
using SabreTools.Helper.Skippers ;
2016-10-24 12:58:57 -07:00
using SabreTools.Helper.Tools ;
2016-10-24 13:51:39 -07:00
2016-10-26 22:10:47 -07:00
using Alphaleonis.Win32.Filesystem ;
2016-10-24 12:58:57 -07:00
using NaturalSort ;
using SharpCompress.Common ;
2016-04-19 01:11:23 -07:00
2016-10-26 22:10:47 -07:00
using FileAccess = System . IO . FileAccess ;
using FileMode = System . IO . FileMode ;
using FileStream = System . IO . FileStream ;
using IOException = System . IO . IOException ;
using MemoryStream = System . IO . MemoryStream ;
using PathTooLongException = System . IO . PathTooLongException ;
using SearchOption = System . IO . SearchOption ;
using StreamReader = System . IO . StreamReader ;
using StreamWriter = System . IO . StreamWriter ;
2016-10-24 12:58:57 -07:00
namespace SabreTools.Helper.Dats
2016-04-19 01:11:23 -07:00
{
2016-09-19 20:08:25 -07:00
public class DatFile : ICloneable
2016-04-19 01:11:23 -07:00
{
2016-09-19 20:08:25 -07:00
#region Private instance variables
// Data common to most DAT types
private string _fileName ;
private string _name ;
private string _description ;
private string _rootDir ;
private string _category ;
private string _version ;
private string _date ;
private string _author ;
private string _email ;
private string _homepage ;
private string _url ;
private string _comment ;
private string _header ;
private string _type ; // Generally only used for SuperDAT
private ForceMerging _forceMerging ;
private ForceNodump _forceNodump ;
private ForcePacking _forcePacking ;
2016-10-25 15:02:02 -07:00
private DatFormat _datFormat ;
2016-10-04 10:26:19 -07:00
private bool _excludeOf ;
2016-09-19 20:08:25 -07:00
private bool _mergeRoms ;
2016-09-22 18:15:02 -07:00
private SortedDictionary < string , List < DatItem > > _files ;
2016-10-06 11:42:55 -07:00
private SortedBy _sortedBy ;
2016-09-19 20:08:25 -07:00
// Data specific to the Miss DAT type
private bool _useGame ;
private string _prefix ;
private string _postfix ;
private bool _quotes ;
private string _repExt ;
private string _addExt ;
private bool _remExt ;
private bool _gameName ;
private bool _romba ;
// Statistical data related to the DAT
private long _romCount ;
private long _diskCount ;
private long _totalSize ;
private long _crcCount ;
private long _md5Count ;
private long _sha1Count ;
2016-09-26 16:42:06 -07:00
private long _baddumpCount ;
private long _nodumpCount ;
2016-09-19 20:08:25 -07:00
#endregion
#region Publicly facing variables
// Data common to most DAT types
public string FileName
{
get { return _fileName ; }
set { _fileName = value ; }
}
public string Name
{
get { return _name ; }
set { _name = value ; }
}
public string Description
{
get { return _description ; }
set { _description = value ; }
}
public string RootDir
{
get { return _rootDir ; }
set { _rootDir = value ; }
}
public string Category
{
get { return _category ; }
set { _category = value ; }
}
public string Version
{
get { return _version ; }
set { _version = value ; }
}
public string Date
{
get { return _date ; }
set { _date = value ; }
}
public string Author
{
get { return _author ; }
set { _author = value ; }
}
public string Email
{
get { return _email ; }
set { _email = value ; }
}
public string Homepage
{
get { return _homepage ; }
set { _homepage = value ; }
}
public string Url
{
get { return _url ; }
set { _url = value ; }
}
public string Comment
{
get { return _comment ; }
set { _comment = value ; }
}
public string Header
{
get { return _header ; }
set { _header = value ; }
}
public string Type // Generally only used for SuperDAT
{
get { return _type ; }
set { _type = value ; }
}
public ForceMerging ForceMerging
{
get { return _forceMerging ; }
set { _forceMerging = value ; }
}
public ForceNodump ForceNodump
{
get { return _forceNodump ; }
set { _forceNodump = value ; }
}
public ForcePacking ForcePacking
{
get { return _forcePacking ; }
set { _forcePacking = value ; }
}
2016-10-25 15:02:02 -07:00
public DatFormat DatFormat
2016-09-19 20:08:25 -07:00
{
2016-10-25 15:02:02 -07:00
get { return _datFormat ; }
set { _datFormat = value ; }
2016-09-19 20:08:25 -07:00
}
2016-10-04 10:26:19 -07:00
public bool ExcludeOf
{
get { return _excludeOf ; }
set { _excludeOf = value ; }
}
2016-09-19 20:08:25 -07:00
public bool MergeRoms
{
get { return _mergeRoms ; }
set { _mergeRoms = value ; }
}
2016-09-22 18:15:02 -07:00
public SortedDictionary < string , List < DatItem > > Files
2016-09-19 20:08:25 -07:00
{
get
{
if ( _files = = null )
{
2016-09-22 18:15:02 -07:00
_files = new SortedDictionary < string , List < DatItem > > ( ) ;
2016-09-19 20:08:25 -07:00
}
return _files ;
}
set
{
_files = value ;
}
}
2016-10-06 11:42:55 -07:00
public SortedBy SortedBy
{
get { return _sortedBy ; }
set { _sortedBy = value ; }
}
2016-09-19 20:08:25 -07:00
// Data specific to the Miss DAT type
public bool UseGame
{
get { return _useGame ; }
set { _useGame = value ; }
}
public string Prefix
{
get { return _prefix ; }
set { _prefix = value ; }
}
public string Postfix
{
get { return _postfix ; }
set { _postfix = value ; }
}
public bool Quotes
{
get { return _quotes ; }
set { _quotes = value ; }
}
public string RepExt
{
get { return _repExt ; }
set { _repExt = value ; }
}
public string AddExt
{
get { return _addExt ; }
set { _addExt = value ; }
}
public bool RemExt
{
get { return _remExt ; }
set { _remExt = value ; }
}
public bool GameName
{
get { return _gameName ; }
set { _gameName = value ; }
}
public bool Romba
{
get { return _romba ; }
set { _romba = value ; }
}
// Statistical data related to the DAT
public long RomCount
{
get { return _romCount ; }
set { _romCount = value ; }
}
public long DiskCount
{
get { return _diskCount ; }
set { _diskCount = value ; }
}
public long TotalSize
{
get { return _totalSize ; }
set { _totalSize = value ; }
}
public long CRCCount
{
get { return _crcCount ; }
set { _crcCount = value ; }
}
public long MD5Count
{
get { return _md5Count ; }
set { _md5Count = value ; }
}
public long SHA1Count
{
get { return _sha1Count ; }
set { _sha1Count = value ; }
}
2016-09-26 16:42:06 -07:00
public long BaddumpCount
{
get { return _baddumpCount ; }
set { _baddumpCount = value ; }
}
2016-09-19 20:08:25 -07:00
public long NodumpCount
{
2016-09-26 16:42:06 -07:00
get { return _nodumpCount ; }
set { _nodumpCount = value ; }
2016-09-19 20:08:25 -07:00
}
#endregion
2016-10-06 11:42:55 -07:00
#region Instance Methods
#region Bucketing
2016-09-19 20:08:25 -07:00
/// <summary>
2016-10-06 11:42:55 -07:00
/// Take the arbitrarily sorted Files Dictionary and convert to one sorted by Game
2016-09-19 20:08:25 -07:00
/// </summary>
2016-10-06 11:42:55 -07:00
/// <param name="mergeroms">True if roms should be deduped, 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>
/// <param name="logger">Logger object for file and console output</param>
/// <param name="output">True if the number of hashes counted is to be output (default), false otherwise</param>
public void BucketByGame ( bool mergeroms , bool norename , Logger logger , bool output = true )
2016-09-19 20:08:25 -07:00
{
2016-10-06 11:42:55 -07:00
// If we already have the right sorting, trust it
if ( _sortedBy = = SortedBy . Game )
{
return ;
}
// Set the sorted type
_sortedBy = SortedBy . Game ;
SortedDictionary < string , List < DatItem > > sortable = new SortedDictionary < string , List < DatItem > > ( ) ;
long count = 0 ;
// If we have a null dict or an empty one, output a new dictionary
if ( Files = = null | | Files . Count = = 0 )
{
Files = sortable ;
}
2016-10-06 16:34:13 -07:00
logger . User ( "Organizing " + ( mergeroms ? "and merging " : "" ) + "roms by game" ) ;
2016-10-06 11:42:55 -07:00
// Process each all of the roms
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = Files [ key ] ;
// If we're merging the roms, do so
if ( mergeroms )
{
roms = DatItem . Merge ( roms , logger ) ;
}
// Now add each of the roms to their respective games
foreach ( DatItem rom in roms )
{
count + + ;
string newkey = ( norename ? ""
: rom . SystemID . ToString ( ) . PadLeft ( 10 , '0' )
+ "-"
+ rom . SourceID . ToString ( ) . PadLeft ( 10 , '0' ) + "-" )
2016-10-24 12:58:57 -07:00
+ ( String . IsNullOrEmpty ( rom . Machine . Name )
2016-10-06 11:42:55 -07:00
? "Default"
2016-10-26 14:59:12 -07:00
: rom . Machine . Name ) ;
2016-10-06 11:42:55 -07:00
newkey = HttpUtility . HtmlEncode ( newkey ) ;
if ( sortable . ContainsKey ( newkey ) )
{
sortable [ newkey ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sortable . Add ( newkey , temp ) ;
}
}
}
// Now go through and sort all of the lists
keys = sortable . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > sortedlist = sortable [ key ] ;
DatItem . Sort ( ref sortedlist , norename ) ;
sortable [ key ] = sortedlist ;
}
// Output the count if told to
if ( output )
{
logger . User ( "A total of " + count + " file hashes will be written out to file" ) ;
}
// Now assign the dictionary back
Files = sortable ;
2016-09-19 20:08:25 -07:00
}
/// <summary>
2016-10-06 11:42:55 -07:00
/// Take the arbitrarily sorted Files Dictionary and convert to one sorted by Size
2016-09-19 20:08:25 -07:00
/// </summary>
2016-10-06 11:42:55 -07:00
/// <param name="mergeroms">True if roms should be deduped, false otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <param name="output">True if the number of hashes counted is to be output (default), false otherwise</param>
public void BucketBySize ( bool mergeroms , Logger logger , bool output = true )
2016-09-19 20:08:25 -07:00
{
2016-10-06 11:42:55 -07:00
// If we already have the right sorting, trust it
if ( _sortedBy = = SortedBy . Size )
{
return ;
}
// Set the sorted type
_sortedBy = SortedBy . Size ;
SortedDictionary < string , List < DatItem > > sortable = new SortedDictionary < string , List < DatItem > > ( ) ;
long count = 0 ;
// If we have a null dict or an empty one, output a new dictionary
if ( Files = = null | | Files . Count = = 0 )
{
Files = sortable ;
}
2016-10-06 16:34:13 -07:00
logger . User ( "Organizing " + ( mergeroms ? "and merging " : "" ) + "roms by size" ) ;
2016-10-06 11:42:55 -07:00
// Process each all of the roms
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = Files [ key ] ;
// If we're merging the roms, do so
if ( mergeroms )
{
roms = DatItem . Merge ( roms , logger ) ;
}
// Now add each of the roms to their respective games
foreach ( DatItem rom in roms )
{
count + + ;
string newkey = ( rom . Type = = ItemType . Rom ? ( ( Rom ) rom ) . Size . ToString ( ) : "-1" ) ;
if ( sortable . ContainsKey ( newkey ) )
{
sortable [ newkey ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sortable . Add ( newkey , temp ) ;
}
}
}
// Now go through and sort all of the lists
keys = sortable . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > sortedlist = sortable [ key ] ;
DatItem . Sort ( ref sortedlist , false ) ;
sortable [ key ] = sortedlist ;
}
// Output the count if told to
if ( output )
{
logger . User ( "A total of " + count + " file hashes will be written out to file" ) ;
}
// Now assign the dictionary back
Files = sortable ;
2016-09-19 20:08:25 -07:00
}
/// <summary>
2016-10-06 11:42:55 -07:00
/// Take the arbitrarily sorted Files Dictionary and convert to one sorted by CRC
2016-09-19 20:08:25 -07:00
/// </summary>
2016-10-06 11:42:55 -07:00
/// <param name="mergeroms">True if roms should be deduped, false otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <param name="output">True if the number of hashes counted is to be output (default), false otherwise</param>
public void BucketByCRC ( bool mergeroms , Logger logger , bool output = true )
2016-09-19 20:08:25 -07:00
{
2016-10-06 11:42:55 -07:00
// If we already have the right sorting, trust it
if ( _sortedBy = = SortedBy . CRC )
{
return ;
}
2016-09-19 20:08:25 -07:00
2016-10-06 11:42:55 -07:00
// Set the sorted type
_sortedBy = SortedBy . CRC ;
2016-09-19 20:08:25 -07:00
2016-10-06 11:42:55 -07:00
SortedDictionary < string , List < DatItem > > sortable = new SortedDictionary < string , List < DatItem > > ( ) ;
long count = 0 ;
2016-09-22 17:52:58 -07:00
2016-10-06 11:42:55 -07:00
// If we have a null dict or an empty one, output a new dictionary
if ( Files = = null | | Files . Count = = 0 )
{
Files = sortable ;
}
2016-10-06 16:34:13 -07:00
logger . User ( "Organizing " + ( mergeroms ? "and merging " : "" ) + "roms by CRC" ) ;
2016-10-06 11:42:55 -07:00
// Process each all of the roms
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = Files [ key ] ;
// If we're merging the roms, do so
if ( mergeroms )
{
roms = DatItem . Merge ( roms , logger ) ;
}
// Now add each of the roms to their respective games
foreach ( DatItem rom in roms )
{
count + + ;
string newkey = ( rom . Type = = ItemType . Rom ? ( ( Rom ) rom ) . CRC : Constants . CRCZero ) ;
if ( sortable . ContainsKey ( newkey ) )
{
sortable [ newkey ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sortable . Add ( newkey , temp ) ;
}
}
}
// Now go through and sort all of the lists
keys = sortable . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > sortedlist = sortable [ key ] ;
DatItem . Sort ( ref sortedlist , false ) ;
sortable [ key ] = sortedlist ;
}
// Output the count if told to
if ( output )
{
logger . User ( "A total of " + count + " file hashes will be written out to file" ) ;
}
// Now assign the dictionary back
Files = sortable ;
}
2016-09-22 18:15:02 -07:00
/// <summary>
2016-10-06 11:42:55 -07:00
/// Take the arbitrarily sorted Files Dictionary and convert to one sorted by MD5
2016-09-22 18:15:02 -07:00
/// </summary>
/// <param name="mergeroms">True if roms should be deduped, false otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <param name="output">True if the number of hashes counted is to be output (default), false otherwise</param>
2016-10-06 11:42:55 -07:00
public void BucketByMD5 ( bool mergeroms , Logger logger , bool output = true )
2016-09-22 18:15:02 -07:00
{
2016-10-06 11:42:55 -07:00
// If we already have the right sorting, trust it
if ( _sortedBy = = SortedBy . MD5 )
{
return ;
}
// Set the sorted type
_sortedBy = SortedBy . MD5 ;
SortedDictionary < string , List < DatItem > > sortable = new SortedDictionary < string , List < DatItem > > ( ) ;
long count = 0 ;
// If we have a null dict or an empty one, output a new dictionary
if ( Files = = null | | Files . Count = = 0 )
{
Files = sortable ;
}
2016-10-06 16:34:13 -07:00
logger . User ( "Organizing " + ( mergeroms ? "and merging " : "" ) + "roms by MD5" ) ;
2016-09-22 18:15:02 -07:00
2016-10-06 11:42:55 -07:00
// Process each all of the roms
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = Files [ key ] ;
// If we're merging the roms, do so
if ( mergeroms )
{
roms = DatItem . Merge ( roms , logger ) ;
}
// Now add each of the roms to their respective games
foreach ( DatItem rom in roms )
{
count + + ;
string newkey = ( rom . Type = = ItemType . Rom
? ( ( Rom ) rom ) . MD5
: ( rom . Type = = ItemType . Disk
? ( ( Disk ) rom ) . MD5
: Constants . MD5Zero ) ) ;
if ( sortable . ContainsKey ( newkey ) )
{
sortable [ newkey ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sortable . Add ( newkey , temp ) ;
}
}
}
// Now go through and sort all of the lists
keys = sortable . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > sortedlist = sortable [ key ] ;
DatItem . Sort ( ref sortedlist , false ) ;
sortable [ key ] = sortedlist ;
}
// Output the count if told to
if ( output )
{
logger . User ( "A total of " + count + " file hashes will be written out to file" ) ;
}
// Now assign the dictionary back
Files = sortable ;
}
/// <summary>
/// Take the arbitrarily sorted Files Dictionary and convert to one sorted by SHA1
/// </summary>
/// <param name="mergeroms">True if roms should be deduped, false otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <param name="output">True if the number of hashes counted is to be output (default), false otherwise</param>
public void BucketBySHA1 ( bool mergeroms , Logger logger , bool output = true )
{
// If we already have the right sorting, trust it
if ( _sortedBy = = SortedBy . SHA1 )
{
return ;
}
// Set the sorted type
_sortedBy = SortedBy . SHA1 ;
2016-09-22 18:15:02 -07:00
SortedDictionary < string , List < DatItem > > sortable = new SortedDictionary < string , List < DatItem > > ( ) ;
long count = 0 ;
// If we have a null dict or an empty one, output a new dictionary
if ( Files = = null | | Files . Count = = 0 )
{
Files = sortable ;
}
2016-10-06 16:34:13 -07:00
logger . User ( "Organizing " + ( mergeroms ? "and merging " : "" ) + "roms by SHA-1" ) ;
2016-10-06 11:42:55 -07:00
2016-09-22 18:15:02 -07:00
// Process each all of the roms
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = Files [ key ] ;
// If we're merging the roms, do so
if ( mergeroms )
{
roms = DatItem . Merge ( roms , logger ) ;
}
// Now add each of the roms to their respective games
foreach ( DatItem rom in roms )
{
count + + ;
2016-10-06 11:42:55 -07:00
string newkey = ( rom . Type = = ItemType . Rom
? ( ( Rom ) rom ) . SHA1
: ( rom . Type = = ItemType . Disk
? ( ( Disk ) rom ) . SHA1
: Constants . MD5Zero ) ) ;
2016-09-22 18:15:02 -07:00
if ( sortable . ContainsKey ( newkey ) )
{
sortable [ newkey ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sortable . Add ( newkey , temp ) ;
}
}
}
// Now go through and sort all of the lists
keys = sortable . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > sortedlist = sortable [ key ] ;
2016-10-06 11:42:55 -07:00
DatItem . Sort ( ref sortedlist , false ) ;
2016-09-22 18:15:02 -07:00
sortable [ key ] = sortedlist ;
}
// Output the count if told to
if ( output )
{
logger . User ( "A total of " + count + " file hashes will be written out to file" ) ;
}
// Now assign the dictionary back
Files = sortable ;
}
#endregion
2016-09-19 20:08:25 -07:00
#region Cloning Methods
public object Clone ( )
{
return new DatFile
{
2016-10-06 11:42:55 -07:00
FileName = _fileName ,
Name = _name ,
Description = _description ,
RootDir = _rootDir ,
Category = _category ,
Version = _version ,
Date = _date ,
Author = _author ,
Email = _email ,
Homepage = _homepage ,
Url = _url ,
Comment = _comment ,
Header = _header ,
Type = _type ,
ForceMerging = _forceMerging ,
ForceNodump = _forceNodump ,
ForcePacking = _forcePacking ,
ExcludeOf = _excludeOf ,
2016-10-25 15:02:02 -07:00
DatFormat = _datFormat ,
2016-10-06 11:42:55 -07:00
MergeRoms = _mergeRoms ,
Files = _files ,
SortedBy = _sortedBy ,
UseGame = _useGame ,
Prefix = _prefix ,
Postfix = _postfix ,
Quotes = _quotes ,
RepExt = _repExt ,
AddExt = _addExt ,
RemExt = _remExt ,
GameName = _gameName ,
Romba = _romba ,
RomCount = _romCount ,
DiskCount = _diskCount ,
TotalSize = _totalSize ,
CRCCount = _crcCount ,
MD5Count = _md5Count ,
SHA1Count = _sha1Count ,
BaddumpCount = _baddumpCount ,
NodumpCount = _nodumpCount ,
2016-09-19 20:08:25 -07:00
} ;
}
public object CloneHeader ( )
{
return new DatFile
{
2016-10-06 11:42:55 -07:00
FileName = _fileName ,
Name = _name ,
Description = _description ,
RootDir = _rootDir ,
Category = _category ,
Version = _version ,
Date = _date ,
Author = _author ,
Email = _email ,
Homepage = _homepage ,
Url = _url ,
Comment = _comment ,
Header = _header ,
Type = _type ,
ForceMerging = _forceMerging ,
ForceNodump = _forceNodump ,
ForcePacking = _forcePacking ,
ExcludeOf = _excludeOf ,
2016-10-25 15:02:02 -07:00
DatFormat = _datFormat ,
2016-10-06 11:42:55 -07:00
MergeRoms = _mergeRoms ,
2016-09-22 18:15:02 -07:00
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
2016-10-06 11:42:55 -07:00
SortedBy = SortedBy . Default ,
UseGame = _useGame ,
Prefix = _prefix ,
Postfix = _postfix ,
Quotes = _quotes ,
RepExt = _repExt ,
AddExt = _addExt ,
RemExt = _remExt ,
GameName = _gameName ,
Romba = _romba ,
2016-09-19 20:08:25 -07:00
} ;
}
#endregion
2016-09-22 18:15:02 -07:00
#region Converting and Updating
2016-09-06 11:38:55 -07:00
/// <summary>
2016-10-26 18:07:06 -07:00
/// Determine if input files should be merged, diffed, or processed invidually
2016-09-06 11:38:55 -07:00
/// </summary>
2016-09-22 18:15:02 -07:00
/// <param name="inputFileNames">Names of the input files and/or folders</param>
/// <param name="outDir">Optional param for output directory</param>
/// <param name="merge">True if input files should be merged into a single file, false otherwise</param>
/// <param name="diff">Non-zero flag for diffing mode, zero otherwise</param>
/// <param name="inplace">True if the cascade-diffed files should overwrite their inputs, false otherwise</param>
/// <param name="skip">True if the first cascaded diff file should be skipped on output, false otherwise</param>
/// <param name="bare">True if the date should not be appended to the default name, false otherwise [OBSOLETE]</param>
/// <param name="clean">True to clean the game names to WoD standard, false otherwise (default)</param>
/// <param name="softlist">True to allow SL DATs to have game names used instead of descriptions, false otherwise (default)</param>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object to be passed to the DatItem level</param>
2016-09-06 11:38:55 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
2016-09-22 18:15:02 -07:00
/// <param name="maxDegreeOfParallelism">Integer representing the maximum amount of parallelization to be used</param>
/// <param name="logger">Logging object for console and file output</param>
2016-10-26 18:07:06 -07:00
public void DetermineUpdateType ( List < string > inputFileNames , string outDir , bool merge , DiffMode diff , bool inplace , bool skip ,
2016-10-26 21:02:01 -07:00
bool bare , bool clean , bool softlist , Filter filter , bool trim , bool single , string root , int maxDegreeOfParallelism , Logger logger )
2016-04-28 10:37:33 -07:00
{
2016-09-22 18:15:02 -07:00
// If we're in merging or diffing mode, use the full list of inputs
if ( merge | | diff ! = 0 )
2016-08-26 12:25:31 -07:00
{
2016-09-22 18:15:02 -07:00
// Make sure there are no folders in inputs
List < string > newInputFileNames = new List < string > ( ) ;
foreach ( string input in inputFileNames )
{
if ( Directory . Exists ( input ) )
{
2016-09-26 17:52:20 -07:00
List < string > files = FileTools . RetrieveFiles ( input , new List < string > ( ) ) ;
2016-09-26 12:31:28 -07:00
foreach ( string file in files )
2016-09-22 18:15:02 -07:00
{
try
{
newInputFileNames . Add ( Path . GetFullPath ( file ) + "¬" + Path . GetFullPath ( input ) ) ;
}
catch ( PathTooLongException )
{
logger . Warning ( "The path for " + file + " was too long" ) ;
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
}
}
}
else if ( File . Exists ( input ) )
{
try
{
newInputFileNames . Add ( Path . GetFullPath ( input ) + "¬" + Path . GetDirectoryName ( Path . GetFullPath ( input ) ) ) ;
}
catch ( PathTooLongException )
{
logger . Warning ( "The path for " + input + " was too long" ) ;
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
}
}
}
2016-08-26 12:25:31 -07:00
2016-09-22 18:15:02 -07:00
// If we're in inverse cascade, reverse the list
2016-10-04 10:26:19 -07:00
if ( ( diff & DiffMode . ReverseCascade ) ! = 0 )
2016-09-22 18:15:02 -07:00
{
newInputFileNames . Reverse ( ) ;
}
2016-05-25 11:29:19 -07:00
2016-09-22 18:15:02 -07:00
// Create a dictionary of all ROMs from the input DATs
List < DatFile > datHeaders = PopulateUserData ( newInputFileNames , inplace , clean , softlist ,
2016-10-26 21:02:01 -07:00
outDir , filter , trim , single , root , maxDegreeOfParallelism , logger ) ;
2016-05-26 21:43:28 -07:00
2016-09-22 18:15:02 -07:00
// Modify the Dictionary if necessary and output the results
2016-10-04 10:26:19 -07:00
if ( diff ! = 0 & & diff < DiffMode . Cascade )
2016-09-22 18:15:02 -07:00
{
DiffNoCascade ( diff , outDir , newInputFileNames , logger ) ;
}
// If we're in cascade and diff, output only cascaded diffs
2016-10-04 10:26:19 -07:00
else if ( diff ! = 0 & & diff > = DiffMode . Cascade )
2016-09-22 18:15:02 -07:00
{
DiffCascade ( outDir , inplace , newInputFileNames , datHeaders , skip , logger ) ;
}
// Output all entries with user-defined merge
else
{
MergeNoDiff ( outDir , newInputFileNames , datHeaders , logger ) ;
}
2016-05-21 00:45:56 -07:00
}
2016-09-22 18:15:02 -07:00
// Otherwise, loop through all of the inputs individually
else
2016-05-27 16:06:19 -07:00
{
2016-10-26 21:02:01 -07:00
Update ( inputFileNames , outDir , clean , softlist , filter , trim , single , root , maxDegreeOfParallelism , logger ) ;
2016-09-22 18:15:02 -07:00
}
return ;
}
/// <summary>
/// Populate the user DatData object from the input files
/// </summary>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object to be passed to the DatItem level</param>
2016-09-06 11:38:55 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
2016-09-22 18:15:02 -07:00
/// <param name="maxDegreeOfParallelism">Integer representing the maximum amount of parallelization to be used</param>
/// <param name="logger">Logging object for console and file output</param>
/// <returns>List of DatData objects representing headers</returns>
private List < DatFile > PopulateUserData ( List < string > inputs , bool inplace , bool clean , bool softlist , string outDir ,
2016-10-26 21:02:01 -07:00
Filter filter , bool trim , bool single , string root , int maxDegreeOfParallelism , Logger logger )
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
DatFile [ ] datHeaders = new DatFile [ inputs . Count ] ;
DateTime start = DateTime . Now ;
logger . User ( "Processing individual DATs" ) ;
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
Parallel . For ( 0 ,
inputs . Count ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
i = >
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
string input = inputs [ i ] ;
logger . User ( "Adding DAT: " + input . Split ( '¬' ) [ 0 ] ) ;
datHeaders [ i ] = new DatFile
2016-05-27 16:06:19 -07:00
{
2016-10-25 15:02:02 -07:00
DatFormat = ( DatFormat ! = 0 ? DatFormat : 0 ) ,
2016-09-22 18:15:02 -07:00
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
MergeRoms = MergeRoms ,
} ;
2016-05-27 16:06:19 -07:00
2016-10-26 21:02:01 -07:00
datHeaders [ i ] . Parse ( input . Split ( '¬' ) [ 0 ] , i , 0 , filter , trim , single , root , logger , true , clean , softlist ) ;
2016-09-22 18:15:02 -07:00
} ) ;
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
logger . User ( "Processing complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
logger . User ( "Populating internal DAT" ) ;
Files = new SortedDictionary < string , List < DatItem > > ( ) ;
for ( int i = 0 ; i < inputs . Count ; i + + )
{
List < string > keys = datHeaders [ i ] . Files . Keys . ToList ( ) ;
foreach ( string key in keys )
2016-09-13 10:48:45 -07:00
{
2016-09-22 18:15:02 -07:00
if ( Files . ContainsKey ( key ) )
2016-09-13 10:48:45 -07:00
{
2016-09-22 18:15:02 -07:00
Files [ key ] . AddRange ( datHeaders [ i ] . Files [ key ] ) ;
2016-09-13 10:48:45 -07:00
}
2016-09-22 18:15:02 -07:00
else
2016-09-13 10:48:45 -07:00
{
2016-09-22 18:15:02 -07:00
Files . Add ( key , datHeaders [ i ] . Files [ key ] ) ;
2016-09-13 10:48:45 -07:00
}
2016-09-22 18:15:02 -07:00
datHeaders [ i ] . Files . Remove ( key ) ;
}
datHeaders [ i ] . Files = null ;
}
2016-09-13 10:48:45 -07:00
2016-09-22 18:15:02 -07:00
logger . User ( "Processing and populating complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
2016-09-19 18:04:24 -07:00
2016-09-22 18:15:02 -07:00
return datHeaders . ToList ( ) ;
}
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
/// <summary>
/// Output non-cascading diffs
/// </summary>
/// <param name="diff">Non-zero flag for diffing mode, zero otherwise</param>
/// <param name="outDir">Output directory to write the DATs to</param>
/// <param name="inputs">List of inputs to write out from</param>
/// <param name="logger">Logging object for console and file output</param>
public void DiffNoCascade ( DiffMode diff , string outDir , List < string > inputs , Logger logger )
{
DateTime start = DateTime . Now ;
logger . User ( "Initializing all output DATs" ) ;
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
// Default vars for use
string post = "" ;
DatFile outerDiffData = new DatFile ( ) ;
DatFile dupeData = new DatFile ( ) ;
2016-09-13 10:48:45 -07:00
2016-09-22 18:15:02 -07:00
// Don't have External dupes
if ( ( diff & DiffMode . NoDupes ) ! = 0 )
{
post = " (No Duplicates)" ;
outerDiffData = ( DatFile ) CloneHeader ( ) ;
outerDiffData . FileName + = post ;
outerDiffData . Name + = post ;
outerDiffData . Description + = post ;
outerDiffData . Files = new SortedDictionary < string , List < DatItem > > ( ) ;
}
// Have External dupes
if ( ( diff & DiffMode . Dupes ) ! = 0 )
{
post = " (Duplicates)" ;
dupeData = ( DatFile ) CloneHeader ( ) ;
dupeData . FileName + = post ;
dupeData . Name + = post ;
dupeData . Description + = post ;
dupeData . Files = new SortedDictionary < string , List < DatItem > > ( ) ;
}
// Create a list of DatData objects representing individual output files
List < DatFile > outDats = new List < DatFile > ( ) ;
// Loop through each of the inputs and get or create a new DatData object
if ( ( diff & DiffMode . Individuals ) ! = 0 )
{
DatFile [ ] outDatsArray = new DatFile [ inputs . Count ] ;
Parallel . For ( 0 , inputs . Count , j = >
{
string innerpost = " (" + Path . GetFileNameWithoutExtension ( inputs [ j ] . Split ( '¬' ) [ 0 ] ) + " Only)" ;
DatFile diffData = ( DatFile ) CloneHeader ( ) ;
diffData . FileName + = innerpost ;
diffData . Name + = innerpost ;
diffData . Description + = innerpost ;
diffData . Files = new SortedDictionary < string , List < DatItem > > ( ) ;
outDatsArray [ j ] = diffData ;
} ) ;
outDats = outDatsArray . ToList ( ) ;
}
logger . User ( "Initializing complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
// Now, loop through the dictionary and populate the correct DATs
start = DateTime . Now ;
logger . User ( "Populating all output DATs" ) ;
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = DatItem . Merge ( Files [ key ] , logger ) ;
if ( roms ! = null & & roms . Count > 0 )
{
foreach ( DatItem rom in roms )
{
// No duplicates
if ( ( diff & DiffMode . NoDupes ) ! = 0 | | ( diff & DiffMode . Individuals ) ! = 0 )
{
2016-10-05 20:33:02 -07:00
if ( ( rom . Dupe & DupeType . Internal ) ! = 0 )
2016-09-22 18:15:02 -07:00
{
// Individual DATs that are output
if ( ( diff & DiffMode . Individuals ) ! = 0 )
2016-09-19 18:04:24 -07:00
{
2016-09-22 18:15:02 -07:00
if ( outDats [ rom . SystemID ] . Files . ContainsKey ( key ) )
{
outDats [ rom . SystemID ] . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > tl = new List < DatItem > ( ) ;
tl . Add ( rom ) ;
outDats [ rom . SystemID ] . Files . Add ( key , tl ) ;
}
}
// Merged no-duplicates DAT
if ( ( diff & DiffMode . NoDupes ) ! = 0 )
{
DatItem newrom = rom ;
2016-10-24 12:58:57 -07:00
newrom . Machine . Name + = " (" + Path . GetFileNameWithoutExtension ( inputs [ newrom . SystemID ] . Split ( '¬' ) [ 0 ] ) + ")" ;
2016-09-22 18:15:02 -07:00
if ( outerDiffData . Files . ContainsKey ( key ) )
{
outerDiffData . Files [ key ] . Add ( newrom ) ;
}
else
{
List < DatItem > tl = new List < DatItem > ( ) ;
tl . Add ( rom ) ;
outerDiffData . Files . Add ( key , tl ) ;
}
2016-09-19 18:04:24 -07:00
}
2016-09-13 10:48:45 -07:00
}
2016-09-22 18:15:02 -07:00
}
// Duplicates only
if ( ( diff & DiffMode . Dupes ) ! = 0 )
{
2016-10-05 20:33:02 -07:00
if ( ( rom . Dupe & DupeType . External ) ! = 0 )
2016-09-21 16:46:01 -07:00
{
2016-09-22 18:15:02 -07:00
DatItem newrom = rom ;
2016-10-24 12:58:57 -07:00
newrom . Machine . Name + = " (" + Path . GetFileNameWithoutExtension ( inputs [ newrom . SystemID ] . Split ( '¬' ) [ 0 ] ) + ")" ;
2016-09-22 18:15:02 -07:00
if ( dupeData . Files . ContainsKey ( key ) )
2016-09-21 16:46:01 -07:00
{
2016-09-22 18:15:02 -07:00
dupeData . Files [ key ] . Add ( newrom ) ;
2016-09-21 16:46:01 -07:00
}
2016-09-22 18:15:02 -07:00
else
2016-09-21 16:46:01 -07:00
{
2016-09-22 18:15:02 -07:00
List < DatItem > tl = new List < DatItem > ( ) ;
tl . Add ( rom ) ;
dupeData . Files . Add ( key , tl ) ;
2016-09-21 16:46:01 -07:00
}
}
2016-09-22 18:15:02 -07:00
}
}
}
}
logger . User ( "Populating complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
2016-09-21 16:46:01 -07:00
2016-09-22 18:15:02 -07:00
// Finally, loop through and output each of the DATs
start = DateTime . Now ;
logger . User ( "Outputting all created DATs" ) ;
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
// Output the difflist (a-b)+(b-a) diff
if ( ( diff & DiffMode . NoDupes ) ! = 0 )
{
outerDiffData . WriteToFile ( outDir , logger ) ;
}
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
// Output the (ab) diff
if ( ( diff & DiffMode . Dupes ) ! = 0 )
{
dupeData . WriteToFile ( outDir , logger ) ;
}
// Output the individual (a-b) DATs
if ( ( diff & DiffMode . Individuals ) ! = 0 )
{
for ( int j = 0 ; j < inputs . Count ; j + + )
{
// If we have an output directory set, replace the path
string path = outDir + ( Path . GetDirectoryName ( inputs [ j ] . Split ( '¬' ) [ 0 ] ) . Remove ( 0 , inputs [ j ] . Split ( '¬' ) [ 1 ] . Length ) ) ;
// If we have more than 0 roms, output
if ( outDats [ j ] . Files . Count > 0 )
{
outDats [ j ] . WriteToFile ( path , logger ) ;
2016-05-27 16:06:19 -07:00
}
2016-09-22 18:15:02 -07:00
}
}
logger . User ( "Outputting complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
}
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
/// <summary>
/// Output cascading diffs
/// </summary>
/// <param name="outDir">Output directory to write the DATs to</param>
/// <param name="inplace">True if cascaded diffs are outputted in-place, false otherwise</param>
/// <param name="inputs">List of inputs to write out from</param>
/// <param name="datHeaders">Dat headers used optionally</param>
/// <param name="skip">True if the first cascaded diff file should be skipped on output, false otherwise</param>
/// <param name="logger">Logging object for console and file output</param>
public void DiffCascade ( string outDir , bool inplace , List < string > inputs , List < DatFile > datHeaders , bool skip , Logger logger )
{
string post = "" ;
// Create a list of DatData objects representing output files
List < DatFile > outDats = new List < DatFile > ( ) ;
// Loop through each of the inputs and get or create a new DatData object
DateTime start = DateTime . Now ;
logger . User ( "Initializing all output DATs" ) ;
DatFile [ ] outDatsArray = new DatFile [ inputs . Count ] ;
Parallel . For ( 0 , inputs . Count , j = >
{
string innerpost = " (" + Path . GetFileNameWithoutExtension ( inputs [ j ] . Split ( '¬' ) [ 0 ] ) + " Only)" ;
DatFile diffData ;
// If we're in inplace mode, take the appropriate DatData object already stored
if ( inplace | | ! String . IsNullOrEmpty ( outDir ) )
{
diffData = datHeaders [ j ] ;
2016-05-27 16:06:19 -07:00
}
2016-09-22 18:15:02 -07:00
else
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
diffData = ( DatFile ) CloneHeader ( ) ;
diffData . FileName + = post ;
diffData . Name + = post ;
diffData . Description + = post ;
}
diffData . Files = new SortedDictionary < string , List < DatItem > > ( ) ;
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
outDatsArray [ j ] = diffData ;
} ) ;
outDats = outDatsArray . ToList ( ) ;
logger . User ( "Initializing complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
// Now, loop through the dictionary and populate the correct DATs
start = DateTime . Now ;
logger . User ( "Populating all output DATs" ) ;
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = DatItem . Merge ( Files [ key ] , logger ) ;
if ( roms ! = null & & roms . Count > 0 )
{
foreach ( DatItem rom in roms )
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
// There's odd cases where there are items with System ID < 0. Skip them for now
if ( rom . SystemID < 0 )
2016-09-12 23:04:28 -07:00
{
2016-09-22 18:15:02 -07:00
logger . Warning ( "Item found with a <0 SystemID: " + rom . Name ) ;
continue ;
2016-09-12 23:04:28 -07:00
}
2016-09-22 18:15:02 -07:00
if ( outDats [ rom . SystemID ] . Files . ContainsKey ( key ) )
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
outDats [ rom . SystemID ] . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > tl = new List < DatItem > ( ) ;
tl . Add ( rom ) ;
outDats [ rom . SystemID ] . Files . Add ( key , tl ) ;
2016-05-27 16:06:19 -07:00
}
}
}
2016-09-22 18:15:02 -07:00
}
logger . User ( "Populating complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
// Finally, loop through and output each of the DATs
start = DateTime . Now ;
logger . User ( "Outputting all created DATs" ) ;
for ( int j = ( skip ? 1 : 0 ) ; j < inputs . Count ; j + + )
{
// If we have an output directory set, replace the path
string path = "" ;
if ( inplace )
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
path = Path . GetDirectoryName ( inputs [ j ] . Split ( '¬' ) [ 0 ] ) ;
}
else if ( ! String . IsNullOrEmpty ( outDir ) )
{
path = outDir + ( Path . GetDirectoryName ( inputs [ j ] . Split ( '¬' ) [ 0 ] ) . Remove ( 0 , inputs [ j ] . Split ( '¬' ) [ 1 ] . Length ) ) ;
}
// If we have more than 0 roms, output
if ( outDats [ j ] . Files . Count > 0 )
{
outDats [ j ] . WriteToFile ( path , logger ) ;
2016-05-27 16:06:19 -07:00
}
}
2016-09-22 18:15:02 -07:00
logger . User ( "Outputting complete in " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
}
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
/// <summary>
/// Output user defined merge
/// </summary>
/// <param name="outDir">Output directory to write the DATs to</param>
/// <param name="inputs">List of inputs to write out from</param>
/// <param name="datHeaders">Dat headers used optionally</param>
/// <param name="logger">Logging object for console and file output</param>
public void MergeNoDiff ( string outDir , List < string > inputs , List < DatFile > datHeaders , Logger logger )
{
// If we're in SuperDAT mode, prefix all games with their respective DATs
if ( Type = = "SuperDAT" )
{
List < string > keys = Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > newroms = new List < DatItem > ( ) ;
foreach ( DatItem rom in Files [ key ] )
{
DatItem newrom = rom ;
string filename = inputs [ newrom . SystemID ] . Split ( '¬' ) [ 0 ] ;
string rootpath = inputs [ newrom . SystemID ] . Split ( '¬' ) [ 1 ] ;
rootpath + = ( rootpath = = "" ? "" : Path . DirectorySeparatorChar . ToString ( ) ) ;
filename = filename . Remove ( 0 , rootpath . Length ) ;
2016-10-24 12:58:57 -07:00
newrom . Machine . Name = Path . GetDirectoryName ( filename ) + Path . DirectorySeparatorChar
2016-09-22 18:15:02 -07:00
+ Path . GetFileNameWithoutExtension ( filename ) + Path . DirectorySeparatorChar
2016-10-24 12:58:57 -07:00
+ newrom . Machine . Name ;
2016-09-22 18:15:02 -07:00
newroms . Add ( newrom ) ;
}
Files [ key ] = newroms ;
}
}
// Output a DAT only if there are roms
if ( Files . Count ! = 0 )
{
WriteToFile ( outDir , logger ) ;
}
2016-05-27 16:06:19 -07:00
}
2016-10-26 18:07:06 -07:00
/// <summary>
/// Convert, update, and filter a DAT file or set of files using a base
/// </summary>
/// <param name="inputFileNames">Names of the input files and/or folders</param>
/// <param name="outDir">Optional param for output directory</param>
/// <param name="merge">True if input files should be merged into a single file, false otherwise</param>
/// <param name="diff">Non-zero flag for diffing mode, zero otherwise</param>
/// <param name="inplace">True if the cascade-diffed files should overwrite their inputs, false otherwise</param>
/// <param name="skip">True if the first cascaded diff file should be skipped on output, false otherwise</param>
/// <param name="bare">True if the date should not be appended to the default name, false otherwise [OBSOLETE]</param>
/// <param name="clean">True to clean the game names to WoD standard, false otherwise (default)</param>
/// <param name="softlist">True to allow SL DATs to have game names used instead of descriptions, false otherwise (default)</param>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object to be passed to the DatItem level</param>
2016-10-26 18:07:06 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
/// <param name="maxDegreeOfParallelism">Integer representing the maximum amount of parallelization to be used</param>
/// <param name="logger">Logging object for console and file output</param>
2016-10-26 21:02:01 -07:00
public void Update ( List < string > inputFileNames , string outDir , bool clean , bool softlist , Filter filter ,
bool trim , bool single , string root , int maxDegreeOfParallelism , Logger logger )
2016-10-26 18:07:06 -07:00
{
Parallel . ForEach ( inputFileNames ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
inputFileName = >
{
// Clean the input string
if ( inputFileName ! = "" )
{
inputFileName = Path . GetFullPath ( inputFileName ) ;
}
if ( File . Exists ( inputFileName ) )
{
DatFile innerDatdata = ( DatFile ) CloneHeader ( ) ;
logger . User ( "Processing \"" + Path . GetFileName ( inputFileName ) + "\"" ) ;
2016-10-26 21:02:01 -07:00
innerDatdata . Parse ( inputFileName , 0 , 0 , filter , trim , single ,
2016-10-26 18:07:06 -07:00
root , logger , true , clean , softlist ,
keepext : ( ( innerDatdata . DatFormat & DatFormat . TSV ) ! = 0 | | ( innerDatdata . DatFormat & DatFormat . CSV ) ! = 0 ) ) ;
// If we have roms, output them
if ( innerDatdata . Files . Count ! = 0 )
{
innerDatdata . WriteToFile ( ( outDir = = "" ? Path . GetDirectoryName ( inputFileName ) : outDir ) , logger , overwrite : ( outDir ! = "" ) ) ;
}
}
else if ( Directory . Exists ( inputFileName ) )
{
inputFileName = Path . GetFullPath ( inputFileName ) + Path . DirectorySeparatorChar ;
Parallel . ForEach ( Directory . EnumerateFiles ( inputFileName , "*" , SearchOption . AllDirectories ) ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
file = >
{
logger . User ( "Processing \"" + Path . GetFullPath ( file ) . Remove ( 0 , inputFileName . Length ) + "\"" ) ;
DatFile innerDatdata = ( DatFile ) Clone ( ) ;
innerDatdata . Files = null ;
2016-10-26 21:02:01 -07:00
innerDatdata . Parse ( file , 0 , 0 , filter ,
2016-10-26 18:07:06 -07:00
trim , single , root , logger , true , clean , softlist ,
keepext : ( ( innerDatdata . DatFormat & DatFormat . TSV ) ! = 0 | | ( innerDatdata . DatFormat & DatFormat . CSV ) ! = 0 ) ) ;
// If we have roms, output them
if ( innerDatdata . Files ! = null & & innerDatdata . Files . Count ! = 0 )
{
innerDatdata . WriteToFile ( ( outDir = = "" ? Path . GetDirectoryName ( file ) : outDir + Path . GetDirectoryName ( file ) . Remove ( 0 , inputFileName . Length - 1 ) ) , logger , overwrite : ( outDir ! = "" ) ) ;
}
} ) ;
}
else
{
logger . Error ( "I'm sorry but " + inputFileName + " doesn't exist!" ) ;
}
} ) ;
}
2016-09-22 18:15:02 -07:00
#endregion
2016-10-24 16:14:22 -07:00
#region Parsing
2016-09-22 18:15:02 -07:00
2016-05-27 16:06:19 -07:00
/// <summary>
2016-09-22 18:15:02 -07:00
/// Parse a 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="datdata">The DatData object representing found roms to this point</param>
/// <param name="logger">Logger object for console and/or file output</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="softlist">True if SL XML names should be kept, false otherwise (default)</param>
/// <param name="keepext">True if original extension should be kept, false otherwise (default)</param>
public void Parse ( string filename , int sysid , int srcid , Logger logger , bool keep = false , bool clean = false , bool softlist = false , bool keepext = false )
{
2016-10-26 21:02:01 -07:00
Parse ( filename , sysid , srcid , new Filter ( ) , false , false , "" , logger , keep , clean , softlist , keepext ) ;
2016-09-22 18:15:02 -07:00
}
/// <summary>
/// Parse a DAT and return all found games and roms within
2016-05-27 16:06:19 -07:00
/// </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>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-06 11:38:55 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
2016-05-27 16:06:19 -07:00
/// <param name="logger">Logger object for console and/or file output</param>
2016-09-22 18:15:02 -07:00
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
2016-05-29 13:36:02 -07:00
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
2016-09-22 18:15:02 -07:00
/// <param name="softlist">True if SL XML names should be kept, false otherwise (default)</param>
/// <param name="keepext">True if original extension should be kept, false otherwise (default)</param>
public void Parse (
2016-09-06 11:38:55 -07:00
// Standard Dat parsing
string filename ,
int sysid ,
int srcid ,
// Rom filtering
2016-10-26 21:02:01 -07:00
Filter filter ,
2016-09-06 11:38:55 -07:00
// Rom renaming
bool trim ,
bool single ,
string root ,
// Miscellaneous
Logger logger ,
2016-09-22 18:15:02 -07:00
bool keep = false ,
bool clean = false ,
bool softlist = false ,
bool keepext = false )
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
// Check the file extension first as a safeguard
string ext = Path . GetExtension ( filename ) . ToLowerInvariant ( ) ;
2016-09-26 17:36:25 -07:00
if ( ext . StartsWith ( "." ) )
{
ext = ext . Substring ( 1 ) ;
}
2016-09-28 11:30:06 -07:00
if ( ext ! = "dat" & & ext ! = "md5" & & ext ! = "sfv" & & ext ! = "sha1" & & ext ! = "txt" & & ext ! = "xml" )
2016-09-22 18:15:02 -07:00
{
return ;
}
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
// If the output filename isn't set already, get the internal filename
FileName = ( String . IsNullOrEmpty ( FileName ) ? ( keepext ? Path . GetFileName ( filename ) : Path . GetFileNameWithoutExtension ( filename ) ) : FileName ) ;
// If the output type isn't set already, get the internal output type
2016-10-25 15:02:02 -07:00
DatFormat = ( DatFormat = = 0 ? FileTools . GetDatFormat ( filename , logger ) : DatFormat ) ;
2016-09-22 18:15:02 -07:00
// Make sure there's a dictionary to read to
if ( Files = = null )
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
Files = new SortedDictionary < string , List < DatItem > > ( ) ;
}
2016-05-27 16:06:19 -07:00
2016-09-22 18:15:02 -07:00
// Now parse the correct type of DAT
2016-10-25 15:02:02 -07:00
switch ( FileTools . GetDatFormat ( filename , logger ) )
2016-09-22 18:15:02 -07:00
{
2016-10-25 15:02:02 -07:00
case DatFormat . ClrMamePro :
case DatFormat . DOSCenter :
2016-10-26 21:02:01 -07:00
ParseCMP ( filename , sysid , srcid , filter , trim , single , root , logger , keep , clean ) ;
2016-09-22 18:15:02 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . Logiqx :
case DatFormat . OfflineList :
case DatFormat . SabreDat :
case DatFormat . SoftwareList :
2016-10-26 21:02:01 -07:00
ParseGenericXML ( filename , sysid , srcid , filter , trim , single , root , logger , keep , clean , softlist ) ;
2016-09-22 18:15:02 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RedumpMD5 :
2016-10-26 21:02:01 -07:00
ParseRedumpMD5 ( filename , sysid , srcid , filter , trim , single , root , logger , clean ) ;
2016-09-28 11:43:21 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RedumpSFV :
2016-10-26 21:02:01 -07:00
ParseRedumpSFV ( filename , sysid , srcid , filter , trim , single , root , logger , clean ) ;
2016-09-28 11:43:21 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RedumpSHA1 :
2016-10-26 21:02:01 -07:00
ParseRedumpSHA1 ( filename , sysid , srcid , filter , trim , single , root , logger , clean ) ;
2016-09-28 11:43:21 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RomCenter :
2016-10-26 21:02:01 -07:00
ParseRC ( filename , sysid , srcid , filter , trim , single , root , logger , clean ) ;
2016-09-27 12:05:29 -07:00
break ;
2016-09-22 18:15:02 -07:00
default :
return ;
2016-05-27 16:06:19 -07:00
}
}
/// <summary>
2016-09-22 18:15:02 -07:00
/// Parse a ClrMamePro DAT and return all found games and roms within
2016-05-27 16:06:19 -07:00
/// </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>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-06 11:38:55 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
2016-05-27 16:06:19 -07:00
/// <param name="logger">Logger object for console and/or file output</param>
2016-05-29 13:27:34 -07:00
/// <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>
2016-09-22 18:15:02 -07:00
private void ParseCMP (
2016-09-06 11:38:55 -07:00
// Standard Dat parsing
string filename ,
int sysid ,
int srcid ,
// Rom filtering
2016-10-26 21:02:01 -07:00
Filter filter ,
2016-09-06 11:38:55 -07:00
// Rom renaming
bool trim ,
bool single ,
string root ,
// Miscellaneous
Logger logger ,
bool keep ,
2016-09-22 18:15:02 -07:00
bool clean )
2016-05-27 16:06:19 -07:00
{
2016-09-22 18:15:02 -07:00
// Open a file reader
Encoding enc = Style . GetEncoding ( filename ) ;
StreamReader sr = new StreamReader ( File . OpenRead ( filename ) , enc ) ;
2016-05-18 23:51:54 -07:00
2016-09-22 18:15:02 -07:00
bool block = false , superdat = false ;
string blockname = "" , tempgamename = "" , gamedesc = "" , cloneof = "" ,
romof = "" , sampleof = "" , year = "" , manufacturer = "" ;
while ( ! sr . EndOfStream )
2016-04-28 10:37:33 -07:00
{
2016-09-22 18:15:02 -07:00
string line = sr . ReadLine ( ) ;
// Comments in CMP DATs start with a #
if ( line . Trim ( ) . StartsWith ( "#" ) )
2016-04-28 10:37:33 -07:00
{
2016-09-22 18:15:02 -07:00
continue ;
}
// If the line is the header or a game
if ( Regex . IsMatch ( line , Constants . HeaderPatternCMP ) )
{
GroupCollection gc = Regex . Match ( line , Constants . HeaderPatternCMP ) . Groups ;
if ( gc [ 1 ] . Value = = "clrmamepro" | | gc [ 1 ] . Value = = "romvault" | | gc [ 1 ] . Value . ToLowerInvariant ( ) = = "doscenter" )
2016-05-18 23:51:54 -07:00
{
2016-09-22 18:15:02 -07:00
blockname = "header" ;
}
2016-05-19 10:28:53 -07:00
2016-09-22 18:15:02 -07:00
block = true ;
}
2016-05-19 10:28:53 -07:00
2016-09-22 18:15:02 -07:00
// If the line is a rom-like item and we're in a block
else if ( ( line . Trim ( ) . StartsWith ( "rom (" )
| | line . Trim ( ) . StartsWith ( "disk (" )
| | line . Trim ( ) . StartsWith ( "file (" )
| | ( line . Trim ( ) . StartsWith ( "sample" ) & & ! line . Trim ( ) . StartsWith ( "sampleof" ) )
) & & block )
{
ItemType temptype = ItemType . Rom ;
if ( line . Trim ( ) . StartsWith ( "rom (" ) )
{
temptype = ItemType . Rom ;
}
else if ( line . Trim ( ) . StartsWith ( "disk (" ) )
{
temptype = ItemType . Disk ;
}
else if ( line . Trim ( ) . StartsWith ( "file (" ) )
{
temptype = ItemType . Rom ;
}
else if ( line . Trim ( ) . StartsWith ( "sample" ) )
{
temptype = ItemType . Sample ;
}
2016-05-19 10:28:53 -07:00
2016-09-22 18:15:02 -07:00
// Create the proper DatItem based on the type
DatItem item ;
switch ( temptype )
{
case ItemType . Archive :
item = new Archive ( ) ;
break ;
case ItemType . BiosSet :
item = new BiosSet ( ) ;
break ;
case ItemType . Disk :
item = new Disk ( ) ;
break ;
case ItemType . Release :
item = new Release ( ) ;
break ;
case ItemType . Sample :
item = new Sample ( ) ;
break ;
case ItemType . Rom :
default :
item = new Rom ( ) ;
break ;
2016-05-18 23:51:54 -07:00
}
2016-09-22 18:15:02 -07:00
// Then populate it with information
2016-10-24 21:32:26 -07:00
item . Machine = new Machine
{
Name = tempgamename ,
Description = gamedesc ,
CloneOf = cloneof ,
RomOf = romof ,
SampleOf = sampleof ,
Manufacturer = manufacturer ,
Year = year ,
} ;
2016-09-22 18:15:02 -07:00
item . SystemID = sysid ;
item . SourceID = srcid ;
// If we have a sample, treat it special
if ( temptype = = ItemType . Sample )
2016-04-28 10:37:33 -07:00
{
2016-09-22 18:15:02 -07:00
line = line . Trim ( ) . Remove ( 0 , 6 ) . Trim ( ) . Replace ( "\"" , "" ) ; // Remove "sample" from the input string
item . Name = line ;
2016-04-28 10:37:33 -07:00
}
2016-09-22 18:15:02 -07:00
// Otherwise, process the rest of the line
else
2016-04-28 10:37:33 -07:00
{
2016-09-22 18:15:02 -07:00
string [ ] gc = line . Trim ( ) . Split ( ' ' ) ;
// Loop over all attributes and add them if possible
bool quote = false ;
string attrib = "" , val = "" ;
for ( int i = 2 ; i < gc . Length ; i + + )
{
//If the item is empty, we automatically skip it because it's a fluke
if ( gc [ i ] . Trim ( ) = = String . Empty )
2016-09-13 11:24:02 -07:00
{
2016-09-22 18:15:02 -07:00
continue ;
2016-09-13 11:24:02 -07:00
}
2016-10-04 13:54:47 -07:00
// Special cases for standalone item statuses
2016-10-04 13:43:21 -07:00
else if ( gc [ i ] = = "baddump" & & attrib ! = "name" & & attrib ! = "status" & & attrib ! = "flags" )
2016-05-27 10:47:41 -07:00
{
2016-09-22 18:15:02 -07:00
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . BadDump ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . BadDump ;
}
2016-05-27 10:47:41 -07:00
}
2016-10-04 13:54:47 -07:00
else if ( gc [ i ] = = "good" & & attrib ! = "name" & & attrib ! = "status" & & attrib ! = "flags" )
{
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . Good ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . Good ;
}
}
2016-10-04 13:43:21 -07:00
else if ( gc [ i ] = = "nodump" & & attrib ! = "name" & & attrib ! = "status" & & attrib ! = "flags" )
2016-05-27 16:59:28 -07:00
{
2016-09-22 18:15:02 -07:00
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . Nodump ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . Nodump ;
}
2016-05-27 16:59:28 -07:00
}
2016-10-04 13:54:47 -07:00
else if ( gc [ i ] = = "verified" & & attrib ! = "name" & & attrib ! = "status" & & attrib ! = "flags" )
{
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . Verified ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . Verified ;
}
}
2016-10-06 16:14:37 -07:00
// Special cases for DOSCenter DATs only
else if ( line . Trim ( ) . StartsWith ( "file (" ) )
{
2016-10-06 16:29:53 -07:00
// Loop over the specifics
for ( int j = i ; j < gc . Length ; j + + )
2016-10-06 16:14:37 -07:00
{
2016-10-06 16:29:53 -07:00
// Names are not quoted, for some stupid reason
if ( gc [ j ] = = "name" )
2016-10-06 16:14:37 -07:00
{
2016-10-06 16:29:53 -07:00
// Advance to the first part of the name
j + + ;
item . Name = gc [ j ] ;
// Advance to the next item, adding until we find "size"
j + + ;
while ( j < gc . Length & & gc [ j ] ! = "size" & & gc [ j ] ! = "date" & & gc [ j ] ! = "crc" )
{
item . Name + = " " + gc [ j ] ;
j + + ;
}
2016-10-06 16:14:37 -07:00
}
2016-10-06 16:29:53 -07:00
// Get the size from the next part
else if ( gc [ j ] = = "size" )
2016-10-06 16:14:37 -07:00
{
2016-10-06 16:29:53 -07:00
j + + ;
long tempsize = - 1 ;
if ( ! Int64 . TryParse ( gc [ j ] , out tempsize ) )
{
tempsize = 0 ;
}
( ( Rom ) item ) . Size = tempsize ;
j + + ;
2016-10-06 16:14:37 -07:00
}
2016-10-06 16:29:53 -07:00
// Get the date from the next part
else if ( gc [ j ] = = "date" )
{
j + + ;
( ( Rom ) item ) . Date = gc [ j ] . Replace ( "\"" , "" ) + " " + gc [ j + 1 ] . Replace ( "\"" , "" ) ;
j + = 3 ;
}
2016-10-06 16:14:37 -07:00
2016-10-06 16:29:53 -07:00
// Get the CRC from the next part
else if ( gc [ j ] = = "crc" )
{
j + + ;
( ( Rom ) item ) . CRC = gc [ j ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
2016-10-06 16:14:37 -07:00
}
}
2016-09-22 18:15:02 -07:00
// Even number of quotes, not in a quote, not in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 0 & & ! quote & & attrib = = "" )
{
attrib = gc [ i ] . Replace ( "\"" , "" ) ;
}
// Even number of quotes, not in a quote, in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 0 & & ! quote & & attrib ! = "" )
{
switch ( attrib . ToLowerInvariant ( ) )
{
case "name" :
item . Name = gc [ i ] . Replace ( "\"" , "" ) ;
break ;
case "size" :
if ( item . Type = = ItemType . Rom )
{
long size = - 1 ;
if ( Int64 . TryParse ( gc [ i ] . Replace ( "\"" , "" ) , out size ) )
2016-05-18 23:51:54 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . Size = size ;
2016-05-18 23:51:54 -07:00
}
2016-09-22 18:15:02 -07:00
}
break ;
case "crc" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . CRC = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
break ;
case "md5" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . MD5 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . MD5 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
break ;
case "sha1" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . SHA1 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . SHA1 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
break ;
2016-10-11 17:53:55 -07:00
case "status" :
2016-09-22 18:15:02 -07:00
case "flags" :
2016-10-11 17:53:55 -07:00
if ( gc [ i ] . ToLowerInvariant ( ) = = "good" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-06-16 17:01:33 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . Good ;
2016-06-16 17:01:33 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-09-13 10:48:45 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . Good ;
2016-09-13 10:48:45 -07:00
}
2016-09-22 18:15:02 -07:00
}
2016-10-11 17:53:55 -07:00
else if ( gc [ i ] . ToLowerInvariant ( ) = = "baddump" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-05-16 14:28:23 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . BadDump ;
2016-05-16 14:28:23 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-05-16 14:28:23 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . BadDump ;
2016-05-16 14:28:23 -07:00
}
2016-09-22 18:15:02 -07:00
}
2016-10-11 17:53:55 -07:00
else if ( gc [ i ] . ToLowerInvariant ( ) = = "nodump" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-05-16 14:28:23 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . Nodump ;
2016-05-16 14:28:23 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-05-18 23:51:54 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . Nodump ;
2016-05-18 23:51:54 -07:00
}
2016-09-22 18:15:02 -07:00
}
2016-10-11 17:53:55 -07:00
else if ( gc [ i ] . ToLowerInvariant ( ) = = "verified" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-09-13 10:48:45 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . Verified ;
2016-09-13 10:48:45 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-09-13 10:48:45 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . Verified ;
2016-09-13 10:48:45 -07:00
}
2016-09-22 18:15:02 -07:00
}
break ;
case "date" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . Date = gc [ i ] . Replace ( "\"" , "" ) + " " + gc [ i + 1 ] . Replace ( "\"" , "" ) ;
}
i + + ;
break ;
2016-09-13 10:48:45 -07:00
2016-09-22 18:15:02 -07:00
// Special cases for item statuses
case "good" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . Good ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . Good ;
}
break ;
case "baddump" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . BadDump ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . BadDump ;
}
break ;
case "nodump" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . Nodump ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . Nodump ;
}
break ;
case "verified" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . ItemStatus = ItemStatus . Verified ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . ItemStatus = ItemStatus . Verified ;
}
break ;
}
2016-05-19 10:28:53 -07:00
2016-09-22 18:15:02 -07:00
attrib = "" ;
}
// Even number of quotes, in a quote, not in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 0 & & quote & & attrib = = "" )
{
// Attributes can't have quoted names
}
// Even number of quotes, in a quote, in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 0 & & quote & & attrib ! = "" )
{
val + = " " + gc [ i ] ;
}
// Odd number of quotes, not in a quote, not in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 1 & & ! quote & & attrib = = "" )
{
// Attributes can't have quoted names
}
// Odd number of quotes, not in a quote, in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 1 & & ! quote & & attrib ! = "" )
{
val = gc [ i ] . Replace ( "\"" , "" ) ;
quote = true ;
}
// Odd number of quotes, in a quote, not in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 1 & & quote & & attrib = = "" )
{
quote = false ;
}
// Odd number of quotes, in a quote, in attribute
else if ( Regex . Matches ( gc [ i ] , "\"" ) . Count % 2 = = 1 & & quote & & attrib ! = "" )
{
val + = " " + gc [ i ] . Replace ( "\"" , "" ) ;
switch ( attrib . ToLowerInvariant ( ) )
{
case "name" :
item . Name = val ;
break ;
case "size" :
if ( item . Type = = ItemType . Rom )
{
long size = - 1 ;
if ( Int64 . TryParse ( gc [ i ] . Replace ( "\"" , "" ) , out size ) )
{
( ( Rom ) item ) . Size = size ;
2016-09-21 15:45:40 -07:00
}
2016-09-22 18:15:02 -07:00
}
break ;
case "crc" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . CRC = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
break ;
case "md5" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . MD5 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . MD5 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
break ;
case "sha1" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . SHA1 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
else if ( item . Type = = ItemType . Disk )
{
( ( Disk ) item ) . SHA1 = gc [ i ] . Replace ( "\"" , "" ) . ToLowerInvariant ( ) ;
}
break ;
2016-10-11 17:53:55 -07:00
case "status" :
2016-09-22 18:15:02 -07:00
case "flags" :
2016-10-11 17:53:55 -07:00
if ( gc [ i ] . ToLowerInvariant ( ) = = "good" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-05-06 10:59:05 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . Good ;
2016-09-21 15:45:40 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-09-21 15:45:40 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . Good ;
2016-05-06 10:59:05 -07:00
}
2016-09-22 18:15:02 -07:00
}
2016-10-11 17:53:55 -07:00
else if ( gc [ i ] . ToLowerInvariant ( ) = = "baddump" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-05-20 10:21:24 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . BadDump ;
2016-05-20 10:21:24 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-05-14 19:41:07 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . BadDump ;
2016-05-14 19:41:07 -07:00
}
2016-09-22 18:15:02 -07:00
}
2016-10-11 17:53:55 -07:00
else if ( gc [ i ] . ToLowerInvariant ( ) = = "nodump" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-05-14 19:41:07 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . Nodump ;
2016-05-14 19:41:07 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-05-14 19:41:07 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . Nodump ;
2016-05-14 19:41:07 -07:00
}
2016-09-22 18:15:02 -07:00
}
2016-10-11 17:53:55 -07:00
else if ( gc [ i ] . ToLowerInvariant ( ) = = "verified" )
2016-09-22 18:15:02 -07:00
{
if ( item . Type = = ItemType . Rom )
2016-05-29 13:27:34 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Rom ) item ) . ItemStatus = ItemStatus . Verified ;
2016-05-29 13:27:34 -07:00
}
2016-09-22 18:15:02 -07:00
else if ( item . Type = = ItemType . Disk )
2016-04-28 10:37:33 -07:00
{
2016-09-22 18:15:02 -07:00
( ( Disk ) item ) . ItemStatus = ItemStatus . Verified ;
2016-09-19 18:04:24 -07:00
}
2016-09-22 18:15:02 -07:00
}
break ;
case "date" :
if ( item . Type = = ItemType . Rom )
{
( ( Rom ) item ) . Date = gc [ i ] . Replace ( "\"" , "" ) + " " + gc [ i + 1 ] . Replace ( "\"" , "" ) ;
}
i + + ;
break ;
2016-04-28 10:37:33 -07:00
}
2016-05-19 10:28:53 -07:00
2016-09-22 18:15:02 -07:00
quote = false ;
attrib = "" ;
val = "" ;
}
}
}
2016-05-19 10:28:53 -07:00
2016-09-22 18:15:02 -07:00
// Now process and add the rom
string key = "" ;
2016-10-26 21:02:01 -07:00
ParseAddHelper ( item , filter , trim , single , root , clean , logger , out key ) ;
2016-09-22 18:15:02 -07:00
}
// If the line is anything but a rom or disk and we're in a block
else if ( Regex . IsMatch ( line , Constants . ItemPatternCMP ) & & block )
{
GroupCollection gc = Regex . Match ( line , Constants . ItemPatternCMP ) . Groups ;
2016-09-06 11:38:55 -07:00
2016-09-22 18:15:02 -07:00
if ( blockname ! = "header" )
{
string itemval = gc [ 2 ] . Value . Replace ( "\"" , "" ) ;
switch ( gc [ 1 ] . Value )
{
case "name" :
tempgamename = ( itemval . ToLowerInvariant ( ) . EndsWith ( ".zip" ) ? itemval . Remove ( itemval . Length - 4 ) : itemval ) ;
break ;
case "description" :
gamedesc = itemval ;
break ;
case "romof" :
romof = itemval ;
break ;
case "cloneof" :
cloneof = itemval ;
break ;
case "year" :
year = itemval ;
break ;
case "manufacturer" :
manufacturer = itemval ;
break ;
case "sampleof" :
sampleof = itemval ;
break ;
}
}
else
{
string itemval = gc [ 2 ] . Value . Replace ( "\"" , "" ) ;
2016-10-06 11:47:24 -07:00
2016-10-06 16:14:37 -07:00
if ( line . Trim ( ) . StartsWith ( "Name:" ) )
2016-10-06 11:47:24 -07:00
{
2016-10-06 16:14:37 -07:00
Name = ( String . IsNullOrEmpty ( Name ) ? line . Substring ( 6 ) : Name ) ;
2016-10-06 11:47:24 -07:00
superdat = superdat | | itemval . Contains ( " - SuperDAT" ) ;
if ( keep & & superdat )
{
Type = ( String . IsNullOrEmpty ( Type ) ? "SuperDAT" : Type ) ;
}
2016-10-06 16:14:37 -07:00
continue ;
2016-10-06 11:47:24 -07:00
}
2016-09-22 18:15:02 -07:00
switch ( gc [ 1 ] . Value )
{
case "name" :
case "Name:" :
Name = ( String . IsNullOrEmpty ( Name ) ? itemval : Name ) ;
superdat = superdat | | itemval . Contains ( " - SuperDAT" ) ;
if ( keep & & superdat )
2016-05-19 10:28:53 -07:00
{
2016-09-22 18:15:02 -07:00
Type = ( String . IsNullOrEmpty ( Type ) ? "SuperDAT" : Type ) ;
2016-05-19 10:28:53 -07:00
}
2016-09-22 18:15:02 -07:00
break ;
case "description" :
case "Description:" :
Description = ( String . IsNullOrEmpty ( Description ) ? itemval : Description ) ;
break ;
case "rootdir" :
RootDir = ( String . IsNullOrEmpty ( RootDir ) ? itemval : RootDir ) ;
break ;
case "category" :
Category = ( String . IsNullOrEmpty ( Category ) ? itemval : Category ) ;
break ;
case "version" :
case "Version:" :
Version = ( String . IsNullOrEmpty ( Version ) ? itemval : Version ) ;
break ;
case "date" :
case "Date:" :
Date = ( String . IsNullOrEmpty ( Date ) ? itemval : Date ) ;
break ;
case "author" :
case "Author:" :
Author = ( String . IsNullOrEmpty ( Author ) ? itemval : Author ) ;
break ;
case "email" :
Email = ( String . IsNullOrEmpty ( Email ) ? itemval : Email ) ;
break ;
case "homepage" :
case "Homepage:" :
Homepage = ( String . IsNullOrEmpty ( Homepage ) ? itemval : Homepage ) ;
break ;
case "url" :
Url = ( String . IsNullOrEmpty ( Url ) ? itemval : Url ) ;
break ;
case "comment" :
case "Comment:" :
Comment = ( String . IsNullOrEmpty ( Comment ) ? itemval : Comment ) ;
break ;
case "header" :
Header = ( String . IsNullOrEmpty ( Header ) ? itemval : Header ) ;
break ;
case "type" :
Type = ( String . IsNullOrEmpty ( Type ) ? itemval : Type ) ;
superdat = superdat | | itemval . Contains ( "SuperDAT" ) ;
break ;
case "forcemerging" :
switch ( itemval )
2016-05-18 23:51:54 -07:00
{
2016-09-22 18:15:02 -07:00
case "none" :
ForceMerging = ForceMerging . None ;
break ;
case "split" :
ForceMerging = ForceMerging . Split ;
break ;
case "full" :
ForceMerging = ForceMerging . Full ;
break ;
2016-05-18 23:51:54 -07:00
}
2016-09-22 18:15:02 -07:00
break ;
case "forcezipping" :
switch ( itemval )
2016-09-19 18:04:24 -07:00
{
2016-09-22 18:15:02 -07:00
case "yes" :
ForcePacking = ForcePacking . Zip ;
break ;
case "no" :
ForcePacking = ForcePacking . Unzip ;
break ;
2016-09-19 18:04:24 -07:00
}
2016-09-22 18:15:02 -07:00
break ;
case "forcepacking" :
switch ( itemval )
2016-05-19 12:19:28 -07:00
{
2016-09-22 18:15:02 -07:00
case "zip" :
ForcePacking = ForcePacking . Zip ;
break ;
case "unzip" :
ForcePacking = ForcePacking . Unzip ;
break ;
2016-05-19 12:19:28 -07:00
}
2016-09-22 18:15:02 -07:00
break ;
}
2016-04-28 10:37:33 -07:00
}
}
2016-06-13 10:29:07 -07:00
2016-09-22 18:15:02 -07:00
// If we find an end bracket that's not associated with anything else, the block is done
else if ( Regex . IsMatch ( line , Constants . EndPatternCMP ) & & block )
{
block = false ;
2016-10-10 18:30:48 -07:00
blockname = "" ; tempgamename = "" ; gamedesc = "" ; cloneof = "" ;
romof = "" ; sampleof = "" ; year = "" ; manufacturer = "" ;
2016-09-22 18:15:02 -07:00
}
2016-04-28 10:37:33 -07:00
}
2016-09-22 18:15:02 -07:00
sr . Dispose ( ) ;
2016-04-28 10:37:33 -07:00
}
2016-09-28 11:43:21 -07:00
/// <summary>
2016-09-30 12:15:36 -07:00
/// Parse an XML DAT (Logiqx, OfflineList, SabreDAT, and Software List) and return all found games and roms within
2016-09-06 11:38:55 -07:00
/// </summary>
2016-09-22 18:15:02 -07:00
/// <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>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-06 11:38:55 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
/// <param name="logger">Logger object for console and/or file output</param>
2016-09-30 12:15:36 -07:00
/// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param>
2016-09-22 18:15:02 -07:00
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
2016-09-30 12:15:36 -07:00
/// <param name="softlist">True if SL XML names should be kept, false otherwise (default)</param>
private void ParseGenericXML (
2016-09-22 18:15:02 -07:00
// Standard Dat parsing
string filename ,
int sysid ,
int srcid ,
// Rom filtering
2016-10-26 21:02:01 -07:00
Filter filter ,
2016-09-22 18:15:02 -07:00
// Rom renaming
bool trim ,
bool single ,
string root ,
// Miscellaneous
Logger logger ,
2016-09-30 12:15:36 -07:00
bool keep ,
bool clean ,
bool softlist )
2016-09-06 11:38:55 -07:00
{
2016-09-30 12:15:36 -07:00
// Prepare all internal variables
XmlReader subreader , headreader , flagreader ;
bool superdat = false , empty = true ;
string key = "" , date = "" ;
long size = - 1 ;
ItemStatus its = ItemStatus . None ;
List < string > parent = new List < string > ( ) ;
2016-09-22 18:15:02 -07:00
Encoding enc = Style . GetEncoding ( filename ) ;
2016-09-30 12:15:36 -07:00
XmlReader xtr = FileTools . GetXmlTextReader ( filename , logger ) ;
2016-09-06 11:38:55 -07:00
2016-09-30 12:15:36 -07:00
// If we got a null reader, just return
if ( xtr = = null )
2016-09-09 10:00:37 -07:00
{
2016-09-30 12:15:36 -07:00
return ;
}
2016-09-09 10:00:37 -07:00
2016-09-30 12:15:36 -07:00
// Otherwise, read the file to the end
try
{
xtr . MoveToContent ( ) ;
while ( ! xtr . EOF )
2016-09-06 11:38:55 -07:00
{
2016-09-30 12:15:36 -07:00
// If we're ending a folder or game, take care of possibly empty games and removing from the parent
if ( xtr . NodeType = = XmlNodeType . EndElement & & ( xtr . Name = = "directory" | | xtr . Name = = "dir" ) )
2016-09-08 21:48:50 -07:00
{
2016-09-30 12:15:36 -07:00
// If we didn't find any items in the folder, make sure to add the blank rom
if ( empty )
2016-09-22 18:15:02 -07:00
{
2016-09-30 12:15:36 -07:00
string tempgame = String . Join ( "\\" , parent ) ;
Rom rom = new Rom ( "null" , tempgame ) ;
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( rom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-22 18:15:02 -07:00
}
2016-09-30 12:15:36 -07:00
// Regardless, end the current folder
int parentcount = parent . Count ;
if ( parentcount = = 0 )
2016-09-22 18:15:02 -07:00
{
2016-09-30 12:15:36 -07:00
logger . Verbose ( "Empty parent: " + String . Join ( "\\" , parent ) ) ;
empty = true ;
2016-09-22 18:15:02 -07:00
}
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
// If we have an end folder element, remove one item from the parent, if possible
if ( parentcount > 0 )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
parent . RemoveAt ( parent . Count - 1 ) ;
if ( keep & & parentcount > 1 )
{
Type = ( String . IsNullOrEmpty ( Type ) ? "SuperDAT" : Type ) ;
superdat = true ;
}
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
}
// We only want elements
if ( xtr . NodeType ! = XmlNodeType . Element )
{
2016-09-27 11:37:12 -07:00
xtr . Read ( ) ;
2016-09-28 12:27:39 -07:00
continue ;
}
switch ( xtr . Name )
{
// Handle MAME listxml since they're halfway between a SL and a Logiqx XML
case "mame" :
if ( xtr . GetAttribute ( "build" ) ! = null )
2016-09-27 11:26:55 -07:00
{
2016-09-28 12:27:39 -07:00
Name = ( String . IsNullOrEmpty ( Name ) ? xtr . GetAttribute ( "build" ) : Name ) ;
Description = ( String . IsNullOrEmpty ( Description ) ? Name : Name ) ;
2016-09-27 11:26:55 -07:00
}
2016-09-28 12:27:39 -07:00
xtr . Read ( ) ;
break ;
// New software lists have this behavior
case "softwarelist" :
if ( xtr . GetAttribute ( "name" ) ! = null )
2016-09-27 11:26:55 -07:00
{
2016-09-28 12:27:39 -07:00
Name = ( String . IsNullOrEmpty ( Name ) ? xtr . GetAttribute ( "name" ) : Name ) ;
2016-09-27 11:26:55 -07:00
}
2016-09-28 12:27:39 -07:00
if ( xtr . GetAttribute ( "description" ) ! = null )
2016-09-27 11:26:55 -07:00
{
2016-09-28 12:27:39 -07:00
Description = ( String . IsNullOrEmpty ( Description ) ? xtr . GetAttribute ( "description" ) : Description ) ;
2016-09-27 11:26:55 -07:00
}
2016-09-28 12:27:39 -07:00
if ( xtr . GetAttribute ( "forcemerging" ) ! = null )
{
switch ( xtr . GetAttribute ( "forcemerging" ) )
{
case "split" :
ForceMerging = ForceMerging . Split ;
break ;
case "none" :
ForceMerging = ForceMerging . None ;
break ;
case "full" :
ForceMerging = ForceMerging . Full ;
break ;
}
}
if ( xtr . GetAttribute ( "forceitemStatus" ) ! = null )
{
switch ( xtr . GetAttribute ( "forceitemStatus" ) )
{
case "obsolete" :
ForceNodump = ForceNodump . Obsolete ;
break ;
case "required" :
ForceNodump = ForceNodump . Required ;
break ;
case "ignore" :
ForceNodump = ForceNodump . Ignore ;
break ;
}
}
if ( xtr . GetAttribute ( "forcepacking" ) ! = null )
{
switch ( xtr . GetAttribute ( "forcepacking" ) )
{
case "zip" :
ForcePacking = ForcePacking . Zip ;
break ;
case "unzip" :
ForcePacking = ForcePacking . Unzip ;
break ;
}
}
xtr . Read ( ) ;
break ;
// Handle M1 DATs since they're 99% the same as a SL DAT
case "m1" :
Name = ( String . IsNullOrEmpty ( Name ) ? "M1" : Name ) ;
Description = ( String . IsNullOrEmpty ( Description ) ? "M1" : Description ) ;
if ( xtr . GetAttribute ( "version" ) ! = null )
{
Version = ( String . IsNullOrEmpty ( Version ) ? xtr . GetAttribute ( "version" ) : Version ) ;
}
xtr . Read ( ) ;
break ;
// OfflineList has a different header format
case "configuration" :
headreader = xtr . ReadSubtree ( ) ;
2016-09-27 12:05:29 -07:00
2016-09-28 12:27:39 -07:00
// If there's no subtree to the header, skip it
if ( headreader = = null )
2016-09-27 12:05:29 -07:00
{
2016-09-28 12:27:39 -07:00
xtr . Skip ( ) ;
2016-09-27 12:05:29 -07:00
continue ;
}
2016-09-28 12:27:39 -07:00
// Otherwise, read what we can from the header
while ( ! headreader . EOF )
2016-09-27 12:05:29 -07:00
{
2016-09-28 12:27:39 -07:00
// We only want elements
if ( headreader . NodeType ! = XmlNodeType . Element | | headreader . Name = = "configuration" )
{
2016-09-27 12:05:29 -07:00
headreader . Read ( ) ;
2016-09-28 12:27:39 -07:00
continue ;
}
2016-09-27 12:05:29 -07:00
2016-09-28 12:27:39 -07:00
// Get all header items (ONLY OVERWRITE IF THERE'S NO DATA)
string content = "" ;
switch ( headreader . Name . ToLowerInvariant ( ) )
{
case "datname" :
content = headreader . ReadElementContentAsString ( ) ; ;
Name = ( String . IsNullOrEmpty ( Name ) ? content : Name ) ;
superdat = superdat | | content . Contains ( " - SuperDAT" ) ;
if ( keep & & superdat )
{
Type = ( String . IsNullOrEmpty ( Type ) ? "SuperDAT" : Type ) ;
}
break ;
case "datversionurl" :
content = headreader . ReadElementContentAsString ( ) ; ;
Url = ( String . IsNullOrEmpty ( Name ) ? content : Url ) ;
break ;
default :
headreader . Read ( ) ;
break ;
}
}
2016-09-27 11:37:12 -07:00
2016-09-28 12:27:39 -07:00
break ;
// We want to process the entire subtree of the header
case "header" :
headreader = xtr . ReadSubtree ( ) ;
2016-09-27 11:37:12 -07:00
2016-09-28 12:27:39 -07:00
// If there's no subtree to the header, skip it
if ( headreader = = null )
2016-09-22 18:15:02 -07:00
{
2016-09-28 12:27:39 -07:00
xtr . Skip ( ) ;
2016-09-27 11:37:12 -07:00
continue ;
2016-09-22 18:15:02 -07:00
}
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
// Otherwise, read what we can from the header
while ( ! headreader . EOF )
2016-09-22 18:15:02 -07:00
{
2016-09-28 12:27:39 -07:00
// We only want elements
if ( headreader . NodeType ! = XmlNodeType . Element | | headreader . Name = = "header" )
{
headreader . Read ( ) ;
continue ;
}
// Get all header items (ONLY OVERWRITE IF THERE'S NO DATA)
string content = "" ;
switch ( headreader . Name )
{
case "name" :
content = headreader . ReadElementContentAsString ( ) ; ;
Name = ( String . IsNullOrEmpty ( Name ) ? content : Name ) ;
superdat = superdat | | content . Contains ( " - SuperDAT" ) ;
if ( keep & & superdat )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
Type = ( String . IsNullOrEmpty ( Type ) ? "SuperDAT" : Type ) ;
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
break ;
case "description" :
content = headreader . ReadElementContentAsString ( ) ;
Description = ( String . IsNullOrEmpty ( Description ) ? content : Description ) ;
break ;
case "rootdir" :
content = headreader . ReadElementContentAsString ( ) ;
RootDir = ( String . IsNullOrEmpty ( RootDir ) ? content : RootDir ) ;
break ;
case "category" :
content = headreader . ReadElementContentAsString ( ) ;
Category = ( String . IsNullOrEmpty ( Category ) ? content : Category ) ;
break ;
case "version" :
content = headreader . ReadElementContentAsString ( ) ;
Version = ( String . IsNullOrEmpty ( Version ) ? content : Version ) ;
break ;
case "date" :
content = headreader . ReadElementContentAsString ( ) ;
Date = ( String . IsNullOrEmpty ( Date ) ? content . Replace ( "." , "/" ) : Date ) ;
break ;
case "author" :
content = headreader . ReadElementContentAsString ( ) ;
Author = ( String . IsNullOrEmpty ( Author ) ? content : Author ) ;
// Special cases for SabreDAT
Email = ( String . IsNullOrEmpty ( Email ) & & ! String . IsNullOrEmpty ( headreader . GetAttribute ( "email" ) ) ?
headreader . GetAttribute ( "email" ) : Email ) ;
Homepage = ( String . IsNullOrEmpty ( Homepage ) & & ! String . IsNullOrEmpty ( headreader . GetAttribute ( "homepage" ) ) ?
headreader . GetAttribute ( "homepage" ) : Email ) ;
Url = ( String . IsNullOrEmpty ( Url ) & & ! String . IsNullOrEmpty ( headreader . GetAttribute ( "url" ) ) ?
headreader . GetAttribute ( "url" ) : Email ) ;
break ;
case "email" :
content = headreader . ReadElementContentAsString ( ) ;
Email = ( String . IsNullOrEmpty ( Email ) ? content : Email ) ;
break ;
case "homepage" :
content = headreader . ReadElementContentAsString ( ) ;
Homepage = ( String . IsNullOrEmpty ( Homepage ) ? content : Homepage ) ;
break ;
case "url" :
content = headreader . ReadElementContentAsString ( ) ;
Url = ( String . IsNullOrEmpty ( Url ) ? content : Url ) ;
break ;
case "comment" :
content = headreader . ReadElementContentAsString ( ) ;
Comment = ( String . IsNullOrEmpty ( Comment ) ? content : Comment ) ;
break ;
case "type" :
content = headreader . ReadElementContentAsString ( ) ;
Type = ( String . IsNullOrEmpty ( Type ) ? content : Type ) ;
superdat = superdat | | content . Contains ( "SuperDAT" ) ;
break ;
case "clrmamepro" :
case "romcenter" :
if ( headreader . GetAttribute ( "header" ) ! = null )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
Header = ( String . IsNullOrEmpty ( Header ) ? headreader . GetAttribute ( "header" ) : Header ) ;
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
if ( headreader . GetAttribute ( "plugin" ) ! = null )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
Header = ( String . IsNullOrEmpty ( Header ) ? headreader . GetAttribute ( "plugin" ) : Header ) ;
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
if ( headreader . GetAttribute ( "forcemerging" ) ! = null )
{
switch ( headreader . GetAttribute ( "forcemerging" ) )
{
case "split" :
ForceMerging = ForceMerging . Split ;
break ;
case "none" :
ForceMerging = ForceMerging . None ;
break ;
case "full" :
ForceMerging = ForceMerging . Full ;
break ;
}
}
if ( headreader . GetAttribute ( "forceitemStatus" ) ! = null )
{
switch ( headreader . GetAttribute ( "forceitemStatus" ) )
{
case "obsolete" :
ForceNodump = ForceNodump . Obsolete ;
break ;
case "required" :
ForceNodump = ForceNodump . Required ;
break ;
case "ignore" :
ForceNodump = ForceNodump . Ignore ;
break ;
}
}
if ( headreader . GetAttribute ( "forcepacking" ) ! = null )
{
switch ( headreader . GetAttribute ( "forcepacking" ) )
{
case "zip" :
ForcePacking = ForcePacking . Zip ;
break ;
case "unzip" :
ForcePacking = ForcePacking . Unzip ;
break ;
}
}
headreader . Read ( ) ;
break ;
case "flags" :
flagreader = xtr . ReadSubtree ( ) ;
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
// If we somehow have a null flag section, skip it
if ( flagreader = = null )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
xtr . Skip ( ) ;
2016-09-27 11:37:12 -07:00
continue ;
}
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
while ( ! flagreader . EOF )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
// We only want elements
if ( flagreader . NodeType ! = XmlNodeType . Element | | flagreader . Name = = "flags" )
{
flagreader . Read ( ) ;
continue ;
}
switch ( flagreader . Name )
{
case "flag" :
if ( flagreader . GetAttribute ( "name" ) ! = null & & flagreader . GetAttribute ( "value" ) ! = null )
2016-09-22 18:15:02 -07:00
{
2016-09-28 12:27:39 -07:00
content = flagreader . GetAttribute ( "value" ) ;
switch ( flagreader . GetAttribute ( "name" ) )
{
case "type" :
Type = ( String . IsNullOrEmpty ( Type ) ? content : Type ) ;
superdat = superdat | | content . Contains ( "SuperDAT" ) ;
break ;
case "forcemerging" :
switch ( content )
{
case "split" :
ForceMerging = ForceMerging . Split ;
break ;
case "none" :
ForceMerging = ForceMerging . None ;
break ;
case "full" :
ForceMerging = ForceMerging . Full ;
break ;
}
break ;
case "forceitemStatus" :
switch ( content )
{
case "obsolete" :
ForceNodump = ForceNodump . Obsolete ;
break ;
case "required" :
ForceNodump = ForceNodump . Required ;
break ;
case "ignore" :
ForceNodump = ForceNodump . Ignore ;
break ;
}
break ;
case "forcepacking" :
switch ( content )
{
case "zip" :
ForcePacking = ForcePacking . Zip ;
break ;
case "unzip" :
ForcePacking = ForcePacking . Unzip ;
break ;
}
break ;
}
2016-09-22 18:15:02 -07:00
}
2016-09-28 12:27:39 -07:00
flagreader . Read ( ) ;
break ;
default :
flagreader . Read ( ) ;
break ;
}
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
headreader . Skip ( ) ;
break ;
default :
headreader . Read ( ) ;
break ;
}
2016-09-22 17:52:58 -07:00
}
2016-09-27 11:37:12 -07:00
2016-09-28 12:27:39 -07:00
// Skip the header node now that we've processed it
2016-09-22 18:15:02 -07:00
xtr . Skip ( ) ;
2016-09-28 12:27:39 -07:00
break ;
case "machine" :
case "game" :
case "software" :
2016-10-24 12:58:57 -07:00
string temptype = xtr . Name , publisher = "" , partname = "" , partinterface = "" , areaname = "" ;
2016-09-30 16:25:58 -07:00
bool? supported = null ;
long? areasize = null ;
2016-10-03 10:00:20 -07:00
List < Tuple < string , string > > infos = new List < Tuple < string , string > > ( ) ;
List < Tuple < string , string > > features = new List < Tuple < string , string > > ( ) ;
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
// We want to process the entire subtree of the game
subreader = xtr . ReadSubtree ( ) ;
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
// Safeguard for interesting case of "software" without anything except roms
bool software = false ;
// If we have an empty machine, skip it
if ( subreader = = null )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
xtr . Skip ( ) ;
continue ;
2016-09-27 11:37:12 -07:00
}
2016-09-27 13:52:40 -07:00
2016-09-28 12:27:39 -07:00
// Otherwise, add what is possible
subreader . MoveToContent ( ) ;
2016-09-30 16:25:58 -07:00
2016-10-24 12:58:57 -07:00
// Create a new machine
Machine machine = new Machine
{
Name = xtr . GetAttribute ( "name" ) ,
Description = xtr . GetAttribute ( "name" ) ,
RomOf = xtr . GetAttribute ( "romof" ) ? ? "" ,
CloneOf = xtr . GetAttribute ( "cloneof" ) ? ? "" ,
SampleOf = xtr . GetAttribute ( "sampleof" ) ? ? "" ,
} ;
2016-09-30 16:25:58 -07:00
if ( subreader . GetAttribute ( "supported" ) ! = null )
2016-09-28 12:27:39 -07:00
{
2016-09-30 16:25:58 -07:00
switch ( subreader . GetAttribute ( "supported" ) )
{
case "no" :
supported = false ;
break ;
case "yes" :
supported = true ;
break ;
}
2016-09-27 11:37:12 -07:00
}
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
if ( superdat & & ! keep )
2016-09-27 11:37:12 -07:00
{
2016-10-24 12:58:57 -07:00
string tempout = Regex . Match ( machine . Name , @".*?\\(.*)" ) . Groups [ 1 ] . Value ;
2016-09-28 12:27:39 -07:00
if ( tempout ! = "" )
{
2016-10-24 12:58:57 -07:00
machine . Name = tempout ;
2016-09-28 12:27:39 -07:00
}
}
// Get the name of the game from the parent
else if ( superdat & & keep & & parent . Count > 0 )
{
2016-10-24 12:58:57 -07:00
machine . Name = String . Join ( "\\" , parent ) + "\\" + machine . Name ;
2016-09-28 12:27:39 -07:00
}
2016-09-27 12:05:29 -07:00
2016-09-28 12:27:39 -07:00
// Special offline list parts
string ext = "" ;
string releaseNumber = "" ;
2016-09-27 13:52:40 -07:00
2016-09-28 12:27:39 -07:00
while ( software | | ! subreader . EOF )
{
software = false ;
2016-09-27 12:05:29 -07:00
2016-09-28 12:27:39 -07:00
// We only want elements
if ( subreader . NodeType ! = XmlNodeType . Element )
{
2016-10-03 00:21:03 -07:00
if ( subreader . NodeType = = XmlNodeType . EndElement & & subreader . Name = = "part" )
{
partname = "" ;
partinterface = "" ;
2016-10-03 10:00:20 -07:00
features = new List < Tuple < string , string > > ( ) ;
2016-10-03 00:21:03 -07:00
}
if ( subreader . NodeType = = XmlNodeType . EndElement & & ( subreader . Name = = "dataarea" | | subreader . Name = = "diskarea" ) )
{
areaname = "" ;
areasize = null ;
}
2016-09-28 12:27:39 -07:00
subreader . Read ( ) ;
continue ;
}
2016-09-27 13:52:40 -07:00
2016-09-28 12:27:39 -07:00
// Get the roms from the machine
switch ( subreader . Name )
{
2016-09-30 16:35:47 -07:00
// For OfflineList only
2016-09-28 12:27:39 -07:00
case "title" :
2016-10-24 12:58:57 -07:00
machine . Name = subreader . ReadElementContentAsString ( ) ;
2016-09-28 12:27:39 -07:00
break ;
case "releaseNumber" :
releaseNumber = subreader . ReadElementContentAsString ( ) ;
break ;
case "romSize" :
if ( ! Int64 . TryParse ( subreader . ReadElementContentAsString ( ) , out size ) )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
size = - 1 ;
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
break ;
case "romCRC" :
empty = false ;
2016-09-22 17:46:21 -07:00
2016-09-28 12:27:39 -07:00
ext = ( subreader . GetAttribute ( "extension" ) ! = null ? subreader . GetAttribute ( "extension" ) : "" ) ;
2016-09-22 17:46:21 -07:00
2016-10-24 12:58:57 -07:00
DatItem olrom = new Rom
{
Name = releaseNumber + " - " + machine . Name + ext ,
Size = size ,
CRC = subreader . ReadElementContentAsString ( ) ,
ItemStatus = ItemStatus . None ,
Machine = machine ,
} ;
2016-09-27 11:37:12 -07:00
2016-09-28 12:27:39 -07:00
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( olrom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-28 12:27:39 -07:00
break ;
2016-09-27 13:52:40 -07:00
2016-09-30 16:25:58 -07:00
// For Software List only
case "publisher" :
publisher = subreader . ReadElementContentAsString ( ) ;
break ;
case "info" :
2016-10-03 10:00:20 -07:00
infos . Add ( Tuple . Create ( subreader . GetAttribute ( "name" ) , subreader . GetAttribute ( "value" ) ) ) ;
2016-09-30 16:25:58 -07:00
subreader . Read ( ) ;
break ;
case "part" :
partname = subreader . GetAttribute ( "name" ) ;
partinterface = subreader . GetAttribute ( "interface" ) ;
subreader . Read ( ) ;
break ;
case "feature" :
2016-10-03 10:00:20 -07:00
features . Add ( Tuple . Create ( subreader . GetAttribute ( "name" ) , subreader . GetAttribute ( "value" ) ) ) ;
2016-09-30 16:25:58 -07:00
subreader . Read ( ) ;
break ;
case "dataarea" :
case "diskarea" :
areaname = subreader . GetAttribute ( "name" ) ;
long areasizetemp = - 1 ;
if ( Int64 . TryParse ( subreader . GetAttribute ( "size" ) , out areasizetemp ) )
{
areasize = areasizetemp ;
}
subreader . Read ( ) ;
break ;
2016-09-28 12:27:39 -07:00
// For Logiqx, SabreDAT, and Software List
case "description" :
2016-10-24 12:58:57 -07:00
machine . Description = subreader . ReadElementContentAsString ( ) ;
2016-10-02 10:45:52 -07:00
if ( ! softlist & & temptype = = "software" )
2016-09-30 16:25:58 -07:00
{
2016-10-24 12:58:57 -07:00
machine . Name = machine . Description . Replace ( '/' , '_' ) . Replace ( "\"" , "''" ) ;
2016-09-30 16:25:58 -07:00
}
2016-09-28 12:27:39 -07:00
break ;
case "year" :
2016-10-24 12:58:57 -07:00
machine . Year = subreader . ReadElementContentAsString ( ) ;
2016-09-28 12:27:39 -07:00
break ;
case "manufacturer" :
2016-10-24 12:58:57 -07:00
machine . Manufacturer = subreader . ReadElementContentAsString ( ) ;
2016-09-28 12:27:39 -07:00
break ;
case "release" :
empty = false ;
bool? defaultrel = null ;
if ( subreader . GetAttribute ( "default" ) ! = null )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
if ( subreader . GetAttribute ( "default" ) = = "yes" )
{
defaultrel = true ;
}
else if ( subreader . GetAttribute ( "default" ) = = "no" )
{
defaultrel = false ;
}
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
2016-10-24 12:58:57 -07:00
DatItem relrom = new Release
{
Name = subreader . GetAttribute ( "name" ) ,
Region = subreader . GetAttribute ( "region" ) ,
Language = subreader . GetAttribute ( "language" ) ,
Date = date ,
Default = defaultrel ,
Machine = machine ,
Supported = supported ,
Publisher = publisher ,
Infos = infos ,
PartName = partname ,
PartInterface = partinterface ,
Features = features ,
AreaName = areaname ,
AreaSize = areasize ,
} ;
2016-09-28 12:27:39 -07:00
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( relrom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-28 12:27:39 -07:00
subreader . Read ( ) ;
break ;
case "biosset" :
empty = false ;
bool? defaultbios = null ;
if ( subreader . GetAttribute ( "default" ) ! = null )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
if ( subreader . GetAttribute ( "default" ) = = "yes" )
{
defaultbios = true ;
}
else if ( subreader . GetAttribute ( "default" ) = = "no" )
{
defaultbios = false ;
}
2016-09-27 11:37:12 -07:00
}
2016-09-22 17:46:21 -07:00
2016-10-24 12:58:57 -07:00
DatItem biosrom = new BiosSet
{
Name = subreader . GetAttribute ( "name" ) ,
Description = subreader . GetAttribute ( "description" ) ,
Default = defaultbios ,
Machine = machine ,
Supported = supported ,
Publisher = publisher ,
Infos = infos ,
PartName = partname ,
PartInterface = partinterface ,
Features = features ,
AreaName = areaname ,
AreaSize = areasize ,
SystemID = sysid ,
System = filename ,
SourceID = srcid ,
} ;
2016-09-22 17:00:34 -07:00
2016-09-28 12:27:39 -07:00
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( biosrom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
subreader . Read ( ) ;
break ;
case "archive" :
empty = false ;
2016-09-22 18:15:02 -07:00
2016-10-24 12:58:57 -07:00
DatItem archiverom = new Archive
{
Name = subreader . GetAttribute ( "name" ) ,
Machine = machine ,
Supported = supported ,
Publisher = publisher ,
Infos = infos ,
PartName = partname ,
PartInterface = partinterface ,
Features = features ,
AreaName = areaname ,
AreaSize = areasize ,
SystemID = sysid ,
System = filename ,
SourceID = srcid ,
} ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( archiverom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-27 13:52:40 -07:00
2016-09-28 12:27:39 -07:00
subreader . Read ( ) ;
break ;
case "sample" :
empty = false ;
2016-09-22 18:15:02 -07:00
2016-10-24 12:58:57 -07:00
DatItem samplerom = new Sample
{
Name = subreader . GetAttribute ( "name" ) ,
Machine = machine ,
Supported = supported ,
Publisher = publisher ,
Infos = infos ,
PartName = partname ,
PartInterface = partinterface ,
Features = features ,
AreaName = areaname ,
AreaSize = areasize ,
SystemID = sysid ,
System = filename ,
SourceID = srcid ,
} ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( samplerom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-27 11:37:12 -07:00
2016-09-28 12:27:39 -07:00
subreader . Read ( ) ;
break ;
case "rom" :
case "disk" :
empty = false ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// If the rom has a status, flag it
its = ItemStatus . None ;
if ( subreader . GetAttribute ( "flags" ) = = "good" | | subreader . GetAttribute ( "status" ) = = "good" )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
its = ItemStatus . Good ;
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
if ( subreader . GetAttribute ( "flags" ) = = "baddump" | | subreader . GetAttribute ( "status" ) = = "baddump" )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
logger . Verbose ( "Bad dump detected: " +
( subreader . GetAttribute ( "name" ) ! = null & & subreader . GetAttribute ( "name" ) ! = "" ? "\"" + xtr . GetAttribute ( "name" ) + "\"" : "ROM NAME NOT FOUND" ) ) ;
its = ItemStatus . BadDump ;
}
2016-09-30 10:59:53 -07:00
if ( subreader . GetAttribute ( "flags" ) = = "nodump" | | subreader . GetAttribute ( "status" ) = = "nodump" )
2016-09-28 12:27:39 -07:00
{
logger . Verbose ( "Nodump detected: " +
( subreader . GetAttribute ( "name" ) ! = null & & subreader . GetAttribute ( "name" ) ! = "" ? "\"" + xtr . GetAttribute ( "name" ) + "\"" : "ROM NAME NOT FOUND" ) ) ;
its = ItemStatus . Nodump ;
}
if ( subreader . GetAttribute ( "flags" ) = = "verified" | | subreader . GetAttribute ( "status" ) = = "verified" )
{
its = ItemStatus . Verified ;
2016-09-27 11:37:12 -07:00
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// If the rom has a Date attached, read it in and then sanitize it
date = "" ;
if ( subreader . GetAttribute ( "date" ) ! = null )
{
DateTime dateTime = DateTime . Now ;
if ( DateTime . TryParse ( subreader . GetAttribute ( "date" ) , out dateTime ) )
{
date = dateTime . ToString ( ) ;
}
else
{
date = subreader . GetAttribute ( "date" ) ;
}
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// Take care of hex-sized files
size = - 1 ;
if ( subreader . GetAttribute ( "size" ) ! = null & & subreader . GetAttribute ( "size" ) . Contains ( "0x" ) )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
size = Convert . ToInt64 ( subreader . GetAttribute ( "size" ) , 16 ) ;
}
else if ( subreader . GetAttribute ( "size" ) ! = null )
{
Int64 . TryParse ( subreader . GetAttribute ( "size" ) , out size ) ;
2016-09-27 11:37:12 -07:00
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// If the rom is continue or ignore, add the size to the previous rom
if ( subreader . GetAttribute ( "loadflag" ) = = "continue" | | subreader . GetAttribute ( "loadflag" ) = = "ignore" )
{
int index = Files [ key ] . Count ( ) - 1 ;
DatItem lastrom = Files [ key ] [ index ] ;
if ( lastrom . Type = = ItemType . Rom )
{
( ( Rom ) lastrom ) . Size + = size ;
}
Files [ key ] . RemoveAt ( index ) ;
Files [ key ] . Add ( lastrom ) ;
subreader . Read ( ) ;
continue ;
}
2016-09-27 11:37:12 -07:00
2016-09-28 12:27:39 -07:00
// If we're in clean mode, sanitize the game name
if ( clean )
{
2016-10-24 12:58:57 -07:00
machine . Name = Style . CleanGameName ( machine . Name . Split ( Path . DirectorySeparatorChar ) ) ;
2016-09-28 12:27:39 -07:00
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
DatItem inrom ;
switch ( subreader . Name . ToLowerInvariant ( ) )
{
case "disk" :
2016-10-24 12:58:57 -07:00
inrom = new Disk
{
Name = subreader . GetAttribute ( "name" ) ,
MD5 = subreader . GetAttribute ( "md5" ) ? . ToLowerInvariant ( ) ,
SHA1 = subreader . GetAttribute ( "sha1" ) ? . ToLowerInvariant ( ) ,
ItemStatus = its ,
Machine = machine ,
Supported = supported ,
Publisher = publisher ,
Infos = infos ,
PartName = partname ,
PartInterface = partinterface ,
Features = features ,
AreaName = areaname ,
AreaSize = areasize ,
SystemID = sysid ,
System = filename ,
SourceID = srcid ,
} ;
2016-09-28 12:27:39 -07:00
break ;
case "rom" :
default :
2016-10-24 12:58:57 -07:00
inrom = new Rom
{
Name = subreader . GetAttribute ( "name" ) ,
Size = size ,
CRC = subreader . GetAttribute ( "crc" ) ,
MD5 = subreader . GetAttribute ( "md5" ) ? . ToLowerInvariant ( ) ,
SHA1 = subreader . GetAttribute ( "sha1" ) ? . ToLowerInvariant ( ) ,
ItemStatus = its ,
Date = date ,
Machine = machine ,
Supported = supported ,
Publisher = publisher ,
Infos = infos ,
PartName = partname ,
PartInterface = partinterface ,
Features = features ,
AreaName = areaname ,
AreaSize = areasize ,
SystemID = sysid ,
System = filename ,
SourceID = srcid ,
} ;
2016-09-28 12:27:39 -07:00
break ;
}
2016-09-22 17:00:34 -07:00
2016-09-28 12:27:39 -07:00
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( inrom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-28 12:27:39 -07:00
subreader . Read ( ) ;
break ;
default :
subreader . Read ( ) ;
break ;
}
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
xtr . Skip ( ) ;
break ;
case "dir" :
case "directory" :
// Set SuperDAT flag for all SabreDAT inputs, regardless of depth
superdat = true ;
if ( keep )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
Type = ( Type = = "" ? "SuperDAT" : Type ) ;
2016-09-22 17:52:58 -07:00
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
string foldername = ( xtr . GetAttribute ( "name" ) = = null ? "" : xtr . GetAttribute ( "name" ) ) ;
if ( foldername ! = "" )
{
parent . Add ( foldername ) ;
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
xtr . Read ( ) ;
break ;
case "file" :
empty = false ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// If the rom is itemStatus, flag it
its = ItemStatus . None ;
flagreader = xtr . ReadSubtree ( ) ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// If the subtree is empty, skip it
if ( flagreader = = null )
2016-09-22 17:52:58 -07:00
{
2016-09-28 12:27:39 -07:00
xtr . Skip ( ) ;
2016-09-27 11:37:12 -07:00
continue ;
2016-09-22 17:52:58 -07:00
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
while ( ! flagreader . EOF )
2016-09-22 17:52:58 -07:00
{
2016-09-28 12:27:39 -07:00
// We only want elements
if ( flagreader . NodeType ! = XmlNodeType . Element | | flagreader . Name = = "flags" )
{
flagreader . Read ( ) ;
continue ;
}
switch ( flagreader . Name )
{
case "flag" :
case "status" :
if ( flagreader . GetAttribute ( "name" ) ! = null & & flagreader . GetAttribute ( "value" ) ! = null )
2016-09-27 11:37:12 -07:00
{
2016-09-28 12:27:39 -07:00
string content = flagreader . GetAttribute ( "value" ) ;
switch ( flagreader . GetAttribute ( "name" ) )
{
case "good" :
its = ItemStatus . Good ;
break ;
case "baddump" :
logger . Verbose ( "Bad dump detected: " + ( xtr . GetAttribute ( "name" ) ! = null & & xtr . GetAttribute ( "name" ) ! = "" ?
"\"" + xtr . GetAttribute ( "name" ) + "\"" : "ROM NAME NOT FOUND" ) ) ;
its = ItemStatus . BadDump ;
break ;
2016-09-30 10:59:53 -07:00
case "nodump" :
2016-09-28 12:27:39 -07:00
logger . Verbose ( "Nodump detected: " + ( xtr . GetAttribute ( "name" ) ! = null & & xtr . GetAttribute ( "name" ) ! = "" ?
"\"" + xtr . GetAttribute ( "name" ) + "\"" : "ROM NAME NOT FOUND" ) ) ;
its = ItemStatus . Nodump ;
break ;
case "verified" :
its = ItemStatus . Verified ;
break ;
}
2016-09-27 11:37:12 -07:00
}
2016-09-28 12:27:39 -07:00
break ;
}
2016-09-22 17:52:58 -07:00
2016-09-28 12:27:39 -07:00
flagreader . Read ( ) ;
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// If the rom has a Date attached, read it in and then sanitize it
date = "" ;
if ( xtr . GetAttribute ( "date" ) ! = null )
{
date = DateTime . Parse ( xtr . GetAttribute ( "date" ) ) . ToString ( ) ;
}
2016-09-27 11:37:12 -07:00
2016-09-28 12:27:39 -07:00
// Take care of hex-sized files
size = - 1 ;
if ( xtr . GetAttribute ( "size" ) ! = null & & xtr . GetAttribute ( "size" ) . Contains ( "0x" ) )
{
size = Convert . ToInt64 ( xtr . GetAttribute ( "size" ) , 16 ) ;
}
else if ( xtr . GetAttribute ( "size" ) ! = null )
{
Int64 . TryParse ( xtr . GetAttribute ( "size" ) , out size ) ;
}
2016-09-22 17:52:58 -07:00
2016-09-28 12:27:39 -07:00
// If the rom is continue or ignore, add the size to the previous rom
if ( xtr . GetAttribute ( "loadflag" ) = = "continue" | | xtr . GetAttribute ( "loadflag" ) = = "ignore" )
2016-09-22 17:52:58 -07:00
{
2016-09-28 12:27:39 -07:00
int index = Files [ key ] . Count ( ) - 1 ;
DatItem lastrom = Files [ key ] [ index ] ;
if ( lastrom . Type = = ItemType . Rom )
{
( ( Rom ) lastrom ) . Size + = size ;
}
Files [ key ] . RemoveAt ( index ) ;
Files [ key ] . Add ( lastrom ) ;
continue ;
2016-09-22 17:52:58 -07:00
}
2016-09-22 18:15:02 -07:00
2016-10-24 12:58:57 -07:00
Machine dir = new Machine ( ) ;
2016-09-28 12:27:39 -07:00
// Get the name of the game from the parent
2016-10-24 12:58:57 -07:00
dir . Name = String . Join ( "\\" , parent ) ;
dir . Description = dir . Name ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// If we aren't keeping names, trim out the path
if ( ! keep | | ! superdat )
2016-09-22 17:52:58 -07:00
{
2016-10-24 12:58:57 -07:00
string tempout = Regex . Match ( dir . Name , @".*?\\(.*)" ) . Groups [ 1 ] . Value ;
2016-09-28 12:27:39 -07:00
if ( tempout ! = "" )
{
2016-10-24 12:58:57 -07:00
dir . Name = tempout ;
2016-09-28 12:27:39 -07:00
}
2016-09-22 17:52:58 -07:00
}
2016-09-28 12:27:39 -07:00
DatItem rom ;
switch ( xtr . GetAttribute ( "type" ) . ToLowerInvariant ( ) )
{
case "disk" :
2016-10-24 12:58:57 -07:00
rom = new Disk
{
Name = xtr . GetAttribute ( "name" ) ,
MD5 = xtr . GetAttribute ( "md5" ) ? . ToLowerInvariant ( ) ,
SHA1 = xtr . GetAttribute ( "sha1" ) ? . ToLowerInvariant ( ) ,
ItemStatus = its ,
Machine = dir ,
SystemID = sysid ,
System = filename ,
SourceID = srcid ,
} ;
2016-09-28 12:27:39 -07:00
break ;
case "rom" :
default :
2016-10-24 12:58:57 -07:00
rom = new Rom
{
Name = xtr . GetAttribute ( "name" ) ,
Size = size ,
CRC = xtr . GetAttribute ( "crc" ) ? . ToLowerInvariant ( ) ,
MD5 = xtr . GetAttribute ( "md5" ) ? . ToLowerInvariant ( ) ,
SHA1 = xtr . GetAttribute ( "sha1" ) ? . ToLowerInvariant ( ) ,
ItemStatus = its ,
Date = date ,
Machine = dir ,
SystemID = sysid ,
System = filename ,
SourceID = srcid ,
} ;
2016-09-28 12:27:39 -07:00
break ;
}
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
// Now process and add the rom
2016-10-26 21:02:01 -07:00
ParseAddHelper ( rom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-22 18:15:02 -07:00
2016-09-28 12:27:39 -07:00
xtr . Read ( ) ;
break ;
default :
xtr . Read ( ) ;
break ;
}
2016-09-22 17:00:34 -07:00
}
}
2016-09-29 12:57:10 -07:00
catch ( Exception ex )
2016-09-28 12:27:39 -07:00
{
2016-09-29 12:57:10 -07:00
logger . Warning ( ex . ToString ( ) ) ;
2016-09-28 12:27:39 -07:00
// For XML errors, just skip the affected node
2016-09-29 12:55:16 -07:00
xtr ? . Read ( ) ;
2016-09-28 12:27:39 -07:00
}
2016-09-27 11:37:12 -07:00
xtr . Dispose ( ) ;
2016-09-22 17:00:34 -07:00
}
2016-09-30 12:15:36 -07:00
/// <summary>
/// Parse a Redump MD5 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>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-30 12:15:36 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
/// <param name="logger">Logger object for console and/or file output</param>
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
private void ParseRedumpMD5 (
// Standard Dat parsing
string filename ,
int sysid ,
int srcid ,
// Rom filtering
2016-10-26 21:02:01 -07:00
Filter filter ,
2016-09-30 12:15:36 -07:00
// Rom renaming
bool trim ,
bool single ,
string root ,
// Miscellaneous
Logger logger ,
bool clean )
{
// Open a file reader
Encoding enc = Style . GetEncoding ( filename ) ;
StreamReader sr = new StreamReader ( File . OpenRead ( filename ) , enc ) ;
while ( ! sr . EndOfStream )
{
string line = sr . ReadLine ( ) ;
2016-10-24 12:58:57 -07:00
Rom rom = new Rom
{
Name = line . Split ( ' ' ) [ 1 ] . Replace ( "*" , String . Empty ) ,
Size = - 1 ,
MD5 = line . Split ( ' ' ) [ 0 ] ,
ItemStatus = ItemStatus . None ,
Machine = new Machine
{
Name = Path . GetFileNameWithoutExtension ( filename ) ,
} ,
SystemID = sysid ,
SourceID = srcid ,
} ;
2016-09-30 12:15:36 -07:00
// Now process and add the rom
string key = "" ;
2016-10-26 21:02:01 -07:00
ParseAddHelper ( rom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-30 12:15:36 -07:00
}
sr . Dispose ( ) ;
}
/// <summary>
/// Parse a Redump 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>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-30 12:15:36 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
/// <param name="logger">Logger object for console and/or file output</param>
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
private void ParseRedumpSFV (
// Standard Dat parsing
string filename ,
int sysid ,
int srcid ,
// Rom filtering
2016-10-26 21:02:01 -07:00
Filter filter ,
2016-09-30 12:15:36 -07:00
// Rom renaming
bool trim ,
bool single ,
string root ,
// Miscellaneous
Logger logger ,
bool clean )
{
// Open a file reader
Encoding enc = Style . GetEncoding ( filename ) ;
StreamReader sr = new StreamReader ( File . OpenRead ( filename ) , enc ) ;
while ( ! sr . EndOfStream )
{
string line = sr . ReadLine ( ) ;
2016-10-24 12:58:57 -07:00
Rom rom = new Rom
{
Name = line . Split ( ' ' ) [ 0 ] . Replace ( "*" , String . Empty ) ,
Size = - 1 ,
CRC = line . Split ( ' ' ) [ 1 ] ,
ItemStatus = ItemStatus . None ,
Machine = new Machine
{
Name = Path . GetFileNameWithoutExtension ( filename ) ,
} ,
SystemID = sysid ,
SourceID = srcid ,
} ;
2016-09-30 12:15:36 -07:00
// Now process and add the rom
string key = "" ;
2016-10-26 21:02:01 -07:00
ParseAddHelper ( rom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-30 12:15:36 -07:00
}
sr . Dispose ( ) ;
}
/// <summary>
/// Parse a Redump SHA-1 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>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-30 12:15:36 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
/// <param name="logger">Logger object for console and/or file output</param>
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
private void ParseRedumpSHA1 (
// Standard Dat parsing
string filename ,
int sysid ,
int srcid ,
// Rom filtering
2016-10-26 21:02:01 -07:00
Filter filter ,
2016-09-30 12:15:36 -07:00
// Rom renaming
bool trim ,
bool single ,
string root ,
// Miscellaneous
Logger logger ,
bool clean )
{
// Open a file reader
Encoding enc = Style . GetEncoding ( filename ) ;
StreamReader sr = new StreamReader ( File . OpenRead ( filename ) , enc ) ;
while ( ! sr . EndOfStream )
{
string line = sr . ReadLine ( ) ;
2016-10-24 12:58:57 -07:00
Rom rom = new Rom
{
Name = line . Split ( ' ' ) [ 1 ] . Replace ( "*" , String . Empty ) ,
Size = - 1 ,
SHA1 = line . Split ( ' ' ) [ 0 ] ,
ItemStatus = ItemStatus . None ,
Machine = new Machine
{
Name = Path . GetFileNameWithoutExtension ( filename ) ,
} ,
SystemID = sysid ,
SourceID = srcid ,
} ;
2016-09-30 12:15:36 -07:00
// Now process and add the rom
string key = "" ;
2016-10-26 21:02:01 -07:00
ParseAddHelper ( rom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-30 12:15:36 -07:00
}
sr . Dispose ( ) ;
}
/// <summary>
/// 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>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-30 12:15:36 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
/// <param name="logger">Logger object for console and/or file output</param>
/// <param name="clean">True if game names are sanitized, false otherwise (default)</param>
private void ParseRC (
// Standard Dat parsing
string filename ,
int sysid ,
int srcid ,
// Rom filtering
2016-10-26 21:02:01 -07:00
Filter filter ,
2016-09-30 12:15:36 -07:00
// Rom renaming
bool trim ,
bool single ,
string root ,
// Miscellaneous
Logger logger ,
bool clean )
{
// Open a file reader
Encoding enc = Style . GetEncoding ( filename ) ;
StreamReader sr = new StreamReader ( File . OpenRead ( filename ) , enc ) ;
string blocktype = "" ;
while ( ! sr . EndOfStream )
{
string line = sr . ReadLine ( ) ;
// If the line is the start of the credits section
2016-10-07 14:08:17 -07:00
if ( line . ToLowerInvariant ( ) . StartsWith ( "[credits]" ) )
2016-09-30 12:15:36 -07:00
{
blocktype = "credits" ;
}
// If the line is the start of the dat section
2016-10-07 14:08:17 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "[dat]" ) )
2016-09-30 12:15:36 -07:00
{
blocktype = "dat" ;
}
// If the line is the start of the emulator section
2016-10-07 14:08:17 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "[emulator]" ) )
2016-09-30 12:15:36 -07:00
{
blocktype = "emulator" ;
}
// If the line is the start of the game section
2016-10-07 14:08:17 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "[games]" ) )
2016-09-30 12:15:36 -07:00
{
blocktype = "games" ;
}
// Otherwise, it's not a section and it's data, so get out all data
else
{
// If we have an author
2016-10-02 20:32:57 -07:00
if ( line . ToLowerInvariant ( ) . StartsWith ( "author=" ) )
2016-09-30 12:15:36 -07:00
{
Author = ( String . IsNullOrEmpty ( Author ) ? line . Split ( '=' ) [ 1 ] : Author ) ;
}
// If we have one of the three version tags
2016-10-02 20:32:57 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "version=" ) )
2016-09-30 12:15:36 -07:00
{
switch ( blocktype )
{
case "credits" :
Version = ( String . IsNullOrEmpty ( Version ) ? line . Split ( '=' ) [ 1 ] : Version ) ;
break ;
case "emulator" :
Description = ( String . IsNullOrEmpty ( Description ) ? line . Split ( '=' ) [ 1 ] : Description ) ;
break ;
}
}
2016-10-21 15:37:34 -07:00
// If we have a URL
else if ( line . ToLowerInvariant ( ) . StartsWith ( "url=" ) )
{
Url = ( String . IsNullOrEmpty ( Url ) ? line . Split ( '=' ) [ 1 ] : Url ) ;
}
2016-09-30 12:15:36 -07:00
// If we have a comment
2016-10-02 20:32:57 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "comment=" ) )
2016-09-30 12:15:36 -07:00
{
Comment = ( String . IsNullOrEmpty ( Comment ) ? line . Split ( '=' ) [ 1 ] : Comment ) ;
}
// If we have the split flag
2016-10-02 20:32:57 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "split=" ) )
2016-09-30 12:15:36 -07:00
{
int split = 0 ;
if ( Int32 . TryParse ( line . Split ( '=' ) [ 1 ] , out split ) )
{
if ( split = = 1 )
{
ForceMerging = ForceMerging . Split ;
}
}
}
// If we have the merge tag
2016-10-02 20:32:57 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "merge=" ) )
2016-09-30 12:15:36 -07:00
{
int merge = 0 ;
if ( Int32 . TryParse ( line . Split ( '=' ) [ 1 ] , out merge ) )
{
if ( merge = = 1 )
{
ForceMerging = ForceMerging . Full ;
}
}
}
// If we have the refname tag
2016-10-02 20:32:57 -07:00
else if ( line . ToLowerInvariant ( ) . StartsWith ( "refname=" ) )
2016-09-30 12:15:36 -07:00
{
Name = ( String . IsNullOrEmpty ( Name ) ? line . Split ( '=' ) [ 1 ] : Name ) ;
}
// If we have a rom
else if ( line . StartsWith ( "¬" ) )
{
2016-10-02 20:32:57 -07:00
// Some old RC DATs have this behavior
if ( line . Contains ( "¬N¬O" ) )
{
line = line . Replace ( "¬N¬O" , "" ) + "¬¬" ;
}
2016-09-30 12:15:36 -07:00
/ *
The rominfo order is as follows :
1 - parent name
2 - parent description
3 - game name
4 - game description
5 - rom name
6 - rom crc
7 - rom size
8 - romof name
9 - merge name
* /
string [ ] rominfo = line . Split ( '¬' ) ;
2016-10-06 11:42:55 -07:00
// Try getting the size separately
long size = 0 ;
if ( ! Int64 . TryParse ( rominfo [ 7 ] , out size ) )
{
size = 0 ;
}
2016-10-24 12:58:57 -07:00
Rom rom = new Rom
{
Name = rominfo [ 5 ] ,
Size = size ,
CRC = rominfo [ 6 ] ,
ItemStatus = ItemStatus . None ,
Machine = new Machine
{
Name = rominfo [ 3 ] ,
Description = rominfo [ 4 ] ,
CloneOf = rominfo [ 1 ] ,
RomOf = rominfo [ 8 ] ,
} ,
SystemID = sysid ,
SourceID = srcid ,
} ;
2016-09-30 12:15:36 -07:00
// Now process and add the rom
string key = "" ;
2016-10-26 21:02:01 -07:00
ParseAddHelper ( rom , filter , trim , single , root , clean , logger , out key ) ;
2016-09-30 12:15:36 -07:00
}
}
}
sr . Dispose ( ) ;
}
2016-09-22 17:00:34 -07:00
/// <summary>
2016-09-22 18:15:02 -07:00
/// Add a rom to the Dat after checking
2016-09-22 17:00:34 -07:00
/// </summary>
2016-09-22 18:15:02 -07:00
/// <param name="item">Item data to check against</param>
2016-10-26 21:02:01 -07:00
/// <param name="filter">Filter object for passing to the DatItem level</param>
2016-09-22 18:15:02 -07:00
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
/// <param name="logger">Logger object for console and/or file output</param>
2016-10-26 21:02:01 -07:00
private void ParseAddHelper ( DatItem item , Filter filter , bool trim , bool single , string root , bool clean , Logger logger , out string key )
2016-09-22 17:00:34 -07:00
{
2016-09-22 18:15:02 -07:00
key = "" ;
// If there's no name in the rom, we log and skip it
2016-10-03 10:13:24 -07:00
if ( item . Name = = null )
2016-09-22 17:00:34 -07:00
{
2016-09-22 18:15:02 -07:00
logger . Warning ( "Rom with no name found! Skipping..." ) ;
return ;
}
2016-09-22 17:52:58 -07:00
2016-09-22 18:15:02 -07:00
// If we're in cleaning mode, sanitize the game name
2016-10-24 12:58:57 -07:00
item . Machine . Name = ( clean ? Style . CleanGameName ( item . Machine . Name ) : item . Machine . Name ) ;
2016-09-22 18:15:02 -07:00
// If we have a Rom or a Disk, clean the hash data
if ( item . Type = = ItemType . Rom )
{
Rom itemRom = ( Rom ) item ;
// Sanitize the hashes from null, hex sizes, and "true blank" strings
itemRom . CRC = Style . CleanHashData ( itemRom . CRC , Constants . CRCLength ) ;
itemRom . MD5 = Style . CleanHashData ( itemRom . MD5 , Constants . MD5Length ) ;
itemRom . SHA1 = Style . CleanHashData ( itemRom . SHA1 , Constants . SHA1Length ) ;
// If we have a rom and it's missing size AND the hashes match a 0-byte file, fill in the rest of the info
if ( ( itemRom . Size = = 0 | | itemRom . Size = = - 1 )
2016-09-23 12:08:21 -07:00
& & ( ( itemRom . CRC = = Constants . CRCZero | | String . IsNullOrEmpty ( itemRom . CRC ) )
| | itemRom . MD5 = = Constants . MD5Zero
| | itemRom . SHA1 = = Constants . SHA1Zero ) )
2016-09-22 17:00:34 -07:00
{
2016-09-22 18:15:02 -07:00
itemRom . Size = Constants . SizeZero ;
itemRom . CRC = Constants . CRCZero ;
itemRom . MD5 = Constants . MD5Zero ;
itemRom . SHA1 = Constants . SHA1Zero ;
}
// If the file has no size and it's not the above case, skip and log
2016-10-11 22:16:27 -07:00
else if ( itemRom . ItemStatus ! = ItemStatus . Nodump & & ( itemRom . Size = = 0 | | itemRom . Size = = - 1 ) )
2016-09-23 12:08:21 -07:00
{
2016-10-11 22:16:27 -07:00
logger . Verbose ( "Incomplete entry for \"" + itemRom . Name + "\" will be output as nodump" ) ;
2016-09-23 12:08:21 -07:00
itemRom . ItemStatus = ItemStatus . Nodump ;
}
// If the file has a size but aboslutely no hashes, skip and log
2016-10-11 22:16:27 -07:00
else if ( itemRom . ItemStatus ! = ItemStatus . Nodump
& & itemRom . Size > 0
2016-09-23 12:08:21 -07:00
& & String . IsNullOrEmpty ( itemRom . CRC )
& & String . IsNullOrEmpty ( itemRom . MD5 )
& & String . IsNullOrEmpty ( itemRom . SHA1 ) )
2016-09-22 18:15:02 -07:00
{
2016-10-11 22:16:27 -07:00
logger . Verbose ( "Incomplete entry for \"" + itemRom . Name + "\" will be output as nodump" ) ;
2016-09-22 18:15:02 -07:00
itemRom . ItemStatus = ItemStatus . Nodump ;
}
item = itemRom ;
}
else if ( item . Type = = ItemType . Disk )
{
Disk itemDisk = ( Disk ) item ;
// Sanitize the hashes from null, hex sizes, and "true blank" strings
itemDisk . MD5 = Style . CleanHashData ( itemDisk . MD5 , Constants . MD5Length ) ;
itemDisk . SHA1 = Style . CleanHashData ( itemDisk . SHA1 , Constants . SHA1Length ) ;
2016-09-23 12:08:21 -07:00
// If the file has aboslutely no hashes, skip and log
2016-10-11 22:16:27 -07:00
if ( itemDisk . ItemStatus ! = ItemStatus . Nodump
& & String . IsNullOrEmpty ( itemDisk . MD5 )
2016-09-23 12:08:21 -07:00
& & String . IsNullOrEmpty ( itemDisk . SHA1 ) )
{
2016-10-11 22:16:27 -07:00
logger . Verbose ( "Incomplete entry for \"" + itemDisk . Name + "\" will be output as nodump" ) ;
2016-09-23 12:08:21 -07:00
itemDisk . ItemStatus = ItemStatus . Nodump ;
}
2016-09-22 18:15:02 -07:00
item = itemDisk ;
}
// If the rom passes the filter, include it
2016-10-26 21:02:01 -07:00
if ( filter . ItemPasses ( item , logger ) )
2016-09-22 18:15:02 -07:00
{
// If we are in single game mode, rename all games
if ( single )
{
2016-10-24 12:58:57 -07:00
item . Machine . Name = "!" ;
2016-09-22 18:15:02 -07:00
}
// If we are in NTFS trim mode, trim the game name
if ( trim )
{
// Windows max name length is 260
2016-10-24 12:58:57 -07:00
int usableLength = 260 - item . Machine . Name . Length - root . Length ;
2016-09-22 18:15:02 -07:00
if ( item . Name . Length > usableLength )
2016-09-22 17:00:34 -07:00
{
2016-09-22 18:15:02 -07:00
string ext = Path . GetExtension ( item . Name ) ;
item . Name = item . Name . Substring ( 0 , usableLength - ext . Length ) ;
item . Name + = ext ;
2016-09-22 17:00:34 -07:00
}
}
2016-09-22 18:15:02 -07:00
lock ( Files )
2016-09-22 17:00:34 -07:00
{
2016-09-22 18:15:02 -07:00
// Get the key and add statistical data
switch ( item . Type )
2016-05-23 14:15:09 -07:00
{
2016-09-22 18:15:02 -07:00
case ItemType . Archive :
case ItemType . BiosSet :
case ItemType . Release :
case ItemType . Sample :
key = item . Type . ToString ( ) ;
break ;
case ItemType . Disk :
key = ( ( Disk ) item ) . MD5 ;
// Add statistical data
DiskCount + = 1 ;
TotalSize + = 0 ;
MD5Count + = ( String . IsNullOrEmpty ( ( ( Disk ) item ) . MD5 ) ? 0 : 1 ) ;
SHA1Count + = ( String . IsNullOrEmpty ( ( ( Disk ) item ) . SHA1 ) ? 0 : 1 ) ;
2016-09-26 16:42:06 -07:00
BaddumpCount + = ( ( ( Disk ) item ) . ItemStatus = = ItemStatus . BadDump ? 1 : 0 ) ;
2016-09-22 18:15:02 -07:00
NodumpCount + = ( ( ( Disk ) item ) . ItemStatus = = ItemStatus . Nodump ? 1 : 0 ) ;
break ;
case ItemType . Rom :
key = ( ( Rom ) item ) . Size + "-" + ( ( Rom ) item ) . CRC ;
// Add statistical data
RomCount + = 1 ;
TotalSize + = ( ( ( Rom ) item ) . ItemStatus = = ItemStatus . Nodump ? 0 : ( ( Rom ) item ) . Size ) ;
CRCCount + = ( String . IsNullOrEmpty ( ( ( Rom ) item ) . CRC ) ? 0 : 1 ) ;
MD5Count + = ( String . IsNullOrEmpty ( ( ( Rom ) item ) . MD5 ) ? 0 : 1 ) ;
SHA1Count + = ( String . IsNullOrEmpty ( ( ( Rom ) item ) . SHA1 ) ? 0 : 1 ) ;
2016-09-26 16:42:06 -07:00
BaddumpCount + = ( ( ( Rom ) item ) . ItemStatus = = ItemStatus . BadDump ? 1 : 0 ) ;
2016-09-22 18:15:02 -07:00
NodumpCount + = ( ( ( Rom ) item ) . ItemStatus = = ItemStatus . Nodump ? 1 : 0 ) ;
break ;
default :
key = "default" ;
2016-09-22 17:52:58 -07:00
break ;
2016-05-23 14:15:09 -07:00
}
2016-09-07 12:16:19 -07:00
2016-09-22 18:15:02 -07:00
// Add the item to the DAT
if ( Files . ContainsKey ( key ) )
{
Files [ key ] . Add ( item ) ;
}
else
{
List < DatItem > newvalue = new List < DatItem > ( ) ;
newvalue . Add ( item ) ;
Files . Add ( key , newvalue ) ;
}
}
2016-06-12 21:52:27 -07:00
}
2016-05-23 14:15:09 -07:00
}
2016-05-29 21:15:05 -07:00
2016-08-29 17:28:08 -07:00
#endregion
2016-10-24 16:14:22 -07:00
#region Populate DAT from Directory
2016-08-29 17:28:08 -07:00
2016-06-20 15:17:58 -07:00
/// <summary>
2016-10-24 16:14:22 -07:00
/// Create a new Dat from a directory
2016-09-12 15:29:07 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="basePath">Base folder to be used in creating the DAT</param>
/// <param name="noMD5">True if MD5 hashes should be skipped over, false otherwise</param>
/// <param name="noSHA1">True if SHA-1 hashes should be skipped over, false otherwise</param>
/// <param name="bare">True if the date should be omitted from the DAT, false otherwise</param>
/// <param name="archivesAsFiles">True if archives should be treated as files, false otherwise</param>
/// <param name="enableGzip">True if GZIP archives should be treated as files, false otherwise</param>
/// <param name="addBlanks">True if blank items should be created for empty folders, false otherwise</param>
/// <param name="addDate">True if dates should be archived for all files, false otherwise</param>
/// <param name="tempDir">Name of the directory to create a temp folder in (blank is current directory)</param>
/// <param name="outDir">Output directory to </param>
/// <param name="copyFiles">True if files should be copied to the temp directory before hashing, false otherwise</param>
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
/// <param name="maxDegreeOfParallelism">Integer representing the maximum amount of parallelization to be used</param>
/// <param name="logger">Logger object for console and file output</param>
public bool PopulateDatFromDir ( string basePath , bool noMD5 , bool noSHA1 , bool bare , bool archivesAsFiles ,
bool enableGzip , bool addBlanks , bool addDate , string tempDir , bool copyFiles , string headerToCheckAgainst ,
int maxDegreeOfParallelism , Logger logger )
2016-09-12 15:29:07 -07:00
{
2016-10-24 16:14:22 -07:00
// If the description is defined but not the name, set the name from the description
if ( String . IsNullOrEmpty ( Name ) & & ! String . IsNullOrEmpty ( Description ) )
2016-06-20 15:17:58 -07:00
{
2016-09-22 17:52:58 -07:00
Name = Description ;
}
2016-10-24 16:14:22 -07:00
// If the name is defined but not the description, set the description from the name
else if ( ! String . IsNullOrEmpty ( Name ) & & String . IsNullOrEmpty ( Description ) )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
Description = Name + ( bare ? "" : " (" + Date + ")" ) ;
2016-09-22 17:52:58 -07:00
}
2016-06-20 15:17:58 -07:00
2016-10-24 16:14:22 -07:00
// If neither the name or description are defined, set them from the automatic values
else if ( String . IsNullOrEmpty ( Name ) & & String . IsNullOrEmpty ( Description ) )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
Name = basePath . Split ( Path . DirectorySeparatorChar ) . Last ( ) ;
Description = Name + ( bare ? "" : " (" + Date + ")" ) ;
2016-09-22 17:52:58 -07:00
}
2016-06-20 15:17:58 -07:00
2016-10-24 16:14:22 -07:00
// Process the input
if ( Directory . Exists ( basePath ) )
2016-06-20 15:17:58 -07:00
{
2016-10-24 16:14:22 -07:00
logger . Verbose ( "Folder found: " + basePath ) ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// Process the files in all subfolders
List < string > files = Directory . EnumerateFiles ( basePath , "*" , SearchOption . AllDirectories ) . ToList ( ) ;
Parallel . ForEach ( files ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
item = >
2016-09-12 15:29:07 -07:00
{
2016-10-24 16:14:22 -07:00
DFDProcessPossibleArchive ( item , basePath , noMD5 , noSHA1 , bare , archivesAsFiles , enableGzip , addBlanks , addDate ,
tempDir , copyFiles , headerToCheckAgainst , maxDegreeOfParallelism , logger ) ;
} ) ;
2016-09-12 15:29:07 -07:00
2016-10-24 16:14:22 -07:00
// Now find all folders that are empty, if we are supposed to
if ( ! Romba & & addBlanks )
{
List < string > empties = Directory . EnumerateDirectories ( basePath , "*" , SearchOption . AllDirectories ) . ToList ( ) ;
Parallel . ForEach ( empties ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
dir = >
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
if ( Directory . EnumerateFiles ( dir , "*" , SearchOption . TopDirectoryOnly ) . Count ( ) = = 0 )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
// Get the full path for the directory
string fulldir = Path . GetFullPath ( dir ) ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// Set the temporary variables
string gamename = "" ;
string romname = "" ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// If we have a SuperDAT, we want anything that's not the base path as the game, and the file as the rom
if ( Type = = "SuperDAT" )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
gamename = fulldir . Remove ( 0 , basePath . Length + 1 ) ;
romname = "-" ;
2016-09-22 18:15:02 -07:00
}
2016-10-24 16:14:22 -07:00
// Otherwise, we want just the top level folder as the game, and the file as everything else
else
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
gamename = fulldir . Remove ( 0 , basePath . Length + 1 ) . Split ( Path . DirectorySeparatorChar ) [ 0 ] ;
romname = Path . Combine ( fulldir . Remove ( 0 , basePath . Length + 1 + gamename . Length ) , "-" ) ;
2016-09-22 18:15:02 -07:00
}
2016-10-24 16:14:22 -07:00
// Sanitize the names
if ( gamename . StartsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
gamename = gamename . Substring ( 1 ) ;
}
if ( gamename . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
gamename = gamename . Substring ( 0 , gamename . Length - 1 ) ;
}
if ( romname . StartsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
romname = romname . Substring ( 1 ) ;
}
if ( romname . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
romname = romname . Substring ( 0 , romname . Length - 1 ) ;
}
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
logger . Verbose ( "Adding blank empty folder: " + gamename ) ;
Files [ "null" ] . Add ( new Rom ( romname , gamename ) ) ;
}
} ) ;
2016-09-22 18:15:02 -07:00
}
}
2016-10-24 16:14:22 -07:00
else if ( File . Exists ( basePath ) )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:19:12 -07:00
DFDProcessPossibleArchive ( basePath , Path . GetDirectoryName ( Path . GetDirectoryName ( basePath ) ) , noMD5 , noSHA1 , bare , archivesAsFiles , enableGzip , addBlanks , addDate ,
2016-10-24 16:14:22 -07:00
tempDir , copyFiles , headerToCheckAgainst , maxDegreeOfParallelism , logger ) ;
2016-09-22 17:52:58 -07:00
}
2016-09-19 11:37:18 -07:00
2016-10-24 16:14:22 -07:00
// Now that we're done, delete the temp folder (if it's not the default)
logger . User ( "Cleaning temp folder" ) ;
2016-09-22 17:52:58 -07:00
try
2016-06-20 15:17:58 -07:00
{
2016-10-24 16:14:22 -07:00
if ( tempDir ! = Path . GetTempPath ( ) )
2016-06-20 15:17:58 -07:00
{
2016-10-24 16:14:22 -07:00
Directory . Delete ( tempDir , true ) ;
2016-06-20 15:17:58 -07:00
}
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
catch
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
// Just absorb the error for now
2016-06-20 15:17:58 -07:00
}
2016-09-22 17:52:58 -07:00
return true ;
2016-06-20 15:17:58 -07:00
}
/// <summary>
2016-10-24 16:14:22 -07:00
/// Check a given file for hashes, based on current settings
2016-06-20 15:17:58 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="item">Filename of the item to be checked</param>
/// <param name="basePath">Base folder to be used in creating the DAT</param>
/// <param name="noMD5">True if MD5 hashes should be skipped over, false otherwise</param>
/// <param name="noSHA1">True if SHA-1 hashes should be skipped over, false otherwise</param>
/// <param name="bare">True if the date should be omitted from the DAT, false otherwise</param>
/// <param name="archivesAsFiles">True if archives should be treated as files, false otherwise</param>
/// <param name="enableGzip">True if GZIP archives should be treated as files, false otherwise</param>
/// <param name="addBlanks">True if blank items should be created for empty folders, false otherwise</param>
/// <param name="addDate">True if dates should be archived for all files, false otherwise</param>
/// <param name="tempDir">Name of the directory to create a temp folder in (blank is current directory)</param>
/// <param name="copyFiles">True if files should be copied to the temp directory before hashing, false otherwise</param>
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
/// <param name="maxDegreeOfParallelism">Integer representing the maximum amount of parallelization to be used</param>
/// <param name="logger">Logger object for console and file output</param>
private void DFDProcessPossibleArchive ( string item , string basePath , bool noMD5 , bool noSHA1 , bool bare , bool archivesAsFiles ,
bool enableGzip , bool addBlanks , bool addDate , string tempDir , bool copyFiles , string headerToCheckAgainst ,
int maxDegreeOfParallelism , Logger logger )
2016-06-20 15:17:58 -07:00
{
2016-10-24 16:14:22 -07:00
// Define the temporary directory
string tempSubDir = Path . GetFullPath ( Path . Combine ( tempDir , Path . GetRandomFileName ( ) ) ) + Path . DirectorySeparatorChar ;
// Special case for if we are in Romba mode (all names are supposed to be SHA-1 hashes)
if ( Romba )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
Rom rom = ArchiveTools . GetTorrentGZFileInfo ( item , logger ) ;
// If the rom is valid, write it out
if ( rom . Name ! = null )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
// Add the list if it doesn't exist already
string key = rom . Size + "-" + rom . CRC ;
2016-09-07 10:58:21 -07:00
2016-10-24 16:14:22 -07:00
lock ( Files )
{
if ( ! Files . ContainsKey ( key ) )
{
Files . Add ( key , new List < DatItem > ( ) ) ;
}
Files [ key ] . Add ( rom ) ;
logger . User ( "File added: " + Path . GetFileNameWithoutExtension ( item ) + Environment . NewLine ) ;
}
}
else
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
logger . User ( "File not added: " + Path . GetFileNameWithoutExtension ( item ) + Environment . NewLine ) ;
return ;
2016-09-22 17:52:58 -07:00
}
2016-09-12 15:29:07 -07:00
2016-10-24 16:14:22 -07:00
return ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
// If we're copying files, copy it first and get the new filename
string newItem = item ;
string newBasePath = basePath ;
if ( copyFiles )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
newBasePath = Path . Combine ( tempDir , Path . GetRandomFileName ( ) ) ;
newItem = Path . GetFullPath ( Path . Combine ( newBasePath , Path . GetFullPath ( item ) . Remove ( 0 , basePath . Length + 1 ) ) ) ;
Directory . CreateDirectory ( Path . GetDirectoryName ( newItem ) ) ;
File . Copy ( item , newItem , true ) ;
2016-09-22 18:15:02 -07:00
}
2016-09-12 15:29:07 -07:00
2016-10-24 16:14:22 -07:00
// If both deep hash skip flags are set, do a quickscan
if ( noMD5 & & noSHA1 )
2016-09-08 21:44:49 -07:00
{
2016-10-24 16:14:22 -07:00
ArchiveType ? type = ArchiveTools . GetCurrentArchiveType ( newItem , logger ) ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// If we have an archive, scan it
if ( type ! = null & & ! archivesAsFiles )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
List < Rom > extracted = ArchiveTools . GetArchiveFileInfo ( newItem , logger ) ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
foreach ( Rom rom in extracted )
{
DFDProcessFileHelper ( newItem ,
rom ,
basePath ,
( Path . GetDirectoryName ( Path . GetFullPath ( item ) ) + Path . DirectorySeparatorChar ) . Remove ( 0 , basePath . Length ) + Path . GetFileNameWithoutExtension ( item ) ,
logger ) ;
}
}
// Otherwise, just get the info on the file itself
else if ( File . Exists ( newItem ) )
{
DFDProcessFile ( newItem , "" , newBasePath , noMD5 , noSHA1 , addDate , headerToCheckAgainst , logger ) ;
}
}
// Otherwise, attempt to extract the files to the temporary directory
else
{
ArchiveScanLevel asl = ( archivesAsFiles ? ArchiveScanLevel . SevenZipExternal : ArchiveScanLevel . SevenZipInternal )
| ( ! archivesAsFiles & & enableGzip ? ArchiveScanLevel . GZipInternal : ArchiveScanLevel . GZipExternal )
| ( archivesAsFiles ? ArchiveScanLevel . RarExternal : ArchiveScanLevel . RarInternal )
| ( archivesAsFiles ? ArchiveScanLevel . ZipExternal : ArchiveScanLevel . ZipInternal ) ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
bool encounteredErrors = ArchiveTools . ExtractArchive ( newItem , tempSubDir , asl , logger ) ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// If the file was an archive and was extracted successfully, check it
if ( ! encounteredErrors )
{
logger . Verbose ( Path . GetFileName ( item ) + " treated like an archive" ) ;
List < string > extracted = Directory . EnumerateFiles ( tempSubDir , "*" , SearchOption . AllDirectories ) . ToList ( ) ;
Parallel . ForEach ( extracted ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
entry = >
{
DFDProcessFile ( entry ,
Path . Combine ( ( Type = = "SuperDAT"
? ( Path . GetDirectoryName ( Path . GetFullPath ( item ) ) + Path . DirectorySeparatorChar ) . Remove ( 0 , basePath . Length )
: "" ) ,
Path . GetFileNameWithoutExtension ( item ) ) ,
tempSubDir ,
noMD5 ,
noSHA1 ,
addDate ,
headerToCheckAgainst ,
logger ) ;
} ) ;
}
// Otherwise, just get the info on the file itself
else if ( File . Exists ( newItem ) )
{
DFDProcessFile ( newItem , "" , newBasePath , noMD5 , noSHA1 , addDate , headerToCheckAgainst , logger ) ;
2016-09-22 18:15:02 -07:00
}
2016-10-24 16:14:22 -07:00
}
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// Cue to delete the file if it's a copy
if ( copyFiles & & item ! = newItem )
{
try
{
Directory . Delete ( newBasePath , true ) ;
}
catch { }
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
// Delete the sub temp directory
if ( Directory . Exists ( tempSubDir ) )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
Directory . Delete ( tempSubDir , true ) ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
}
2016-09-12 15:29:07 -07:00
2016-10-24 16:14:22 -07:00
/// <summary>
/// Process a single file as a file
/// </summary>
/// <param name="item">File to be added</param>
/// <param name="parent">Parent game to be used</param>
/// <param name="basePath">Path the represents the parent directory</param>
/// <param name="noMD5">True if MD5 hashes should be skipped over, false otherwise</param>
/// <param name="noSHA1">True if SHA-1 hashes should be skipped over, false otherwise</param>
/// <param name="addDate">True if dates should be archived for all files, false otherwise</param>
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
/// <param name="logger">Logger object for console and file output</param>
private void DFDProcessFile ( string item , string parent , string basePath , bool noMD5 , bool noSHA1 , bool addDate , string headerToCheckAgainst , Logger logger )
{
logger . Verbose ( Path . GetFileName ( item ) + " treated like a file" ) ;
Rom rom = FileTools . GetFileInfo ( item , logger , noMD5 : noMD5 , noSHA1 : noSHA1 , date : addDate , header : headerToCheckAgainst ) ;
DFDProcessFileHelper ( item , rom , basePath , parent , logger ) ;
2016-09-22 18:15:02 -07:00
}
/// <summary>
2016-10-24 16:14:22 -07:00
/// Process a single file as a file (with found Rom data)
2016-09-22 18:15:02 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="item">File to be added</param>
/// <param name="item">Rom data to be used to write to file</param>
/// <param name="basepath">Path the represents the parent directory</param>
/// <param name="parent">Parent game to be used</param>
private void DFDProcessFileHelper ( string item , DatItem datItem , string basepath , string parent , Logger logger )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
// If the datItem isn't a Rom or Disk, return
if ( datItem . Type ! = ItemType . Rom & & datItem . Type ! = ItemType . Disk )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
return ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
string key = "" ;
if ( datItem . Type = = ItemType . Rom )
{
key = ( ( Rom ) datItem ) . Size + "-" + ( ( Rom ) datItem ) . CRC ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
else
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
key = ( ( Disk ) datItem ) . MD5 ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
// Add the list if it doesn't exist already
lock ( Files )
{
if ( ! Files . ContainsKey ( key ) )
{
Files . Add ( key , new List < DatItem > ( ) ) ;
}
}
2016-09-22 17:52:58 -07:00
2016-09-22 18:15:02 -07:00
try
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
// If the basepath ends with a directory separator, remove it
if ( ! basepath . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
basepath + = Path . DirectorySeparatorChar . ToString ( ) ;
}
// Make sure we have the full item path
item = Path . GetFullPath ( item ) ;
// Get the data to be added as game and item names
string gamename = "" ;
string romname = "" ;
// If the parent is blank, then we have a non-archive file
if ( parent = = "" )
{
// If we have a SuperDAT, we want anything that's not the base path as the game, and the file as the rom
if ( Type = = "SuperDAT" )
{
gamename = Path . GetDirectoryName ( item . Remove ( 0 , basepath . Length ) ) ;
romname = Path . GetFileName ( item ) ;
}
2016-09-08 21:44:49 -07:00
2016-10-24 16:14:22 -07:00
// Otherwise, we want just the top level folder as the game, and the file as everything else
else
{
gamename = item . Remove ( 0 , basepath . Length ) . Split ( Path . DirectorySeparatorChar ) [ 0 ] ;
romname = item . Remove ( 0 , ( Path . Combine ( basepath , gamename ) . Length ) ) ;
}
}
// Otherwise, we assume that we have an archive
else
2016-09-12 15:29:07 -07:00
{
2016-10-24 16:14:22 -07:00
// If we have a SuperDAT, we want the archive name as the game, and the file as everything else (?)
if ( Type = = "SuperDAT" )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
gamename = parent ;
romname = item . Remove ( 0 , basepath . Length ) ;
}
// Otherwise, we want the archive name as the game, and the file as everything else
else
{
gamename = parent ;
romname = item . Remove ( 0 , basepath . Length ) ;
2016-09-22 18:15:02 -07:00
}
2016-09-12 15:29:07 -07:00
}
2016-09-08 21:44:49 -07:00
2016-10-24 16:14:22 -07:00
// Sanitize the names
if ( gamename . StartsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
gamename = gamename . Substring ( 1 ) ;
}
if ( gamename . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
gamename = gamename . Substring ( 0 , gamename . Length - 1 ) ;
}
if ( romname . StartsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
romname = romname . Substring ( 1 ) ;
}
if ( romname . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
romname = romname . Substring ( 0 , romname . Length - 1 ) ;
}
// Update rom information
datItem . Name = romname ;
if ( datItem . Machine = = null )
{
datItem . Machine = new Machine
{
Name = gamename ,
Description = gamename ,
} ;
}
2016-09-22 18:15:02 -07:00
else
{
2016-10-24 16:14:22 -07:00
datItem . Machine . Name = gamename ;
datItem . Machine . Description = gamename ;
}
// Add the file information to the DAT
lock ( Files )
{
if ( Files . ContainsKey ( key ) )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
Files [ key ] . Add ( datItem ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( datItem ) ;
Files . Add ( key , temp ) ;
2016-09-22 18:15:02 -07:00
}
}
2016-09-18 22:52:59 -07:00
2016-10-24 16:14:22 -07:00
logger . User ( "File added: " + romname + Environment . NewLine ) ;
2016-09-22 18:15:02 -07:00
}
2016-10-24 16:14:22 -07:00
catch ( IOException ex )
2016-09-18 22:52:59 -07:00
{
2016-09-22 18:15:02 -07:00
logger . Error ( ex . ToString ( ) ) ;
2016-10-24 16:14:22 -07:00
return ;
2016-09-18 22:52:59 -07:00
}
}
2016-09-07 10:58:21 -07:00
#endregion
2016-10-24 21:32:26 -07:00
#region Rebuilding and Verifying
/// <summary>
/// Process the DAT and find all matches in input files and folders
/// </summary>
/// <param name="inputs">List of input files/folders to check</param>
/// <param name="outDir">Output directory to use to build to</param>
/// <param name="tempDir">Temporary directory for archive extraction</param>
/// <param name="quickScan">True to enable external scanning of archives, false otherwise</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise</param>
/// <param name="delete">True if input files should be deleted, false otherwise</param>
2016-10-25 18:02:28 -07:00
/// <param name="inverse">True if the DAT should be used as a filter instead of a template, false otherwise</param>
2016-10-25 16:04:45 -07:00
/// <param name="outputFormat">Output format that files should be written to</param>
2016-10-24 21:32:26 -07:00
/// <param name="romba">True if files should be output in Romba depot folders, false otherwise</param>
/// <param name="archiveScanLevel">ArchiveScanLevel representing the archive handling levels</param>
/// <param name="updateDat">True if the updated DAT should be output, false otherwise</param>
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if rebuilding was a success, false otherwise</returns>
public bool RebuildToOutput ( List < string > inputs , string outDir , string tempDir , bool quickScan , bool date ,
2016-10-25 18:02:28 -07:00
bool delete , bool inverse , OutputFormat outputFormat , bool romba , ArchiveScanLevel archiveScanLevel , bool updateDat ,
string headerToCheckAgainst , int maxDegreeOfParallelism , Logger logger )
2016-10-24 21:32:26 -07:00
{
2016-10-25 10:38:26 -07:00
#region Perform setup
2016-10-26 17:16:13 -07:00
// If the DAT is not populated and inverse is not set, inform the user and quit
if ( ( Files = = null | | Files . Count = = 0 ) & & ! inverse )
{
logger . User ( "No entries were found to rebuild, exiting..." ) ;
return false ;
}
// Check that the output directory exists
2016-10-24 21:32:26 -07:00
if ( ! Directory . Exists ( outDir ) )
{
Directory . CreateDirectory ( outDir ) ;
outDir = Path . GetFullPath ( outDir ) ;
}
// Check the temp directory
if ( String . IsNullOrEmpty ( tempDir ) )
{
tempDir = Path . Combine ( Path . GetTempPath ( ) , Path . GetRandomFileName ( ) ) ;
}
// Then create or clean the temp directory
if ( ! Directory . Exists ( tempDir ) )
{
Directory . CreateDirectory ( tempDir ) ;
}
else
{
FileTools . CleanDirectory ( tempDir ) ;
}
2016-10-25 10:38:26 -07:00
// Preload the Skipper list
int listcount = Skipper . List . Count ;
#endregion
2016-10-24 21:32:26 -07:00
bool success = true ;
DatFile matched = new DatFile ( ) ;
List < string > files = new List < string > ( ) ;
#region Retrieve a list of all files
logger . User ( "Retrieving list all files from input" ) ;
DateTime start = DateTime . Now ;
// Create a list of just files from inputs
Parallel . ForEach ( inputs ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism , } ,
input = > {
if ( File . Exists ( input ) )
{
logger . Verbose ( "File found: '" + input + "'" ) ;
files . Add ( Path . GetFullPath ( input ) ) ;
}
else if ( Directory . Exists ( input ) )
{
logger . Verbose ( "Directory found: '" + input + "'" ) ;
Parallel . ForEach ( Directory . EnumerateFiles ( input , "*" , SearchOption . AllDirectories ) ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism , } ,
file = >
{
logger . Verbose ( "File found: '" + file + "'" ) ;
files . Add ( Path . GetFullPath ( file ) ) ;
} ) ;
}
else
{
logger . Error ( "'" + input + "' is not a file or directory!" ) ;
}
} ) ;
logger . User ( "Retrieving complete in: " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
#endregion
DatFile current = new DatFile ( ) ;
2016-10-25 10:38:26 -07:00
Dictionary < string , SkipperRule > fileToSkipperRule = new Dictionary < string , SkipperRule > ( ) ;
2016-10-24 21:32:26 -07:00
#region Create a dat from input files
logger . User ( "Getting hash information for all input files" ) ;
start = DateTime . Now ;
// Now that we have a list of just files, we get a DAT from the input files
2016-10-25 10:38:26 -07:00
Parallel . ForEach ( files ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
file = >
2016-10-24 21:32:26 -07:00
{
// Define the temporary directory
string tempSubDir = Path . GetFullPath ( Path . Combine ( tempDir , Path . GetRandomFileName ( ) ) ) + Path . DirectorySeparatorChar ;
// Get the required scanning level for the file
bool shouldExternalProcess = false ;
bool shouldInternalProcess = false ;
ArchiveTools . GetInternalExternalProcess ( file , archiveScanLevel , logger , out shouldExternalProcess , out shouldInternalProcess ) ;
// If we're supposed to scan the file externally
if ( shouldExternalProcess )
{
Rom rom = FileTools . GetFileInfo ( file , logger , noMD5 : quickScan , noSHA1 : quickScan , header : headerToCheckAgainst ) ;
rom . Name = Path . GetFullPath ( file ) ;
2016-10-25 10:38:26 -07:00
lock ( Files )
2016-10-24 21:32:26 -07:00
{
2016-10-25 10:38:26 -07:00
string key = rom . Size + "-" + rom . CRC ;
if ( current . Files . ContainsKey ( key ) )
{
current . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
current . Files . Add ( key , temp ) ;
}
2016-10-24 21:32:26 -07:00
}
2016-10-25 10:38:26 -07:00
// If we had a header, we want the full file information too
if ( headerToCheckAgainst ! = null )
2016-10-24 21:32:26 -07:00
{
2016-10-25 10:38:26 -07:00
rom = FileTools . GetFileInfo ( file , logger , noMD5 : quickScan , noSHA1 : quickScan ) ;
rom . Name = Path . GetFullPath ( file ) ;
lock ( Files )
{
string key = rom . Size + "-" + rom . CRC ;
if ( current . Files . ContainsKey ( key ) )
{
current . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
current . Files . Add ( key , temp ) ;
}
}
2016-10-24 21:32:26 -07:00
}
}
// If we're supposed to scan the file internally
if ( shouldInternalProcess )
{
// If quickscan is set, do so
if ( quickScan )
{
List < Rom > extracted = ArchiveTools . GetArchiveFileInfo ( file , logger ) ;
foreach ( Rom rom in extracted )
{
Rom newrom = rom ;
newrom . Machine = new Machine ( Path . GetFullPath ( file ) , "" ) ;
2016-10-25 10:38:26 -07:00
lock ( Files )
2016-10-24 21:32:26 -07:00
{
2016-10-25 10:38:26 -07:00
string key = rom . Size + "-" + rom . CRC ;
if ( current . Files . ContainsKey ( key ) )
{
current . Files [ key ] . Add ( newrom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( newrom ) ;
current . Files . Add ( key , temp ) ;
}
2016-10-24 21:32:26 -07:00
}
}
}
// Otherwise, attempt to extract the files to the temporary directory
else
{
bool encounteredErrors = ArchiveTools . ExtractArchive ( file , tempSubDir , archiveScanLevel , logger ) ;
// If the file was an archive and was extracted successfully, check it
if ( ! encounteredErrors )
{
logger . Verbose ( Path . GetFileName ( file ) + " treated like an archive" ) ;
List < string > extracted = Directory . EnumerateFiles ( tempSubDir , "*" , SearchOption . AllDirectories ) . ToList ( ) ;
Parallel . ForEach ( extracted ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
entry = >
2016-10-25 10:38:26 -07:00
{
Rom rom = FileTools . GetFileInfo ( entry , logger , noMD5 : quickScan , noSHA1 : quickScan , header : headerToCheckAgainst ) ;
rom . Machine = new Machine ( Path . GetFullPath ( file ) , "" ) ;
2016-10-24 21:32:26 -07:00
2016-10-25 10:38:26 -07:00
lock ( Files )
{
2016-10-24 21:32:26 -07:00
string key = rom . Size + "-" + rom . CRC ;
if ( current . Files . ContainsKey ( key ) )
{
current . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
current . Files . Add ( key , temp ) ;
}
2016-10-25 10:38:26 -07:00
}
// If we had a header, we want the full file information too
if ( headerToCheckAgainst ! = null )
{
rom = FileTools . GetFileInfo ( file , logger , noMD5 : quickScan , noSHA1 : quickScan ) ;
rom . Machine = new Machine ( Path . GetFullPath ( file ) , "" ) ;
lock ( Files )
{
string key = rom . Size + "-" + rom . CRC ;
if ( current . Files . ContainsKey ( key ) )
{
current . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
current . Files . Add ( key , temp ) ;
}
}
}
} ) ;
2016-10-24 21:32:26 -07:00
}
// Otherwise, just get the info on the file itself
else if ( File . Exists ( file ) )
{
Rom rom = FileTools . GetFileInfo ( file , logger , noMD5 : quickScan , noSHA1 : quickScan , header : headerToCheckAgainst ) ;
rom . Name = Path . GetFullPath ( file ) ;
2016-10-25 10:38:26 -07:00
lock ( Files )
2016-10-24 21:32:26 -07:00
{
2016-10-25 10:38:26 -07:00
string key = rom . Size + "-" + rom . CRC ;
if ( current . Files . ContainsKey ( key ) )
{
current . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
current . Files . Add ( key , temp ) ;
}
2016-10-24 21:32:26 -07:00
}
}
}
}
// Now delete the temp directory
try
{
Directory . Delete ( tempSubDir , true ) ;
}
catch { }
2016-10-25 10:38:26 -07:00
} ) ;
2016-10-24 21:32:26 -07:00
logger . User ( "Getting hash information complete in: " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
#endregion
// Create a mapping from destination file to source file
Dictionary < DatItem , DatItem > toFromMap = new Dictionary < DatItem , DatItem > ( ) ;
#region Find all required files for rebuild
logger . User ( "Determining files to rebuild" ) ;
start = DateTime . Now ;
// Order the DATs by hash first to make things easier
2016-10-25 21:03:16 -07:00
logger . User ( "Sorting input DAT..." ) ;
2016-10-24 21:32:26 -07:00
BucketByCRC ( false , logger , output : false ) ;
2016-10-25 21:03:16 -07:00
logger . User ( "Sorting found files..." ) ;
2016-10-24 21:32:26 -07:00
current . BucketByCRC ( false , logger , output : false ) ;
// Now loop over and find all files that need to be rebuilt
List < string > keys = current . Files . Keys . ToList ( ) ;
2016-10-25 10:38:26 -07:00
Parallel . ForEach ( keys ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
key = >
2016-10-24 21:32:26 -07:00
{
2016-10-25 18:02:28 -07:00
// If we are using the DAT as a filter, treat the files one way
if ( inverse )
2016-10-24 21:32:26 -07:00
{
2016-10-25 18:02:28 -07:00
// Check for duplicates
List < DatItem > datItems = current . Files [ key ] ;
foreach ( Rom rom in datItems )
{
// If the rom has duplicates, we skip it
if ( rom . HasDuplicates ( this , logger ) )
{
return ;
}
// Otherwise, map the file to itself
try
{
Rom newrom = new Rom
{
Name = rom . Name . Remove ( 0 , Path . GetDirectoryName ( rom . Name ) . Length ) ,
Size = rom . Size ,
CRC = rom . CRC ,
MD5 = rom . MD5 ,
SHA1 = rom . SHA1 ,
Machine = new Machine
{
Name = Path . GetFileNameWithoutExtension ( rom . Machine . Name ) ,
} ,
} ;
newrom . Name = newrom . Name . Remove ( 0 , ( newrom . Name . StartsWith ( "\\" ) | | newrom . Name . StartsWith ( "/" ) ? 1 : 0 ) ) ;
lock ( toFromMap )
{
toFromMap . Add ( newrom , rom ) ;
}
}
catch { }
}
2016-10-24 21:32:26 -07:00
}
2016-10-25 18:02:28 -07:00
// Otherwise, treat it like a standard rebuild
else
2016-10-24 21:32:26 -07:00
{
2016-10-25 18:02:28 -07:00
// If the input DAT doesn't have the key, then nothing from the current DAT are there
if ( ! Files . ContainsKey ( key ) )
{
return ;
}
2016-10-24 21:32:26 -07:00
2016-10-25 18:02:28 -07:00
// Otherwise, we try to find duplicates
List < DatItem > datItems = current . Files [ key ] ;
foreach ( Rom rom in datItems )
2016-10-24 21:32:26 -07:00
{
2016-10-25 18:02:28 -07:00
List < DatItem > found = rom . GetDuplicates ( this , logger , false ) ;
// Now add all of the duplicates mapped to the current file
foreach ( Rom mid in found )
2016-10-24 21:32:26 -07:00
{
2016-10-25 18:02:28 -07:00
try
{
lock ( toFromMap )
{
toFromMap . Add ( mid , rom ) ;
}
}
catch { }
2016-10-24 21:32:26 -07:00
}
}
}
2016-10-25 10:38:26 -07:00
} ) ;
2016-10-24 21:32:26 -07:00
logger . User ( "Determining complete in: " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
#endregion
// Now bucket the list of keys by game so that we can rebuild properly
2016-10-25 10:38:26 -07:00
SortedDictionary < string , List < DatItem > > keysGroupedByGame = BucketListByGame ( toFromMap . Keys . ToList ( ) , false , true , logger , output : false ) ;
2016-10-24 21:32:26 -07:00
#region Rebuild games in order
2016-10-25 16:04:45 -07:00
switch ( outputFormat )
{
case OutputFormat . Folder :
logger . User ( "Rebuilding all files to directory" ) ;
break ;
case OutputFormat . TapeArchive :
logger . User ( "Rebuilding all files to TAR" ) ;
break ;
case OutputFormat . Torrent7Zip :
logger . User ( "Rebuilding all files to Torrent7Z" ) ;
break ;
case OutputFormat . TorrentGzip :
logger . User ( "Rebuilding all files to TorrentGZ" ) ;
break ;
case OutputFormat . TorrentLrzip :
logger . User ( "Rebuilding all files to TorrentLRZ" ) ;
break ;
case OutputFormat . TorrentRar :
logger . User ( "Rebuilding all files to TorrentRAR" ) ;
break ;
case OutputFormat . TorrentXZ :
logger . User ( "Rebuilding all files to TorrentXZ" ) ;
break ;
case OutputFormat . TorrentZip :
logger . User ( "Rebuilding all files to TorrentZip" ) ;
break ;
}
2016-10-24 21:32:26 -07:00
start = DateTime . Now ;
// Now loop through the keys and create the correct output items
List < string > games = keysGroupedByGame . Keys . ToList ( ) ;
2016-10-25 10:38:26 -07:00
Parallel . ForEach ( games ,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism } ,
game = >
2016-10-24 21:32:26 -07:00
{
// Define the temporary directory
string tempSubDir = Path . GetFullPath ( Path . Combine ( tempDir , Path . GetRandomFileName ( ) ) ) + Path . DirectorySeparatorChar ;
// Create an empty list for getting paths for rebuilding
List < string > pathsToFiles = new List < string > ( ) ;
// Loop through all of the matched items in the game
List < DatItem > itemsInGame = keysGroupedByGame [ game ] ;
List < Rom > romsInGame = new List < Rom > ( ) ;
foreach ( Rom rom in itemsInGame )
{
// Get the rom that's mapped to this item
Rom source = ( Rom ) toFromMap [ rom ] ;
// If the file is in an archive, we need to treat it specially
string machinename = source . Machine . Name . ToLowerInvariant ( ) ;
if ( machinename . EndsWith ( ".7z" )
| | machinename . EndsWith ( ".gz" )
| | machinename . EndsWith ( ".rar" )
| | machinename . EndsWith ( ".zip" ) )
{
2016-10-25 10:38:26 -07:00
string tempPath = ArchiveTools . ExtractItem ( source . Machine . Name , Path . GetFileName ( source . Name ) , tempSubDir , logger ) ;
pathsToFiles . Add ( tempPath ) ;
2016-10-24 21:32:26 -07:00
}
// Otherwise, we want to just add the full path
else
{
pathsToFiles . Add ( source . Name ) ;
}
2016-10-25 10:38:26 -07:00
// If the size doesn't match, then we add the CRC as a postfix to the file
Rom fi = FileTools . GetFileInfo ( pathsToFiles . Last ( ) , logger ) ;
if ( fi . Size ! = source . Size )
{
rom . Name = Path . GetDirectoryName ( rom . Name )
+ ( String . IsNullOrEmpty ( Path . GetDirectoryName ( rom . Name ) ) ? "" : Path . DirectorySeparatorChar . ToString ( ) )
+ Path . GetFileNameWithoutExtension ( rom . Name )
+ " (" + fi . CRC + ")"
+ Path . GetExtension ( rom . Name ) ;
rom . CRC = fi . CRC ;
rom . Size = fi . Size ;
}
2016-10-24 21:32:26 -07:00
// Now add the rom to the output list
romsInGame . Add ( rom ) ;
}
// And now rebuild accordingly
2016-10-25 16:04:45 -07:00
switch ( outputFormat )
2016-10-24 21:32:26 -07:00
{
2016-10-25 16:04:45 -07:00
case OutputFormat . Folder :
for ( int i = 0 ; i < romsInGame . Count ; i + + )
{
string infile = pathsToFiles [ i ] ;
Rom outrom = romsInGame [ i ] ;
2016-10-25 16:34:50 -07:00
string outfile = Path . Combine ( outDir , outrom . Machine . Name , outrom . Name ) ;
2016-10-24 21:32:26 -07:00
2016-10-25 16:04:45 -07:00
// Make sure the output folder is created
Directory . CreateDirectory ( Path . GetDirectoryName ( outfile ) ) ;
2016-10-24 21:32:26 -07:00
2016-10-25 16:04:45 -07:00
// Now copy the file over
try
{
File . Copy ( infile , outfile ) ;
}
catch { }
}
break ;
case OutputFormat . TapeArchive :
2016-10-25 21:20:43 -07:00
ArchiveTools . WriteTAR ( pathsToFiles , outDir , romsInGame , logger ) ;
2016-10-25 16:04:45 -07:00
break ;
case OutputFormat . Torrent7Zip :
break ;
case OutputFormat . TorrentGzip :
for ( int i = 0 ; i < itemsInGame . Count ; i + + )
2016-10-24 21:32:26 -07:00
{
2016-10-25 16:04:45 -07:00
string infile = pathsToFiles [ i ] ;
Rom outrom = romsInGame [ i ] ;
ArchiveTools . WriteTorrentGZ ( infile , outDir , romba , logger ) ;
2016-10-24 21:32:26 -07:00
}
2016-10-25 16:04:45 -07:00
break ;
case OutputFormat . TorrentLrzip :
break ;
case OutputFormat . TorrentRar :
break ;
case OutputFormat . TorrentXZ :
break ;
case OutputFormat . TorrentZip :
ArchiveTools . WriteTorrentZip ( pathsToFiles , outDir , romsInGame , logger ) ;
break ;
2016-10-24 21:32:26 -07:00
}
// And now clear the temp folder to get rid of any transient files
try
{
Directory . Delete ( tempSubDir , true ) ;
}
catch { }
2016-10-25 10:38:26 -07:00
} ) ;
2016-10-24 21:32:26 -07:00
logger . User ( "Rebuilding complete in: " + DateTime . Now . Subtract ( start ) . ToString ( @"hh\:mm\:ss\.fffff" ) ) ;
#endregion
return success ;
}
/// <summary>
/// Process the DAT and verify the output directory
/// </summary>
/// <param name="datFile">DAT to use to verify the directory</param>
/// <param name="inputs">List of input directories to compare against</param>
/// <param name="tempDir">Temporary directory for archive extraction</param>
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if verification was a success, false otherwise</returns>
public bool VerifyDirectory ( List < string > inputs , string tempDir , string headerToCheckAgainst , Logger logger )
{
// First create or clean the temp directory
if ( ! Directory . Exists ( tempDir ) )
{
Directory . CreateDirectory ( tempDir ) ;
}
else
{
FileTools . CleanDirectory ( tempDir ) ;
}
bool success = true ;
/ *
We want the cross section of what ' s the folder and what ' s in the DAT . Right now , it just has what ' s in the DAT that ' s not in the folder
* /
// Then, loop through and check each of the inputs
logger . User ( "Processing files:\n" ) ;
foreach ( string input in inputs )
{
PopulateDatFromDir ( input , false /* noMD5 */ , false /* noSHA1 */ , true /* bare */ , false /* archivesAsFiles */ ,
true /* enableGzip */ , false /* addBlanks */ , false /* addDate */ , tempDir /* tempDir */ , false /* copyFiles */ ,
headerToCheckAgainst , 4 /* maxDegreeOfParallelism */ , logger ) ;
}
// Setup the fixdat
DatFile matched = ( DatFile ) CloneHeader ( ) ;
matched . Files = new SortedDictionary < string , List < DatItem > > ( ) ;
matched . FileName = "fixDat_" + matched . FileName ;
matched . Name = "fixDat_" + matched . Name ;
matched . Description = "fixDat_" + matched . Description ;
2016-10-25 15:02:02 -07:00
matched . DatFormat = DatFormat . Logiqx ;
2016-10-24 21:32:26 -07:00
// Now that all files are parsed, get only files found in directory
bool found = false ;
foreach ( List < DatItem > roms in Files . Values )
{
List < DatItem > newroms = DatItem . Merge ( roms , logger ) ;
foreach ( Rom rom in newroms )
{
if ( rom . SourceID = = 99 )
{
found = true ;
string key = rom . Size + "-" + rom . CRC ;
if ( matched . Files . ContainsKey ( key ) )
{
matched . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
matched . Files . Add ( key , temp ) ;
}
}
}
}
// Now output the fixdat to the main folder
if ( found )
{
matched . WriteToFile ( "" , logger , stats : true ) ;
}
else
{
logger . User ( "No fixDat needed" ) ;
}
return success ;
}
#endregion
2016-10-24 16:14:22 -07:00
#region Splitting
2016-09-01 20:38:41 -07:00
/// <summary>
2016-10-24 16:14:22 -07:00
/// Split a DAT by input extensions
2016-09-01 20:38:41 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="outDir">Name of the directory to write the DATs out to</param>
/// <param name="basepath">Parent path for replacement</param>
/// <param name="extA">List of extensions to split on (first DAT)</param>
/// <param name="extB">List of extensions to split on (second DAT)</param>
/// <param name="logger">Logger object for console and file writing</param>
/// <returns>True if split succeeded, false otherwise</returns>
public bool SplitByExt ( string outDir , string basepath , List < string > extA , List < string > extB , Logger logger )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
// Make sure all of the extensions have a dot at the beginning
List < string > newExtA = new List < string > ( ) ;
foreach ( string s in extA )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
newExtA . Add ( ( s . StartsWith ( "." ) ? s : "." + s ) . ToUpperInvariant ( ) ) ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
string newExtAString = string . Join ( "," , newExtA ) ;
2016-09-01 20:38:41 -07:00
2016-10-24 16:14:22 -07:00
List < string > newExtB = new List < string > ( ) ;
foreach ( string s in extB )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
newExtB . Add ( ( s . StartsWith ( "." ) ? s : "." + s ) . ToUpperInvariant ( ) ) ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
string newExtBString = string . Join ( "," , newExtB ) ;
2016-09-01 20:38:41 -07:00
2016-10-24 16:14:22 -07:00
// Set all of the appropriate outputs for each of the subsets
DatFile datdataA = new DatFile
2016-09-07 10:31:57 -07:00
{
2016-10-24 16:14:22 -07:00
FileName = this . FileName + " (" + newExtAString + ")" ,
Name = this . Name + " (" + newExtAString + ")" ,
Description = this . Description + " (" + newExtAString + ")" ,
Category = this . Category ,
Version = this . Version ,
Date = this . Date ,
Author = this . Author ,
Email = this . Email ,
Homepage = this . Homepage ,
Url = this . Url ,
Comment = this . Comment ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
} ;
DatFile datdataB = new DatFile
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
FileName = this . FileName + " (" + newExtBString + ")" ,
Name = this . Name + " (" + newExtBString + ")" ,
Description = this . Description + " (" + newExtBString + ")" ,
Category = this . Category ,
Version = this . Version ,
Date = this . Date ,
Author = this . Author ,
Email = this . Email ,
Homepage = this . Homepage ,
Url = this . Url ,
Comment = this . Comment ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
} ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// If roms is empty, return false
if ( this . Files . Count = = 0 )
2016-10-24 16:01:03 -07:00
{
2016-10-24 16:14:22 -07:00
return false ;
2016-09-22 17:52:58 -07:00
}
2016-09-01 20:38:41 -07:00
2016-10-24 16:14:22 -07:00
// Now separate the roms accordingly
foreach ( string key in this . Files . Keys )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
foreach ( DatItem rom in this . Files [ key ] )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
if ( newExtA . Contains ( Path . GetExtension ( rom . Name . ToUpperInvariant ( ) ) ) )
{
if ( datdataA . Files . ContainsKey ( key ) )
{
datdataA . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
datdataA . Files . Add ( key , temp ) ;
}
}
else if ( newExtB . Contains ( Path . GetExtension ( rom . Name . ToUpperInvariant ( ) ) ) )
{
if ( datdataB . Files . ContainsKey ( key ) )
{
datdataB . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
datdataB . Files . Add ( key , temp ) ;
}
}
else
{
if ( datdataA . Files . ContainsKey ( key ) )
{
datdataA . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
datdataA . Files . Add ( key , temp ) ;
}
if ( datdataB . Files . ContainsKey ( key ) )
{
datdataB . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
datdataB . Files . Add ( key , temp ) ;
}
}
2016-09-22 18:15:02 -07:00
}
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
// Get the output directory
if ( outDir ! = "" )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
outDir = outDir + Path . GetDirectoryName ( this . FileName ) . Remove ( 0 , basepath . Length - 1 ) ;
}
else
{
outDir = Path . GetDirectoryName ( this . FileName ) ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
// Then write out both files
bool success = datdataA . WriteToFile ( outDir , logger ) ;
success & = datdataB . WriteToFile ( outDir , logger ) ;
return success ;
2016-09-01 20:38:41 -07:00
}
/// <summary>
2016-10-24 16:14:22 -07:00
/// Split a DAT by best available hashes
2016-09-01 20:38:41 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="outDir">Name of the directory to write the DATs out to</param>
/// <param name="basepath">Parent path for replacement</param>
/// <param name="logger">Logger object for console and file writing</param>
/// <returns>True if split succeeded, false otherwise</returns>
public bool SplitByHash ( string outDir , string basepath , Logger logger )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
// Sanitize the basepath to be more predictable
basepath = ( basepath . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) ? basepath : basepath + Path . DirectorySeparatorChar ) ;
2016-09-22 17:52:58 -07:00
2016-10-24 16:14:22 -07:00
// Create each of the respective output DATs
logger . User ( "Creating and populating new DATs" ) ;
DatFile itemStatus = new DatFile
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
FileName = this . FileName + " (Nodump)" ,
Name = this . Name + " (Nodump)" ,
Description = this . Description + " (Nodump)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
DatFile sha1 = new DatFile
{
FileName = this . FileName + " (SHA-1)" ,
Name = this . Name + " (SHA-1)" ,
Description = this . Description + " (SHA-1)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
DatFile md5 = new DatFile
{
FileName = this . FileName + " (MD5)" ,
Name = this . Name + " (MD5)" ,
Description = this . Description + " (MD5)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
DatFile crc = new DatFile
{
FileName = this . FileName + " (CRC)" ,
Name = this . Name + " (CRC)" ,
Description = this . Description + " (CRC)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
2016-09-22 17:52:58 -07:00
2016-10-24 16:14:22 -07:00
DatFile other = new DatFile
{
FileName = this . FileName + " (Other)" ,
Name = this . Name + " (Other)" ,
Description = this . Description + " (Other)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
// Now populate each of the DAT objects in turn
List < string > keys = this . Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = this . Files [ key ] ;
foreach ( DatItem rom in roms )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
// If the file is not a Rom or Disk, continue
if ( rom . Type ! = ItemType . Disk & & rom . Type ! = ItemType . Rom )
{
continue ;
}
2016-09-22 17:52:58 -07:00
2016-10-24 16:14:22 -07:00
// If the file is a itemStatus
if ( ( rom . Type = = ItemType . Rom & & ( ( Rom ) rom ) . ItemStatus = = ItemStatus . Nodump )
| | ( rom . Type = = ItemType . Disk & & ( ( Disk ) rom ) . ItemStatus = = ItemStatus . Nodump ) )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
if ( itemStatus . Files . ContainsKey ( key ) )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
itemStatus . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
itemStatus . Files . Add ( key , temp ) ;
2016-09-22 18:15:02 -07:00
}
}
2016-10-24 16:14:22 -07:00
// If the file has a SHA-1
else if ( ( rom . Type = = ItemType . Rom & & ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . SHA1 ) )
| | ( rom . Type = = ItemType . Disk & & ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . SHA1 ) ) )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
if ( sha1 . Files . ContainsKey ( key ) )
{
sha1 . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sha1 . Files . Add ( key , temp ) ;
}
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
// If the file has no SHA-1 but has an MD5
else if ( ( rom . Type = = ItemType . Rom & & ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . MD5 ) )
| | ( rom . Type = = ItemType . Disk & & ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . MD5 ) ) )
{
if ( md5 . Files . ContainsKey ( key ) )
{
md5 . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
md5 . Files . Add ( key , temp ) ;
}
}
// If the file has no MD5 but a CRC
else if ( ( rom . Type = = ItemType . Rom & & ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . SHA1 ) )
| | ( rom . Type = = ItemType . Disk & & ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . SHA1 ) ) )
{
if ( crc . Files . ContainsKey ( key ) )
{
crc . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
crc . Files . Add ( key , temp ) ;
}
}
else
{
if ( other . Files . ContainsKey ( key ) )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
other . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
other . Files . Add ( key , temp ) ;
}
}
2016-09-22 17:52:58 -07:00
}
}
2016-09-01 20:38:41 -07:00
2016-10-24 16:14:22 -07:00
// Get the output directory
if ( outDir ! = "" )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
outDir = outDir + Path . GetDirectoryName ( this . FileName ) . Remove ( 0 , basepath . Length - 1 ) ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
else
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
outDir = Path . GetDirectoryName ( this . FileName ) ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
// Now, output all of the files to the output directory
logger . User ( "DAT information created, outputting new files" ) ;
bool success = true ;
if ( itemStatus . Files . Count > 0 )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
success & = itemStatus . WriteToFile ( outDir , logger ) ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
if ( sha1 . Files . Count > 0 )
2016-09-01 20:38:41 -07:00
{
2016-10-24 16:14:22 -07:00
success & = sha1 . WriteToFile ( outDir , logger ) ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
if ( md5 . Files . Count > 0 )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
success & = md5 . WriteToFile ( outDir , logger ) ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
if ( crc . Files . Count > 0 )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
success & = crc . WriteToFile ( outDir , logger ) ;
2016-09-22 17:52:58 -07:00
}
2016-09-01 20:38:41 -07:00
2016-10-24 16:14:22 -07:00
return success ;
}
2016-09-19 18:04:24 -07:00
2016-10-26 14:29:29 -07:00
/// <summary>
/// Split a SuperDAT by lowest available directory level
/// </summary>
/// <param name="outDir">Name of the directory to write the DATs out to</param>
/// <param name="basepath">Parent path for replacement</param>
/// <param name="logger">Logger object for console and file writing</param>
/// <returns>True if split succeeded, false otherwise</returns>
public bool SplitByLevel ( string outDir , string basepath , Logger logger )
{
// Sanitize the basepath to be more predictable
basepath = ( basepath . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) ? basepath : basepath + Path . DirectorySeparatorChar ) ;
// First, organize by games so that we can do the right thing
BucketByGame ( false , true , logger , output : false ) ;
// Create a temporary DAT to add things to
DatFile tempDat = ( DatFile ) CloneHeader ( ) ;
tempDat . Name = null ;
// Then, we loop over the games
foreach ( string key in Files . Keys )
{
// Here, the key is the name of the game to be used for comparison
if ( tempDat . Name ! = null & & tempDat . Name ! = Path . GetDirectoryName ( key ) )
{
// Get the path that the file will be written out to
2016-10-26 14:59:12 -07:00
string path = HttpUtility . HtmlDecode ( String . IsNullOrEmpty ( tempDat . Name )
2016-10-26 14:29:29 -07:00
? outDir
: Path . Combine ( outDir , tempDat . Name ) ) ;
2016-10-26 14:59:12 -07:00
// Now set the new output values
2016-10-26 16:51:46 -07:00
tempDat . FileName = HttpUtility . HtmlDecode ( String . IsNullOrEmpty ( tempDat . Name )
? FileName
: tempDat . Name . Replace ( Path . DirectorySeparatorChar . ToString ( ) , " - " ) . Replace ( Path . AltDirectorySeparatorChar . ToString ( ) , " - " ) ) ;
2016-10-26 14:29:29 -07:00
tempDat . Description + = " (" + tempDat . Name . Replace ( Path . DirectorySeparatorChar , '-' ) . Replace ( Path . AltDirectorySeparatorChar , '-' ) + ")" ;
tempDat . Name = Name + " (" + tempDat . Name . Replace ( Path . DirectorySeparatorChar , '-' ) . Replace ( Path . AltDirectorySeparatorChar , '-' ) + ")" ;
2016-10-26 14:59:12 -07:00
tempDat . Type = null ;
2016-10-26 14:29:29 -07:00
// Write out the temporary DAT to the proper directory
2016-10-26 14:59:12 -07:00
tempDat . WriteToFile ( path , logger ) ;
2016-10-26 14:29:29 -07:00
// Reset the DAT for the next items
tempDat = ( DatFile ) CloneHeader ( ) ;
tempDat . Name = null ;
}
2016-10-26 14:35:09 -07:00
// Clean the input list and set all games to be pathless
List < DatItem > items = Files [ key ] ;
items . ForEach ( item = > item . Machine . Name = Path . GetFileName ( item . Machine . Name ) ) ;
2016-10-26 14:59:12 -07:00
items . ForEach ( item = > item . Machine . Description = Path . GetFileName ( item . Machine . Description ) ) ;
2016-10-26 14:35:09 -07:00
// Now add the game to the output DAT
2016-10-26 14:29:29 -07:00
if ( tempDat . Files . ContainsKey ( key ) )
{
2016-10-26 14:35:09 -07:00
tempDat . Files [ key ] . AddRange ( items ) ;
2016-10-26 14:29:29 -07:00
}
else
{
2016-10-26 14:35:09 -07:00
tempDat . Files . Add ( key , items ) ;
2016-10-26 14:29:29 -07:00
}
// Then set the DAT name to be the parent directory name
tempDat . Name = Path . GetDirectoryName ( key ) ;
}
2016-10-26 16:37:20 -07:00
// Then we write the last DAT out since it would be skipped otherwise
string lastpath = HttpUtility . HtmlDecode ( String . IsNullOrEmpty ( tempDat . Name )
? outDir
: Path . Combine ( outDir , tempDat . Name ) ) ;
// Now set the new output values
2016-10-26 16:51:46 -07:00
tempDat . FileName = HttpUtility . HtmlDecode ( String . IsNullOrEmpty ( tempDat . Name )
? FileName
: tempDat . Name . Replace ( Path . DirectorySeparatorChar . ToString ( ) , " - " ) . Replace ( Path . AltDirectorySeparatorChar . ToString ( ) , " - " ) ) ;
2016-10-26 16:37:20 -07:00
tempDat . Description + = " (" + tempDat . Name . Replace ( Path . DirectorySeparatorChar , '-' ) . Replace ( Path . AltDirectorySeparatorChar , '-' ) + ")" ;
tempDat . Name = Name + " (" + tempDat . Name . Replace ( Path . DirectorySeparatorChar , '-' ) . Replace ( Path . AltDirectorySeparatorChar , '-' ) + ")" ;
tempDat . Type = null ;
// Write out the temporary DAT to the proper directory
tempDat . WriteToFile ( lastpath , logger ) ;
2016-10-26 14:29:29 -07:00
return true ;
}
2016-10-24 16:14:22 -07:00
/// <summary>
/// Split a DAT by type of Rom
/// </summary>
/// <param name="outDir">Name of the directory to write the DATs out to</param>
/// <param name="basepath">Parent path for replacement</param>
/// <param name="logger">Logger object for console and file writing</param>
/// <returns>True if split succeeded, false otherwise</returns>
public bool SplitByType ( string outDir , string basepath , Logger logger )
{
// Sanitize the basepath to be more predictable
basepath = ( basepath . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) ) ? basepath : basepath + Path . DirectorySeparatorChar ) ;
2016-09-22 17:52:58 -07:00
2016-10-24 16:14:22 -07:00
// Create each of the respective output DATs
logger . User ( "Creating and populating new DATs" ) ;
DatFile romdat = new DatFile
{
FileName = this . FileName + " (ROM)" ,
Name = this . Name + " (ROM)" ,
Description = this . Description + " (ROM)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
DatFile diskdat = new DatFile
{
FileName = this . FileName + " (Disk)" ,
Name = this . Name + " (Disk)" ,
Description = this . Description + " (Disk)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
DatFile sampledat = new DatFile
{
FileName = this . FileName + " (Sample)" ,
Name = this . Name + " (Sample)" ,
Description = this . Description + " (Sample)" ,
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 ,
2016-10-25 15:02:02 -07:00
DatFormat = this . DatFormat ,
2016-10-24 16:14:22 -07:00
MergeRoms = this . MergeRoms ,
Files = new SortedDictionary < string , List < DatItem > > ( ) ,
} ;
2016-09-22 17:52:58 -07:00
2016-10-24 16:14:22 -07:00
// Now populate each of the DAT objects in turn
List < string > keys = this . Files . Keys . ToList ( ) ;
foreach ( string key in keys )
{
List < DatItem > roms = this . Files [ key ] ;
foreach ( DatItem rom in roms )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
// If the file is a Rom
if ( rom . Type = = ItemType . Rom )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
if ( romdat . Files . ContainsKey ( key ) )
{
romdat . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
romdat . Files . Add ( key , temp ) ;
}
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
// If the file is a Disk
else if ( rom . Type = = ItemType . Disk )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
if ( diskdat . Files . ContainsKey ( key ) )
{
diskdat . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
diskdat . Files . Add ( key , temp ) ;
}
2016-09-22 18:15:02 -07:00
}
2016-09-19 18:04:24 -07:00
2016-10-24 16:14:22 -07:00
// If the file is a Sample
else if ( rom . Type = = ItemType . Sample )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
if ( sampledat . Files . ContainsKey ( key ) )
{
sampledat . Files [ key ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sampledat . Files . Add ( key , temp ) ;
}
2016-09-22 18:15:02 -07:00
}
}
2016-10-24 16:14:22 -07:00
}
2016-09-22 17:52:58 -07:00
2016-10-24 16:14:22 -07:00
// Get the output directory
if ( outDir ! = "" )
{
outDir = outDir + Path . GetDirectoryName ( this . FileName ) . Remove ( 0 , basepath . Length - 1 ) ;
}
else
{
outDir = Path . GetDirectoryName ( this . FileName ) ;
}
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
// Now, output all of the files to the output directory
logger . User ( "DAT information created, outputting new files" ) ;
bool success = true ;
if ( romdat . Files . Count > 0 )
{
success & = romdat . WriteToFile ( outDir , logger ) ;
2016-09-22 17:52:58 -07:00
}
2016-10-24 16:14:22 -07:00
if ( diskdat . Files . Count > 0 )
2016-09-22 17:52:58 -07:00
{
2016-10-24 16:14:22 -07:00
success & = diskdat . WriteToFile ( outDir , logger ) ;
2016-09-22 18:15:02 -07:00
}
2016-10-24 16:14:22 -07:00
if ( sampledat . Files . Count > 0 )
{
success & = sampledat . WriteToFile ( outDir , logger ) ;
}
return success ;
2016-09-22 18:15:02 -07:00
}
2016-09-01 20:38:41 -07:00
2016-09-22 18:15:02 -07:00
#endregion
2016-09-01 20:38:41 -07:00
2016-09-22 18:15:02 -07:00
#region Statistics
2016-09-22 17:52:58 -07:00
2016-09-22 18:15:02 -07:00
/// <summary>
/// Recalculate the statistics for the Dat
/// </summary>
public void RecalculateStats ( )
{
// Wipe out any stats already there
RomCount = 0 ;
DiskCount = 0 ;
TotalSize = 0 ;
CRCCount = 0 ;
MD5Count = 0 ;
SHA1Count = 0 ;
2016-09-26 16:42:06 -07:00
BaddumpCount = 0 ;
2016-09-22 18:15:02 -07:00
NodumpCount = 0 ;
2016-09-22 17:52:58 -07:00
2016-09-22 18:15:02 -07:00
// If we have a blank Dat in any way, return
if ( this = = null | | Files = = null | | Files . Count = = 0 )
{
return ;
}
// Loop through and add
foreach ( List < DatItem > roms in Files . Values )
{
foreach ( Rom rom in roms )
{
RomCount + = ( rom . Type = = ItemType . Rom ? 1 : 0 ) ;
DiskCount + = ( rom . Type = = ItemType . Disk ? 1 : 0 ) ;
TotalSize + = ( rom . ItemStatus = = ItemStatus . Nodump ? 0 : rom . Size ) ;
CRCCount + = ( String . IsNullOrEmpty ( rom . CRC ) ? 0 : 1 ) ;
MD5Count + = ( String . IsNullOrEmpty ( rom . MD5 ) ? 0 : 1 ) ;
SHA1Count + = ( String . IsNullOrEmpty ( rom . SHA1 ) ? 0 : 1 ) ;
2016-09-26 16:42:06 -07:00
BaddumpCount + = ( rom . Type = = ItemType . Disk
? ( ( ( Disk ) rom ) . ItemStatus = = ItemStatus . BadDump ? 1 : 0 )
: ( rom . Type = = ItemType . Rom
? ( ( ( Rom ) rom ) . ItemStatus = = ItemStatus . BadDump ? 1 : 0 )
: 0 )
) ;
NodumpCount + = ( rom . Type = = ItemType . Disk
? ( ( ( Disk ) rom ) . ItemStatus = = ItemStatus . Nodump ? 1 : 0 )
: ( rom . Type = = ItemType . Rom
? ( ( ( Rom ) rom ) . ItemStatus = = ItemStatus . Nodump ? 1 : 0 )
: 0 )
) ;
2016-09-22 18:15:02 -07:00
}
2016-09-22 17:52:58 -07:00
}
2016-09-01 20:38:41 -07:00
}
/// <summary>
2016-09-22 18:15:02 -07:00
/// Output the stats for the Dat in a human-readable format
2016-09-01 20:38:41 -07:00
/// </summary>
2016-09-26 14:38:05 -07:00
/// <param name="sw">StreamWriter representing the output file or stream for the statistics</param>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat">Set the statistics output format to use</param>
2016-09-22 18:15:02 -07:00
/// <param name="logger">Logger object for file and console writing</param>
/// <param name="recalculate">True if numbers should be recalculated for the DAT, false otherwise (default)</param>
/// <param name="game">Number of games to use, -1 means recalculate games (default)</param>
2016-09-26 16:42:06 -07:00
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise (default)</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise (default)</param>
2016-10-25 15:02:02 -07:00
public void OutputStats ( StreamWriter sw , StatDatFormat statDatFormat , Logger logger , bool recalculate = false , long game = - 1 , bool baddumpCol = false , bool nodumpCol = false )
2016-09-01 20:38:41 -07:00
{
2016-09-22 18:15:02 -07:00
// If we're supposed to recalculate the statistics, do so
if ( recalculate )
{
RecalculateStats ( ) ;
}
2016-09-01 20:38:41 -07:00
2016-09-22 18:15:02 -07:00
BucketByGame ( false , true , logger , false ) ;
if ( TotalSize < 0 )
{
TotalSize = Int64 . MaxValue + TotalSize ;
}
2016-09-26 14:38:05 -07:00
// Log the results to screen
2016-09-26 16:42:06 -07:00
string results = @"For '" + FileName + @ "':
2016-09-26 14:38:05 -07:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2016-10-24 12:58:57 -07:00
Uncompressed size : " + Style.GetBytesReadable(TotalSize) + @"
Games found : " + (game == -1 ? Files.Count : game) + @"
Roms found : " + RomCount + @"
Disks found : " + DiskCount + @"
Roms with CRC : " + CRCCount + @"
Roms with MD5 : " + MD5Count + @"
Roms with SHA - 1 : " + SHA1Count + " \ n ";
2016-09-26 16:42:06 -07:00
if ( baddumpCol )
{
2016-10-24 12:58:57 -07:00
results + = " Roms with BadDump status: " + BaddumpCount + "\n" ;
2016-09-26 16:42:06 -07:00
}
if ( nodumpCol )
{
2016-10-24 12:58:57 -07:00
results + = " Roms with Nodump status: " + NodumpCount + "\n" ;
2016-09-26 16:42:06 -07:00
}
2016-09-01 20:38:41 -07:00
2016-09-26 16:42:06 -07:00
logger . User ( results ) ;
2016-09-23 16:32:24 -07:00
2016-09-26 14:38:05 -07:00
// Now write it out to file as well
string line = "" ;
2016-10-25 15:02:02 -07:00
switch ( statDatFormat )
2016-09-23 16:32:24 -07:00
{
2016-10-25 15:02:02 -07:00
case StatDatFormat . CSV :
2016-09-26 14:38:05 -07:00
line = "\"" + FileName + "\","
+ "\"" + Style . GetBytesReadable ( TotalSize ) + "\","
+ "\"" + ( game = = - 1 ? Files . Count : game ) + "\","
+ "\"" + RomCount + "\","
+ "\"" + DiskCount + "\","
+ "\"" + CRCCount + "\","
+ "\"" + MD5Count + "\","
2016-09-26 16:42:06 -07:00
+ "\"" + SHA1Count + "\"" ;
2016-10-24 16:14:22 -07:00
if ( baddumpCol )
{
line + = ",\"" + BaddumpCount + "\"" ;
}
if ( nodumpCol )
{
line + = ",\"" + NodumpCount + "\"" ;
}
line + = "\n" ;
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . HTML :
2016-10-24 16:14:22 -07:00
line = "\t\t\t<tr" + ( FileName . StartsWith ( "DIR: " )
? " class=\"dir\"><td>" + HttpUtility . HtmlEncode ( FileName . Remove ( 0 , 5 ) )
: "><td>" + HttpUtility . HtmlEncode ( FileName ) ) + "</td>"
+ "<td align=\"right\">" + Style . GetBytesReadable ( TotalSize ) + "</td>"
+ "<td align=\"right\">" + ( game = = - 1 ? Files . Count : game ) + "</td>"
+ "<td align=\"right\">" + RomCount + "</td>"
+ "<td align=\"right\">" + DiskCount + "</td>"
+ "<td align=\"right\">" + CRCCount + "</td>"
+ "<td align=\"right\">" + MD5Count + "</td>"
+ "<td align=\"right\">" + SHA1Count + "</td>" ;
if ( baddumpCol )
{
line + = "<td align=\"right\">" + BaddumpCount + "</td>" ;
}
if ( nodumpCol )
{
line + = "<td align=\"right\">" + NodumpCount + "</td>" ;
}
line + = "</tr>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . None :
2016-10-24 16:14:22 -07:00
default :
line = @"'" + FileName + @ "':
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Uncompressed size : " + Style.GetBytesReadable(TotalSize) + @"
Games found : " + (game == -1 ? Files.Count : game) + @"
Roms found : " + RomCount + @"
Disks found : " + DiskCount + @"
Roms with CRC : " + CRCCount + @"
Roms with MD5 : " + MD5Count + @"
Roms with SHA - 1 : " + SHA1Count + " \ n ";
if ( baddumpCol )
{
line + = " Roms with BadDump status: " + BaddumpCount + "\n" ;
}
if ( nodumpCol )
{
line + = " Roms with Nodump status: " + NodumpCount + "\n" ;
}
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . TSV :
2016-10-24 16:14:22 -07:00
line = "\"" + FileName + "\"\t"
+ "\"" + Style . GetBytesReadable ( TotalSize ) + "\"\t"
+ "\"" + ( game = = - 1 ? Files . Count : game ) + "\"\t"
+ "\"" + RomCount + "\"\t"
+ "\"" + DiskCount + "\"\t"
+ "\"" + CRCCount + "\"\t"
+ "\"" + MD5Count + "\"\t"
+ "\"" + SHA1Count + "\"" ;
if ( baddumpCol )
{
line + = "\t\"" + BaddumpCount + "\"" ;
}
if ( nodumpCol )
{
line + = "\t\"" + NodumpCount + "\"" ;
}
line + = "\n" ;
break ;
}
// Output the line to the streamwriter
sw . Write ( line ) ;
}
#endregion
#region Writing
/// <summary>
/// Create and open an output file for writing direct from a dictionary
/// </summary>
/// <param name="datdata">All information for creating the datfile header</param>
/// <param name="outDir">Set the output directory</param>
/// <param name="logger">Logger object for console and/or file output</param>
/// <param name="norename">True if games should only be compared on game and file name (default), false if system and source are counted</param>
/// <param name="stats">True if DAT statistics should be output on write, false otherwise (default)</param>
/// <param name="ignoreblanks">True if blank roms should be skipped on output, false otherwise (default)</param>
/// <param name="overwrite">True if files should be overwritten (default), false if they should be renamed instead</param>
/// <returns>True if the DAT was written correctly, false otherwise</returns>
/// <remarks>
/// The following features have been requested for file output:
/// - Have the ability to strip special (non-ASCII) characters from rom information
/// </remarks>
public bool WriteToFile ( string outDir , Logger logger , bool norename = true , bool stats = false , bool ignoreblanks = false , bool overwrite = true )
{
// If there's nothing there, abort
if ( Files = = null | | Files . Count = = 0 )
{
return false ;
}
// If output directory is empty, use the current folder
if ( outDir . Trim ( ) = = "" )
{
outDir = Environment . CurrentDirectory ;
}
// Create the output directory if it doesn't already exist
if ( ! Directory . Exists ( outDir ) )
{
Directory . CreateDirectory ( outDir ) ;
}
// If the DAT has no output format, default to XML
2016-10-25 15:02:02 -07:00
if ( DatFormat = = 0 )
2016-10-24 16:14:22 -07:00
{
2016-10-25 15:02:02 -07:00
DatFormat = DatFormat . Logiqx ;
2016-10-24 16:14:22 -07:00
}
// Make sure that the three essential fields are filled in
if ( String . IsNullOrEmpty ( FileName ) & & String . IsNullOrEmpty ( Name ) & & String . IsNullOrEmpty ( Description ) )
{
FileName = Name = Description = "Default" ;
}
else if ( String . IsNullOrEmpty ( FileName ) & & String . IsNullOrEmpty ( Name ) & & ! String . IsNullOrEmpty ( Description ) )
{
FileName = Name = Description ;
}
else if ( String . IsNullOrEmpty ( FileName ) & & ! String . IsNullOrEmpty ( Name ) & & String . IsNullOrEmpty ( Description ) )
{
FileName = Description = Name ;
}
else if ( String . IsNullOrEmpty ( FileName ) & & ! String . IsNullOrEmpty ( Name ) & & ! String . IsNullOrEmpty ( Description ) )
{
FileName = Description ;
}
else if ( ! String . IsNullOrEmpty ( FileName ) & & String . IsNullOrEmpty ( Name ) & & String . IsNullOrEmpty ( Description ) )
{
Name = Description = FileName ;
}
else if ( ! String . IsNullOrEmpty ( FileName ) & & String . IsNullOrEmpty ( Name ) & & ! String . IsNullOrEmpty ( Description ) )
{
Name = Description ;
}
else if ( ! String . IsNullOrEmpty ( FileName ) & & ! String . IsNullOrEmpty ( Name ) & & String . IsNullOrEmpty ( Description ) )
{
Description = Name ;
}
else if ( ! String . IsNullOrEmpty ( FileName ) & & ! String . IsNullOrEmpty ( Name ) & & ! String . IsNullOrEmpty ( Description ) )
{
// Nothing is needed
}
// Output initial statistics, for kicks
if ( stats )
{
StreamWriter sw = new StreamWriter ( new MemoryStream ( ) ) ;
2016-10-25 15:02:02 -07:00
OutputStats ( sw , StatDatFormat . None , logger , recalculate : ( RomCount + DiskCount = = 0 ) , baddumpCol : true , nodumpCol : true ) ;
2016-10-24 16:14:22 -07:00
sw . Dispose ( ) ;
}
// Bucket roms by game name and optionally dedupe
BucketByGame ( MergeRoms , norename , logger ) ;
// Get the outfile name
2016-10-25 15:02:02 -07:00
Dictionary < DatFormat , string > outfiles = Style . CreateOutfileNames ( outDir , this , overwrite ) ;
2016-10-24 16:14:22 -07:00
try
{
2016-10-25 15:02:02 -07:00
foreach ( DatFormat datFormat in outfiles . Keys )
2016-10-24 16:14:22 -07:00
{
2016-10-25 15:02:02 -07:00
string outfile = outfiles [ datFormat ] ;
2016-10-24 16:14:22 -07:00
logger . User ( "Opening file for writing: " + outfile ) ;
FileStream fs = File . Create ( outfile ) ;
StreamWriter sw = new StreamWriter ( fs , new UTF8Encoding ( true ) ) ;
// Write out the header
2016-10-25 15:02:02 -07:00
WriteHeader ( sw , datFormat , logger ) ;
2016-10-24 16:14:22 -07:00
// Write out each of the machines and roms
int depth = 2 , last = - 1 ;
string lastgame = null ;
List < string > splitpath = new List < string > ( ) ;
// Get a properly sorted set of keys
List < string > keys = Files . Keys . ToList ( ) ;
keys . Sort ( new NaturalComparer ( ) ) ;
foreach ( string key in keys )
{
List < DatItem > roms = Files [ key ] ;
for ( int index = 0 ; index < roms . Count ; index + + )
{
DatItem rom = roms [ index ] ;
// There are apparently times when a null rom can skip by, skip them
if ( rom . Name = = null | | rom . Machine . Name = = null )
{
logger . Warning ( "Null rom found!" ) ;
continue ;
}
List < string > newsplit = rom . Machine . Name . Split ( '\\' ) . ToList ( ) ;
// 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 . Machine . Name . ToLowerInvariant ( ) )
{
2016-10-25 15:02:02 -07:00
depth = WriteEndGame ( sw , datFormat , rom , splitpath , newsplit , lastgame , depth , out last , logger ) ;
2016-10-24 16:14:22 -07:00
}
// If we have a new game, output the beginning of the new item
if ( lastgame = = null | | lastgame . ToLowerInvariant ( ) ! = rom . Machine . Name . ToLowerInvariant ( ) )
{
2016-10-25 15:02:02 -07:00
depth = WriteStartGame ( sw , datFormat , rom , newsplit , lastgame , depth , last , logger ) ;
2016-10-24 16:14:22 -07:00
}
// If we have a "null" game (created by DATFromDir or something similar), log it to file
if ( rom . Type = = ItemType . Rom
& & ( ( Rom ) rom ) . Size = = - 1
& & ( ( Rom ) rom ) . CRC = = "null"
& & ( ( Rom ) rom ) . MD5 = = "null"
& & ( ( Rom ) rom ) . SHA1 = = "null" )
{
logger . Verbose ( "Empty folder found: " + rom . Machine . Name ) ;
// If we're in a mode that doesn't allow for actual empty folders, add the blank info
2016-10-25 15:02:02 -07:00
if ( datFormat ! = DatFormat . CSV
& & datFormat ! = DatFormat . MissFile
& & datFormat ! = DatFormat . SabreDat
& & datFormat ! = DatFormat . TSV )
2016-10-24 16:14:22 -07:00
{
rom . Name = ( rom . Name = = "null" ? "-" : rom . Name ) ;
( ( Rom ) rom ) . Size = Constants . SizeZero ;
( ( Rom ) rom ) . CRC = Constants . CRCZero ;
( ( Rom ) rom ) . MD5 = Constants . MD5Zero ;
( ( Rom ) rom ) . SHA1 = Constants . SHA1Zero ;
}
2016-09-26 16:42:06 -07:00
2016-10-24 16:14:22 -07:00
// Otherwise, set the new path and such, write out, and continue
else
{
splitpath = newsplit ;
lastgame = rom . Machine . Name ;
continue ;
}
}
2016-09-26 16:42:06 -07:00
2016-10-24 16:14:22 -07:00
// Now, output the rom data
2016-10-25 15:02:02 -07:00
WriteRomData ( sw , datFormat , rom , lastgame , depth , logger , ignoreblanks ) ;
2016-10-24 16:14:22 -07:00
// Set the new data to compare against
splitpath = newsplit ;
lastgame = rom . Machine . Name ;
}
2016-09-26 16:42:06 -07:00
}
2016-10-24 16:14:22 -07:00
// Write the file footer out
2016-10-25 15:02:02 -07:00
WriteFooter ( sw , datFormat , depth , logger ) ;
2016-09-26 14:38:05 -07:00
2016-10-24 16:14:22 -07:00
logger . Verbose ( "File written!" + Environment . NewLine ) ;
sw . Dispose ( ) ;
fs . Dispose ( ) ;
}
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
return false ;
}
2016-09-26 16:42:06 -07:00
2016-10-24 16:14:22 -07:00
return true ;
}
/// <summary>
/// Write out DAT header using the supplied StreamWriter
/// </summary>
/// <param name="sw">StreamWriter to output to</param>
2016-10-25 15:02:02 -07:00
/// <param name="datFormat">Output format to write to</param>
2016-10-24 16:14:22 -07:00
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the data was written, false on error</returns>
2016-10-25 15:02:02 -07:00
private bool WriteHeader ( StreamWriter sw , DatFormat datFormat , Logger logger )
2016-10-24 16:14:22 -07:00
{
try
{
string header = "" ;
2016-10-25 15:02:02 -07:00
switch ( datFormat )
2016-10-24 16:14:22 -07:00
{
2016-10-25 15:02:02 -07:00
case DatFormat . ClrMamePro :
2016-10-24 16:14:22 -07:00
header = "clrmamepro (\n" +
"\tname \"" + Name + "\"\n" +
"\tdescription \"" + Description + "\"\n" +
( ! String . IsNullOrEmpty ( Category ) ? "\tcategory \"" + Category + "\"\n" : "" ) +
"\tversion \"" + Version + "\"\n" +
( ! String . IsNullOrEmpty ( Date ) ? "\tdate \"" + Date + "\"\n" : "" ) +
"\tauthor \"" + Author + "\"\n" +
( ! String . IsNullOrEmpty ( Email ) ? "\temail \"" + Email + "\"\n" : "" ) +
( ! String . IsNullOrEmpty ( Homepage ) ? "\thomepage \"" + Homepage + "\"\n" : "" ) +
( ! String . IsNullOrEmpty ( Url ) ? "\turl \"" + Url + "\"\n" : "" ) +
( ! String . IsNullOrEmpty ( Comment ) ? "\tcomment \"" + Comment + "\"\n" : "" ) +
( ForcePacking = = ForcePacking . Unzip ? "\tforcezipping no\n" : "" ) +
( ForcePacking = = ForcePacking . Zip ? "\tforcezipping yes\n" : "" ) +
( ForceMerging = = ForceMerging . Full ? "\tforcemerging full\n" : "" ) +
( ForceMerging = = ForceMerging . Split ? "\tforcemerging split\n" : "" ) +
")\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . CSV :
2016-10-24 16:14:22 -07:00
header = "\"File Name\",\"Internal Name\",\"Description\",\"Game Name\",\"Game Description\",\"Type\",\"" +
"Rom Name\",\"Disk Name\",\"Size\",\"CRC\",\"MD5\",\"SHA1\",\"Nodump\"\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . DOSCenter :
2016-10-24 16:14:22 -07:00
header = "DOSCenter (\n" +
"\tName: " + Name + "\n" +
"\tDescription: " + Description + "\n" +
"\tVersion: " + Version + "\n" +
"\tDate: " + Date + "\n" +
"\tAuthor: " + Author + "\n" +
"\tHomepage: " + Homepage + "\n" +
"\tComment: " + Comment + "\n" +
")\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . Logiqx :
2016-10-24 16:14:22 -07:00
header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE datafile PUBLIC \"-//Logiqx//DTD ROM Management Datafile//EN\" \"http://www.logiqx.com/Dats/datafile.dtd\">\n\n" +
"<datafile>\n" +
"\t<header>\n" +
"\t\t<name>" + HttpUtility . HtmlEncode ( Name ) + "</name>\n" +
"\t\t<description>" + HttpUtility . HtmlEncode ( Description ) + "</description>\n" +
( ! String . IsNullOrEmpty ( RootDir ) ? "\t\t<rootdir>" + HttpUtility . HtmlEncode ( RootDir ) + "</rootdir>\n" : "" ) +
( ! String . IsNullOrEmpty ( Category ) ? "\t\t<category>" + HttpUtility . HtmlEncode ( Category ) + "</category>\n" : "" ) +
"\t\t<version>" + HttpUtility . HtmlEncode ( Version ) + "</version>\n" +
( ! String . IsNullOrEmpty ( Date ) ? "\t\t<date>" + HttpUtility . HtmlEncode ( Date ) + "</date>\n" : "" ) +
"\t\t<author>" + HttpUtility . HtmlEncode ( Author ) + "</author>\n" +
( ! String . IsNullOrEmpty ( Email ) ? "\t\t<email>" + HttpUtility . HtmlEncode ( Email ) + "</email>\n" : "" ) +
( ! String . IsNullOrEmpty ( Homepage ) ? "\t\t<homepage>" + HttpUtility . HtmlEncode ( Homepage ) + "</homepage>\n" : "" ) +
( ! String . IsNullOrEmpty ( Url ) ? "\t\t<url>" + HttpUtility . HtmlEncode ( Url ) + "</url>\n" : "" ) +
( ! String . IsNullOrEmpty ( Comment ) ? "\t\t<comment>" + HttpUtility . HtmlEncode ( Comment ) + "</comment>\n" : "" ) +
( ! String . IsNullOrEmpty ( Type ) ? "\t\t<type>" + HttpUtility . HtmlEncode ( Type ) + "</type>\n" : "" ) +
( ForcePacking ! = ForcePacking . None | | ForceMerging ! = ForceMerging . None | | ForceNodump ! = ForceNodump . None ?
"\t\t<clrmamepro" +
( ForcePacking = = ForcePacking . Unzip ? " forcepacking=\"unzip\"" : "" ) +
( ForcePacking = = ForcePacking . Zip ? " forcepacking=\"zip\"" : "" ) +
( ForceMerging = = ForceMerging . Full ? " forcemerging=\"full\"" : "" ) +
( ForceMerging = = ForceMerging . Split ? " forcemerging=\"split\"" : "" ) +
( ForceNodump = = ForceNodump . Ignore ? " forceitemStatus=\"ignore\"" : "" ) +
( ForceNodump = = ForceNodump . Obsolete ? " forceitemStatus=\"obsolete\"" : "" ) +
( ForceNodump = = ForceNodump . Required ? " forceitemStatus=\"required\"" : "" ) +
" />\n"
: "" ) +
"\t</header>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . TSV :
2016-10-24 16:14:22 -07:00
header = "\"File Name\"\t\"Internal Name\"\t\"Description\"\t\"Game Name\"\t\"Game Description\"\t\"Type\"\t\"" +
"Rom Name\"\t\"Disk Name\"\t\"Size\"\t\"CRC\"\t\"MD5\"\t\"SHA1\"\t\"Nodump\"\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . OfflineList :
2016-10-24 16:14:22 -07:00
header = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ "<dat xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"datas.xsd\">\n"
+ "\t<configuration>\n"
+ "\t\t<datName>" + HttpUtility . HtmlEncode ( Name ) + "</datName>\n"
+ "\t\t<datVersion>" + Files . Count + "</datVersion>\n"
+ "\t\t<system>none</system>\n"
+ "\t\t<screenshotsWidth>240</screenshotsWidth>\n"
+ "\t\t<screenshotsHeight>160</screenshotsHeight>\n"
+ "\t\t<infos>\n"
+ "\t\t\t<title visible=\"false\" inNamingOption=\"true\" default=\"false\"/>\n"
+ "\t\t\t<location visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n"
+ "\t\t\t<publisher visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n"
+ "\t\t\t<sourceRom visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n"
+ "\t\t\t<saveType visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n"
+ "\t\t\t<romSize visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n"
+ "\t\t\t<releaseNumber visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n"
+ "\t\t\t<languageNumber visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n"
+ "\t\t\t<comment visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n"
+ "\t\t\t<romCRC visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n"
+ "\t\t\t<im1CRC visible=\"false\" inNamingOption=\"false\" default=\"false\"/>\n"
+ "\t\t\t<im2CRC visible=\"false\" inNamingOption=\"false\" default=\"false\"/>\n"
+ "\t\t\t<languages visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n"
+ "\t\t</infos>\n"
+ "\t\t<canOpen>\n"
+ "\t\t\t<extension>.bin</extension>\n"
+ "\t\t</canOpen>\n"
+ "\t\t<newDat>\n"
+ "\t\t\t<datVersionURL>" + HttpUtility . HtmlEncode ( Url ) + "</datVersionURL>\n"
+ "\t\t\t<datURL fileName=\"" + HttpUtility . HtmlEncode ( FileName ) + ".zip\">" + HttpUtility . HtmlEncode ( Url ) + "</datURL>\n"
+ "\t\t\t<imURL>" + HttpUtility . HtmlEncode ( Url ) + "</imURL>\n"
+ "\t\t</newDat>\n"
+ "\t\t<search>\n"
+ "\t\t\t<to value=\"location\" default=\"true\" auto=\"true\"/>\n"
+ "\t\t\t<to value=\"romSize\" default=\"true\" auto=\"false\"/>\n"
+ "\t\t\t<to value=\"languages\" default=\"true\" auto=\"true\"/>\n"
+ "\t\t\t<to value=\"saveType\" default=\"false\" auto=\"false\"/>\n"
+ "\t\t\t<to value=\"publisher\" default=\"false\" auto=\"true\"/>\n"
+ "\t\t\t<to value=\"sourceRom\" default=\"false\" auto=\"true\"/>\n"
+ "\t\t</search>\n"
+ "\t\t<romTitle >%u - %n</romTitle>\n"
+ "\t</configuration>\n"
+ "\t<games>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RomCenter :
2016-10-24 16:14:22 -07:00
header = "[CREDITS]\n" +
"author=" + Author + "\n" +
"version=" + Version + "\n" +
"comment=" + Comment + "\n" +
"[DAT]\n" +
"version=2.50\n" +
"split=" + ( ForceMerging = = ForceMerging . Split ? "1" : "0" ) + "\n" +
"merge=" + ( ForceMerging = = ForceMerging . Full ? "1" : "0" ) + "\n" +
"[EMULATOR]\n" +
"refname=" + Name + "\n" +
"version=" + Description + "\n" +
"[GAMES]\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SabreDat :
2016-10-24 16:14:22 -07:00
header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE sabredat SYSTEM \"newdat.xsd\">\n\n" +
"<datafile>\n" +
"\t<header>\n" +
"\t\t<name>" + HttpUtility . HtmlEncode ( Name ) + "</name>\n" +
"\t\t<description>" + HttpUtility . HtmlEncode ( Description ) + "</description>\n" +
( ! String . IsNullOrEmpty ( RootDir ) ? "\t\t<rootdir>" + HttpUtility . HtmlEncode ( RootDir ) + "</rootdir>\n" : "" ) +
( ! String . IsNullOrEmpty ( Category ) ? "\t\t<category>" + HttpUtility . HtmlEncode ( Category ) + "</category>\n" : "" ) +
"\t\t<version>" + HttpUtility . HtmlEncode ( Version ) + "</version>\n" +
( ! String . IsNullOrEmpty ( Date ) ? "\t\t<date>" + HttpUtility . HtmlEncode ( Date ) + "</date>\n" : "" ) +
"\t\t<author>" + HttpUtility . HtmlEncode ( Author ) + "</author>\n" +
( ! String . IsNullOrEmpty ( Comment ) ? "\t\t<comment>" + HttpUtility . HtmlEncode ( Comment ) + "</comment>\n" : "" ) +
( ! String . IsNullOrEmpty ( Type ) | | ForcePacking ! = ForcePacking . None | | ForceMerging ! = ForceMerging . None | | ForceNodump ! = ForceNodump . None ?
"\t\t<flags>\n" +
( ! String . IsNullOrEmpty ( Type ) ? "\t\t\t<flag name=\"type\" value=\"" + HttpUtility . HtmlEncode ( Type ) + "\"/>\n" : "" ) +
( ForcePacking = = ForcePacking . Unzip ? "\t\t\t<flag name=\"forcepacking\" value=\"unzip\"/>\n" : "" ) +
( ForcePacking = = ForcePacking . Zip ? "\t\t\t<flag name=\"forcepacking\" value=\"zip\"/>\n" : "" ) +
( ForceMerging = = ForceMerging . Full ? "\t\t\t<flag name=\"forcemerging\" value=\"full\"/>\n" : "" ) +
( ForceMerging = = ForceMerging . Split ? "\t\t\t<flag name=\"forcemerging\" value=\"split\"/>\n" : "" ) +
( ForceNodump = = ForceNodump . Ignore ? "\t\t\t<flag name=\"forceitemStatus\" value=\"ignore\"/>\n" : "" ) +
( ForceNodump = = ForceNodump . Obsolete ? "\t\t\t<flag name=\"forceitemStatus\" value=\"obsolete\"/>\n" : "" ) +
( ForceNodump = = ForceNodump . Required ? "\t\t\t<flag name=\"forceitemStatus\" value=\"required\"/>\n" : "" ) +
"\t\t</flags>\n"
: "" ) +
"\t</header>\n" +
"\t<data>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SoftwareList :
2016-10-24 16:14:22 -07:00
header = "<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE softwarelist SYSTEM \"softwarelist.dtd\">\n\n" +
"<softwarelist name=\"" + HttpUtility . HtmlEncode ( Name ) + "\"" +
" description=\"" + HttpUtility . HtmlEncode ( Description ) + "\"" +
( ForcePacking = = ForcePacking . Unzip ? " forcepacking=\"unzip\"" : "" ) +
( ForcePacking = = ForcePacking . Zip ? " forcepacking=\"zip\"" : "" ) +
( ForceMerging = = ForceMerging . Full ? " forcemerging=\"full\"" : "" ) +
( ForceMerging = = ForceMerging . Split ? " forcemerging=\"split\"" : "" ) +
( ForceNodump = = ForceNodump . Ignore ? " forceitemStatus=\"ignore\"" : "" ) +
( ForceNodump = = ForceNodump . Obsolete ? " forceitemStatus=\"obsolete\"" : "" ) +
( ForceNodump = = ForceNodump . Required ? " forceitemStatus=\"required\"" : "" ) +
">\n\n" ;
break ;
}
2016-09-26 16:42:06 -07:00
2016-10-24 16:14:22 -07:00
// Write the header out
sw . Write ( header ) ;
sw . Flush ( ) ;
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
return false ;
2016-09-23 16:32:24 -07:00
}
2016-09-26 14:38:05 -07:00
2016-10-24 16:14:22 -07:00
return true ;
2016-09-23 16:32:24 -07:00
}
2016-09-22 18:15:02 -07:00
/// <summary>
2016-10-24 16:14:22 -07:00
/// Write out Game start using the supplied StreamWriter
2016-09-22 18:15:02 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="sw">StreamWriter to output to</param>
2016-10-25 15:02:02 -07:00
/// <param name="datFormat">Output format to write to</param>
2016-10-24 16:14:22 -07:00
/// <param name="rom">RomData 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>
2016-09-22 18:15:02 -07:00
/// <param name="logger">Logger object for file and console output</param>
2016-10-24 16:14:22 -07:00
/// <returns>The new depth of the tag</returns>
2016-10-25 15:02:02 -07:00
private int WriteStartGame ( StreamWriter sw , DatFormat datFormat , DatItem rom , List < string > newsplit , string lastgame , int depth , int last , Logger logger )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
try
{
// No game should start with a path separator
if ( rom . Machine . Name . StartsWith ( Path . DirectorySeparatorChar . ToString ( ) ) )
{
rom . Machine . Name = rom . Machine . Name . Substring ( 1 ) ;
}
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
string state = "" ;
2016-10-25 15:02:02 -07:00
switch ( datFormat )
2016-10-24 16:14:22 -07:00
{
2016-10-25 15:02:02 -07:00
case DatFormat . ClrMamePro :
2016-10-24 16:14:22 -07:00
state + = "game (\n\tname \"" + rom . Machine . Name + "\"\n" +
( ExcludeOf ? "" :
( String . IsNullOrEmpty ( rom . Machine . RomOf ) ? "" : "\tromof \"" + rom . Machine . RomOf + "\"\n" ) +
( String . IsNullOrEmpty ( rom . Machine . CloneOf ) ? "" : "\tcloneof \"" + rom . Machine . CloneOf + "\"\n" ) +
( String . IsNullOrEmpty ( rom . Machine . SampleOf ) ? "" : "\tsampleof \"" + rom . Machine . SampleOf + "\"\n" )
) +
"\tdescription \"" + ( String . IsNullOrEmpty ( rom . Machine . Description ) ? rom . Machine . Name : rom . Machine . Description ) + "\"\n" +
( String . IsNullOrEmpty ( rom . Machine . Year ) ? "" : "\tyear " + rom . Machine . Year + "\n" ) +
( String . IsNullOrEmpty ( rom . Machine . Manufacturer ) ? "" : "\tmanufacturer \"" + rom . Machine . Manufacturer + "\"\n" ) ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . DOSCenter :
2016-10-24 16:14:22 -07:00
state + = "game (\n\tname \"" + rom . Machine . Name + ".zip\"\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . Logiqx :
2016-10-24 16:14:22 -07:00
state + = "\t<machine name=\"" + HttpUtility . HtmlEncode ( rom . Machine . Name ) + "\"" +
( rom . Machine . IsBios ? " isbios=\"yes\"" : "" ) +
( ExcludeOf ? "" :
( String . IsNullOrEmpty ( rom . Machine . CloneOf ) | | ( rom . Machine . Name . ToLowerInvariant ( ) = = rom . Machine . CloneOf . ToLowerInvariant ( ) )
? ""
: " cloneof=\"" + HttpUtility . HtmlEncode ( rom . Machine . CloneOf ) + "\"" ) +
( String . IsNullOrEmpty ( rom . Machine . RomOf ) | | ( rom . Machine . Name . ToLowerInvariant ( ) = = rom . Machine . RomOf . ToLowerInvariant ( ) )
? ""
: " romof=\"" + HttpUtility . HtmlEncode ( rom . Machine . RomOf ) + "\"" ) +
( String . IsNullOrEmpty ( rom . Machine . SampleOf ) | | ( rom . Machine . Name . ToLowerInvariant ( ) = = rom . Machine . SampleOf . ToLowerInvariant ( ) )
? ""
: " sampleof=\"" + HttpUtility . HtmlEncode ( rom . Machine . SampleOf ) + "\"" )
) +
">\n" +
( String . IsNullOrEmpty ( rom . Machine . Comment ) ? "" : "\t\t<comment>" + HttpUtility . HtmlEncode ( rom . Machine . Comment ) + "</comment>\n" ) +
"\t\t<description>" + HttpUtility . HtmlEncode ( ( String . IsNullOrEmpty ( rom . Machine . Description ) ? rom . Machine . Name : rom . Machine . Description ) ) + "</description>\n" +
( String . IsNullOrEmpty ( rom . Machine . Year ) ? "" : "\t\t<year>" + HttpUtility . HtmlEncode ( rom . Machine . Year ) + "</year>\n" ) +
( String . IsNullOrEmpty ( rom . Machine . Manufacturer ) ? "" : "\t\t<manufacturer>" + HttpUtility . HtmlEncode ( rom . Machine . Manufacturer ) + "</manufacturer>\n" ) ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SabreDat :
2016-10-24 16:14:22 -07:00
for ( int i = ( last = = - 1 ? 0 : last ) ; i < newsplit . Count ; i + + )
{
for ( int j = 0 ; j < depth - last + i - ( lastgame = = null ? 1 : 0 ) ; j + + )
{
state + = "\t" ;
}
state + = "<directory name=\"" + HttpUtility . HtmlEncode ( newsplit [ i ] ) + "\" description=\"" +
HttpUtility . HtmlEncode ( newsplit [ i ] ) + "\">\n" ;
}
depth = depth - ( last = = - 1 ? 0 : last ) + newsplit . Count ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SoftwareList :
2016-10-24 16:14:22 -07:00
state + = "\t<software name=\"" + HttpUtility . HtmlEncode ( rom . Machine . Name ) + "\""
+ ( rom . Supported ! = null ? " supported=\"" + ( rom . Supported = = true ? "yes" : "no" ) + "\"" : "" ) +
( ExcludeOf ? "" :
( String . IsNullOrEmpty ( rom . Machine . CloneOf ) | | ( rom . Machine . Name . ToLowerInvariant ( ) = = rom . Machine . CloneOf . ToLowerInvariant ( ) )
? ""
: " cloneof=\"" + HttpUtility . HtmlEncode ( rom . Machine . CloneOf ) + "\"" ) +
( String . IsNullOrEmpty ( rom . Machine . RomOf ) | | ( rom . Machine . Name . ToLowerInvariant ( ) = = rom . Machine . RomOf . ToLowerInvariant ( ) )
? ""
: " romof=\"" + HttpUtility . HtmlEncode ( rom . Machine . RomOf ) + "\"" ) +
( String . IsNullOrEmpty ( rom . Machine . SampleOf ) | | ( rom . Machine . Name . ToLowerInvariant ( ) = = rom . Machine . SampleOf . ToLowerInvariant ( ) )
? ""
: " sampleof=\"" + HttpUtility . HtmlEncode ( rom . Machine . SampleOf ) + "\"" )
) + ">\n"
+ "\t\t<description>" + HttpUtility . HtmlEncode ( rom . Machine . Description ) + "</description>\n"
+ ( rom . Machine . Year ! = null ? "\t\t<year>" + HttpUtility . HtmlEncode ( rom . Machine . Year ) + "</year>\n" : "" )
+ ( rom . Publisher ! = null ? "\t\t<publisher>" + HttpUtility . HtmlEncode ( rom . Publisher ) + "</publisher>\n" : "" ) ;
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
foreach ( Tuple < string , string > kvp in rom . Infos )
{
state + = "\t\t<info name=\"" + HttpUtility . HtmlEncode ( kvp . Item1 ) + "\" value=\"" + HttpUtility . HtmlEncode ( kvp . Item2 ) + "\" />\n" ;
}
break ;
}
2016-09-22 18:15:02 -07:00
2016-10-24 16:14:22 -07:00
sw . Write ( state ) ;
sw . Flush ( ) ;
}
catch ( Exception ex )
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
logger . Error ( ex . ToString ( ) ) ;
return depth ;
2016-09-22 18:15:02 -07:00
}
2016-10-24 16:14:22 -07:00
return depth ;
}
/// <summary>
/// Write out Game start using the supplied StreamWriter
/// </summary>
/// <param name="sw">StreamWriter to output to</param>
2016-10-25 15:02:02 -07:00
/// <param name="datFormat">Output format to write to</param>
2016-10-24 16:14:22 -07:00
/// <param name="rom">RomData object to be output</param>
/// <param name="splitpath">Split path representing last kwown parent game (SabreDAT only)</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>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>The new depth of the tag</returns>
2016-10-25 15:02:02 -07:00
private int WriteEndGame ( StreamWriter sw , DatFormat datFormat , DatItem rom , List < string > splitpath , List < string > newsplit , string lastgame , int depth , out int last , Logger logger )
2016-10-24 16:14:22 -07:00
{
last = 0 ;
try
2016-09-22 18:15:02 -07:00
{
2016-10-24 16:14:22 -07:00
string state = "" ;
2016-10-25 15:02:02 -07:00
switch ( datFormat )
2016-09-22 18:15:02 -07:00
{
2016-10-25 15:02:02 -07:00
case DatFormat . ClrMamePro :
case DatFormat . DOSCenter :
2016-10-24 16:14:22 -07:00
state + = ( String . IsNullOrEmpty ( rom . Machine . SampleOf ) ? "" : "\tsampleof \"" + rom . Machine . SampleOf + "\"\n" ) + ")\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . Logiqx :
2016-10-24 16:14:22 -07:00
state + = "\t</machine>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . OfflineList :
2016-10-24 16:14:22 -07:00
state + = "\t\t</game>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SabreDat :
2016-10-24 16:14:22 -07:00
if ( splitpath ! = null )
{
for ( int i = 0 ; i < newsplit . Count & & i < splitpath . Count ; i + + )
{
// Always keep track of the last seen item
last = i ;
// If we find a difference, break
if ( newsplit [ i ] ! = splitpath [ i ] )
{
break ;
}
}
// Now that we have the last known position, take down all open folders
for ( int i = depth - 1 ; i > last + 1 ; i - - )
{
// Print out the number of tabs and the end folder
for ( int j = 0 ; j < i ; j + + )
{
state + = "\t" ;
}
state + = "</directory>\n" ;
}
// Reset the current depth
depth = 2 + last ;
}
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SoftwareList :
2016-10-24 16:14:22 -07:00
state + = "\t</software>\n\n" ;
break ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
sw . Write ( state ) ;
sw . Flush ( ) ;
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
return depth ;
2016-09-01 20:38:41 -07:00
}
2016-10-24 16:14:22 -07:00
return depth ;
2016-09-01 20:38:41 -07:00
}
2016-09-06 17:20:16 -07:00
/// <summary>
2016-10-24 16:14:22 -07:00
/// Write out RomData using the supplied StreamWriter
2016-09-06 17:20:16 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="sw">StreamWriter to output to</param>
2016-10-25 15:02:02 -07:00
/// <param name="datFormat">Output format to write to</param>
2016-10-24 16:14:22 -07:00
/// <param name="rom">RomData object to be output</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="logger">Logger object for file and console output</param>
/// <param name="ignoreblanks">True if blank roms should be skipped on output, false otherwise (default)</param>
/// <returns>True if the data was written, false on error</returns>
2016-10-25 15:02:02 -07:00
private bool WriteRomData ( StreamWriter sw , DatFormat datFormat , DatItem rom , string lastgame , int depth , Logger logger , bool ignoreblanks = false )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
// If we are in ignore blanks mode AND we have a blank (0-size) rom, skip
if ( ignoreblanks
& & ( rom . Type = = ItemType . Rom
& & ( ( ( Rom ) rom ) . Size = = 0 | | ( ( Rom ) rom ) . Size = = - 1 ) ) )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
return true ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
try
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
string state = "" , name = "" , pre = "" , post = "" ;
2016-10-25 15:02:02 -07:00
switch ( datFormat )
2016-10-24 16:14:22 -07:00
{
2016-10-25 15:02:02 -07:00
case DatFormat . ClrMamePro :
2016-10-24 16:14:22 -07:00
switch ( rom . Type )
{
case ItemType . Archive :
state + = "\tarchive ( name\"" + rom . Name + "\""
+ " )\n" ;
break ;
case ItemType . BiosSet :
state + = "\tbiosset ( name\"" + rom . Name + "\""
+ ( ! String . IsNullOrEmpty ( ( ( BiosSet ) rom ) . Description ) ? " description \"" + ( ( BiosSet ) rom ) . Description + "\"" : "" )
+ ( ( ( BiosSet ) rom ) . Default ! = null
? "default " + ( ( BiosSet ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ " )\n" ;
break ;
case ItemType . Disk :
state + = "\tdisk ( name \"" + rom . Name + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . MD5 ) ? " md5 " + ( ( Disk ) rom ) . MD5 . ToLowerInvariant ( ) : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . SHA1 ) ? " sha1 " + ( ( Disk ) rom ) . SHA1 . ToLowerInvariant ( ) : "" )
+ ( ( ( Disk ) rom ) . ItemStatus ! = ItemStatus . None ? " flags " + ( ( Disk ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) : "" )
+ " )\n" ;
break ;
case ItemType . Release :
state + = "\trelease ( name\"" + rom . Name + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Region ) ? " region \"" + ( ( Release ) rom ) . Region + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Language ) ? " language \"" + ( ( Release ) rom ) . Language + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Date ) ? " date \"" + ( ( Release ) rom ) . Date + "\"" : "" )
+ ( ( ( Release ) rom ) . Default ! = null
? "default " + ( ( Release ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ " )\n" ;
break ;
case ItemType . Rom :
state + = "\trom ( name \"" + rom . Name + "\""
+ ( ( ( Rom ) rom ) . Size ! = - 1 ? " size " + ( ( Rom ) rom ) . Size : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . CRC ) ? " crc " + ( ( Rom ) rom ) . CRC . ToLowerInvariant ( ) : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . MD5 ) ? " md5 " + ( ( Rom ) rom ) . MD5 . ToLowerInvariant ( ) : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . SHA1 ) ? " sha1 " + ( ( Rom ) rom ) . SHA1 . ToLowerInvariant ( ) : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . Date ) ? " date \"" + ( ( Rom ) rom ) . Date + "\"" : "" )
+ ( ( ( Rom ) rom ) . ItemStatus ! = ItemStatus . None ? " flags " + ( ( Rom ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) : "" )
+ " )\n" ;
break ;
case ItemType . Sample :
state + = "\tsample ( name\"" + rom . Name + "\""
+ " )\n" ;
break ;
}
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . CSV :
2016-10-24 16:14:22 -07:00
// CSV should only output Rom and Disk
if ( rom . Type ! = ItemType . Disk & & rom . Type ! = ItemType . Rom )
{
return true ;
}
2016-09-06 20:57:03 -07:00
2016-10-24 16:14:22 -07:00
pre = Prefix + ( Quotes ? "\"" : "" ) ;
post = ( Quotes ? "\"" : "" ) + Postfix ;
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
{
// Check for special strings in prefix and postfix
pre = pre
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%crc%" , ( ( Rom ) rom ) . CRC )
. Replace ( "%md5%" , ( ( Rom ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Rom ) rom ) . SHA1 )
. Replace ( "%size%" , ( ( Rom ) rom ) . Size . ToString ( ) ) ;
post = post
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%crc%" , ( ( Rom ) rom ) . CRC )
. Replace ( "%md5%" , ( ( Rom ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Rom ) rom ) . SHA1 )
. Replace ( "%size%" , ( ( Rom ) rom ) . Size . ToString ( ) ) ;
}
else if ( rom . Type = = ItemType . Disk )
{
// Check for special strings in prefix and postfix
pre = pre
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%md5%" , ( ( Disk ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Disk ) rom ) . SHA1 ) ;
post = post
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%md5%" , ( ( Disk ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Disk ) rom ) . SHA1 ) ;
}
if ( rom . Type = = ItemType . Rom )
{
string inline = "\"" + FileName + "\""
+ ",\"" + Name + "\""
+ ",\"" + Description + "\""
+ ",\"" + rom . Machine . Name + "\""
+ ",\"" + rom . Machine . Description + "\""
+ "," + "\"rom\""
+ ",\"" + rom . Name + "\""
+ "," + "\"\""
+ ",\"" + ( ( Rom ) rom ) . Size + "\""
+ ",\"" + ( ( Rom ) rom ) . CRC + "\""
+ ",\"" + ( ( Rom ) rom ) . MD5 + "\""
+ ",\"" + ( ( Rom ) rom ) . SHA1 + "\""
+ "," + ( ( ( Rom ) rom ) . ItemStatus ! = ItemStatus . None ? "\"" + ( ( Rom ) rom ) . ItemStatus . ToString ( ) + "\"" : "\"\"" ) ;
state + = pre + inline + post + "\n" ;
}
else if ( rom . Type = = ItemType . Disk )
{
string inline = "\"" + FileName + "\""
+ ",\"" + Name + "\""
+ ",\"" + Description + "\""
+ ",\"" + rom . Machine . Name + "\""
+ ",\"" + rom . Machine . Description + "\""
+ "," + "\"disk\""
+ "," + "\"\""
+ ",\"" + rom . Name + "\""
+ "," + "\"\""
+ "," + "\"\""
+ ",\"" + ( ( Disk ) rom ) . MD5 + "\""
+ ",\"" + ( ( Disk ) rom ) . SHA1 + "\""
+ "," + ( ( ( Disk ) rom ) . ItemStatus ! = ItemStatus . None ? "\"" + ( ( Disk ) rom ) . ItemStatus . ToString ( ) + "\"" : "\"\"" ) ;
state + = pre + inline + post + "\n" ;
}
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . DOSCenter :
2016-10-24 16:14:22 -07:00
switch ( rom . Type )
{
case ItemType . Archive :
case ItemType . BiosSet :
case ItemType . Disk :
case ItemType . Release :
case ItemType . Sample :
// We don't output these at all
break ;
case ItemType . Rom :
state + = "\tfile ( name " + ( ( Rom ) rom ) . Name
+ ( ( ( Rom ) rom ) . Size ! = - 1 ? " size " + ( ( Rom ) rom ) . Size : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . Date ) ? " date " + ( ( Rom ) rom ) . Date : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . CRC ) ? " crc " + ( ( Rom ) rom ) . CRC . ToLowerInvariant ( ) : "" )
+ " )\n" ;
break ;
}
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . Logiqx :
2016-10-24 16:14:22 -07:00
switch ( rom . Type )
{
case ItemType . Archive :
state + = "\t\t<archive name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ "/>\n" ;
break ;
case ItemType . BiosSet :
state + = "\t\t<biosset name\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( BiosSet ) rom ) . Description ) ? " description=\"" + HttpUtility . HtmlEncode ( ( ( BiosSet ) rom ) . Description ) + "\"" : "" )
+ ( ( ( BiosSet ) rom ) . Default ! = null
? ( ( BiosSet ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ "/>\n" ;
break ;
case ItemType . Disk :
state + = "\t\t<disk name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . MD5 ) ? " md5=\"" + ( ( Disk ) rom ) . MD5 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . SHA1 ) ? " sha1=\"" + ( ( Disk ) rom ) . SHA1 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ( ( Disk ) rom ) . ItemStatus ! = ItemStatus . None ? " status=\"" + ( ( Disk ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) + "\"" : "" )
+ "/>\n" ;
break ;
case ItemType . Release :
state + = "\t\t<release name\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Region ) ? " region=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Region ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Language ) ? " language=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Language ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Date ) ? " date=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Date ) + "\"" : "" )
+ ( ( ( Release ) rom ) . Default ! = null
? ( ( Release ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ "/>\n" ;
break ;
case ItemType . Rom :
state + = "\t\t<rom name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ( ( Rom ) rom ) . Size ! = - 1 ? " size=\"" + ( ( Rom ) rom ) . Size + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . CRC ) ? " crc=\"" + ( ( Rom ) rom ) . CRC . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . MD5 ) ? " md5=\"" + ( ( Rom ) rom ) . MD5 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . SHA1 ) ? " sha1=\"" + ( ( Rom ) rom ) . SHA1 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . Date ) ? " date=\"" + ( ( Rom ) rom ) . Date + "\"" : "" )
+ ( ( ( Rom ) rom ) . ItemStatus ! = ItemStatus . None ? " status=\"" + ( ( Rom ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) + "\"" : "" )
+ "/>\n" ;
break ;
case ItemType . Sample :
state + = "\t\t<file type=\"sample\" name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ "/>\n" ;
break ;
}
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . MissFile :
2016-10-24 16:14:22 -07:00
// Missfile should only output Rom and Disk
if ( rom . Type ! = ItemType . Disk & & rom . Type ! = ItemType . Rom )
{
return true ;
}
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
pre = Prefix + ( Quotes ? "\"" : "" ) ;
post = ( Quotes ? "\"" : "" ) + Postfix ;
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
// Check for special strings in prefix and postfix
pre = pre
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%crc%" , ( ( Rom ) rom ) . CRC )
. Replace ( "%md5%" , ( ( Rom ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Rom ) rom ) . SHA1 )
. Replace ( "%size%" , ( ( Rom ) rom ) . Size . ToString ( ) ) ;
post = post
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%crc%" , ( ( Rom ) rom ) . CRC )
. Replace ( "%md5%" , ( ( Rom ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Rom ) rom ) . SHA1 )
. Replace ( "%size%" , ( ( Rom ) rom ) . Size . ToString ( ) ) ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
else if ( rom . Type = = ItemType . Disk )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
// Check for special strings in prefix and postfix
pre = pre
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%md5%" , ( ( Disk ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Disk ) rom ) . SHA1 ) ;
post = post
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%md5%" , ( ( Disk ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Disk ) rom ) . SHA1 ) ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
// If we're in Romba mode, the state is consistent
if ( Romba )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
{
// We can only write out if there's a SHA-1
if ( ( ( Rom ) rom ) . SHA1 ! = "" )
{
name = ( ( Rom ) rom ) . SHA1 . Substring ( 0 , 2 )
+ "/" + ( ( Rom ) rom ) . SHA1 . Substring ( 2 , 2 )
+ "/" + ( ( Rom ) rom ) . SHA1 . Substring ( 4 , 2 )
+ "/" + ( ( Rom ) rom ) . SHA1 . Substring ( 6 , 2 )
+ "/" + ( ( Rom ) rom ) . SHA1 + ".gz" ;
state + = pre + name + post + "\n" ;
}
}
else if ( rom . Type = = ItemType . Disk )
{
// We can only write out if there's a SHA-1
if ( ( ( Disk ) rom ) . SHA1 ! = "" )
{
name = ( ( Disk ) rom ) . SHA1 . Substring ( 0 , 2 )
+ "/" + ( ( Disk ) rom ) . SHA1 . Substring ( 2 , 2 )
+ "/" + ( ( Disk ) rom ) . SHA1 . Substring ( 4 , 2 )
+ "/" + ( ( Disk ) rom ) . SHA1 . Substring ( 6 , 2 )
+ "/" + ( ( Disk ) rom ) . SHA1 + ".gz" ;
state + = pre + name + post + "\n" ;
}
}
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
// Otherwise, use any flags
name = ( UseGame ? rom . Machine . Name : rom . Name ) ;
if ( RepExt ! = "" | | RemExt )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
if ( RemExt )
{
RepExt = "" ;
}
string dir = Path . GetDirectoryName ( name ) ;
dir = ( dir . StartsWith ( Path . DirectorySeparatorChar . ToString ( ) ) ? dir . Remove ( 0 , 1 ) : dir ) ;
name = Path . Combine ( dir , Path . GetFileNameWithoutExtension ( name ) + RepExt ) ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
if ( AddExt ! = "" )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
name + = AddExt ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
if ( ! UseGame & & GameName )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
name = Path . Combine ( rom . Machine . Name , name ) ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
if ( UseGame & & rom . Machine . Name ! = lastgame )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
state + = pre + name + post + "\n" ;
lastgame = rom . Machine . Name ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
else if ( ! UseGame )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
state + = pre + name + post + "\n" ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . OfflineList :
2016-10-24 16:14:22 -07:00
state + = "\t\t<game>\n"
+ "\t\t\t<imageNumber>1</imageNumber>\n"
+ "\t\t\t<releaseNumber>1</releaseNumber>\n"
+ "\t\t\t<title>" + HttpUtility . HtmlEncode ( rom . Name ) + "</title>\n"
+ "\t\t\t<saveType>None</saveType>\n" ;
2016-09-06 20:57:03 -07:00
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
{
state + = "\t\t\t<romSize>" + ( ( Rom ) rom ) . Size + "</romSize>\n" ;
}
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
state + = "\t\t\t<publisher>None</publisher>\n"
+ "\t\t\t<location>0</location>\n"
+ "\t\t\t<sourceRom>None</sourceRom>\n"
+ "\t\t\t<language>0</language>\n" ;
2016-09-19 18:04:24 -07:00
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Disk )
{
state + = "\t\t\t<files>\n"
+ ( ( ( Disk ) rom ) . MD5 ! = null
? "\t\t\t\t<romMD5 extension=\".chd\">" + ( ( Disk ) rom ) . MD5 . ToUpperInvariant ( ) + "</romMD5>\n"
: "\t\t\t\t<romSHA1 extension=\".chd\">" + ( ( Disk ) rom ) . SHA1 . ToUpperInvariant ( ) + "</romSHA1>\n" )
+ "\t\t\t</files>\n" ;
}
else if ( rom . Type = = ItemType . Rom )
{
string tempext = Path . GetExtension ( ( ( Rom ) rom ) . Name ) ;
if ( ! tempext . StartsWith ( "." ) )
{
tempext = "." + tempext ;
}
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
state + = "\t\t\t<files>\n"
+ ( ( ( Rom ) rom ) . CRC ! = null
? "\t\t\t\t<romCRC extension=\"" + tempext + "\">" + ( ( Rom ) rom ) . CRC . ToUpperInvariant ( ) + "</romMD5>\n"
: ( ( Rom ) rom ) . MD5 ! = null
? "\t\t\t\t<romMD5 extension=\"" + tempext + "\">" + ( ( Rom ) rom ) . MD5 . ToUpperInvariant ( ) + "</romMD5>\n"
: "\t\t\t\t<romSHA1 extension=\"" + tempext + "\">" + ( ( Rom ) rom ) . SHA1 . ToUpperInvariant ( ) + "</romSHA1>\n" )
+ "\t\t\t</files>\n" ;
}
2016-09-19 18:04:24 -07:00
2016-10-24 16:14:22 -07:00
state + = "\t\t\t<im1CRC>00000000</im1CRC>\n"
+ "\t\t\t<im2CRC>00000000</im2CRC>\n"
+ "\t\t\t<comment></comment>\n"
+ "\t\t\t<duplicateID>0</duplicateID>\n"
+ "\t\t</game>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RedumpMD5 :
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
{
state + = ( ( Rom ) rom ) . MD5 + " *" + ( GameName ? rom . Machine . Name + Path . DirectorySeparatorChar : "" ) + rom . Name + "\n" ;
}
else if ( rom . Type = = ItemType . Disk )
{
state + = ( ( Disk ) rom ) . MD5 + " *" + ( GameName ? rom . Machine . Name + Path . DirectorySeparatorChar : "" ) + rom . Name + "\n" ;
}
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RedumpSFV :
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
{
state + = ( GameName ? rom . Machine . Name + Path . DirectorySeparatorChar : "" ) + rom . Name + " " + ( ( Rom ) rom ) . CRC + "\n" ;
}
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RedumpSHA1 :
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
{
state + = ( ( Rom ) rom ) . SHA1 + " *" + ( GameName ? rom . Machine . Name + Path . DirectorySeparatorChar : "" ) + rom . Name + "\n" ;
}
else if ( rom . Type = = ItemType . Disk )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
state + = ( ( Disk ) rom ) . SHA1 + " *" + ( GameName ? rom . Machine . Name + Path . DirectorySeparatorChar : "" ) + rom . Name + "\n" ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . RomCenter :
2016-10-24 16:14:22 -07:00
if ( rom . Type = = ItemType . Rom )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
state + = "¬" + ( String . IsNullOrEmpty ( rom . Machine . CloneOf ) ? "" : HttpUtility . HtmlEncode ( rom . Machine . CloneOf ) ) +
"¬" + ( String . IsNullOrEmpty ( rom . Machine . CloneOf ) ? "" : HttpUtility . HtmlEncode ( rom . Machine . CloneOf ) ) +
"¬" + HttpUtility . HtmlEncode ( rom . Machine . Name ) +
"¬" + HttpUtility . HtmlEncode ( ( String . IsNullOrEmpty ( rom . Machine . Description ) ? rom . Machine . Name : rom . Machine . Description ) ) +
"¬" + HttpUtility . HtmlEncode ( rom . Name ) +
"¬" + ( ( Rom ) rom ) . CRC . ToLowerInvariant ( ) +
"¬" + ( ( ( Rom ) rom ) . Size ! = - 1 ? ( ( Rom ) rom ) . Size . ToString ( ) : "" ) + "¬¬¬\n" ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
else if ( rom . Type = = ItemType . Disk )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
state + = "¬" + ( String . IsNullOrEmpty ( rom . Machine . CloneOf ) ? "" : HttpUtility . HtmlEncode ( rom . Machine . CloneOf ) ) +
"¬" + ( String . IsNullOrEmpty ( rom . Machine . CloneOf ) ? "" : HttpUtility . HtmlEncode ( rom . Machine . CloneOf ) ) +
"¬" + HttpUtility . HtmlEncode ( rom . Machine . Name ) +
"¬" + HttpUtility . HtmlEncode ( ( String . IsNullOrEmpty ( rom . Machine . Description ) ? rom . Machine . Name : rom . Machine . Description ) ) +
"¬" + HttpUtility . HtmlEncode ( rom . Name ) +
"¬¬¬¬¬\n" ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SabreDat :
2016-10-24 16:14:22 -07:00
string prefix = "" ;
for ( int i = 0 ; i < depth ; i + + )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
prefix + = "\t" ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
state + = prefix ;
switch ( rom . Type )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
case ItemType . Archive :
state + = "<file type=\"archive\" name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ "/>\n" ;
break ;
case ItemType . BiosSet :
state + = "<file type=\"biosset\" name\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( BiosSet ) rom ) . Description ) ? " description=\"" + HttpUtility . HtmlEncode ( ( ( BiosSet ) rom ) . Description ) + "\"" : "" )
+ ( ( ( BiosSet ) rom ) . Default ! = null
? ( ( BiosSet ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ "/>\n" ;
break ;
case ItemType . Disk :
state + = "<file type=\"disk\" name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . MD5 ) ? " md5=\"" + ( ( Disk ) rom ) . MD5 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . SHA1 ) ? " sha1=\"" + ( ( Disk ) rom ) . SHA1 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ( ( Disk ) rom ) . ItemStatus ! = ItemStatus . None ? prefix + "/>\n" + prefix + "\t<flags>\n" +
prefix + "\t\t<flag name=\"status\" value=\"" + ( ( Disk ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) + "\"/>\n" +
prefix + "\t</flags>\n" +
prefix + "</file>\n" : "/>\n" ) ;
break ;
case ItemType . Release :
state + = "<file type=\"release\" name\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Region ) ? " region=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Region ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Language ) ? " language=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Language ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Date ) ? " date=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Date ) + "\"" : "" )
+ ( ( ( Release ) rom ) . Default ! = null
? ( ( Release ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ "/>\n" ;
break ;
case ItemType . Rom :
state + = "<file type=\"rom\" name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ( ( Rom ) rom ) . Size ! = - 1 ? " size=\"" + ( ( Rom ) rom ) . Size + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . CRC ) ? " crc=\"" + ( ( Rom ) rom ) . CRC . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . MD5 ) ? " md5=\"" + ( ( Rom ) rom ) . MD5 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . SHA1 ) ? " sha1=\"" + ( ( Rom ) rom ) . SHA1 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . Date ) ? " date=\"" + ( ( Rom ) rom ) . Date + "\"" : "" )
+ ( ( ( Rom ) rom ) . ItemStatus ! = ItemStatus . None ? prefix + "/>\n" + prefix + "\t<flags>\n" +
prefix + "\t\t<flag name=\"status\" value=\"" + ( ( Rom ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) + "\"/>\n" +
prefix + "\t</flags>\n" +
prefix + "</file>\n" : "/>\n" ) ;
break ;
case ItemType . Sample :
state + = "<file type=\"sample\" name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ "/>\n" ;
break ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SoftwareList :
2016-10-24 16:14:22 -07:00
state + = "\t\t<part name=\"" + rom . PartName + "\" interface=\"" + rom . PartInterface + "\">\n" ;
foreach ( Tuple < string , string > kvp in rom . Features )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
state + = "\t\t\t<feature name=\"" + HttpUtility . HtmlEncode ( kvp . Item1 ) + "\" value=\"" + HttpUtility . HtmlEncode ( kvp . Item2 ) + "\"/>\n" ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
switch ( rom . Type )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
case ItemType . Archive :
state + = "\t\t\t<dataarea name=\"" + ( String . IsNullOrEmpty ( rom . AreaName ) ? "archive" : rom . AreaName ) + "\""
+ ( rom . AreaSize ! = null ? " size=\"" + rom . AreaSize + "\"" : "" ) + ">\n"
+ "\t\t\t\t<archive name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ "/>\n"
+ "\t\t\t</dataarea>\n" ;
break ;
case ItemType . BiosSet :
state + = "\t\t\t<dataarea name=\"" + ( String . IsNullOrEmpty ( rom . AreaName ) ? "biosset" : rom . AreaName ) + "\""
+ ( rom . AreaSize ! = null ? " size=\"" + rom . AreaSize + "\"" : "" ) + ">\n"
+ "\t\t\t\t<biosset name\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( BiosSet ) rom ) . Description ) ? " description=\"" + HttpUtility . HtmlEncode ( ( ( BiosSet ) rom ) . Description ) + "\"" : "" )
+ ( ( ( BiosSet ) rom ) . Default ! = null
? ( ( BiosSet ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ "/>\n"
+ "\t\t\t</dataarea>\n" ;
break ;
case ItemType . Disk :
state + = "\t\t\t<diskarea name=\"" + ( String . IsNullOrEmpty ( rom . AreaName ) ? "cdrom" : rom . AreaName ) + "\""
+ ( rom . AreaSize ! = null ? " size=\"" + rom . AreaSize + "\"" : "" ) + ">\n"
+ "\t\t\t\t<disk name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . MD5 ) ? " md5=\"" + ( ( Disk ) rom ) . MD5 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Disk ) rom ) . SHA1 ) ? " sha1=\"" + ( ( Disk ) rom ) . SHA1 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ( ( Disk ) rom ) . ItemStatus ! = ItemStatus . None ? " status=\"" + ( ( Disk ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) + "\"" : "" )
+ "/>\n"
+ "\t\t\t</diskarea>\n" ;
break ;
case ItemType . Release :
state + = "\t\t\t<dataarea name=\"" + ( String . IsNullOrEmpty ( rom . AreaName ) ? "release" : rom . AreaName ) + "\""
+ ( rom . AreaSize ! = null ? " size=\"" + rom . AreaSize + "\"" : "" ) + ">\n"
+ "\t\t\t\t<release name\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Region ) ? " region=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Region ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Language ) ? " language=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Language ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Release ) rom ) . Date ) ? " date=\"" + HttpUtility . HtmlEncode ( ( ( Release ) rom ) . Date ) + "\"" : "" )
+ ( ( ( Release ) rom ) . Default ! = null
? ( ( Release ) rom ) . Default . ToString ( ) . ToLowerInvariant ( )
: "" )
+ "/>\n"
+ "\t\t\t</dataarea>\n" ;
break ;
case ItemType . Rom :
state + = "\t\t\t<dataarea name=\"" + ( String . IsNullOrEmpty ( rom . AreaName ) ? "rom" : rom . AreaName ) + "\""
+ ( rom . AreaSize ! = null ? " size=\"" + rom . AreaSize + "\"" : "" ) + ">\n"
+ "\t\t\t\t<rom name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ ( ( ( Rom ) rom ) . Size ! = - 1 ? " size=\"" + ( ( Rom ) rom ) . Size + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . CRC ) ? " crc=\"" + ( ( Rom ) rom ) . CRC . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . MD5 ) ? " md5=\"" + ( ( Rom ) rom ) . MD5 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . SHA1 ) ? " sha1=\"" + ( ( Rom ) rom ) . SHA1 . ToLowerInvariant ( ) + "\"" : "" )
+ ( ! String . IsNullOrEmpty ( ( ( Rom ) rom ) . Date ) ? " date=\"" + ( ( Rom ) rom ) . Date + "\"" : "" )
+ ( ( ( Rom ) rom ) . ItemStatus ! = ItemStatus . None ? " status=\"" + ( ( Rom ) rom ) . ItemStatus . ToString ( ) . ToLowerInvariant ( ) + "\"" : "" )
+ "/>\n"
+ "\t\t\t</dataarea>\n" ;
break ;
case ItemType . Sample :
state + = "\t\t\t<dataarea name=\"" + ( String . IsNullOrEmpty ( rom . AreaName ) ? "sample" : rom . AreaName ) + "\""
+ ( rom . AreaSize ! = null ? " size=\"" + rom . AreaSize + "\"" : "" ) + ">\n"
+ "\t\t\t\t<sample type=\"sample\" name=\"" + HttpUtility . HtmlEncode ( rom . Name ) + "\""
+ "/>\n"
+ "\t\t\t</dataarea>\n" ;
break ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
state + = "\t\t</part>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . TSV :
2016-10-24 16:14:22 -07:00
// TSV should only output Rom and Disk
if ( rom . Type ! = ItemType . Disk & & rom . Type ! = ItemType . Rom )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
return true ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
pre = Prefix + ( Quotes ? "\"" : "" ) ;
post = ( Quotes ? "\"" : "" ) + Postfix ;
if ( rom . Type = = ItemType . Rom )
2016-09-19 18:04:24 -07:00
{
2016-10-24 16:14:22 -07:00
// Check for special strings in prefix and postfix
pre = pre
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%crc%" , ( ( Rom ) rom ) . CRC )
. Replace ( "%md5%" , ( ( Rom ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Rom ) rom ) . SHA1 )
. Replace ( "%size%" , ( ( Rom ) rom ) . Size . ToString ( ) ) ;
post = post
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%crc%" , ( ( Rom ) rom ) . CRC )
. Replace ( "%md5%" , ( ( Rom ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Rom ) rom ) . SHA1 )
. Replace ( "%size%" , ( ( Rom ) rom ) . Size . ToString ( ) ) ;
2016-09-19 18:04:24 -07:00
}
2016-10-24 16:14:22 -07:00
else if ( rom . Type = = ItemType . Disk )
2016-09-19 18:04:24 -07:00
{
2016-10-24 16:14:22 -07:00
// Check for special strings in prefix and postfix
pre = pre
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%md5%" , ( ( Disk ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Disk ) rom ) . SHA1 ) ;
post = post
. Replace ( "%game%" , rom . Machine . Name )
. Replace ( "%name%" , rom . Name )
. Replace ( "%md5%" , ( ( Disk ) rom ) . MD5 )
. Replace ( "%sha1%" , ( ( Disk ) rom ) . SHA1 ) ;
}
if ( rom . Type = = ItemType . Rom )
{
string inline = "\"" + FileName + "\""
+ "\t\"" + Name + "\""
+ "\t\"" + Description + "\""
+ "\t\"" + rom . Machine . Name + "\""
+ "\t\"" + rom . Machine . Description + "\""
+ "\t" + "\"rom\""
+ "\t\"" + rom . Name + "\""
+ "\t" + "\"\""
+ "\t\"" + ( ( Rom ) rom ) . Size + "\""
+ "\t\"" + ( ( Rom ) rom ) . CRC + "\""
+ "\t\"" + ( ( Rom ) rom ) . MD5 + "\""
+ "\t\"" + ( ( Rom ) rom ) . SHA1 + "\""
+ "\t" + ( ( ( Rom ) rom ) . ItemStatus ! = ItemStatus . None ? "\"" + ( ( Rom ) rom ) . ItemStatus . ToString ( ) + "\"" : "\"\"" ) ;
state + = pre + inline + post + "\n" ;
}
else if ( rom . Type = = ItemType . Disk )
{
string inline = "\"" + FileName + "\""
+ "\t\"" + Name + "\""
+ "\t\"" + Description + "\""
+ "\t\"" + rom . Machine . Name + "\""
+ "\t\"" + rom . Machine . Description + "\""
+ "\t" + "\"disk\""
+ "\t" + "\"\""
+ "\t\"" + rom . Name + "\""
+ "\t" + "\"\""
+ "\t" + "\"\""
+ "\t\"" + ( ( Disk ) rom ) . MD5 + "\""
+ "\t\"" + ( ( Disk ) rom ) . SHA1 + "\""
+ "\t" + ( ( ( Disk ) rom ) . ItemStatus ! = ItemStatus . None ? "\"" + ( ( Disk ) rom ) . ItemStatus . ToString ( ) + "\"" : "\"\"" ) ;
state + = pre + inline + post + "\n" ;
2016-09-19 18:04:24 -07:00
}
2016-10-24 16:14:22 -07:00
break ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
sw . Write ( state ) ;
sw . Flush ( ) ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
catch ( Exception ex )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
logger . Error ( ex . ToString ( ) ) ;
return false ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
return true ;
2016-09-06 17:20:16 -07:00
}
/// <summary>
2016-10-24 16:14:22 -07:00
/// Write out DAT footer using the supplied StreamWriter
2016-09-06 17:20:16 -07:00
/// </summary>
2016-10-24 16:14:22 -07:00
/// <param name="sw">StreamWriter to output to</param>
2016-10-25 15:02:02 -07:00
/// <param name="datFormat">Output format to write to</param>
2016-10-24 16:14:22 -07:00
/// <param name="depth">Current depth to output file at (SabreDAT only)</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the data was written, false on error</returns>
2016-10-25 15:02:02 -07:00
private bool WriteFooter ( StreamWriter sw , DatFormat datFormat , int depth , Logger logger )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
try
2016-09-13 11:02:02 -07:00
{
2016-10-24 16:14:22 -07:00
string footer = "" ;
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
// If we have roms, output the full footer
if ( Files ! = null & & Files . Count > 0 )
2016-09-06 17:20:16 -07:00
{
2016-10-25 15:02:02 -07:00
switch ( datFormat )
2016-09-06 17:20:16 -07:00
{
2016-10-25 15:02:02 -07:00
case DatFormat . ClrMamePro :
case DatFormat . DOSCenter :
2016-10-24 16:14:22 -07:00
footer = ")\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . Logiqx :
2016-10-24 16:14:22 -07:00
footer = "\t</machine>\n</datafile>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . OfflineList :
2016-10-24 16:14:22 -07:00
footer = "\t\t</game>"
+ "\t</games>\n"
+ "\t<gui>\n"
+ "\t\t<images width=\"487\" height=\"162\">\n"
+ "\t\t\t<image x=\"0\" y=\"0\" width=\"240\" height=\"160\"/>\n"
+ "\t\t\t<image x=\"245\" y=\"0\" width=\"240\" height=\"160\"/>\n"
+ "\t\t</images>\n"
+ "\t</gui>\n"
+ "</dat>" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SabreDat :
2016-10-24 16:14:22 -07:00
for ( int i = depth - 1 ; i > = 2 ; i - - )
{
// Print out the number of tabs and the end folder
for ( int j = 0 ; j < i ; j + + )
{
footer + = "\t" ;
}
footer + = "</directory>\n" ;
}
footer + = "\t</data>\n</datafile>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SoftwareList :
2016-10-24 16:14:22 -07:00
footer = "\t</software>\n\n</softwarelist>\n" ;
break ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
}
2016-09-13 11:02:02 -07:00
2016-10-24 16:14:22 -07:00
// Otherwise, output the abbreviated form
else
{
2016-10-25 15:02:02 -07:00
switch ( datFormat )
2016-09-13 11:02:02 -07:00
{
2016-10-25 15:02:02 -07:00
case DatFormat . Logiqx :
case DatFormat . SabreDat :
2016-10-24 16:14:22 -07:00
footer = "</datafile>\n" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . OfflineList :
2016-10-24 16:14:22 -07:00
footer = "\t</games>\n"
+ "\t<gui>\n"
+ "\t\t<images width=\"487\" height=\"162\">\n"
+ "\t\t\t<image x=\"0\" y=\"0\" width=\"240\" height=\"160\"/>\n"
+ "\t\t\t<image x=\"245\" y=\"0\" width=\"240\" height=\"160\"/>\n"
+ "\t\t</images>\n"
+ "\t</gui>\n"
+ "</dat>" ;
break ;
2016-10-25 15:02:02 -07:00
case DatFormat . SoftwareList :
2016-10-24 16:14:22 -07:00
footer = "</softwarelist>\n" ;
break ;
2016-09-13 11:02:02 -07:00
}
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
// Write the footer out
sw . Write ( footer ) ;
sw . Flush ( ) ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
catch ( Exception ex )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
logger . Error ( ex . ToString ( ) ) ;
return false ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
return true ;
}
#endregion
#endregion // Instance Methods
#region Static Methods
#region Bucketing
/// <summary>
/// Take an arbitrarily ordered List and return a Dictionary sorted by Game
/// </summary>
/// <param name="list">Input unsorted list</param>
/// <param name="mergeroms">True if roms should be deduped, 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>
/// <param name="logger">Logger object for file and console output</param>
/// <param name="output">True if the number of hashes counted is to be output (default), false otherwise</param>
/// <returns>SortedDictionary bucketed by game name</returns>
public static SortedDictionary < string , List < DatItem > > BucketListByGame ( List < DatItem > list , bool mergeroms , bool norename , Logger logger , bool output = true )
{
logger . User ( "Organizing " + ( mergeroms ? "and merging " : "" ) + "roms for output" ) ;
SortedDictionary < string , List < DatItem > > sortable = new SortedDictionary < string , List < DatItem > > ( ) ;
long count = 0 ;
// If we have a null dict or an empty one, output a new dictionary
if ( list = = null | | list . Count = = 0 )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
return sortable ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
// If we're merging the roms, do so
if ( mergeroms )
2016-09-06 17:20:16 -07:00
{
2016-10-24 16:14:22 -07:00
list = DatItem . Merge ( list , logger ) ;
2016-09-06 17:20:16 -07:00
}
2016-10-24 16:14:22 -07:00
// Now add each of the roms to their respective games
foreach ( DatItem rom in list )
2016-09-13 11:02:02 -07:00
{
2016-10-25 10:38:26 -07:00
if ( rom = = null )
{
continue ;
}
2016-10-24 16:14:22 -07:00
count + + ;
string newkey = ( norename ? ""
: rom . SystemID . ToString ( ) . PadLeft ( 10 , '0' )
+ "-"
+ rom . SourceID . ToString ( ) . PadLeft ( 10 , '0' ) + "-" )
2016-10-25 10:38:26 -07:00
+ ( rom . Machine = = null | | String . IsNullOrEmpty ( rom . Machine . Name )
2016-10-24 16:14:22 -07:00
? "Default"
: rom . Machine . Name . ToLowerInvariant ( ) ) ;
newkey = HttpUtility . HtmlEncode ( newkey ) ;
if ( sortable . ContainsKey ( newkey ) )
{
sortable [ newkey ] . Add ( rom ) ;
}
else
{
List < DatItem > temp = new List < DatItem > ( ) ;
temp . Add ( rom ) ;
sortable . Add ( newkey , temp ) ;
}
2016-09-13 11:02:02 -07:00
}
2016-09-06 17:20:16 -07:00
2016-10-24 16:14:22 -07:00
return sortable ;
2016-09-06 17:20:16 -07:00
}
#endregion
2016-09-22 17:52:58 -07:00
#region Statistics
/// <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>
2016-09-26 14:38:05 -07:00
/// <param name="reportName">Name of the output file</param>
2016-09-22 17:52:58 -07:00
/// <param name="single">True if single DAT stats are output, false otherwise</param>
2016-09-26 16:42:06 -07:00
/// <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>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat" > Set the statistics output format to use</param>
2016-09-22 17:52:58 -07:00
/// <param name="logger">Logger object for file and console output</param>
2016-09-26 16:42:06 -07:00
public static void OutputStats ( List < string > inputs , string reportName , bool single , bool baddumpCol ,
2016-10-25 15:02:02 -07:00
bool nodumpCol , StatDatFormat statDatFormat , Logger logger )
2016-09-22 17:52:58 -07:00
{
2016-10-25 15:02:02 -07:00
reportName + = OutputStatsGetExtension ( statDatFormat ) ;
2016-09-26 14:51:30 -07:00
StreamWriter sw = new StreamWriter ( File . Open ( reportName , FileMode . Create , FileAccess . Write ) ) ;
2016-09-26 14:38:05 -07:00
2016-09-22 17:52:58 -07:00
// Make sure we have all files
2016-09-26 17:10:17 -07:00
List < Tuple < string , string > > newinputs = new List < Tuple < string , string > > ( ) ; // item, basepath
2016-09-22 17:52:58 -07:00
foreach ( string input in inputs )
{
if ( File . Exists ( input ) )
{
2016-09-26 17:10:17 -07:00
newinputs . Add ( Tuple . Create ( Path . GetFullPath ( input ) , Path . GetDirectoryName ( Path . GetFullPath ( input ) ) ) ) ;
2016-09-22 17:52:58 -07:00
}
if ( Directory . Exists ( input ) )
{
foreach ( string file in Directory . GetFiles ( input , "*" , SearchOption . AllDirectories ) )
{
2016-09-26 17:10:17 -07:00
newinputs . Add ( Tuple . Create ( Path . GetFullPath ( file ) , Path . GetFullPath ( input ) ) ) ;
2016-09-22 17:52:58 -07:00
}
}
}
2016-09-26 15:26:32 -07:00
newinputs = newinputs
2016-09-26 17:10:17 -07:00
. OrderBy ( i = > Path . GetDirectoryName ( i . Item1 ) )
. ThenBy ( i = > Path . GetFileName ( i . Item1 ) )
2016-09-26 15:40:39 -07:00
. ToList ( ) ;
2016-09-22 17:52:58 -07:00
2016-09-26 14:45:55 -07:00
// Write the header, if any
2016-10-25 15:02:02 -07:00
OutputStatsWriteHeader ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-26 14:45:55 -07:00
2016-09-22 17:52:58 -07:00
// Init all total variables
long totalSize = 0 ;
long totalGame = 0 ;
long totalRom = 0 ;
long totalDisk = 0 ;
long totalCRC = 0 ;
long totalMD5 = 0 ;
long totalSHA1 = 0 ;
2016-09-26 16:42:06 -07:00
long totalBaddump = 0 ;
2016-09-22 17:52:58 -07:00
long totalNodump = 0 ;
2016-09-26 15:26:32 -07:00
// Init directory-level variables
string lastdir = null ;
2016-09-26 17:10:17 -07:00
string basepath = null ;
2016-09-26 15:26:32 -07:00
long dirSize = 0 ;
long dirGame = 0 ;
long dirRom = 0 ;
long dirDisk = 0 ;
long dirCRC = 0 ;
long dirMD5 = 0 ;
long dirSHA1 = 0 ;
2016-09-26 16:42:06 -07:00
long dirBaddump = 0 ;
2016-09-26 15:26:32 -07:00
long dirNodump = 0 ;
// Now process each of the input files
2016-09-26 17:10:17 -07:00
foreach ( Tuple < string , string > filename in newinputs )
2016-09-22 17:52:58 -07:00
{
2016-09-26 15:26:32 -07:00
// Get the directory for the current file
2016-09-26 17:10:17 -07:00
string thisdir = Path . GetDirectoryName ( filename . Item1 ) ;
basepath = Path . GetDirectoryName ( filename . Item2 ) ;
2016-09-26 15:26:32 -07:00
// 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
2016-10-25 15:02:02 -07:00
OutputStatsWriteMidSeparator ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-26 15:26:32 -07:00
DatFile lastdirdat = new DatFile
{
2016-09-26 17:41:39 -07:00
FileName = "DIR: " + HttpUtility . HtmlEncode ( lastdir . Remove ( 0 , basepath . Length + ( basepath . Length = = 0 ? 0 : 1 ) ) ) ,
2016-09-26 15:26:32 -07:00
TotalSize = dirSize ,
RomCount = dirRom ,
DiskCount = dirDisk ,
CRCCount = dirCRC ,
MD5Count = dirMD5 ,
SHA1Count = dirSHA1 ,
2016-09-26 16:42:06 -07:00
BaddumpCount = dirBaddump ,
2016-09-26 15:26:32 -07:00
NodumpCount = dirNodump ,
} ;
2016-10-25 15:02:02 -07:00
lastdirdat . OutputStats ( sw , statDatFormat , logger , game : dirGame , baddumpCol : baddumpCol , nodumpCol : nodumpCol ) ;
2016-09-26 15:26:32 -07:00
2016-09-26 15:40:39 -07:00
// Write the mid-footer, if any
2016-10-25 15:02:02 -07:00
OutputStatsWriteMidFooter ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-26 15:26:32 -07:00
// Write the header, if any
2016-10-25 15:02:02 -07:00
OutputStatsWriteMidHeader ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-26 15:26:32 -07:00
// Reset the directory stats
dirSize = 0 ;
dirGame = 0 ;
dirRom = 0 ;
dirDisk = 0 ;
dirCRC = 0 ;
dirMD5 = 0 ;
dirSHA1 = 0 ;
2016-09-26 16:42:06 -07:00
dirBaddump = 0 ;
2016-09-26 15:26:32 -07:00
dirNodump = 0 ;
}
2016-10-24 13:55:31 -07:00
logger . Verbose ( "Beginning stat collection for '" + filename . Item1 + "'" , false ) ;
2016-09-22 17:52:58 -07:00
List < string > games = new List < string > ( ) ;
DatFile datdata = new DatFile ( ) ;
2016-09-26 17:10:17 -07:00
datdata . Parse ( filename . Item1 , 0 , 0 , logger ) ;
2016-09-22 18:15:02 -07:00
datdata . BucketByGame ( false , true , logger , false ) ;
2016-09-22 17:52:58 -07:00
// Output single DAT stats (if asked)
2016-10-24 13:55:31 -07:00
logger . User ( "Adding stats for file '" + filename . Item1 + "'\n" , false ) ;
2016-09-22 17:52:58 -07:00
if ( single )
{
2016-10-25 15:02:02 -07:00
datdata . OutputStats ( sw , statDatFormat , logger , baddumpCol : baddumpCol , nodumpCol : nodumpCol ) ;
2016-09-26 14:38:05 -07:00
}
2016-09-23 16:32:24 -07:00
2016-09-26 15:26:32 -07:00
// Add single DAT stats to dir
dirSize + = datdata . TotalSize ;
dirGame + = datdata . Files . Count ;
dirRom + = datdata . RomCount ;
dirDisk + = datdata . DiskCount ;
dirCRC + = datdata . CRCCount ;
dirMD5 + = datdata . MD5Count ;
dirSHA1 + = datdata . SHA1Count ;
2016-09-26 16:42:06 -07:00
dirBaddump + = datdata . BaddumpCount ;
2016-09-26 15:26:32 -07:00
dirNodump + = datdata . NodumpCount ;
2016-09-23 16:32:24 -07:00
// Add single DAT stats to totals
totalSize + = datdata . TotalSize ;
totalGame + = datdata . Files . Count ;
totalRom + = datdata . RomCount ;
totalDisk + = datdata . DiskCount ;
totalCRC + = datdata . CRCCount ;
totalMD5 + = datdata . MD5Count ;
totalSHA1 + = datdata . SHA1Count ;
2016-09-26 16:42:06 -07:00
totalBaddump + = datdata . BaddumpCount ;
2016-09-23 16:32:24 -07:00
totalNodump + = datdata . NodumpCount ;
2016-09-26 15:26:32 -07:00
// Make sure to assign the new directory
lastdir = thisdir ;
2016-09-23 16:32:24 -07:00
}
2016-09-26 15:26:32 -07:00
// Output the directory stats one last time
2016-10-25 15:02:02 -07:00
OutputStatsWriteMidSeparator ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-23 16:32:24 -07:00
2016-09-26 16:42:06 -07:00
if ( single )
2016-09-26 15:26:32 -07:00
{
2016-09-26 16:42:06 -07:00
DatFile dirdat = new DatFile
{
2016-09-26 17:41:39 -07:00
FileName = "DIR: " + HttpUtility . HtmlEncode ( lastdir . Remove ( 0 , basepath . Length + ( basepath . Length = = 0 ? 0 : 1 ) ) ) ,
2016-09-26 16:42:06 -07:00
TotalSize = dirSize ,
RomCount = dirRom ,
DiskCount = dirDisk ,
CRCCount = dirCRC ,
MD5Count = dirMD5 ,
SHA1Count = dirSHA1 ,
BaddumpCount = dirBaddump ,
NodumpCount = dirNodump ,
} ;
2016-10-25 15:02:02 -07:00
dirdat . OutputStats ( sw , statDatFormat , logger , game : dirGame , baddumpCol : baddumpCol , nodumpCol : nodumpCol ) ;
2016-09-26 16:42:06 -07:00
}
2016-09-26 15:26:32 -07:00
2016-09-26 15:40:39 -07:00
// Write the mid-footer, if any
2016-10-25 15:02:02 -07:00
OutputStatsWriteMidFooter ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-26 15:40:39 -07:00
// Write the header, if any
2016-10-25 15:02:02 -07:00
OutputStatsWriteMidHeader ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-26 15:40:39 -07:00
// Reset the directory stats
dirSize = 0 ;
dirGame = 0 ;
dirRom = 0 ;
dirDisk = 0 ;
dirCRC = 0 ;
dirMD5 = 0 ;
dirSHA1 = 0 ;
dirNodump = 0 ;
// Output total DAT stats
DatFile totaldata = new DatFile
{
2016-09-27 09:32:59 -07:00
FileName = "DIR: All DATs" ,
2016-09-26 15:40:39 -07:00
TotalSize = totalSize ,
RomCount = totalRom ,
DiskCount = totalDisk ,
CRCCount = totalCRC ,
MD5Count = totalMD5 ,
SHA1Count = totalSHA1 ,
2016-09-26 16:42:06 -07:00
BaddumpCount = totalBaddump ,
2016-09-26 15:40:39 -07:00
NodumpCount = totalNodump ,
} ;
2016-10-25 15:02:02 -07:00
totaldata . OutputStats ( sw , statDatFormat , logger , game : totalGame , baddumpCol : baddumpCol , nodumpCol : nodumpCol ) ;
2016-09-26 15:40:39 -07:00
// Output footer if needed
2016-10-25 15:02:02 -07:00
OutputStatsWriteFooter ( sw , statDatFormat ) ;
2016-09-26 15:40:39 -07:00
sw . Flush ( ) ;
sw . Dispose ( ) ;
logger . User ( @ "
Please check the log folder if the stats scrolled offscreen ", false);
}
/// <summary>
/// Get the proper extension for the stat output format
/// </summary>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat">StatDatFormat to get the extension for</param>
2016-09-26 15:40:39 -07:00
/// <returns>File extension with leading period</returns>
2016-10-25 15:02:02 -07:00
private static string OutputStatsGetExtension ( StatDatFormat statDatFormat )
2016-09-26 15:40:39 -07:00
{
string reportExtension = "" ;
2016-10-25 15:02:02 -07:00
switch ( statDatFormat )
2016-09-26 15:26:32 -07:00
{
2016-10-25 15:02:02 -07:00
case StatDatFormat . CSV :
2016-09-26 15:40:39 -07:00
reportExtension = ".csv" ;
2016-09-26 15:26:32 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . HTML :
2016-09-26 15:40:39 -07:00
reportExtension = ".html" ;
2016-09-26 15:26:32 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . None :
2016-09-26 15:26:32 -07:00
default :
2016-09-26 15:40:39 -07:00
reportExtension = ".txt" ;
2016-09-26 15:26:32 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . TSV :
2016-09-26 15:40:39 -07:00
reportExtension = ".csv" ;
2016-09-26 15:26:32 -07:00
break ;
}
2016-09-26 15:40:39 -07:00
return reportExtension ;
}
2016-09-26 15:26:32 -07:00
2016-09-26 15:40:39 -07:00
/// <summary>
/// Write out the header to the stream, if any exists
/// </summary>
/// <param name="sw">StreamWriter representing the output</param>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat">StatDatFormat representing output format</param>
2016-09-26 16:42:06 -07:00
/// <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>
2016-10-25 15:02:02 -07:00
private static void OutputStatsWriteHeader ( StreamWriter sw , StatDatFormat statDatFormat , bool baddumpCol , bool nodumpCol )
2016-09-26 15:40:39 -07:00
{
string head = "" ;
2016-10-25 15:02:02 -07:00
switch ( statDatFormat )
2016-09-26 15:26:32 -07:00
{
2016-10-25 15:02:02 -07:00
case StatDatFormat . CSV :
2016-09-26 15:26:32 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . HTML :
2016-09-26 15:26:32 -07:00
head = @ "<!DOCTYPE html>
< html >
< header >
< title > DAT Statistics Report < / title >
2016-09-27 09:32:59 -07:00
< style >
2016-09-27 09:48:48 -07:00
body {
background - color : lightgray ;
}
2016-09-27 10:32:12 -07:00
. dir {
color : # 0088F F ;
2016-09-27 09:32:59 -07:00
}
2016-09-27 09:48:48 -07:00
. right {
align : right ;
}
2016-09-27 09:32:59 -07:00
< / style >
2016-09-26 15:26:32 -07:00
< / header >
2016-09-27 09:48:48 -07:00
< body >
< h2 > DAT Statistics Report ( " + DateTime.Now.ToShortDateString() + @" ) < / h2 >
2016-09-26 15:26:32 -07:00
< table border = "" 1 "" cellpadding = "" 5 "" cellspacing = "" 0 "" >
2016-09-26 17:10:17 -07:00
";
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . None :
2016-09-26 17:10:17 -07:00
default :
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . TSV :
2016-09-26 17:10:17 -07:00
break ;
}
sw . Write ( head ) ;
// Now write the mid header for those who need it
2016-10-25 15:02:02 -07:00
OutputStatsWriteMidHeader ( sw , statDatFormat , baddumpCol , nodumpCol ) ;
2016-09-26 17:10:17 -07:00
}
/// <summary>
/// Write out the mid-header to the stream, if any exists
/// </summary>
/// <param name="sw">StreamWriter representing the output</param>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat">StatDatFormat representing output format</param>
2016-09-26 17:10:17 -07:00
/// <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>
2016-10-25 15:02:02 -07:00
private static void OutputStatsWriteMidHeader ( StreamWriter sw , StatDatFormat statDatFormat , bool baddumpCol , bool nodumpCol )
2016-09-26 17:10:17 -07:00
{
string head = "" ;
2016-10-25 15:02:02 -07:00
switch ( statDatFormat )
2016-09-26 17:10:17 -07:00
{
2016-10-25 15:02:02 -07:00
case StatDatFormat . CSV :
2016-09-26 17:10:17 -07:00
head = "\"File Name\",\"Total Size\",\"Games\",\"Roms\",\"Disks\",\"# with CRC\",\"# with MD5\",\"# with SHA-1\""
+ ( baddumpCol ? ",\"BadDumps\"" : "" ) + ( nodumpCol ? ",\"Nodumps\"" : "" ) + "\n" ;
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . HTML :
2016-09-27 09:32:59 -07:00
head = @" <tr bgcolor=""gray""><th>File Name</th><th align=""right"">Total Size</th><th align=""right"">Games</th><th align=""right"">Roms</th>"
2016-09-26 21:01:05 -07:00
+ @"<th align=""right"">Disks</th><th align=""right""># with CRC</th><th align=""right""># with MD5</th><th align=""right""># with SHA-1</th>"
2016-09-27 09:48:48 -07:00
+ ( baddumpCol ? "<th class=\".right\">Baddumps</th>" : "" ) + ( nodumpCol ? "<th class=\".right\">Nodumps</th>" : "" ) + "</tr>\n" ;
2016-09-26 15:26:32 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . None :
2016-09-26 15:26:32 -07:00
default :
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . TSV :
2016-09-26 16:42:06 -07:00
head = "\"File Name\"\t\"Total Size\"\t\"Games\"\t\"Roms\"\t\"Disks\"\t\"# with CRC\"\t\"# with MD5\"\t\"# with SHA-1\""
+ ( baddumpCol ? "\t\"BadDumps\"" : "" ) + ( nodumpCol ? "\t\"Nodumps\"" : "" ) + "\n" ;
2016-09-26 15:26:32 -07:00
break ;
}
sw . Write ( head ) ;
2016-09-26 15:40:39 -07:00
}
2016-09-26 15:26:32 -07:00
2016-09-26 15:40:39 -07:00
/// <summary>
/// Write out the separator to the stream, if any exists
/// </summary>
/// <param name="sw">StreamWriter representing the output</param>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat">StatDatFormat representing output format</param>
2016-09-26 16:42:06 -07:00
/// <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>
2016-10-25 15:02:02 -07:00
private static void OutputStatsWriteMidSeparator ( StreamWriter sw , StatDatFormat statDatFormat , bool baddumpCol , bool nodumpCol )
2016-09-26 15:40:39 -07:00
{
string mid = "" ;
2016-10-25 15:02:02 -07:00
switch ( statDatFormat )
2016-09-26 15:40:39 -07:00
{
2016-10-25 15:02:02 -07:00
case StatDatFormat . CSV :
2016-09-26 15:40:39 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . HTML :
2016-09-26 16:42:06 -07:00
mid = "<tr><td colspan=\""
+ ( baddumpCol & & nodumpCol
? "11"
: ( baddumpCol ^ nodumpCol
? "10"
: "9" )
)
+ "\"></td></tr>\n" ;
2016-09-26 15:40:39 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . None :
2016-09-26 15:40:39 -07:00
default :
break ;
}
sw . Write ( mid ) ;
}
2016-09-26 15:26:32 -07:00
2016-09-26 15:40:39 -07:00
/// <summary>
/// Write out the footer-separator to the stream, if any exists
/// </summary>
/// <param name="sw">StreamWriter representing the output</param>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat">StatDatFormat representing output format</param>
2016-09-26 17:10:17 -07:00
/// <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>
2016-10-25 15:02:02 -07:00
private static void OutputStatsWriteMidFooter ( StreamWriter sw , StatDatFormat statDatFormat , bool baddumpCol , bool nodumpCol )
2016-09-26 15:40:39 -07:00
{
string end = "" ;
2016-10-25 15:02:02 -07:00
switch ( statDatFormat )
2016-09-23 16:32:24 -07:00
{
2016-10-25 15:02:02 -07:00
case StatDatFormat . CSV :
2016-09-26 15:48:51 -07:00
end = "\n" ;
2016-09-26 15:40:39 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . HTML :
2016-09-26 17:10:17 -07:00
end = "<tr border=\"0\"><td colspan=\""
+ ( baddumpCol & & nodumpCol
? "11"
: ( baddumpCol ^ nodumpCol
? "10"
: "9" )
)
+ "\"></td></tr>\n" ;
2016-09-26 15:40:39 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . None :
2016-09-26 15:40:39 -07:00
default :
2016-09-26 15:48:51 -07:00
end = "\n" ;
2016-09-26 15:40:39 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . TSV :
2016-09-26 15:48:51 -07:00
end = "\n" ;
2016-09-26 15:40:39 -07:00
break ;
}
sw . Write ( end ) ;
}
2016-09-23 16:32:24 -07:00
2016-09-26 15:40:39 -07:00
/// <summary>
/// Write out the footer to the stream, if any exists
/// </summary>
/// <param name="sw">StreamWriter representing the output</param>
2016-10-25 15:02:02 -07:00
/// <param name="statDatFormat">StatDatFormat representing output format</param>
private static void OutputStatsWriteFooter ( StreamWriter sw , StatDatFormat statDatFormat )
2016-09-26 15:40:39 -07:00
{
string end = "" ;
2016-10-25 15:02:02 -07:00
switch ( statDatFormat )
2016-09-26 14:38:05 -07:00
{
2016-10-25 15:02:02 -07:00
case StatDatFormat . CSV :
2016-09-26 14:38:05 -07:00
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . HTML :
2016-09-26 14:38:05 -07:00
end = @ " </table>
2016-09-23 16:32:24 -07:00
< / body >
< / html >
2016-09-26 14:38:05 -07:00
";
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . None :
2016-09-26 14:38:05 -07:00
default :
break ;
2016-10-25 15:02:02 -07:00
case StatDatFormat . TSV :
2016-09-26 15:26:32 -07:00
break ;
2016-09-26 14:38:05 -07:00
}
sw . Write ( end ) ;
2016-09-23 16:32:24 -07:00
}
2016-09-22 17:52:58 -07:00
#endregion
#endregion // Static Methods
2016-04-19 01:11:23 -07:00
}
}