mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 11:14:23 +00:00
Remove RombaSharp
This commit is contained in:
@@ -70,12 +70,6 @@ A small tool that allows users to extract, store, and remove copier headers for
|
||||
|
||||
This tool is no longer hosted in this repository. If you wish to use this tool, you can find it [here](https://github.com/SabreTools/SabreTools.Skippers/).
|
||||
|
||||
### RombaSharp \[Deprecated\]
|
||||
|
||||
An in-progress tool that will try to act as a C# port of the Go-based [Romba](https://github.com/uwedeportivo/romba/) program. All features that are not already a part of SabreTools will be attempted to be added to this program. It is NOT ready for use yet. For any inquiries into the full features of this tool, please contact the project.
|
||||
|
||||
As of 2024-10-29, this tool is considered deprecated and will be removed at some future point. Previous releases of RombaSharp will still be made available in their respective release pages. No further explicit updates will be made.
|
||||
|
||||
## Builds
|
||||
|
||||
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools/releases)
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatItems;
|
||||
using SabreTools.DatItems.Formats;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.FileTypes;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Archive : BaseFeature
|
||||
{
|
||||
public const string Value = "Archive";
|
||||
|
||||
public Archive()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["archive"]);
|
||||
Description = "Adds ROM files from the specified directories to the ROM archive.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"Adds ROM files from the specified directories to the ROM archive.
|
||||
Traverses the specified directory trees looking for zip files and normal files.
|
||||
Unpacked files will be stored as individual entries. Prior to unpacking a zip
|
||||
file, the external SHA1 is checked against the DAT index.
|
||||
If -only-needed is set, only those files are put in the ROM archive that
|
||||
have a current entry in the DAT index.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(OnlyNeededFlag);
|
||||
AddFeature(ResumeStringInput);
|
||||
AddFeature(IncludeZipsInt32Input); // Defaults to 0
|
||||
AddFeature(WorkersInt32Input);
|
||||
AddFeature(IncludeGZipsInt32Input); // Defaults to 0
|
||||
AddFeature(Include7ZipsInt32Input); // Defaults to 0
|
||||
AddFeature(SkipInitialScanFlag);
|
||||
AddFeature(UseGolangZipFlag);
|
||||
AddFeature(NoDbFlag);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, SabreTools.Help.Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get the archive scanning level
|
||||
// TODO: Remove usage
|
||||
int sevenzip = GetInt32(features, Include7ZipsInt32Value);
|
||||
int gz = GetInt32(features, IncludeGZipsInt32Value);
|
||||
int zip = GetInt32(features, IncludeZipsInt32Value);
|
||||
|
||||
// Get feature flags
|
||||
bool noDb = GetBoolean(features, NoDbValue);
|
||||
bool onlyNeeded = GetBoolean(features, OnlyNeededValue);
|
||||
HashType[] hashes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
var dfd = new DatFromDir(hashes, SkipFileType.None, addBlanks: false);
|
||||
|
||||
// First we want to get just all directories from the inputs
|
||||
List<string> onlyDirs = [];
|
||||
foreach (string input in Inputs)
|
||||
{
|
||||
if (Directory.Exists(input))
|
||||
onlyDirs.Add(Path.GetFullPath(input));
|
||||
}
|
||||
|
||||
// Then process all of the input directories into an internal DAT
|
||||
DatFile df = DatFile.Create();
|
||||
foreach (string dir in onlyDirs)
|
||||
{
|
||||
dfd.PopulateFromDir(df, dir, TreatAsFile.NonArchive);
|
||||
dfd.PopulateFromDir(df, dir, TreatAsFile.All);
|
||||
}
|
||||
|
||||
// Create an empty Dat for files that need to be rebuilt
|
||||
var need = DatFile.Create();
|
||||
|
||||
// Get the first depot as output
|
||||
var depotKeyEnumerator = _depots.Keys.GetEnumerator();
|
||||
depotKeyEnumerator.MoveNext();
|
||||
string firstDepot = depotKeyEnumerator.Current;
|
||||
|
||||
// Open the database connection
|
||||
var dbc = new SqliteConnection(_connectionString);
|
||||
dbc.Open();
|
||||
|
||||
// Now that we have the Dats, add the files to the database
|
||||
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, depot) VALUES";
|
||||
string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES";
|
||||
string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES";
|
||||
|
||||
foreach (string key in df.Items.Keys)
|
||||
{
|
||||
List<DatItem>? datItems = df.Items[key];
|
||||
if (datItems == null)
|
||||
continue;
|
||||
|
||||
foreach (Rom rom in datItems)
|
||||
{
|
||||
string? crc = rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey);
|
||||
string? md5 = rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key);
|
||||
string? sha1 = rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key);
|
||||
|
||||
// If we care about if the file exists, check the databse first
|
||||
if (onlyNeeded && !noDb)
|
||||
{
|
||||
string query = "SELECT * FROM crcsha1 JOIN md5sha1 ON crcsha1.sha1=md5sha1.sha1"
|
||||
+ $" WHERE crcsha1.crc=\"{crc}\""
|
||||
+ $" OR md5sha1.md5=\"{md5}\""
|
||||
+ $" OR md5sha1.sha1=\"{sha1}\"";
|
||||
var slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
// Add to the queries
|
||||
if (!string.IsNullOrWhiteSpace(crc))
|
||||
crcquery += $" (\"{crc}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(md5))
|
||||
md5query += $" (\"{md5}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(sha1))
|
||||
{
|
||||
sha1query += $" (\"{sha1}\", \"{firstDepot}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(crc))
|
||||
crcsha1query += $" (\"{crc}\", \"{sha1}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(md5))
|
||||
md5sha1query += $" (\"{md5}\", \"{sha1}\"),";
|
||||
}
|
||||
|
||||
// Add to the Dat
|
||||
need.Items.Add(key, rom);
|
||||
}
|
||||
}
|
||||
// Otherwise, just add the file to the list
|
||||
else
|
||||
{
|
||||
// Add to the queries
|
||||
if (!noDb)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(crc))
|
||||
crcquery += $" (\"{crc}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(md5))
|
||||
md5query += $" (\"{md5}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(sha1))
|
||||
{
|
||||
sha1query += $" (\"{sha1}\", \"{firstDepot}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(crc))
|
||||
crcsha1query += $" (\"{crc}\", \"{sha1}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(md5))
|
||||
md5sha1query += $" (\"{md5}\", \"{sha1}\"),";
|
||||
}
|
||||
}
|
||||
|
||||
// Add to the Dat
|
||||
need.Items.Add(key, rom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now run the queries, if they're populated
|
||||
if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES")
|
||||
{
|
||||
var slc = new SqliteCommand(crcquery.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
}
|
||||
|
||||
if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES")
|
||||
{
|
||||
var slc = new SqliteCommand(md5query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
}
|
||||
|
||||
if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES")
|
||||
{
|
||||
var slc = new SqliteCommand(sha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
}
|
||||
|
||||
if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES")
|
||||
{
|
||||
var slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
}
|
||||
|
||||
if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES")
|
||||
{
|
||||
SqliteCommand slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
}
|
||||
|
||||
// Create the sorting object to use and rebuild the needed files
|
||||
Rebuilder.RebuildGeneric(
|
||||
need,
|
||||
onlyDirs,
|
||||
outDir: firstDepot,
|
||||
outputFormat: OutputFormat.TorrentGzipRomba,
|
||||
asFiles: TreatAsFile.NonArchive);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,916 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SabreTools.Core;
|
||||
using SabreTools.Core.Tools;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatItems;
|
||||
using SabreTools.DatItems.Formats;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.FileTypes;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.IO.Logging;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class BaseFeature : TopLevel
|
||||
{
|
||||
#region Logging
|
||||
|
||||
/// <summary>
|
||||
/// Logging object
|
||||
/// </summary>
|
||||
protected Logger logger = new Logger();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Features
|
||||
|
||||
#region Flag features
|
||||
|
||||
internal const string CopyValue = "copy";
|
||||
internal static SabreTools.Help.Feature CopyFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
CopyValue,
|
||||
"-copy",
|
||||
"Copy files to output instead of rebuilding",
|
||||
ParameterType.Flag);
|
||||
}
|
||||
} // Unique to RombaSharp
|
||||
|
||||
internal const string FixdatOnlyValue = "fixdat-only";
|
||||
internal static SabreTools.Help.Feature FixdatOnlyFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
FixdatOnlyValue,
|
||||
"-fixdatOnly",
|
||||
"only fix dats and don't generate torrentzips",
|
||||
ParameterType.Flag);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string LogOnlyValue = "log-only";
|
||||
internal static SabreTools.Help.Feature LogOnlyFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
LogOnlyValue,
|
||||
"-log-only",
|
||||
"Only write out actions to log",
|
||||
ParameterType.Flag);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string NoDbValue = "no-db";
|
||||
internal static SabreTools.Help.Feature NoDbFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
NoDbValue,
|
||||
"-no-db",
|
||||
"archive into depot but do not touch DB index and ignore only-needed flag",
|
||||
ParameterType.Flag);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string OnlyNeededValue = "only-needed";
|
||||
internal static SabreTools.Help.Feature OnlyNeededFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
OnlyNeededValue,
|
||||
"-only-needed",
|
||||
"only archive ROM files actually referenced by DAT files from the DAT index",
|
||||
ParameterType.Flag);
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
internal const string SkipInitialScanValue = "skip-initial-scan";
|
||||
internal static SabreTools.Help.Feature SkipInitialScanFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
SkipInitialScanValue,
|
||||
"-skip-initial-scan",
|
||||
"skip the initial scan of the files to determine amount of work",
|
||||
ParameterType.Flag);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string UseGolangZipValue = "use-golang-zip";
|
||||
internal static SabreTools.Help.Feature UseGolangZipFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
UseGolangZipValue,
|
||||
"-use-golang-zip",
|
||||
"use go zip implementation instead of zlib",
|
||||
ParameterType.Flag);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Int32 features
|
||||
|
||||
internal const string Include7ZipsInt32Value = "include-7zips";
|
||||
internal static SabreTools.Help.Feature Include7ZipsInt32Input
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
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",
|
||||
ParameterType.Int32);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string IncludeGZipsInt32Value = "include-gzips";
|
||||
internal static SabreTools.Help.Feature IncludeGZipsInt32Input
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
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",
|
||||
ParameterType.Int32);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string IncludeZipsInt32Value = "include-zips";
|
||||
internal static SabreTools.Help.Feature IncludeZipsInt32Input
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
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",
|
||||
ParameterType.Int32);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string SubworkersInt32Value = "subworkers";
|
||||
internal static SabreTools.Help.Feature SubworkersInt32Input
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
SubworkersInt32Value,
|
||||
"-subworkers",
|
||||
"how many subworkers to launch for each worker",
|
||||
ParameterType.Int32);
|
||||
}
|
||||
} // Defaults to Workers count in config
|
||||
|
||||
internal const string WorkersInt32Value = "workers";
|
||||
internal static SabreTools.Help.Feature WorkersInt32Input
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
WorkersInt32Value,
|
||||
"-workers",
|
||||
"how many workers to launch for the job",
|
||||
ParameterType.Int32);
|
||||
}
|
||||
} // Defaults to Workers count in config
|
||||
|
||||
#endregion
|
||||
|
||||
#region Int64 features
|
||||
|
||||
internal const string SizeInt64Value = "size";
|
||||
internal static SabreTools.Help.Feature SizeInt64Input
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
SizeInt64Value,
|
||||
"-size",
|
||||
"size of the rom to lookup",
|
||||
ParameterType.Int64);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region List<String> features
|
||||
|
||||
internal const string DatsListStringValue = "dats";
|
||||
internal static SabreTools.Help.Feature DatsListStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
DatsListStringValue,
|
||||
"-dats",
|
||||
"purge only roms declared in these dats",
|
||||
ParameterType.List);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string DepotListStringValue = "depot";
|
||||
internal static SabreTools.Help.Feature DepotListStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
DepotListStringValue,
|
||||
"-depot",
|
||||
"work only on specified depot path",
|
||||
ParameterType.List);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String features
|
||||
|
||||
internal const string BackupStringValue = "backup";
|
||||
internal static SabreTools.Help.Feature BackupStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
BackupStringValue,
|
||||
"-backup",
|
||||
"backup directory where backup files are moved to",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string DescriptionStringValue = "description";
|
||||
internal static SabreTools.Help.Feature DescriptionStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
DescriptionStringValue,
|
||||
"-description",
|
||||
"description value in DAT header",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
internal const string MissingSha1sStringValue = "missing-sha1s";
|
||||
internal static SabreTools.Help.Feature MissingSha1sStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
MissingSha1sStringValue,
|
||||
"-missingSha1s",
|
||||
"write paths of dats with missing sha1s into this file",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string NameStringValue = "name";
|
||||
internal static SabreTools.Help.Feature NameStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
NameStringValue,
|
||||
"-name",
|
||||
"name value in DAT header",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string NewStringValue = "new";
|
||||
internal static SabreTools.Help.Feature NewStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
NewStringValue,
|
||||
"-new",
|
||||
"new DAT file",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string OldStringValue = "old";
|
||||
internal static SabreTools.Help.Feature OldStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
OldStringValue,
|
||||
"-old",
|
||||
"old DAT file",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string OutStringValue = "out";
|
||||
internal static SabreTools.Help.Feature OutStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
OutStringValue,
|
||||
"-out",
|
||||
"output file",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string ResumeStringValue = "resume";
|
||||
internal static SabreTools.Help.Feature ResumeStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
ResumeStringValue,
|
||||
"-resume",
|
||||
"resume a previously interrupted operation from the specified path",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
internal const string SourceStringValue = "source";
|
||||
internal static SabreTools.Help.Feature SourceStringInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SabreTools.Help.Feature(
|
||||
SourceStringValue,
|
||||
"-source",
|
||||
"source directory",
|
||||
ParameterType.String);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#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
|
||||
|
||||
// General settings
|
||||
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
|
||||
internal static int _verbosity; // Verbosity of the output
|
||||
internal static int _cores; // Forced CPU cores
|
||||
|
||||
// DatRoot settings
|
||||
internal static string? _dats; // DatRoot folder location
|
||||
internal static string? _db; // Database name
|
||||
|
||||
// Depot settings
|
||||
internal static Dictionary<string, Tuple<long, bool>> _depots = []; // Folder location, Max size
|
||||
|
||||
// Server settings
|
||||
internal static int _port; // Web server port
|
||||
|
||||
// Other internal variables
|
||||
internal const string _config = "config.xml";
|
||||
internal static string? _connectionString;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Add Feature Groups
|
||||
|
||||
/// <summary>
|
||||
/// Add common features
|
||||
/// </summary>
|
||||
protected void AddCommonFeatures()
|
||||
{
|
||||
AddFeature(ScriptFlag);
|
||||
AddFeature(LogLevelStringInput);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, SabreTools.Help.Feature?> features)
|
||||
{
|
||||
LogLevel = GetString(features, LogLevelStringValue).AsEnumValue<LogLevel>();
|
||||
ScriptMode = GetBoolean(features, ScriptValue);
|
||||
|
||||
InitializeConfiguration();
|
||||
EnsureDatabase(_db, _connectionString);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public void EnsureDatabase(string? db, string? connectionString)
|
||||
{
|
||||
// Missing database or connection string can't work
|
||||
if (db == null || connectionString == null)
|
||||
return;
|
||||
|
||||
// Make sure the file exists
|
||||
if (!System.IO.File.Exists(db))
|
||||
System.IO.File.Create(db);
|
||||
|
||||
// Open the database connection
|
||||
var 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();
|
||||
}
|
||||
}
|
||||
|
||||
#region Helper methods
|
||||
|
||||
/// <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>
|
||||
internal Dictionary<string, string> GetValidDats(List<string> inputs)
|
||||
{
|
||||
// Get a dictionary of filenames that actually exist in the DATRoot, logging which ones are not
|
||||
#if NET20 || NET35
|
||||
List<string> datRootDats = [.. Directory.GetFiles(_dats!, "*")];
|
||||
#else
|
||||
List<string> datRootDats = [.. Directory.EnumerateFiles(_dats!, "*", SearchOption.AllDirectories)];
|
||||
#endif
|
||||
List<string> lowerCaseDats = datRootDats.ConvertAll(i => Path.GetFileName(i).ToLowerInvariant());
|
||||
Dictionary<string, string> foundDats = [];
|
||||
foreach (string input in inputs)
|
||||
{
|
||||
if (lowerCaseDats.Contains(input.ToLowerInvariant()))
|
||||
{
|
||||
string fullpath = Path.GetFullPath(datRootDats[lowerCaseDats.IndexOf(input.ToLowerInvariant())]);
|
||||
string? sha1 = ByteArrayExtensions.ByteArrayToString(BaseFile.GetInfo(fullpath, hashes: [HashType.SHA1])?.SHA1);
|
||||
if (sha1 != null)
|
||||
foundDats.Add(sha1, fullpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warning($"The file '{input}' could not be found in the DAT root");
|
||||
}
|
||||
}
|
||||
|
||||
return foundDats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Romba application from XML config
|
||||
/// </summary>
|
||||
private static void InitializeConfiguration()
|
||||
{
|
||||
// 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",
|
||||
db = "db";
|
||||
Dictionary<string, Tuple<long, bool>> depots = [];
|
||||
|
||||
// Get the XML text reader for the configuration file, if possible
|
||||
XmlReader xtr = XmlReader.Create(_config, new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
IgnoreComments = true,
|
||||
IgnoreWhitespace = true,
|
||||
ValidationFlags = XmlSchemaValidationFlags.None,
|
||||
ValidationType = ValidationType.None,
|
||||
});
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
string root = string.Empty;
|
||||
long maxsize = -1;
|
||||
bool online = true;
|
||||
|
||||
while (!subreader.EOF)
|
||||
{
|
||||
// We only want elements
|
||||
if (subreader.NodeType != XmlNodeType.Element)
|
||||
{
|
||||
subreader.Read();
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
depots.Add(root, new Tuple<long, bool>(maxsize, online));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore add errors
|
||||
}
|
||||
}
|
||||
|
||||
xtr.Skip();
|
||||
break;
|
||||
case "port":
|
||||
port = xtr.ReadElementContentAsInt();
|
||||
break;
|
||||
default:
|
||||
xtr.Read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now validate the values given
|
||||
if (workers < 1)
|
||||
workers = 1;
|
||||
if (workers > 8)
|
||||
workers = 8;
|
||||
|
||||
if (!Directory.Exists(logdir))
|
||||
Directory.CreateDirectory(logdir);
|
||||
|
||||
if (!Directory.Exists(tmpdir))
|
||||
Directory.CreateDirectory(tmpdir);
|
||||
|
||||
if (!Directory.Exists(webdir))
|
||||
Directory.CreateDirectory(webdir);
|
||||
|
||||
if (!Directory.Exists(baddir))
|
||||
Directory.CreateDirectory(baddir);
|
||||
|
||||
if (verbosity < 0)
|
||||
verbosity = 0;
|
||||
|
||||
if (verbosity > 3)
|
||||
verbosity = 3;
|
||||
|
||||
if (cores < 1)
|
||||
cores = 1;
|
||||
|
||||
if (cores > 16)
|
||||
cores = 16;
|
||||
|
||||
if (!Directory.Exists(dats))
|
||||
Directory.CreateDirectory(dats);
|
||||
|
||||
db = $"{Path.GetFileNameWithoutExtension(db)}.sqlite";
|
||||
string connectionString = $"Data Source={db};Version = 3;";
|
||||
foreach (string key in depots.Keys)
|
||||
{
|
||||
if (!Directory.Exists(key))
|
||||
{
|
||||
Directory.CreateDirectory(key);
|
||||
System.IO.File.CreateText(Path.Combine(key, ".romba_size"));
|
||||
System.IO.File.CreateText(Path.Combine(key, ".romba_size.backup"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!System.IO.File.Exists(Path.Combine(key, ".romba_size")))
|
||||
System.IO.File.CreateText(Path.Combine(key, ".romba_size"));
|
||||
|
||||
if (!System.IO.File.Exists(Path.Combine(key, ".romba_size.backup")))
|
||||
System.IO.File.CreateText(Path.Combine(key, ".romba_size.backup"));
|
||||
}
|
||||
}
|
||||
|
||||
if (port < 0)
|
||||
port = 0;
|
||||
|
||||
if (port > 65535)
|
||||
port = 65535;
|
||||
|
||||
// Finally set all of the fields
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
Globals.MaxThreads = workers;
|
||||
#endif
|
||||
_logdir = logdir;
|
||||
_tmpdir = tmpdir;
|
||||
_webdir = webdir;
|
||||
_baddir = baddir;
|
||||
_verbosity = verbosity;
|
||||
_cores = cores;
|
||||
_dats = dats;
|
||||
_db = db;
|
||||
_connectionString = connectionString;
|
||||
_depots = depots;
|
||||
_port = port;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
internal void AddDatToDatabase(Rom dat, SqliteConnection dbc)
|
||||
{
|
||||
// Get the machine
|
||||
var machine = dat.GetFieldValue<Machine>(DatItem.MachineKey);
|
||||
string? machineName = machine?.GetStringFieldValue(SabreTools.Models.Metadata.Machine.NameKey);
|
||||
if (machine == null || machineName == null)
|
||||
return;
|
||||
|
||||
// Get the dat full path
|
||||
string fullpath = Path.Combine(_dats!, machineName == "dats" ? string.Empty : machineName, dat.GetName()!);
|
||||
|
||||
// Parse the Dat if possible
|
||||
logger.User($"Adding from '{dat.GetName()}'");
|
||||
DatFile tempdat = Parser.CreateAndParse(fullpath);
|
||||
|
||||
// If the Dat wasn't empty, add the information
|
||||
SqliteCommand? slc = null;
|
||||
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;
|
||||
foreach (string romkey in tempdat.Items.Keys)
|
||||
{
|
||||
foreach (DatItem datItem in tempdat.Items[romkey]!)
|
||||
{
|
||||
logger.Verbose($"Checking and adding file '{datItem.GetName() ?? string.Empty}'");
|
||||
|
||||
if (datItem is Disk disk)
|
||||
{
|
||||
hasItems = true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key)))
|
||||
md5query += $" (\"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.SHA1Key)))
|
||||
{
|
||||
sha1query += $" (\"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.SHA1Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key)))
|
||||
md5sha1query += $" (\"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.MD5Key)}\", \"{disk.GetStringFieldValue(SabreTools.Models.Metadata.Disk.SHA1Key)}\"),";
|
||||
}
|
||||
}
|
||||
else if (datItem is Media media)
|
||||
{
|
||||
hasItems = true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key)))
|
||||
md5query += $" (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA1Key)))
|
||||
{
|
||||
sha1query += $" (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA1Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key)))
|
||||
md5sha1query += $" (\"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.MD5Key)}\", \"{media.GetStringFieldValue(SabreTools.Models.Metadata.Media.SHA1Key)}\"),";
|
||||
}
|
||||
}
|
||||
else if (datItem is Rom rom)
|
||||
{
|
||||
hasItems = true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)))
|
||||
crcquery += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)))
|
||||
md5query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)))
|
||||
{
|
||||
sha1query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)))
|
||||
crcsha1query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)))
|
||||
md5sha1query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\"),";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now run the queries after fixing them
|
||||
if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(crcquery.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(md5query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(sha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
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.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\")";
|
||||
slc = new SqliteCommand(datquery, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
slc?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.FileTypes;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Build : BaseFeature
|
||||
{
|
||||
public const string Value = "Build";
|
||||
|
||||
public Build()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["build"]);
|
||||
Description = "For each specified DAT file it creates the torrentzip files.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"For each specified DAT file it creates the torrentzip files in the specified
|
||||
output dir. The files will be placed in the specified location using a folder
|
||||
structure according to the original DAT master directory tree structure.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(OutStringInput);
|
||||
AddFeature(FixdatOnlyFlag);
|
||||
AddFeature(CopyFlag);
|
||||
AddFeature(WorkersInt32Input);
|
||||
AddFeature(SubworkersInt32Input);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
bool copy = GetBoolean(features, CopyValue);
|
||||
string? outdat = GetString(features, OutStringValue);
|
||||
|
||||
// Verify the filenames
|
||||
Dictionary<string, string> foundDats = GetValidDats(Inputs);
|
||||
|
||||
// Ensure the output directory is set
|
||||
if (string.IsNullOrWhiteSpace(outdat))
|
||||
outdat = "out";
|
||||
|
||||
// Now that we have the dictionary, we can loop through and output to a new folder for each
|
||||
foreach (string key in foundDats.Keys)
|
||||
{
|
||||
// Get the DAT file associated with the key
|
||||
DatFile datFile = Parser.CreateAndParse(Path.Combine(_dats!, foundDats[key]));
|
||||
|
||||
// Set the depot values
|
||||
datFile.Header.SetFieldValue<DepotInformation?>(DatHeader.InputDepotKey, new DepotInformation(true, 4));
|
||||
datFile.Header.SetFieldValue<DepotInformation?>(DatHeader.OutputDepotKey, new DepotInformation(true, 4));
|
||||
|
||||
// Create the new output directory if it doesn't exist
|
||||
string outputFolder = Path.Combine(outdat, Path.GetFileNameWithoutExtension(foundDats[key]));
|
||||
outputFolder.Ensure(create: true);
|
||||
|
||||
// Get all online depots
|
||||
List<string> onlineDepots = _depots
|
||||
.Where(d => d.Value.Item2)
|
||||
.Select(d => d.Key)
|
||||
.ToList();
|
||||
|
||||
// Now scan all of those depots and rebuild
|
||||
Rebuilder.RebuildDepot(
|
||||
datFile,
|
||||
onlineDepots,
|
||||
outDir: outputFolder,
|
||||
outputFormat: copy ? OutputFormat.TorrentGzipRomba : OutputFormat.TorrentZip);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Cancel : BaseFeature
|
||||
{
|
||||
public const string Value = "Cancel";
|
||||
|
||||
public Cancel()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["cancel"]);
|
||||
Description = "Cancels current long-running job";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Cancels current long-running job.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
logger.User("This feature is not yet implemented: cancel");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.Reports;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class DatStats : BaseFeature
|
||||
{
|
||||
public const string Value = "DatStats";
|
||||
|
||||
public DatStats()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["datstats"]);
|
||||
Description = "Prints dat stats.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Print dat stats.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// If we have no inputs listed, we want to use datroot
|
||||
if (Inputs.Count == 0)
|
||||
Inputs.Add(Path.GetFullPath(_dats!));
|
||||
|
||||
// Now output the stats for all inputs
|
||||
var statistics = Statistics.CalculateStatistics(Inputs, single: true);
|
||||
Statistics.Write(
|
||||
statistics,
|
||||
"rombasharp-datstats",
|
||||
outDir: null,
|
||||
baddumpCol: true,
|
||||
nodumpCol: true,
|
||||
StatReportFormat.Textfile);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class DbStats : BaseFeature
|
||||
{
|
||||
public const string Value = "DbStats";
|
||||
|
||||
public DbStats()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["dbstats"]);
|
||||
Description = "Prints db stats.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Print db stats.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
SqliteConnection dbc = new SqliteConnection(_connectionString);
|
||||
dbc.Open();
|
||||
|
||||
// Total number of CRCs
|
||||
string query = "SELECT COUNT(*) FROM crc";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
logger.User($"Total CRCs: {(long)slc.ExecuteScalar()!}");
|
||||
|
||||
// Total number of MD5s
|
||||
query = "SELECT COUNT(*) FROM md5";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
logger.User($"Total MD5s: {(long)slc.ExecuteScalar()!}");
|
||||
|
||||
// Total number of SHA1s
|
||||
query = "SELECT COUNT(*) FROM sha1";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
logger.User($"Total SHA1s: {(long)slc.ExecuteScalar()!}");
|
||||
|
||||
// Total number of DATs
|
||||
query = "SELECT COUNT(*) FROM dat";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
logger.User($"Total DATs: {(long)slc.ExecuteScalar()!}");
|
||||
|
||||
slc.Dispose();
|
||||
dbc.Dispose();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Diffdat : BaseFeature
|
||||
{
|
||||
public const string Value = "Diffdat";
|
||||
|
||||
public Diffdat()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["diffdat"]);
|
||||
Description = "Creates a DAT file with those entries that are in -new DAT.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"Creates a DAT file with those entries that are in -new DAT file and not
|
||||
in -old DAT file. Ignores those entries in -old that are not in -new.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(OutStringInput);
|
||||
AddFeature(OldStringInput);
|
||||
AddFeature(NewStringInput);
|
||||
AddFeature(NameStringInput);
|
||||
AddFeature(DescriptionStringInput);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
string? name = GetString(features, NameStringValue);
|
||||
string? description = GetString(features, DescriptionStringValue);
|
||||
string? newdat = GetString(features, NewStringValue);
|
||||
string? olddat = GetString(features, OldStringValue);
|
||||
string? outdat = GetString(features, OutStringValue);
|
||||
|
||||
// Ensure the output directory
|
||||
outdat = outdat.Ensure(create: true);
|
||||
|
||||
// Check that all required files exist
|
||||
if (olddat == null || !File.Exists(olddat))
|
||||
{
|
||||
logger.Error($"File '{olddat}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newdat == null || !File.Exists(newdat))
|
||||
{
|
||||
logger.Error($"File '{newdat}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the encapsulating datfile
|
||||
DatFile datfile = DatFile.Create();
|
||||
datfile.Header.SetFieldValue<string?>(SabreTools.Models.Metadata.Header.NameKey, name);
|
||||
datfile.Header.SetFieldValue<string?>(SabreTools.Models.Metadata.Header.DescriptionKey, description);
|
||||
Parser.ParseInto(datfile, olddat);
|
||||
|
||||
// Diff against the new datfile
|
||||
DatFile intDat = Parser.CreateAndParse(newdat);
|
||||
DatFileTool.DiffAgainst(datfile, intDat, false);
|
||||
Writer.Write(intDat, outdat!);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.FileTypes;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Dir2Dat : BaseFeature
|
||||
{
|
||||
public const string Value = "Dir2Dat";
|
||||
|
||||
public Dir2Dat()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["dir2dat"]);
|
||||
Description = "Creates a DAT file for the specified input directory and saves it to the -out filename.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Creates a DAT file for the specified input directory and saves it to the -out filename.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(OutStringInput);
|
||||
AddFeature(SourceStringInput);
|
||||
AddFeature(NameStringInput); // Defaults to "untitled"
|
||||
AddFeature(DescriptionStringInput);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
string? name = GetString(features, NameStringValue);
|
||||
string? description = GetString(features, DescriptionStringValue);
|
||||
string? source = GetString(features, SourceStringValue);
|
||||
string? outdat = GetString(features, OutStringValue);
|
||||
HashType[] hashes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
var dfd = new DatFromDir(hashes, SkipFileType.None, addBlanks: false);
|
||||
|
||||
// Ensure the output directory
|
||||
outdat = outdat.Ensure(create: true);
|
||||
|
||||
// Check that all required directories exist
|
||||
if (source == null || !Directory.Exists(source))
|
||||
{
|
||||
logger.Error($"File '{source}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and write the encapsulating datfile
|
||||
DatFile datfile = DatFile.Create();
|
||||
datfile.Header.SetFieldValue<string?>(SabreTools.Models.Metadata.Header.NameKey, string.IsNullOrWhiteSpace(name) ? "untitled" : name);
|
||||
datfile.Header.SetFieldValue<string?>(SabreTools.Models.Metadata.Header.DescriptionKey, description);
|
||||
dfd.PopulateFromDir(datfile, source, TreatAsFile.NonArchive);
|
||||
Writer.Write(datfile, outdat!);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class DisplayHelp : BaseFeature
|
||||
{
|
||||
public const string Value = "Help";
|
||||
|
||||
public DisplayHelp()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["-?", "-h", "--help"]);
|
||||
Description = "Show this help";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Built-in to most of the programs is a basic help text.";
|
||||
}
|
||||
|
||||
public override bool ProcessArgs(string[] args, FeatureSet help)
|
||||
{
|
||||
// If we had something else after help
|
||||
if (args.Length > 1)
|
||||
{
|
||||
help.OutputIndividualFeature(args[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, show generic help
|
||||
else
|
||||
{
|
||||
help.OutputGenericHelp();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class DisplayHelpDetailed : BaseFeature
|
||||
{
|
||||
public const string Value = "Help (Detailed)";
|
||||
|
||||
public DisplayHelpDetailed()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["-??", "-hd", "--help-detailed"]);
|
||||
Description = "Show this detailed help";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Display a detailed help text to the screen.";
|
||||
}
|
||||
|
||||
public override bool ProcessArgs(string[] args, FeatureSet help)
|
||||
{
|
||||
// If we had something else after help
|
||||
if (args.Length > 1)
|
||||
{
|
||||
help.OutputIndividualFeature(args[1], includeLongDescription: true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, show generic help
|
||||
else
|
||||
{
|
||||
help.OutputAllHelp();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class EDiffdat : BaseFeature
|
||||
{
|
||||
public const string Value = "EDiffdat";
|
||||
|
||||
public EDiffdat()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["ediffdat"]);
|
||||
Description = "Creates a DAT file with those entries that are in -new DAT.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"Creates a DAT file with those entries that are in -new DAT files and not in -old DAT files. Ignores those entries in -old that are not in -new.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(OutStringInput);
|
||||
AddFeature(OldStringInput);
|
||||
AddFeature(NewStringInput);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
string? olddat = GetString(features, OldStringValue);
|
||||
string? outdat = GetString(features, OutStringValue);
|
||||
string? newdat = GetString(features, NewStringValue);
|
||||
|
||||
// Ensure the output directory
|
||||
outdat = outdat.Ensure(create: true);
|
||||
|
||||
// Check that all required files exist
|
||||
if (!File.Exists(olddat))
|
||||
{
|
||||
logger.Error($"File '{olddat}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!File.Exists(newdat))
|
||||
{
|
||||
logger.Error($"File '{newdat}' does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the encapsulating datfile
|
||||
DatFile datfile = Parser.CreateAndParse(olddat);
|
||||
|
||||
// Diff against the new datfile
|
||||
DatFile intDat = Parser.CreateAndParse(newdat);
|
||||
DatFileTool.DiffAgainst(datfile, intDat, false);
|
||||
Writer.Write(intDat, outdat!);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Export : BaseFeature
|
||||
{
|
||||
public const string Value = "Export";
|
||||
|
||||
// Unique to RombaSharp
|
||||
public Export()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["export"]);
|
||||
Description = "Exports db to export.csv";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Exports db to standardized export.csv";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
// TODO: Add ability to say which depot the files are found in
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
SqliteConnection dbc = new SqliteConnection(_connectionString);
|
||||
dbc.Open();
|
||||
StreamWriter sw = new StreamWriter(File.Create("export.csv"));
|
||||
|
||||
// First take care of all file hashes
|
||||
sw.WriteLine("CRC,MD5,SHA-1"); // ,Depot
|
||||
|
||||
string query = "SELECT crcsha1.crc, md5sha1.md5, md5sha1.sha1 FROM crcsha1 JOIN md5sha1 ON crcsha1.sha1=md5sha1.sha1"; // md5sha1.sha1=sha1depot.sha1
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
while (sldr.Read())
|
||||
{
|
||||
string line = $"{sldr.GetString(0)},{sldr.GetString(1)},{sldr.GetString(2)}"; // + ",{sldr.GetString(3)}";
|
||||
sw.WriteLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
// Then take care of all DAT hashes
|
||||
sw.WriteLine();
|
||||
sw.WriteLine("DAT Hash");
|
||||
|
||||
query = "SELECT hash FROM dat";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
sldr = slc.ExecuteReader();
|
||||
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
while (sldr.Read())
|
||||
{
|
||||
sw.WriteLine(sldr.GetString(0));
|
||||
}
|
||||
}
|
||||
|
||||
sldr.Dispose();
|
||||
slc.Dispose();
|
||||
sw.Dispose();
|
||||
dbc.Dispose();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Fixdat : BaseFeature
|
||||
{
|
||||
public const string Value = "Fixdat";
|
||||
|
||||
public Fixdat()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["fixdat"]);
|
||||
Description = "For each specified DAT file it creates a fix DAT.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"For each specified DAT file it creates a fix DAT with the missing entries for that DAT. If nothing is missing it doesn't create a fix DAT for that particular DAT.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(OutStringInput);
|
||||
AddFeature(FixdatOnlyFlag); // Enabled by default
|
||||
AddFeature(WorkersInt32Input);
|
||||
AddFeature(SubworkersInt32Input);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
// Inputs
|
||||
bool fixdatOnly = GetBoolean(features, FixdatOnlyValue);
|
||||
int subworkers = GetInt32(features, SubworkersInt32Value);
|
||||
int workers = GetInt32(features, WorkersInt32Value);
|
||||
string? outdat = GetString(features, OutStringValue);
|
||||
|
||||
logger.Error("This feature is not yet implemented: fixdat");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Import : BaseFeature
|
||||
{
|
||||
public const string Value = "Import";
|
||||
|
||||
// Unique to RombaSharp
|
||||
public Import()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["import"]);
|
||||
Description = "Import a database from a formatted CSV file";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Import a database from a formatted CSV file";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
logger.Error("This feature is not yet implemented: import");
|
||||
|
||||
// Ensure the inputs
|
||||
var files = PathTool.GetFilesOnly(Inputs).ConvertAll(p => p.CurrentPath);
|
||||
Inputs.Clear();
|
||||
Inputs.AddRange(files);
|
||||
|
||||
// Ensure the database connection
|
||||
var dbc = new SqliteConnection(_connectionString);
|
||||
var slc = new SqliteCommand();
|
||||
dbc.Open();
|
||||
|
||||
// Now, for each of these files, attempt to add the data found inside
|
||||
foreach (string input in Inputs)
|
||||
{
|
||||
var sr = new StreamReader(File.OpenRead(input));
|
||||
|
||||
// The first line should be the hash header
|
||||
string? line = sr.ReadLine();
|
||||
if (line != "CRC,MD5,SHA-1") // ,Depot
|
||||
{
|
||||
logger.Error($"{input} is not a valid export file");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Define the insert queries
|
||||
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";
|
||||
|
||||
// For each line until we hit a blank line...
|
||||
while (!sr.EndOfStream && line != string.Empty)
|
||||
{
|
||||
line = sr.ReadLine() ?? string.Empty;
|
||||
string[] hashes = line.Split(',');
|
||||
|
||||
// Loop through the parsed entries
|
||||
if (!string.IsNullOrWhiteSpace(hashes[0]))
|
||||
crcquery += $" (\"{hashes[0]}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(hashes[1]))
|
||||
md5query += $" (\"{hashes[1]}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(hashes[2]))
|
||||
{
|
||||
sha1query += $" (\"{hashes[2]}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(hashes[0]))
|
||||
crcsha1query += $" (\"{hashes[0]}\", \"{hashes[2]}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(hashes[1]))
|
||||
md5sha1query += $" (\"{hashes[1]}\", \"{hashes[2]}\"),";
|
||||
}
|
||||
}
|
||||
|
||||
// Now run the queries after fixing them
|
||||
if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(crcquery.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(md5query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(sha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// Now add all of the DAT hashes
|
||||
// TODO: Do we really need to save the DAT hashes?
|
||||
|
||||
sr.Dispose();
|
||||
}
|
||||
|
||||
slc.Dispose();
|
||||
dbc.Dispose();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Help;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Lookup : BaseFeature
|
||||
{
|
||||
public const string Value = "Lookup";
|
||||
|
||||
public Lookup()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["lookup"]);
|
||||
Description = "For each specified hash it looks up any available information.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "For each specified hash it looks up any available information (dat or rom).";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(SizeInt64Input); // Defaults to -1
|
||||
AddFeature(OutStringInput);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
long size = GetInt64(features, SizeInt64Value);
|
||||
string? outdat = GetString(features, OutStringValue);
|
||||
|
||||
// First, try to figure out what type of hash each is by length and clean it
|
||||
List<string> crc = [];
|
||||
List<string> md5 = [];
|
||||
List<string> sha1 = [];
|
||||
foreach (string input in Inputs)
|
||||
{
|
||||
if (input.Length == Constants.CRCLength)
|
||||
crc.Add(input);
|
||||
else if (input.Length == Constants.MD5Length)
|
||||
md5.Add(input);
|
||||
else if (input.Length == Constants.SHA1Length)
|
||||
sha1.Add(input);
|
||||
}
|
||||
|
||||
SqliteConnection dbc = new SqliteConnection(_connectionString);
|
||||
dbc.Open();
|
||||
|
||||
// Now, search for each of them and return true or false for each
|
||||
foreach (string input in crc)
|
||||
{
|
||||
string query = $"SELECT * FROM crc WHERE crc=\"{input}\"";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
int count = 0;
|
||||
while (sldr.Read())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
logger.User($"For hash '{input}' there were {count} matches in the database");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.User($"Hash '{input}' had no matches in the database");
|
||||
}
|
||||
|
||||
sldr.Dispose();
|
||||
slc.Dispose();
|
||||
}
|
||||
foreach (string input in md5)
|
||||
{
|
||||
string query = $"SELECT * FROM md5 WHERE md5=\"{input}\"";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
int count = 0;
|
||||
while (sldr.Read())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
logger.User($"For hash '{input}' there were {count} matches in the database");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.User($"Hash '{input}' had no matches in the database");
|
||||
}
|
||||
|
||||
sldr.Dispose();
|
||||
slc.Dispose();
|
||||
}
|
||||
foreach (string input in sha1)
|
||||
{
|
||||
string query = $"SELECT * FROM sha1 WHERE sha1=\"{input}\"";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
int count = 0;
|
||||
while (sldr.Read())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
logger.User($"For hash '{input}' there were {count} matches in the database");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.User($"Hash '{input}' had no matches in the database");
|
||||
}
|
||||
|
||||
sldr.Dispose();
|
||||
slc.Dispose();
|
||||
}
|
||||
|
||||
dbc.Dispose();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Memstats : BaseFeature
|
||||
{
|
||||
public const string Value = "Memstats";
|
||||
|
||||
public Memstats()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["memstats"]);
|
||||
Description = "Prints memory stats.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Print memory stats.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
logger.User("This feature is not yet implemented: memstats");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Merge : BaseFeature
|
||||
{
|
||||
public const string Value = "Merge";
|
||||
|
||||
public Merge()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["merge"]);
|
||||
Description = "Merges depot";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Merges specified depot into current depot.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(OnlyNeededFlag);
|
||||
AddFeature(ResumeStringInput);
|
||||
AddFeature(WorkersInt32Input);
|
||||
AddFeature(SkipInitialScanFlag);
|
||||
}
|
||||
|
||||
// TODO: Add way of specifying "current depot" since that's what Romba relies on
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
bool onlyNeeded = GetBoolean(features, OnlyNeededValue);
|
||||
bool skipInitialscan = GetBoolean(features, SkipInitialScanValue);
|
||||
int workers = GetInt32(features, WorkersInt32Value);
|
||||
string? resume = GetString(features, ResumeStringValue);
|
||||
|
||||
logger.Error("This feature is not yet implemented: merge");
|
||||
|
||||
// Verify that the inputs are valid directories
|
||||
var dirs = PathTool.GetDirectoriesOnly(Inputs).ConvertAll(p => p.CurrentPath);
|
||||
Inputs.Clear();
|
||||
Inputs.AddRange(dirs);
|
||||
|
||||
// Loop over all input directories
|
||||
foreach (string input in Inputs)
|
||||
{
|
||||
#if NET20 || NET35
|
||||
List<string> depotFiles = [.. Directory.GetFiles(input, "*.gz")];
|
||||
#else
|
||||
List<string> depotFiles = [.. Directory.EnumerateFiles(input, "*.gz", SearchOption.AllDirectories)];
|
||||
#endif
|
||||
|
||||
// If we are copying all that is possible but we want to scan first
|
||||
if (!onlyNeeded && !skipInitialscan)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// If we are copying all that is possible but we don't care to scan first
|
||||
else if (!onlyNeeded && skipInitialscan)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// If we are copying only what is needed but we want to scan first
|
||||
else if (onlyNeeded && !skipInitialscan)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// If we are copying only what is needed but we don't care to scan first
|
||||
else if (onlyNeeded && skipInitialscan)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Miss : BaseFeature
|
||||
{
|
||||
public const string Value = "Miss";
|
||||
|
||||
// Unique to RombaSharp
|
||||
public Miss()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["miss"]);
|
||||
Description = "Create miss and have file";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "For each specified DAT file, create miss and have file";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Verify the filenames
|
||||
Dictionary<string, string> foundDats = GetValidDats(Inputs);
|
||||
|
||||
// Create the new output directory if it doesn't exist
|
||||
Path.Combine(PathTool.GetRuntimeDirectory(), "out").Ensure(create: true);
|
||||
|
||||
// Now that we have the dictionary, we can loop through and output to a new folder for each
|
||||
foreach (string key in foundDats.Keys)
|
||||
{
|
||||
// Get the DAT file associated with the key
|
||||
DatFile datFile = Parser.CreateAndParse(Path.Combine(_dats!, foundDats[key]));
|
||||
|
||||
// Now loop through and see if all of the hash combinations exist in the database
|
||||
/* ended here */
|
||||
}
|
||||
|
||||
logger.Error("This feature is not yet implemented: miss");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Progress : BaseFeature
|
||||
{
|
||||
public const string Value = "Progress";
|
||||
|
||||
public Progress()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["progress"]);
|
||||
Description = "Shows progress of the currently running command.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Shows progress of the currently running command.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
logger.User("This feature is not yet implemented: progress");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class PurgeBackup : BaseFeature
|
||||
{
|
||||
public const string Value = "Purge Backup";
|
||||
|
||||
public PurgeBackup()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["purge-backup"]);
|
||||
Description = "Moves DAT index entries for orphaned DATs.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"Deletes DAT index entries for orphaned DATs and moves ROM files that are no
|
||||
longer associated with any current DATs to the specified backup folder.
|
||||
The files will be placed in the backup location using
|
||||
a folder structure according to the original DAT master directory tree
|
||||
structure. It also deletes the specified DATs from the DAT index.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(BackupStringInput);
|
||||
AddFeature(WorkersInt32Input);
|
||||
AddFeature(DepotListStringInput);
|
||||
AddFeature(DatsListStringInput);
|
||||
AddFeature(LogOnlyFlag);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
bool logOnly = GetBoolean(features, LogOnlyValue);
|
||||
int workers = GetInt32(features, WorkersInt32Value);
|
||||
string? backup = GetString(features, BackupStringValue);
|
||||
List<string> dats = GetList(features, DatsListStringValue);
|
||||
List<string> depot = GetList(features, DepotListStringValue);
|
||||
|
||||
logger.Error("This feature is not yet implemented: purge-backup");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class PurgeDelete : BaseFeature
|
||||
{
|
||||
public const string Value = "Purge Delete";
|
||||
|
||||
// Unique to RombaSharp
|
||||
public PurgeDelete()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["purge-delete"]);
|
||||
Description = "Deletes DAT index entries for orphaned DATs";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"Deletes DAT index entries for orphaned DATs and moves ROM files that are no
|
||||
longer associated with any current DATs to the specified backup folder.
|
||||
The files will be placed in the backup location using
|
||||
a folder structure according to the original DAT master directory tree
|
||||
structure. It also deletes the specified DATs from the DAT index.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(WorkersInt32Input);
|
||||
AddFeature(DepotListStringInput);
|
||||
AddFeature(DatsListStringInput);
|
||||
AddFeature(LogOnlyFlag);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
bool logOnly = GetBoolean(features, LogOnlyValue);
|
||||
int workers = GetInt32(features, WorkersInt32Value);
|
||||
List<string> dats = GetList(features, DatsListStringValue);
|
||||
List<string> depot = GetList(features, DepotListStringValue);
|
||||
|
||||
logger.Error("This feature is not yet implemented: purge-delete");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatItems;
|
||||
using SabreTools.DatItems.Formats;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.FileTypes;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.IO.Logging;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class RefreshDats : BaseFeature
|
||||
{
|
||||
public const string Value = "Refresh DATs";
|
||||
|
||||
public RefreshDats()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["refresh-dats"]);
|
||||
Description = "Refreshes the DAT index from the files in the DAT master directory tree.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = @"Refreshes the DAT index from the files in the DAT master directory tree.
|
||||
Detects any changes in the DAT master directory tree and updates the DAT index
|
||||
accordingly, marking deleted or overwritten dats as orphaned and updating
|
||||
contents of any changed dats.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
|
||||
AddFeature(WorkersInt32Input);
|
||||
AddFeature(MissingSha1sStringInput);
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, SabreTools.Help.Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
// Get feature flags
|
||||
int workers = GetInt32(features, WorkersInt32Value);
|
||||
string? missingSha1s = GetString(features, MissingSha1sStringValue);
|
||||
HashType[] hashes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
var dfd = new DatFromDir(hashes, SkipFileType.None, addBlanks: false);
|
||||
|
||||
// Make sure the db is set
|
||||
if (string.IsNullOrWhiteSpace(_db))
|
||||
{
|
||||
_db = "db.sqlite";
|
||||
_connectionString = $"Data Source={_db};Version = 3;";
|
||||
}
|
||||
|
||||
// Make sure the file exists
|
||||
if (!System.IO.File.Exists(_db))
|
||||
EnsureDatabase(_db, _connectionString);
|
||||
|
||||
// Make sure the dats dir is set
|
||||
if (string.IsNullOrWhiteSpace(_dats))
|
||||
_dats = "dats";
|
||||
|
||||
_dats = Path.Combine(PathTool.GetRuntimeDirectory(), _dats);
|
||||
|
||||
// Make sure the folder exists
|
||||
if (!Directory.Exists(_dats))
|
||||
Directory.CreateDirectory(_dats);
|
||||
|
||||
// First get a list of SHA-1's from the input DATs
|
||||
DatFile datroot = DatFile.Create();
|
||||
datroot.Header.SetFieldValue<string?>(SabreTools.Models.Metadata.Header.TypeKey, "SuperDAT");
|
||||
dfd.PopulateFromDir(datroot, _dats, TreatAsFile.NonArchive);
|
||||
datroot.Items.BucketBy(ItemKey.SHA1, DedupeType.None);
|
||||
|
||||
// Create a List of dat hashes in the database (SHA-1)
|
||||
List<string> databaseDats = [];
|
||||
List<string> unneeded = [];
|
||||
|
||||
SqliteConnection dbc = new SqliteConnection(_connectionString);
|
||||
dbc.Open();
|
||||
|
||||
// Populate the List from the database
|
||||
InternalStopwatch watch = new InternalStopwatch("Populating the list of existing DATs");
|
||||
|
||||
string query = "SELECT DISTINCT hash FROM dat";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
sldr.Read();
|
||||
string hash = sldr.GetString(0);
|
||||
if (datroot.Items.ContainsKey(hash))
|
||||
{
|
||||
datroot.Items.Remove(hash);
|
||||
databaseDats.Add(hash);
|
||||
}
|
||||
else if (!databaseDats.Contains(hash))
|
||||
{
|
||||
unneeded.Add(hash);
|
||||
}
|
||||
}
|
||||
|
||||
datroot.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
|
||||
|
||||
watch.Stop();
|
||||
|
||||
slc.Dispose();
|
||||
sldr.Dispose();
|
||||
|
||||
// Loop through the Dictionary and add all data
|
||||
watch.Start("Adding new DAT information");
|
||||
foreach (string key in datroot.Items.Keys)
|
||||
{
|
||||
foreach (Rom value in datroot.Items[key]!)
|
||||
{
|
||||
AddDatToDatabase(value, dbc);
|
||||
}
|
||||
}
|
||||
|
||||
watch.Stop();
|
||||
|
||||
// Now loop through and remove all references to old Dats
|
||||
if (unneeded.Count > 0)
|
||||
{
|
||||
watch.Start("Removing unmatched DAT information");
|
||||
|
||||
query = "DELETE FROM dat WHERE";
|
||||
foreach (string dathash in unneeded)
|
||||
{
|
||||
query += $" OR hash=\"{dathash}\"";
|
||||
}
|
||||
|
||||
query = query.Replace("WHERE OR", "WHERE");
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
|
||||
watch.Stop();
|
||||
}
|
||||
|
||||
dbc.Dispose();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SabreTools.DatFiles;
|
||||
using SabreTools.DatItems;
|
||||
using SabreTools.DatItems.Formats;
|
||||
using SabreTools.DatTools;
|
||||
using SabreTools.FileTypes;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class RescanDepots : BaseFeature
|
||||
{
|
||||
public const string Value = "Rescan Depots";
|
||||
|
||||
// Unique to RombaSharp
|
||||
public RescanDepots()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["depot-rescan"]);
|
||||
Description = "Rescan a specific depot to get new information";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Rescan a specific depot to get new information";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, SabreTools.Help.Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
HashType[] hashes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
var dfd = new DatFromDir(hashes, SkipFileType.None, addBlanks: false);
|
||||
|
||||
logger.Error("This feature is not yet implemented: rescan-depots");
|
||||
|
||||
foreach (string depotname in Inputs)
|
||||
{
|
||||
// Check that it's a valid depot first
|
||||
if (!_depots.ContainsKey(depotname))
|
||||
{
|
||||
logger.User($"'{depotname}' is not a recognized depot. Please add it to your configuration file and try again");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then check that the depot is online
|
||||
if (!Directory.Exists(depotname))
|
||||
{
|
||||
logger.User($"'{depotname}' does not appear to be online. Please check its status and try again");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the database connection
|
||||
SqliteConnection dbc = new SqliteConnection(_connectionString);
|
||||
dbc.Open();
|
||||
|
||||
// If we have it, then check for all hashes that are in that depot
|
||||
List<string> sha1Hashes = [];
|
||||
string query = $"SELECT sha1 FROM sha1 WHERE depot=\"{depotname}\"";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
while (sldr.Read())
|
||||
{
|
||||
sha1Hashes.Add(sldr.GetString(0));
|
||||
}
|
||||
}
|
||||
|
||||
// Now rescan the depot itself
|
||||
DatFile depot = DatFile.Create();
|
||||
dfd.PopulateFromDir(depot, depotname, TreatAsFile.NonArchive);
|
||||
depot.Items.BucketBy(ItemKey.SHA1, DedupeType.None);
|
||||
|
||||
// Set the base queries to use
|
||||
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, depot) VALUES";
|
||||
string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES";
|
||||
string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES";
|
||||
|
||||
// Once we have both, check for any new files
|
||||
List<string> dupehashes = [];
|
||||
IEnumerable<string> keys = depot.Items.Keys;
|
||||
foreach (string key in keys)
|
||||
{
|
||||
List<DatItem>? roms = depot.Items[key];
|
||||
if (roms == null)
|
||||
continue;
|
||||
|
||||
foreach (Rom rom in roms)
|
||||
{
|
||||
if (sha1Hashes.Contains(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)!))
|
||||
{
|
||||
dupehashes.Add(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)!);
|
||||
sha1Hashes.Remove(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)!);
|
||||
}
|
||||
else if (!dupehashes.Contains(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)!))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)))
|
||||
crcquery += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)))
|
||||
md5query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)))
|
||||
{
|
||||
sha1query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\", \"{depotname}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)))
|
||||
crcsha1query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.CRCKey)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\"),";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)))
|
||||
md5sha1query += $" (\"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.MD5Key)}\", \"{rom.GetStringFieldValue(SabreTools.Models.Metadata.Rom.SHA1Key)}\"),";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now run the queries after fixing them
|
||||
if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(crcquery.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(md5query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(sha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES")
|
||||
{
|
||||
slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// Now that we've added the information, we get to remove all of the hashes that we want to
|
||||
query = @"DELETE FROM sha1
|
||||
JOIN crcsha1
|
||||
ON sha1.sha1=crcsha1.sha1
|
||||
JOIN md5sha1
|
||||
ON sha1.sha1=md5sha1.sha1
|
||||
JOIN crc
|
||||
ON crcsha1.crc=crc.crc
|
||||
JOIN md5
|
||||
ON md5sha1.md5=md5.md5
|
||||
WHERE sha1.sha1 IN ";
|
||||
query += $"({string.Join("\",\"", sha1Hashes)}\")";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
|
||||
// Dispose of the database connection
|
||||
slc.Dispose();
|
||||
dbc.Dispose();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Shutdown : BaseFeature
|
||||
{
|
||||
public const string Value = "Shutdown";
|
||||
|
||||
public Shutdown()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["shutdown"]);
|
||||
Description = "Gracefully shuts down server.";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Gracefully shuts down server saving all the cached data.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
logger.User("This feature is not yet implemented: shutdown");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Core;
|
||||
using SabreTools.Help;
|
||||
|
||||
namespace RombaSharp.Features
|
||||
{
|
||||
internal class Version : BaseFeature
|
||||
{
|
||||
public const string Value = "Version";
|
||||
|
||||
public Version()
|
||||
{
|
||||
Name = Value;
|
||||
Flags.AddRange(["version"]);
|
||||
Description = "Prints version";
|
||||
_featureType = ParameterType.Flag;
|
||||
LongDescription = "Prints current program version.";
|
||||
|
||||
// Common Features
|
||||
AddCommonFeatures();
|
||||
}
|
||||
|
||||
public override bool ProcessFeatures(Dictionary<string, Feature?> features)
|
||||
{
|
||||
// If the base fails, just fail out
|
||||
if (!base.ProcessFeatures(features))
|
||||
return false;
|
||||
|
||||
logger.User($"RombaSharp version: {Globals.Version}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using RombaSharp.Features;
|
||||
using SabreTools.Core;
|
||||
using SabreTools.Help;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.IO.Logging;
|
||||
|
||||
namespace RombaSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry class for the RombaSharp application
|
||||
/// </summary>
|
||||
/// <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>
|
||||
public class Program
|
||||
{
|
||||
#region Static Variables
|
||||
|
||||
/// <summary>
|
||||
/// Help object that determines available functionality
|
||||
/// </summary>
|
||||
private static FeatureSet? _help;
|
||||
|
||||
/// <summary>
|
||||
/// Logging object
|
||||
/// </summary>
|
||||
private static readonly Logger logger = new();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Entry class for the RombaSharp application
|
||||
/// </summary>
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Perform initial setup and verification
|
||||
LoggerImpl.SetFilename(Path.Combine(PathTool.GetRuntimeDirectory(), "logs", "romba.log"), true);
|
||||
LoggerImpl.AppendPrefix = true;
|
||||
LoggerImpl.LowestLogLevel = LogLevel.VERBOSE;
|
||||
LoggerImpl.ThrowOnError = false;
|
||||
LoggerImpl.Start();
|
||||
|
||||
// Create a new Help object for this program
|
||||
_help = RetrieveHelp();
|
||||
|
||||
// Credits take precidence over all
|
||||
if (new List<string>(args).Contains("--credits"))
|
||||
{
|
||||
FeatureSet.OutputCredits();
|
||||
LoggerImpl.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's no arguments, show help
|
||||
if (args.Length == 0)
|
||||
{
|
||||
_help.OutputGenericHelp();
|
||||
LoggerImpl.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the first argument as a feature flag
|
||||
string featureName = args[0];
|
||||
|
||||
// Verify that the flag is valid
|
||||
if (!_help.TopLevelFlag(featureName))
|
||||
{
|
||||
logger.User($"'{featureName}' is not valid feature flag");
|
||||
_help.OutputIndividualFeature(featureName);
|
||||
LoggerImpl.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the proper name for the feature
|
||||
featureName = _help.GetFeatureName(featureName);
|
||||
|
||||
// Get the associated feature
|
||||
BaseFeature? feature = _help[featureName] as BaseFeature;
|
||||
|
||||
// If we had the help feature first
|
||||
if (featureName == DisplayHelp.Value || featureName == DisplayHelpDetailed.Value)
|
||||
{
|
||||
feature!.ProcessArgs(args, _help);
|
||||
LoggerImpl.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Now verify that all other flags are valid
|
||||
if (!feature!.ProcessArgs(args, _help))
|
||||
{
|
||||
LoggerImpl.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the new log level based on settings
|
||||
LoggerImpl.LowestLogLevel = feature.LogLevel;
|
||||
|
||||
// If output is being redirected or we are in script mode, don't allow clear screens
|
||||
if (!Console.IsOutputRedirected && feature.ScriptMode)
|
||||
{
|
||||
Console.Clear();
|
||||
Globals.SetConsoleHeader("RombaSharp [Deprecated]");
|
||||
}
|
||||
|
||||
// Now process the current feature
|
||||
Dictionary<string, Feature?> features = _help.GetEnabledFeatures();
|
||||
bool success = false;
|
||||
switch (featureName)
|
||||
{
|
||||
case DisplayHelpDetailed.Value:
|
||||
case DisplayHelp.Value:
|
||||
// No-op as this should be caught
|
||||
break;
|
||||
|
||||
// Require input verification
|
||||
case Archive.Value:
|
||||
case Build.Value:
|
||||
case DatStats.Value:
|
||||
case Fixdat.Value:
|
||||
case Import.Value:
|
||||
case Lookup.Value:
|
||||
case Merge.Value:
|
||||
case Miss.Value:
|
||||
case RescanDepots.Value:
|
||||
VerifyInputs(feature.Inputs, featureName);
|
||||
success = feature.ProcessFeatures(features);
|
||||
break;
|
||||
|
||||
// Requires no input verification
|
||||
case Cancel.Value:
|
||||
case DbStats.Value:
|
||||
case Diffdat.Value:
|
||||
case Dir2Dat.Value:
|
||||
case EDiffdat.Value:
|
||||
case Export.Value:
|
||||
case Memstats.Value:
|
||||
case Progress.Value:
|
||||
case PurgeBackup.Value:
|
||||
case PurgeDelete.Value:
|
||||
case RefreshDats.Value:
|
||||
case Shutdown.Value:
|
||||
case Features.Version.Value:
|
||||
success = feature.ProcessFeatures(features);
|
||||
break;
|
||||
|
||||
// If nothing is set, show the help
|
||||
default:
|
||||
_help.OutputGenericHelp();
|
||||
break;
|
||||
}
|
||||
|
||||
// If the feature failed, output help
|
||||
if (!success)
|
||||
{
|
||||
logger.Error("An error occurred during processing!");
|
||||
_help.OutputIndividualFeature(featureName);
|
||||
}
|
||||
|
||||
LoggerImpl.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Help object for this program
|
||||
/// </summary>
|
||||
/// <returns>Populated Help object</returns>
|
||||
private static FeatureSet RetrieveHelp()
|
||||
{
|
||||
// Create and add the header to the Help object
|
||||
string barrier = "-----------------------------------------";
|
||||
List<string> helpHeader = new()
|
||||
{
|
||||
"RombaSharp - C# port of the Romba rom management tool",
|
||||
barrier,
|
||||
"Usage: RombaSharp [option] [filename|dirname] ...",
|
||||
string.Empty
|
||||
};
|
||||
|
||||
// Create the base help object with header
|
||||
FeatureSet help = new(helpHeader);
|
||||
|
||||
// Add all of the features
|
||||
help.Add(new DisplayHelp());
|
||||
help.Add(new DisplayHelpDetailed());
|
||||
help.Add(new Archive());
|
||||
help.Add(new Build());
|
||||
help.Add(new Cancel());
|
||||
help.Add(new DatStats());
|
||||
help.Add(new DbStats());
|
||||
help.Add(new Diffdat());
|
||||
help.Add(new Dir2Dat());
|
||||
help.Add(new EDiffdat());
|
||||
help.Add(new Export());
|
||||
help.Add(new Fixdat());
|
||||
help.Add(new Import());
|
||||
help.Add(new Lookup());
|
||||
help.Add(new Memstats());
|
||||
help.Add(new Merge());
|
||||
help.Add(new Miss());
|
||||
help.Add(new PurgeBackup());
|
||||
help.Add(new PurgeDelete());
|
||||
help.Add(new RefreshDats());
|
||||
help.Add(new RescanDepots());
|
||||
help.Add(new Progress());
|
||||
help.Add(new Shutdown());
|
||||
help.Add(new Features.Version());
|
||||
|
||||
return help;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that there are inputs, show help otherwise
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of inputs</param>
|
||||
/// <param name="feature">Name of the current feature</param>
|
||||
private static void VerifyInputs(List<string> inputs, string feature)
|
||||
{
|
||||
if (inputs.Count == 0)
|
||||
{
|
||||
logger.Error("This feature requires at least one input");
|
||||
_help?.OutputIndividualFeature(feature);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<NoWarn>NU1902;NU1903</NoWarn>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.1.2</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Copyright>Copyright (c)2016-2024 Matt Nadareski</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/SabreTools/SabreTools</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.DatFiles\SabreTools.DatFiles.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.DatItems\SabreTools.DatItems.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.DatTools\SabreTools.DatTools.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.FileTypes\SabreTools.FileTypes.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Help\SabreTools.Help.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -7,8 +7,7 @@ Table of Contents
|
||||
-----------------
|
||||
1.0 Introduction and History
|
||||
2.0 Included Programs
|
||||
2.1 RombaSharp
|
||||
2.2 SabreTools
|
||||
2.1 SabreTools
|
||||
3.0 Examples
|
||||
4.0 Contributors
|
||||
5.0 Licensing
|
||||
@@ -86,56 +85,7 @@ Below are a list of the programs that are included in the current SabreTools
|
||||
release. Each of them have a brief description of the tool along with in-depth
|
||||
desciptions of all flags.
|
||||
|
||||
** Section 2.1 - RombaSharp
|
||||
|
||||
RombaSharp is an ongoing "spiritual port" of the Romba tool that can be found at
|
||||
https://github.com/uwedeportivo/romba. The code is not based on the actual
|
||||
source, rather taking the features and using the code already written for a lot
|
||||
of other features. The following descriptions are based on what WILL be done
|
||||
with each flag. Not all features are currently available.
|
||||
|
||||
Usage:
|
||||
RombaSharp.exe [feature] [options] [filename|dirname] ...
|
||||
|
||||
Universal Options:
|
||||
These parameters can be enabled on any feature except Detailed Help and Help
|
||||
|
||||
-ll=, --log-level= Set the lowest log level for output
|
||||
Set the lowest log level for output.
|
||||
Possible values are: Verbose, User, Warning, Error
|
||||
|
||||
-sc, --script Enable script mode (no clear screen)
|
||||
For times when SabreTools 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.
|
||||
|
||||
Features and Options:
|
||||
-?, -h, --help Show the built-in help text
|
||||
Built-in to most of the programs is a basic help text
|
||||
|
||||
archive Adds ROM files from the specified directories to depot
|
||||
-only-needed Only archive ROM files in database
|
||||
build For each specified DAT file it creates TZip files
|
||||
-copy Copy files instead of rebuilding
|
||||
dbstats Prints db stats
|
||||
depot-rescan Rescan a specific depot to get new information
|
||||
diffdat Creates a DAT file for entries found in the new DAT
|
||||
-new= DAT to compare to
|
||||
dir2dat Creates a DAT file for the specified input directory
|
||||
-out= Filename to save out to
|
||||
export Exports db to export.csv
|
||||
fixdat For each specified DAT file it creates a fix DAT
|
||||
lookup For each specified hash, look up available information
|
||||
memstats Prints memory stats
|
||||
miss For each specified DAT file, create miss and have file
|
||||
progress Shows progress of currently running command [OBSOLETE]
|
||||
purge-backup Moves DAT index entries for orphaned DATs
|
||||
purge-delete Deletes DAT index entries for orphaned DATs
|
||||
refresh-dats Refreshes the DAT index from the files in the DAT root
|
||||
shutdown Gracefully shuts down server [OBSOLETE]
|
||||
|
||||
** Section 2.2 - SabreTools
|
||||
** Section 2.1 - SabreTools
|
||||
|
||||
SabreTools is the main application of the SabreTools suite. It is mostly just a
|
||||
frontend to a lot of features that are available in the Library and can be
|
||||
|
||||
@@ -5,8 +5,6 @@ VisualStudioVersion = 17.9.34607.119
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SabreTools.DatFiles", "SabreTools.DatFiles\SabreTools.DatFiles.csproj", "{CA54EDF8-100F-46E7-933C-0E081C7FCBD3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RombaSharp", "RombaSharp\RombaSharp.csproj", "{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SabreTools", "SabreTools\SabreTools.csproj", "{A7F4C9D8-C086-4296-9BD3-637CF9C78F51}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E848678E-0B14-4D37-B647-F2D75958C600}"
|
||||
@@ -46,14 +44,6 @@ Global
|
||||
{CA54EDF8-100F-46E7-933C-0E081C7FCBD3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CA54EDF8-100F-46E7-933C-0E081C7FCBD3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CA54EDF8-100F-46E7-933C-0E081C7FCBD3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{99B5AAC0-A92C-4FD8-82B6-F82B44D7279D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7F4C9D8-C086-4296-9BD3-637CF9C78F51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7F4C9D8-C086-4296-9BD3-637CF9C78F51}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7F4C9D8-C086-4296-9BD3-637CF9C78F51}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
||||
@@ -69,45 +69,6 @@ if [ $NO_BUILD = false ]; then
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Build RombaSharp
|
||||
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Build RombaSharp - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
# Only include Debug if set
|
||||
if [ $INCLUDE_DEBUG = true ]; then
|
||||
dotnet publish RombaSharp/RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
fi
|
||||
dotnet publish RombaSharp/RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
else
|
||||
# Only include Debug if set
|
||||
if [ $INCLUDE_DEBUG = true ]; then
|
||||
dotnet publish RombaSharp/RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
fi
|
||||
dotnet publish RombaSharp/RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Build SabreTools
|
||||
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${RUNTIMES[@]}"; do
|
||||
@@ -150,38 +111,6 @@ fi
|
||||
|
||||
# Only create archives if requested
|
||||
if [ $NO_ARCHIVE = false ]; then
|
||||
# Create RombaSharp archives
|
||||
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Archive RombaSharp - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only include Debug if set
|
||||
if [ $INCLUDE_DEBUG = true ]; then
|
||||
cd $BUILD_FOLDER/RombaSharp/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/RombaSharp_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
fi
|
||||
cd $BUILD_FOLDER/RombaSharp/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/RombaSharp_${FRAMEWORK}_${RUNTIME}_release.zip .
|
||||
done
|
||||
done
|
||||
|
||||
# Create SabreTools archives
|
||||
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${RUNTIMES[@]}"; do
|
||||
|
||||
@@ -60,42 +60,6 @@ if (!$NO_BUILD.IsPresent) {
|
||||
Write-Host "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Build RombaSharp
|
||||
foreach ($FRAMEWORK in $FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Build RombaSharp - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) {
|
||||
# Only include Debug if set
|
||||
if ($INCLUDE_DEBUG.IsPresent) {
|
||||
dotnet publish RombaSharp\RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
}
|
||||
dotnet publish RombaSharp\RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
else {
|
||||
# Only include Debug if set
|
||||
if ($INCLUDE_DEBUG.IsPresent) {
|
||||
dotnet publish RombaSharp\RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
}
|
||||
dotnet publish RombaSharp\RombaSharp.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Build SabreTools
|
||||
foreach ($FRAMEWORK in $FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
@@ -135,35 +99,6 @@ if (!$NO_BUILD.IsPresent) {
|
||||
|
||||
# Only create archives if requested
|
||||
if (!$NO_ARCHIVE.IsPresent) {
|
||||
# Create RombaSharp archives
|
||||
foreach ($FRAMEWORK in $FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Archive RombaSharp - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
# Only include Debug if set
|
||||
if ($INCLUDE_DEBUG.IsPresent) {
|
||||
Set-Location -Path $BUILD_FOLDER\RombaSharp\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\RombaSharp_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
|
||||
Set-Location -Path $BUILD_FOLDER\RombaSharp\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\RombaSharp_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
}
|
||||
}
|
||||
|
||||
# Create SabreTools archives
|
||||
foreach ($FRAMEWORK in $FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
|
||||
Reference in New Issue
Block a user