2016-10-24 12:58:57 -07:00
|
|
|
|
using System;
|
2016-08-30 15:02:48 -07:00
|
|
|
|
using System.Collections.Generic;
|
2016-10-24 12:58:57 -07:00
|
|
|
|
|
2017-05-04 02:41:11 -07:00
|
|
|
|
using SabreTools.Library.Data;
|
|
|
|
|
|
using SabreTools.Library.Help;
|
|
|
|
|
|
using SabreTools.Library.Tools;
|
2016-08-30 15:02:48 -07:00
|
|
|
|
|
2017-12-05 18:13:42 -08:00
|
|
|
|
#if MONO
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
#else
|
|
|
|
|
|
using Alphaleonis.Win32.Filesystem;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2017-02-02 15:08:21 -08:00
|
|
|
|
namespace RombaSharp
|
2016-08-30 15:02:48 -07:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Entry class for the RombaSharp application
|
|
|
|
|
|
/// </summary>
|
2017-02-02 22:06:20 -08:00
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// In the database, we want to enable "offline mode". That is, when a user does an operation
|
|
|
|
|
|
/// that needs to read from the depot themselves, if the depot folder cannot be found, the
|
|
|
|
|
|
/// user is prompted to reconnect the depot OR skip that depot entirely.
|
|
|
|
|
|
/// </remarks>
|
2016-09-02 13:59:25 -07:00
|
|
|
|
public partial class RombaSharp
|
2016-08-30 15:02:48 -07:00
|
|
|
|
{
|
|
|
|
|
|
// General settings
|
2017-02-02 22:06:20 -08:00
|
|
|
|
private static string _logdir; // Log folder location
|
|
|
|
|
|
private static string _tmpdir; // Temp folder location
|
2016-09-02 13:59:25 -07:00
|
|
|
|
private static string _webdir; // Web frontend location
|
|
|
|
|
|
private static string _baddir; // Fail-to-unpack file folder location
|
|
|
|
|
|
private static int _verbosity; // Verbosity of the output
|
|
|
|
|
|
private static int _cores; // Forced CPU cores
|
2016-08-30 15:02:48 -07:00
|
|
|
|
|
|
|
|
|
|
// DatRoot settings
|
2016-09-02 13:59:25 -07:00
|
|
|
|
private static string _dats; // DatRoot folder location
|
|
|
|
|
|
private static string _db; // Database name
|
2016-08-30 15:02:48 -07:00
|
|
|
|
|
|
|
|
|
|
// Depot settings
|
2016-09-02 13:59:25 -07:00
|
|
|
|
private static Dictionary<string, Tuple<long, bool>> _depots; // Folder location, Max size
|
2016-08-30 15:02:48 -07:00
|
|
|
|
|
|
|
|
|
|
// Server settings
|
2016-09-02 13:59:25 -07:00
|
|
|
|
private static int _port; // Web server port
|
2016-08-30 15:02:48 -07:00
|
|
|
|
|
|
|
|
|
|
// Other private variables
|
2016-09-02 13:59:25 -07:00
|
|
|
|
private static string _config = "config.xml";
|
|
|
|
|
|
private static string _dbSchema = "rombasharp";
|
|
|
|
|
|
private static string _connectionString;
|
2017-02-02 15:08:21 -08:00
|
|
|
|
private static Help _help;
|
2016-08-30 15:02:48 -07:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2016-09-02 13:59:25 -07:00
|
|
|
|
/// Entry class for the RombaSharp application
|
2016-08-30 15:02:48 -07:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static void Main(string[] args)
|
|
|
|
|
|
{
|
2016-09-02 13:42:18 -07:00
|
|
|
|
// Perform initial setup and verification
|
2017-03-01 21:26:27 -08:00
|
|
|
|
Globals.Logger = new Logger(true, "romba.log");
|
2017-02-02 15:08:21 -08:00
|
|
|
|
|
2016-09-02 13:59:25 -07:00
|
|
|
|
InitializeConfiguration();
|
2016-09-22 21:04:41 -07:00
|
|
|
|
DatabaseTools.EnsureDatabase(_dbSchema, _db, _connectionString);
|
2016-09-02 13:42:18 -07:00
|
|
|
|
|
2017-02-02 15:08:21 -08:00
|
|
|
|
// Create a new Help object for this program
|
|
|
|
|
|
_help = RetrieveHelp();
|
|
|
|
|
|
|
2016-09-02 13:42:18 -07:00
|
|
|
|
// If output is being redirected, don't allow clear screens
|
|
|
|
|
|
if (!Console.IsOutputRedirected)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Credits take precidence over all
|
|
|
|
|
|
if ((new List<string>(args)).Contains("--credits"))
|
|
|
|
|
|
{
|
2017-02-02 22:12:48 -08:00
|
|
|
|
_help.OutputCredits();
|
2017-03-01 21:26:27 -08:00
|
|
|
|
Globals.Logger.Close();
|
2016-09-02 13:42:18 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If there's no arguments, show help
|
|
|
|
|
|
if (args.Length == 0)
|
|
|
|
|
|
{
|
2017-02-02 15:08:21 -08:00
|
|
|
|
_help.OutputGenericHelp();
|
2017-03-01 21:26:27 -08:00
|
|
|
|
Globals.Logger.Close();
|
2016-09-02 13:42:18 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-10-20 16:00:54 -07:00
|
|
|
|
// Feature flags
|
2017-02-02 15:08:21 -08:00
|
|
|
|
bool archive = false,
|
2016-09-02 13:42:18 -07:00
|
|
|
|
build = false,
|
|
|
|
|
|
dbstats = false,
|
2016-10-17 11:27:50 -07:00
|
|
|
|
depotRescan = false,
|
2016-09-02 13:42:18 -07:00
|
|
|
|
diffdat = false,
|
|
|
|
|
|
dir2dat = false,
|
2016-10-12 16:51:42 -07:00
|
|
|
|
export = false,
|
2016-09-02 13:42:18 -07:00
|
|
|
|
fixdat = false,
|
2017-02-02 22:06:20 -08:00
|
|
|
|
import = false,
|
2016-09-02 13:42:18 -07:00
|
|
|
|
lookup = false,
|
|
|
|
|
|
memstats = false,
|
2017-02-02 21:57:34 -08:00
|
|
|
|
merge = false,
|
2016-09-02 13:42:18 -07:00
|
|
|
|
miss = false,
|
|
|
|
|
|
progress = false,
|
|
|
|
|
|
purgeBackup = false,
|
|
|
|
|
|
purgeDelete = false,
|
|
|
|
|
|
refreshDats = false,
|
|
|
|
|
|
shutdown = false;
|
2016-10-20 16:00:54 -07:00
|
|
|
|
|
|
|
|
|
|
// User flags
|
|
|
|
|
|
bool copy = false,
|
2017-02-02 22:06:20 -08:00
|
|
|
|
logOnly = false,
|
2016-10-20 16:00:54 -07:00
|
|
|
|
onlyNeeded = false;
|
|
|
|
|
|
|
|
|
|
|
|
// User inputs
|
2017-02-02 21:57:34 -08:00
|
|
|
|
string depotPath = "",
|
|
|
|
|
|
newdat = "",
|
2017-01-27 16:53:29 -08:00
|
|
|
|
outdat = "";
|
2016-09-02 13:42:18 -07:00
|
|
|
|
List<string> inputs = new List<string>();
|
|
|
|
|
|
|
2017-12-05 18:13:42 -08:00
|
|
|
|
// Get the first argument as a feature flag
|
|
|
|
|
|
string feature = args[0];
|
|
|
|
|
|
|
|
|
|
|
|
// Verify that the flag is valid
|
|
|
|
|
|
if (!_help.TopLevelFlag(feature))
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.User("'{0}' is not valid feature flag", feature);
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
Globals.Logger.Close();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now get the proper name for the feature
|
|
|
|
|
|
feature = _help.GetFeatureName(feature);
|
|
|
|
|
|
|
|
|
|
|
|
// If we had the help feature first
|
|
|
|
|
|
if (feature == "Help")
|
2016-09-02 13:42:18 -07:00
|
|
|
|
{
|
2017-12-05 18:13:42 -08:00
|
|
|
|
// If we had something else after help
|
|
|
|
|
|
if (args.Length > 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
_help.OutputIndividualFeature(args[1]);
|
|
|
|
|
|
Globals.Logger.Close();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Otherwise, show generic help
|
|
|
|
|
|
else
|
2016-09-02 13:42:18 -07:00
|
|
|
|
{
|
2017-12-05 18:13:42 -08:00
|
|
|
|
_help.OutputGenericHelp();
|
|
|
|
|
|
Globals.Logger.Close();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now verify that all other flags are valid
|
|
|
|
|
|
for (int i = 1; i < args.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Verify that the current flag is proper for the feature
|
|
|
|
|
|
if (!_help[feature].ValidateInput(args[i]))
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("Invalid input detected: {0}", args[i]);
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
Globals.Logger.Close();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Special precautions for files and directories
|
|
|
|
|
|
if (File.Exists(args[i]) || Directory.Exists(args[i]))
|
|
|
|
|
|
{
|
|
|
|
|
|
inputs.Add(args[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now loop through all inputs
|
|
|
|
|
|
Dictionary<string, Feature> features = _help.GetEnabledFeatures();
|
|
|
|
|
|
foreach (KeyValuePair<string, Feature> feat in features)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check all of the flag names and translate to arguments
|
|
|
|
|
|
switch (feat.Key)
|
|
|
|
|
|
{
|
2016-10-20 16:00:54 -07:00
|
|
|
|
// User flags
|
2017-12-05 18:13:42 -08:00
|
|
|
|
case "copy":
|
2016-10-20 16:00:54 -07:00
|
|
|
|
copy = true;
|
|
|
|
|
|
break;
|
2017-12-05 18:13:42 -08:00
|
|
|
|
case "log-only":
|
2017-02-02 22:06:20 -08:00
|
|
|
|
logOnly = true;
|
|
|
|
|
|
break;
|
2017-12-05 18:13:42 -08:00
|
|
|
|
case "only-needed":
|
2016-10-20 16:00:54 -07:00
|
|
|
|
onlyNeeded = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
// User inputs
|
2017-12-05 18:13:42 -08:00
|
|
|
|
case "depot":
|
|
|
|
|
|
depotPath = (string)feat.Value.GetValue();
|
2017-02-02 21:57:34 -08:00
|
|
|
|
break;
|
2017-12-05 18:13:42 -08:00
|
|
|
|
case "new":
|
|
|
|
|
|
newdat = (string)feat.Value.GetValue();
|
2016-10-20 16:00:54 -07:00
|
|
|
|
break;
|
2017-12-05 18:13:42 -08:00
|
|
|
|
case "out":
|
|
|
|
|
|
outdat = (string)feat.Value.GetValue();
|
2016-09-02 13:42:18 -07:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now take care of each mode in succesion
|
2017-12-05 19:09:32 -08:00
|
|
|
|
switch(feature)
|
2016-10-12 16:51:42 -07:00
|
|
|
|
{
|
2017-12-05 19:09:32 -08:00
|
|
|
|
case "Help":
|
|
|
|
|
|
// No-op as this should be caught
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Adds ROM files from the specified directories to the ROM archive
|
|
|
|
|
|
case "Archive":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitArchive(inputs, onlyNeeded);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// For each specified DAT file it creates the torrentzip files
|
|
|
|
|
|
case "Build":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitBuild(inputs, copy);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Prints db stats
|
|
|
|
|
|
case "Stats":
|
|
|
|
|
|
DisplayDBStats();
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Rescan a specific depot
|
|
|
|
|
|
case "Rescan Depots":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (string input in inputs)
|
|
|
|
|
|
{
|
|
|
|
|
|
Rescan(input);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Creates a DAT file with those entries that are in new DAT
|
|
|
|
|
|
case "Diffdat":
|
|
|
|
|
|
InitDiffDat(newdat);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Creates a DAT file for the specified input directory
|
|
|
|
|
|
case "Dir2Dat":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitDir2Dat(inputs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Export the database to file
|
|
|
|
|
|
case "Export":
|
|
|
|
|
|
ExportDatabase();
|
|
|
|
|
|
break;
|
|
|
|
|
|
// For each specified DAT file it creates a fix DAT
|
|
|
|
|
|
case "Fixdat":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitFixdat(inputs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Import a CSV into the database
|
|
|
|
|
|
case "Import":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitImport(inputs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// For each specified hash it looks up any available information
|
|
|
|
|
|
case "Lookup":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitLookup(inputs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Prints memory stats
|
|
|
|
|
|
case "Memstats":
|
|
|
|
|
|
DisplayMemoryStats();
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Merges depots
|
|
|
|
|
|
case "Merge":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitMerge(inputs, depotPath, onlyNeeded);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// For each specified DAT file it creates a miss file and a have file
|
|
|
|
|
|
case "Miss":
|
|
|
|
|
|
if (inputs.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.Logger.Error("This feature requires at least one input");
|
|
|
|
|
|
_help.OutputIndividualFeature(feature);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
InitMiss(inputs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Moves DAT index entries for orphaned DATs
|
|
|
|
|
|
case "Purge Backup":
|
|
|
|
|
|
PurgeBackup(logOnly);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Deletes DAT index entries for orphaned DATs
|
|
|
|
|
|
case "Purge Delete":
|
|
|
|
|
|
PurgeDelete(logOnly);
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Refreshes the DAT index from the files in the DAT master directory tree
|
|
|
|
|
|
case "Refresh DATs":
|
|
|
|
|
|
RefreshDatabase();
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Shows progress of the currently running command
|
|
|
|
|
|
case "Progress":
|
|
|
|
|
|
Globals.Logger.User("This feature is not used in RombaSharp: progress");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "Shutdown":
|
|
|
|
|
|
Globals.Logger.User("This feature is not used in RombaSharp: shutdown");
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
_help.OutputGenericHelp();
|
|
|
|
|
|
break;
|
2016-09-02 13:42:18 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-03-01 21:26:27 -08:00
|
|
|
|
Globals.Logger.Close();
|
2016-09-02 13:42:18 -07:00
|
|
|
|
return;
|
2016-08-30 15:02:48 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|