Remove RombaSharp

This commit is contained in:
Matt Nadareski
2024-12-06 12:55:02 -05:00
parent b6c6a0160b
commit 1b2375beac
32 changed files with 2 additions and 3193 deletions

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) {