2016-10-24 12:58:57 -07:00
using System ;
2016-09-02 13:59:25 -07:00
using System.Collections.Generic ;
2020-06-10 22:37:19 -07:00
using System.IO ;
2016-09-08 17:42:53 -07:00
using System.Linq ;
2016-09-02 13:59:25 -07:00
using System.Xml ;
2020-12-08 00:13:22 -08:00
using System.Xml.Schema ;
2024-03-04 23:56:05 -05:00
using Microsoft.Data.Sqlite ;
2020-12-08 13:23:59 -08:00
using SabreTools.Core ;
2020-12-08 16:37:08 -08:00
using SabreTools.Core.Tools ;
using SabreTools.DatFiles ;
2020-12-08 15:15:41 -08:00
using SabreTools.DatItems ;
2021-02-02 10:23:43 -08:00
using SabreTools.DatItems.Formats ;
2020-12-10 23:24:09 -08:00
using SabreTools.DatTools ;
2020-12-08 14:53:49 -08:00
using SabreTools.FileTypes ;
2024-03-04 23:56:05 -05:00
using SabreTools.Hashing ;
2020-12-07 13:57:26 -08:00
using SabreTools.Help ;
2020-12-07 14:29:45 -08:00
using SabreTools.Logging ;
2016-09-02 13:59:25 -07:00
2020-08-01 13:25:32 -07:00
namespace RombaSharp.Features
2016-09-02 13:59:25 -07:00
{
2020-08-01 13:25:32 -07:00
internal class BaseFeature : TopLevel
2019-02-08 20:31:07 -08:00
{
2021-02-03 11:10:19 -08:00
#region Logging
/// <summary>
/// Logging object
/// </summary>
protected Logger logger = new Logger ( ) ;
#endregion
#region Features
#region Flag features
2020-08-01 13:25:32 -07:00
internal const string CopyValue = "copy" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature CopyFlag
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
CopyValue ,
"-copy" ,
"Copy files to output instead of rebuilding" ,
2020-12-07 13:57:26 -08:00
ParameterType . Flag ) ;
2020-08-01 13:25:32 -07:00
}
} // Unique to RombaSharp
internal const string FixdatOnlyValue = "fixdat-only" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature FixdatOnlyFlag
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
FixdatOnlyValue ,
"-fixdatOnly" ,
"only fix dats and don't generate torrentzips" ,
2020-12-07 13:57:26 -08:00
ParameterType . Flag ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string LogOnlyValue = "log-only" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature LogOnlyFlag
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
LogOnlyValue ,
"-log-only" ,
"Only write out actions to log" ,
2020-12-07 13:57:26 -08:00
ParameterType . Flag ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string NoDbValue = "no-db" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature NoDbFlag
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
NoDbValue ,
"-no-db" ,
"archive into depot but do not touch DB index and ignore only-needed flag" ,
2020-12-07 13:57:26 -08:00
ParameterType . Flag ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string OnlyNeededValue = "only-needed" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature OnlyNeededFlag
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
OnlyNeededValue ,
"-only-needed" ,
"only archive ROM files actually referenced by DAT files from the DAT index" ,
2020-12-07 13:57:26 -08:00
ParameterType . Flag ) ;
2020-08-01 13:25:32 -07:00
}
}
2021-02-03 11:10:19 -08:00
internal const string ScriptValue = "script" ;
internal static SabreTools . Help . Feature ScriptFlag
{
get
{
return new SabreTools . Help . Feature (
ScriptValue ,
new List < string > ( ) { "-sc" , "--script" } ,
"Enable script mode (no clear screen)" ,
ParameterType . Flag ,
"For times when RombaSharp is being used in a scripted environment, the user may not want the screen to be cleared every time that it is called. This flag allows the user to skip clearing the screen on run just like if the console was being redirected." ) ;
}
}
2020-08-01 13:25:32 -07:00
internal const string SkipInitialScanValue = "skip-initial-scan" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature SkipInitialScanFlag
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
SkipInitialScanValue ,
"-skip-initial-scan" ,
"skip the initial scan of the files to determine amount of work" ,
2020-12-07 13:57:26 -08:00
ParameterType . Flag ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string UseGolangZipValue = "use-golang-zip" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature UseGolangZipFlag
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
UseGolangZipValue ,
"-use-golang-zip" ,
"use go zip implementation instead of zlib" ,
2020-12-07 13:57:26 -08:00
ParameterType . Flag ) ;
2020-08-01 13:25:32 -07:00
}
}
#endregion
2021-02-03 11:10:19 -08:00
#region Int32 features
2020-08-01 13:25:32 -07:00
internal const string Include7ZipsInt32Value = "include-7zips" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature Include7ZipsInt32Input
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
Include7ZipsInt32Value ,
"-include-7zips" ,
"flag value == 0 means: add 7zip files themselves into the depot in addition to their contents, flag value == 2 means add 7zip files themselves but don't add content" ,
2020-12-07 13:57:26 -08:00
ParameterType . Int32 ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string IncludeGZipsInt32Value = "include-gzips" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature IncludeGZipsInt32Input
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
IncludeGZipsInt32Value ,
"-include-gzips" ,
"flag value == 0 means: add gzip files themselves into the depot in addition to their contents, flag value == 2 means add gzip files themselves but don't add content" ,
2020-12-07 13:57:26 -08:00
ParameterType . Int32 ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string IncludeZipsInt32Value = "include-zips" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature IncludeZipsInt32Input
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
IncludeZipsInt32Value ,
"-include-zips" ,
"flag value == 0 means: add zip files themselves into the depot in addition to their contents, flag value == 2 means add zip files themselves but don't add content" ,
2020-12-07 13:57:26 -08:00
ParameterType . Int32 ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string SubworkersInt32Value = "subworkers" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature SubworkersInt32Input
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
SubworkersInt32Value ,
"-subworkers" ,
"how many subworkers to launch for each worker" ,
2020-12-07 13:57:26 -08:00
ParameterType . Int32 ) ;
2020-08-01 13:25:32 -07:00
}
} // Defaults to Workers count in config
internal const string WorkersInt32Value = "workers" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature WorkersInt32Input
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
WorkersInt32Value ,
"-workers" ,
"how many workers to launch for the job" ,
2020-12-07 13:57:26 -08:00
ParameterType . Int32 ) ;
2020-08-01 13:25:32 -07:00
}
} // Defaults to Workers count in config
#endregion
2021-02-03 11:10:19 -08:00
#region Int64 features
2020-08-01 13:25:32 -07:00
internal const string SizeInt64Value = "size" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature SizeInt64Input
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
SizeInt64Value ,
"-size" ,
"size of the rom to lookup" ,
2020-12-07 13:57:26 -08:00
ParameterType . Int64 ) ;
2020-08-01 13:25:32 -07:00
}
}
#endregion
2021-02-03 11:10:19 -08:00
#region List < String > features
2020-08-01 13:25:32 -07:00
internal const string DatsListStringValue = "dats" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature DatsListStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
DatsListStringValue ,
"-dats" ,
"purge only roms declared in these dats" ,
2020-12-07 13:57:26 -08:00
ParameterType . List ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string DepotListStringValue = "depot" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature DepotListStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
DepotListStringValue ,
"-depot" ,
"work only on specified depot path" ,
2020-12-07 13:57:26 -08:00
ParameterType . List ) ;
2020-08-01 13:25:32 -07:00
}
}
#endregion
2021-02-03 11:10:19 -08:00
#region String features
2020-08-01 13:25:32 -07:00
internal const string BackupStringValue = "backup" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature BackupStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
BackupStringValue ,
"-backup" ,
"backup directory where backup files are moved to" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string DescriptionStringValue = "description" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature DescriptionStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
DescriptionStringValue ,
"-description" ,
"description value in DAT header" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
2021-02-03 11:10:19 -08:00
internal const string LogLevelStringValue = "log-level" ;
internal static SabreTools . Help . Feature LogLevelStringInput
{
get
{
return new SabreTools . Help . Feature (
LogLevelStringValue ,
new List < string > ( ) { "-ll" , "--log-level" } ,
"Set the lowest log level for output" ,
ParameterType . String ,
longDescription : @ "Set the lowest log level for output.
Possible values are : Verbose , User , Warning , Error ");
}
}
2020-08-01 13:25:32 -07:00
internal const string MissingSha1sStringValue = "missing-sha1s" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature MissingSha1sStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
MissingSha1sStringValue ,
"-missingSha1s" ,
"write paths of dats with missing sha1s into this file" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string NameStringValue = "name" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature NameStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
NameStringValue ,
"-name" ,
"name value in DAT header" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string NewStringValue = "new" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature NewStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
NewStringValue ,
"-new" ,
"new DAT file" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string OldStringValue = "old" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature OldStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
OldStringValue ,
"-old" ,
"old DAT file" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string OutStringValue = "out" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature OutStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
OutStringValue ,
"-out" ,
"output file" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string ResumeStringValue = "resume" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature ResumeStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
ResumeStringValue ,
"-resume" ,
"resume a previously interrupted operation from the specified path" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
internal const string SourceStringValue = "source" ;
2020-12-07 13:57:26 -08:00
internal static SabreTools . Help . Feature SourceStringInput
2020-08-01 13:25:32 -07:00
{
get
{
2020-12-07 13:57:26 -08:00
return new SabreTools . Help . Feature (
2020-08-01 13:25:32 -07:00
SourceStringValue ,
"-source" ,
"source directory" ,
2020-12-07 13:57:26 -08:00
ParameterType . String ) ;
2020-08-01 13:25:32 -07:00
}
}
#endregion
2021-02-03 11:10:19 -08:00
#endregion // Features
#region Fields
/// <summary>
/// Lowest log level for output
/// </summary>
public LogLevel LogLevel { get ; protected set ; }
/// <summary>
/// Determines if scripting mode is enabled
/// </summary>
public bool ScriptMode { get ; protected set ; }
#endregion
#region Settings
2020-08-01 13:25:32 -07:00
// General settings
2024-03-05 20:26:38 -05:00
internal static string? _logdir ; // Log folder location
internal static string? _tmpdir ; // Temp folder location
internal static string? _webdir ; // Web frontend location
internal static string? _baddir ; // Fail-to-unpack file folder location
2020-08-01 13:25:32 -07:00
internal static int _verbosity ; // Verbosity of the output
internal static int _cores ; // Forced CPU cores
// DatRoot settings
2024-03-05 20:26:38 -05:00
internal static string? _dats ; // DatRoot folder location
internal static string? _db ; // Database name
2020-08-01 13:25:32 -07:00
// Depot settings
2024-03-05 20:26:38 -05:00
internal static Dictionary < string , Tuple < long , bool > > ? _depots ; // Folder location, Max size
2020-08-01 13:25:32 -07:00
// Server settings
internal static int _port ; // Web server port
// Other internal variables
internal const string _config = "config.xml" ;
2024-03-05 20:26:38 -05:00
internal static string? _connectionString ;
2020-08-01 13:25:32 -07:00
2021-02-03 11:10:19 -08:00
#endregion
#region Add Feature Groups
2020-10-07 15:42:30 -07:00
/// <summary>
2021-02-03 11:10:19 -08:00
/// Add common features
2020-10-07 15:42:30 -07:00
/// </summary>
2021-02-03 11:10:19 -08:00
protected void AddCommonFeatures ( )
{
AddFeature ( ScriptFlag ) ;
AddFeature ( LogLevelStringInput ) ;
}
#endregion
2020-10-07 15:42:30 -07:00
2024-03-05 20:26:38 -05:00
public override bool ProcessFeatures ( Dictionary < string , SabreTools . Help . Feature ? > features )
2020-08-01 13:25:32 -07:00
{
2024-03-05 15:24:11 -05:00
LogLevel = GetString ( features , LogLevelStringValue ) . AsEnumValue < LogLevel > ( ) ;
2021-02-03 11:10:19 -08:00
ScriptMode = GetBoolean ( features , ScriptValue ) ;
2020-08-01 13:25:32 -07:00
InitializeConfiguration ( ) ;
2020-10-07 15:42:30 -07:00
EnsureDatabase ( _db , _connectionString ) ;
2021-03-19 20:52:11 -07:00
return true ;
2020-10-07 15:42:30 -07:00
}
/// <summary>
/// Ensure that the databse exists and has the proper schema
/// </summary>
/// <param name="db">Name of the databse</param>
/// <param name="connectionString">Connection string for SQLite</param>
2024-03-05 20:26:38 -05:00
public void EnsureDatabase ( string? db , string? connectionString )
2020-10-07 15:42:30 -07:00
{
2024-03-05 20:26:38 -05:00
// Missing database or connection string can't work
if ( db = = null | | connectionString = = null )
return ;
2020-10-07 15:42:30 -07:00
// Make sure the file exists
2023-04-17 13:22:35 -04:00
if ( ! System . IO . File . Exists ( db ) )
System . IO . File . Create ( db ) ;
2020-10-07 15:42:30 -07:00
// Open the database connection
SqliteConnection dbc = new SqliteConnection ( connectionString ) ;
dbc . Open ( ) ;
// Make sure the database has the correct schema
try
{
string query = @ "
CREATE TABLE IF NOT EXISTS crc (
' crc ' TEXT NOT NULL ,
PRIMARY KEY ( crc )
) ";
SqliteCommand slc = new SqliteCommand ( query , dbc ) ;
slc . ExecuteNonQuery ( ) ;
query = @ "
CREATE TABLE IF NOT EXISTS md5 (
' md5 ' TEXT NOT NULL ,
PRIMARY KEY ( md5 )
) ";
slc = new SqliteCommand ( query , dbc ) ;
slc . ExecuteNonQuery ( ) ;
query = @ "
CREATE TABLE IF NOT EXISTS sha1 (
' sha1 ' TEXT NOT NULL ,
' depot ' TEXT ,
PRIMARY KEY ( sha1 )
) ";
slc = new SqliteCommand ( query , dbc ) ;
slc . ExecuteNonQuery ( ) ;
query = @ "
CREATE TABLE IF NOT EXISTS crcsha1 (
' crc ' TEXT NOT NULL ,
' sha1 ' TEXT NOT NULL ,
PRIMARY KEY ( crc , sha1 )
) ";
slc = new SqliteCommand ( query , dbc ) ;
slc . ExecuteNonQuery ( ) ;
query = @ "
CREATE TABLE IF NOT EXISTS md5sha1 (
' md5 ' TEXT NOT NULL ,
' sha1 ' TEXT NOT NULL ,
PRIMARY KEY ( md5 , sha1 )
) ";
slc = new SqliteCommand ( query , dbc ) ;
slc . ExecuteNonQuery ( ) ;
query = @ "
CREATE TABLE IF NOT EXISTS dat (
' hash ' TEXT NOT NULL ,
PRIMARY KEY ( hash )
) ";
slc = new SqliteCommand ( query , dbc ) ;
slc . ExecuteNonQuery ( ) ;
slc . Dispose ( ) ;
}
catch ( Exception ex )
{
logger . Error ( ex ) ;
}
finally
{
dbc . Dispose ( ) ;
}
2020-08-01 13:25:32 -07:00
}
2019-02-08 20:31:07 -08:00
#region Helper methods
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
/// <summary>
/// Gets all valid DATs that match in the DAT root
/// </summary>
/// <param name="inputs">List of input strings to check for, presumably file names</param>
/// <returns>Dictionary of hash/full path for each of the valid DATs</returns>
2020-10-07 15:42:30 -07:00
internal Dictionary < string , string > GetValidDats ( List < string > inputs )
2019-02-08 20:31:07 -08:00
{
// Get a dictionary of filenames that actually exist in the DATRoot, logging which ones are not
2024-02-29 00:14:16 -05:00
#if NET20 | | NET35
2024-03-06 01:04:51 -05:00
List < string > datRootDats = Directory . GetFiles ( _dats ! , "*" ) . ToList ( ) ;
2024-02-29 00:14:16 -05:00
#else
2024-03-06 01:04:51 -05:00
List < string > datRootDats = Directory . EnumerateFiles ( _dats ! , "*" , SearchOption . AllDirectories ) . ToList ( ) ;
2024-02-29 00:14:16 -05:00
#endif
2019-02-08 20:31:07 -08:00
List < string > lowerCaseDats = datRootDats . ConvertAll ( i = > Path . GetFileName ( i ) . ToLowerInvariant ( ) ) ;
2024-03-04 23:56:05 -05:00
Dictionary < string , string > foundDats = [ ] ;
2019-02-08 20:31:07 -08:00
foreach ( string input in inputs )
{
if ( lowerCaseDats . Contains ( input . ToLowerInvariant ( ) ) )
{
string fullpath = Path . GetFullPath ( datRootDats [ lowerCaseDats . IndexOf ( input . ToLowerInvariant ( ) ) ] ) ;
2024-03-05 20:26:38 -05:00
string? sha1 = TextHelper . ByteArrayToString ( BaseFile . GetInfo ( fullpath , hashes : [ HashType . SHA1 ] ) ? . SHA1 ) ;
if ( sha1 ! = null )
foundDats . Add ( sha1 , fullpath ) ;
2019-02-08 20:31:07 -08:00
}
else
{
2020-10-07 15:42:30 -07:00
logger . Warning ( $"The file '{input}' could not be found in the DAT root" ) ;
2019-02-08 20:31:07 -08:00
}
}
2016-10-17 11:04:07 -07:00
2019-02-08 20:31:07 -08:00
return foundDats ;
}
2016-10-17 11:04:07 -07:00
2019-02-08 20:31:07 -08:00
/// <summary>
/// Initialize the Romba application from XML config
/// </summary>
2020-10-07 15:42:30 -07:00
private void InitializeConfiguration ( )
2019-02-08 20:31:07 -08:00
{
// Get default values if they're not written
int workers = 4 ,
verbosity = 1 ,
cores = 4 ,
port = 4003 ;
string logdir = "logs" ,
tmpdir = "tmp" ,
webdir = "web" ,
baddir = "bad" ,
dats = "dats" ,
2020-07-15 09:41:59 -07:00
db = "db" ;
2019-02-08 20:31:07 -08:00
Dictionary < string , Tuple < long , bool > > depots = new Dictionary < string , Tuple < long , bool > > ( ) ;
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
// Get the XML text reader for the configuration file, if possible
2020-12-08 00:13:22 -08:00
XmlReader xtr = XmlReader . Create ( _config , new XmlReaderSettings
{
CheckCharacters = false ,
DtdProcessing = DtdProcessing . Ignore ,
IgnoreComments = true ,
IgnoreWhitespace = true ,
ValidationFlags = XmlSchemaValidationFlags . None ,
ValidationType = ValidationType . None ,
} ) ;
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
// Now parse the XML file for settings
if ( xtr ! = null )
{
xtr . MoveToContent ( ) ;
while ( ! xtr . EOF )
{
// We only want elements
if ( xtr . NodeType ! = XmlNodeType . Element )
{
xtr . Read ( ) ;
continue ;
}
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
switch ( xtr . Name )
{
case "workers" :
workers = xtr . ReadElementContentAsInt ( ) ;
break ;
case "logdir" :
logdir = xtr . ReadElementContentAsString ( ) ;
break ;
case "tmpdir" :
tmpdir = xtr . ReadElementContentAsString ( ) ;
break ;
case "webdir" :
webdir = xtr . ReadElementContentAsString ( ) ;
break ;
case "baddir" :
baddir = xtr . ReadElementContentAsString ( ) ;
break ;
case "verbosity" :
verbosity = xtr . ReadElementContentAsInt ( ) ;
break ;
case "cores" :
cores = xtr . ReadElementContentAsInt ( ) ;
break ;
case "dats" :
dats = xtr . ReadElementContentAsString ( ) ;
break ;
case "db" :
db = xtr . ReadElementContentAsString ( ) ;
break ;
case "depot" :
XmlReader subreader = xtr . ReadSubtree ( ) ;
if ( subreader ! = null )
{
2020-06-10 22:37:19 -07:00
string root = string . Empty ;
2019-02-08 20:31:07 -08:00
long maxsize = - 1 ;
bool online = true ;
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
while ( ! subreader . EOF )
{
// We only want elements
if ( subreader . NodeType ! = XmlNodeType . Element )
{
subreader . Read ( ) ;
continue ;
}
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
switch ( subreader . Name )
{
case "root" :
root = subreader . ReadElementContentAsString ( ) ;
break ;
case "maxsize" :
maxsize = subreader . ReadElementContentAsLong ( ) ;
break ;
case "online" :
online = subreader . ReadElementContentAsBoolean ( ) ;
break ;
default :
subreader . Read ( ) ;
break ;
}
}
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
try
{
depots . Add ( root , new Tuple < long , bool > ( maxsize , online ) ) ;
}
catch
{
// Ignore add errors
}
}
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
xtr . Skip ( ) ;
break ;
case "port" :
port = xtr . ReadElementContentAsInt ( ) ;
break ;
default :
xtr . Read ( ) ;
break ;
}
}
}
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
// Now validate the values given
if ( workers < 1 )
workers = 1 ;
if ( workers > 8 )
workers = 8 ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( ! Directory . Exists ( logdir ) )
Directory . CreateDirectory ( logdir ) ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( ! Directory . Exists ( tmpdir ) )
Directory . CreateDirectory ( tmpdir ) ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( ! Directory . Exists ( webdir ) )
Directory . CreateDirectory ( webdir ) ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( ! Directory . Exists ( baddir ) )
Directory . CreateDirectory ( baddir ) ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( verbosity < 0 )
verbosity = 0 ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( verbosity > 3 )
verbosity = 3 ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( cores < 1 )
cores = 1 ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( cores > 16 )
cores = 16 ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( ! Directory . Exists ( dats ) )
Directory . CreateDirectory ( dats ) ;
2020-08-01 13:25:32 -07:00
2020-06-10 22:37:19 -07:00
db = $"{Path.GetFileNameWithoutExtension(db)}.sqlite" ;
2020-07-15 09:41:59 -07:00
string connectionString = $"Data Source={db};Version = 3;" ;
2019-02-08 20:31:07 -08:00
foreach ( string key in depots . Keys )
{
if ( ! Directory . Exists ( key ) )
{
Directory . CreateDirectory ( key ) ;
2023-04-17 13:22:35 -04:00
System . IO . File . CreateText ( Path . Combine ( key , ".romba_size" ) ) ;
System . IO . File . CreateText ( Path . Combine ( key , ".romba_size.backup" ) ) ;
2019-02-08 20:31:07 -08:00
}
else
{
2023-04-17 13:22:35 -04:00
if ( ! System . IO . File . Exists ( Path . Combine ( key , ".romba_size" ) ) )
System . IO . File . CreateText ( Path . Combine ( key , ".romba_size" ) ) ;
2020-06-10 22:37:19 -07:00
2023-04-17 13:22:35 -04:00
if ( ! System . IO . File . Exists ( Path . Combine ( key , ".romba_size.backup" ) ) )
System . IO . File . CreateText ( Path . Combine ( key , ".romba_size.backup" ) ) ;
2019-02-08 20:31:07 -08:00
}
}
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( port < 0 )
port = 0 ;
2020-06-10 22:37:19 -07:00
2019-02-08 20:31:07 -08:00
if ( port > 65535 )
port = 65535 ;
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
// Finally set all of the fields
Globals . MaxThreads = workers ;
_logdir = logdir ;
2020-12-11 22:52:28 -08:00
_tmpdir = tmpdir ;
2019-02-08 20:31:07 -08:00
_webdir = webdir ;
_baddir = baddir ;
_verbosity = verbosity ;
_cores = cores ;
_dats = dats ;
_db = db ;
_connectionString = connectionString ;
_depots = depots ;
_port = port ;
}
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
/// <summary>
/// Add a new DAT to the database
/// </summary>
/// <param name="dat">DatFile hash information to add</param>
/// <param name="dbc">Database connection to use</param>
2020-10-07 15:42:30 -07:00
internal void AddDatToDatabase ( Rom dat , SqliteConnection dbc )
2019-02-08 20:31:07 -08:00
{
// Get the dat full path
2024-03-06 01:04:51 -05:00
string fullpath = Path . Combine ( _dats ! , ( dat . Machine . Name = = "dats" ? string . Empty : dat . Machine . Name ) ! , dat . Name ! ) ;
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
// Parse the Dat if possible
2020-10-07 15:42:30 -07:00
logger . User ( $"Adding from '{dat.Name}'" ) ;
2020-12-10 13:53:34 -08:00
DatFile tempdat = Parser . CreateAndParse ( fullpath ) ;
2016-10-19 10:47:23 -07:00
2019-02-08 20:31:07 -08:00
// If the Dat wasn't empty, add the information
2024-03-05 20:26:38 -05:00
SqliteCommand ? slc = null ;
2020-07-15 10:47:13 -07:00
string crcquery = "INSERT OR IGNORE INTO crc (crc) VALUES" ;
string md5query = "INSERT OR IGNORE INTO md5 (md5) VALUES" ;
string sha1query = "INSERT OR IGNORE INTO sha1 (sha1) VALUES" ;
string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES" ;
string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES" ;
// Loop through the parsed entries
bool hasItems = false ;
2020-07-26 22:34:45 -07:00
foreach ( string romkey in tempdat . Items . Keys )
2019-02-08 20:31:07 -08:00
{
2024-03-05 20:26:38 -05:00
foreach ( DatItem datItem in tempdat . Items [ romkey ] ! )
2019-02-08 20:31:07 -08:00
{
2020-10-07 15:42:30 -07:00
logger . Verbose ( $"Checking and adding file '{datItem.GetName() ?? string.Empty}'" ) ;
2020-07-15 10:47:13 -07:00
2020-08-27 16:57:22 -07:00
if ( datItem . ItemType = = ItemType . Disk )
{
Disk disk = ( Disk ) datItem ;
hasItems = true ;
if ( ! string . IsNullOrWhiteSpace ( disk . MD5 ) )
md5query + = $" (\" { disk . MD5 } \ ")," ;
if ( ! string . IsNullOrWhiteSpace ( disk . SHA1 ) )
{
sha1query + = $" (\" { disk . SHA1 } \ ")," ;
if ( ! string . IsNullOrWhiteSpace ( disk . MD5 ) )
md5sha1query + = $" (\" { disk . MD5 } \ ", \"{disk.SHA1}\")," ;
}
}
else if ( datItem . ItemType = = ItemType . Media )
{
Media media = ( Media ) datItem ;
hasItems = true ;
if ( ! string . IsNullOrWhiteSpace ( media . MD5 ) )
md5query + = $" (\" { media . MD5 } \ ")," ;
if ( ! string . IsNullOrWhiteSpace ( media . SHA1 ) )
{
sha1query + = $" (\" { media . SHA1 } \ ")," ;
if ( ! string . IsNullOrWhiteSpace ( media . MD5 ) )
md5sha1query + = $" (\" { media . MD5 } \ ", \"{media.SHA1}\")," ;
}
}
else if ( datItem . ItemType = = ItemType . Rom )
2019-02-08 20:31:07 -08:00
{
2020-07-15 10:47:13 -07:00
Rom rom = ( Rom ) datItem ;
hasItems = true ;
if ( ! string . IsNullOrWhiteSpace ( rom . CRC ) )
crcquery + = $" (\" { rom . CRC } \ ")," ;
if ( ! string . IsNullOrWhiteSpace ( rom . MD5 ) )
md5query + = $" (\" { rom . MD5 } \ ")," ;
2016-10-19 10:47:23 -07:00
2020-07-15 10:47:13 -07:00
if ( ! string . IsNullOrWhiteSpace ( rom . SHA1 ) )
2019-02-08 20:31:07 -08:00
{
2020-07-15 10:47:13 -07:00
sha1query + = $" (\" { rom . SHA1 } \ ")," ;
2016-10-10 13:42:52 -07:00
2020-06-10 22:37:19 -07:00
if ( ! string . IsNullOrWhiteSpace ( rom . CRC ) )
2020-07-15 10:47:13 -07:00
crcsha1query + = $" (\" { rom . CRC } \ ", \"{rom.SHA1}\")," ;
2020-06-10 22:37:19 -07:00
2020-07-15 10:47:13 -07:00
if ( ! string . IsNullOrWhiteSpace ( rom . MD5 ) )
md5sha1query + = $" (\" { rom . MD5 } \ ", \"{rom.SHA1}\")," ;
}
}
2019-02-08 20:31:07 -08:00
}
2020-07-15 10:47:13 -07:00
}
2016-10-10 13:45:41 -07:00
2020-07-15 10:47:13 -07:00
// Now run the queries after fixing them
if ( crcquery ! = "INSERT OR IGNORE INTO crc (crc) VALUES" )
{
slc = new SqliteCommand ( crcquery . TrimEnd ( ',' ) , dbc ) ;
slc . ExecuteNonQuery ( ) ;
}
2020-06-10 22:37:19 -07:00
2020-07-15 10:47:13 -07:00
if ( md5query ! = "INSERT OR IGNORE INTO md5 (md5) VALUES" )
{
slc = new SqliteCommand ( md5query . TrimEnd ( ',' ) , dbc ) ;
slc . ExecuteNonQuery ( ) ;
}
2020-06-10 22:37:19 -07:00
2020-07-15 10:47:13 -07:00
if ( sha1query ! = "INSERT OR IGNORE INTO sha1 (sha1) VALUES" )
{
slc = new SqliteCommand ( sha1query . TrimEnd ( ',' ) , dbc ) ;
slc . ExecuteNonQuery ( ) ;
}
2020-06-10 22:37:19 -07:00
2020-07-15 10:47:13 -07:00
if ( crcsha1query ! = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES" )
{
slc = new SqliteCommand ( crcsha1query . TrimEnd ( ',' ) , dbc ) ;
slc . ExecuteNonQuery ( ) ;
}
2020-06-10 22:37:19 -07:00
2020-07-15 10:47:13 -07:00
if ( md5sha1query ! = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES" )
{
slc = new SqliteCommand ( md5sha1query . TrimEnd ( ',' ) , dbc ) ;
slc . ExecuteNonQuery ( ) ;
}
// Only add the DAT if it's non-empty
if ( hasItems )
{
string datquery = $"INSERT OR IGNORE INTO dat (hash) VALUES (\" { dat . SHA1 } \ ")" ;
slc = new SqliteCommand ( datquery , dbc ) ;
slc . ExecuteNonQuery ( ) ;
2019-02-08 20:31:07 -08:00
}
2016-10-10 10:51:19 -07:00
2020-07-15 10:47:13 -07:00
slc ? . Dispose ( ) ;
2019-02-08 20:31:07 -08:00
}
2016-09-02 13:59:25 -07:00
2019-02-08 20:31:07 -08:00
#endregion
}
2016-09-02 13:59:25 -07:00
}