[SabreTools, DatFIle] Add base replacement

In this new update mode, a set of base DATs can be used as a replacement naming scheme for the input DATs. Only the item names will be updated, so this willl not fill in additional hashes or the like.
This commit is contained in:
Matt Nadareski
2017-10-30 16:09:58 -07:00
parent c7f8fc12c0
commit 3bdad8b455
5 changed files with 166 additions and 3 deletions

View File

@@ -1608,7 +1608,7 @@ namespace SabreTools.Library.DatFiles
bool bare, bool clean, bool remUnicode, bool descAsName, Filter filter, SplitType splitType, bool trim, bool single, string root)
{
// If we're in merging or diffing mode, use the full list of inputs
if (merge || (diff != 0 && (diff & DiffMode.Against) == 0))
if (merge || (diff != 0 && (diff & DiffMode.Against) == 0) && (diff & DiffMode.BaseReplace) == 0)
{
// Make sure there are no folders in inputs
List<string> newInputFileNames = FileTools.GetOnlyFilesFromInputs(inputPaths, appendparent: true);
@@ -1644,6 +1644,11 @@ namespace SabreTools.Library.DatFiles
{
DiffAgainst(inputPaths, basePaths, outDir, inplace, clean, remUnicode, descAsName, filter, splitType, trim, single, root);
}
// If we're in "base replacement" mode, we treat the inputs differently
else if ((diff & DiffMode.BaseReplace) != 0)
{
BaseReplace(inputPaths, basePaths, outDir, inplace, clean, remUnicode, descAsName, filter, splitType, trim, single, root);
}
// Otherwise, loop through all of the inputs individually
else
{
@@ -1716,6 +1721,122 @@ namespace SabreTools.Library.DatFiles
return datHeaders.ToList();
}
/// <summary>
/// Replace item names from on a base set
/// </summary>
/// <param name="inputPaths">Names of the input files and/or folders</param>
/// <param name="basePaths">Names of base files and/or folders</param>
/// <param name="outDir">Optional param for output directory</param>
/// <param name="inplace">True if the output files should overwrite their inputs, false otherwise</param>
/// <param name="clean">True to clean the game names to WoD standard, false otherwise (default)</param>
/// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param>
/// <param name="descAsName">True to allow SL DATs to have game names used instead of descriptions, false otherwise (default)</param>
/// <param name="filter">Filter object to be passed to the DatItem level</param>
/// <param name="splitType">Type of the split that should be performed (split, merged, fully merged)</param>
/// <param name="trim">True if we are supposed to trim names to NTFS length, false otherwise</param>
/// <param name="single">True if all games should be replaced by '!', false otherwise</param>
/// <param name="root">String representing root directory to compare against for length calculation</param>
public void BaseReplace(List<string> inputPaths, List<string> basePaths, string outDir, bool inplace, bool clean, bool remUnicode,
bool descAsName, Filter filter, SplitType splitType, bool trim, bool single, string root)
{
// First we want to parse all of the base DATs into the input
InternalStopwatch watch = new InternalStopwatch("Populating base DAT for replacement...");
List<string> baseFileNames = FileTools.GetOnlyFilesFromInputs(basePaths);
Parallel.For(0, baseFileNames.Count, Globals.ParallelOptions, i =>
{
string path = "";
int id = 0;
lock (baseFileNames)
{
path = baseFileNames[i];
id = baseFileNames.Count - i; // Inverse because larger numbers take precedence
}
Parse(path, id, id, keep: true, clean: clean, remUnicode: remUnicode, descAsName: descAsName);
});
watch.Stop();
// For comparison's sake, we want to use CRC as the base ordering
BucketBy(SortedBy.CRC, DedupeType.Full);
// Now we want to try to replace each item in each input DAT from the base
List<string> inputFileNames = FileTools.GetOnlyFilesFromInputs(inputPaths, appendparent: true);
foreach (string path in inputFileNames)
{
// Get the two halves of the path
string[] splitpath = path.Split('¬');
Globals.Logger.User("Replacing items in '{0}'' from the base DAT", splitpath[0]);
// First we parse in the DAT internally
DatFile intDat = new DatFile();
intDat.Parse(splitpath[0], 1, 1, keep: true, clean: clean, remUnicode: remUnicode, descAsName: descAsName);
// For comparison's sake, we want to use CRC as the base ordering
intDat.BucketBy(SortedBy.CRC, DedupeType.Full);
// Then we do a hashwise comparison against the base DAT
List<string> keys = intDat.Keys.ToList();
Parallel.ForEach(keys, Globals.ParallelOptions, key =>
{
List<DatItem> datItems = intDat[key];
List<DatItem> newDatItems = new List<DatItem>();
foreach (DatItem datItem in datItems)
{
List<DatItem> dupes = datItem.GetDuplicates(this);
if (dupes.Count > 0)
{
datItem.Name = dupes[0].Name;
}
newDatItems.Add(datItem);
}
// Now add the new list to the key
intDat.Remove(key);
intDat.AddRange(key, newDatItems);
});
// Determine the output path for the DAT
string interOutDir = outDir;
if (inplace)
{
interOutDir = Path.GetDirectoryName(path);
}
else if (!String.IsNullOrEmpty(interOutDir))
{
if (splitpath[0].Length == splitpath[1].Length)
{
interOutDir = Path.GetDirectoryName(Path.Combine(interOutDir, Path.GetFileName(splitpath[0])));
}
else
{
interOutDir = Path.GetDirectoryName(Path.Combine(interOutDir, splitpath[0].Remove(0, splitpath[1].Length + 1)));
}
}
else
{
if (splitpath[0].Length == splitpath[1].Length)
{
interOutDir = Path.GetDirectoryName(Path.Combine(Environment.CurrentDirectory, Path.GetFileName(splitpath[0])));
}
else
{
interOutDir = Path.GetDirectoryName(Path.Combine(Environment.CurrentDirectory, splitpath[0].Remove(0, splitpath[1].Length + 1)));
}
}
// Once we're done, try writing out
intDat.WriteToFile(interOutDir);
// Due to possible memory requirements, we force a garbage collection
GC.Collect();
}
}
/// <summary>
/// Output diffs against a base set
/// </summary>
@@ -1791,11 +1912,25 @@ namespace SabreTools.Library.DatFiles
}
else if (!String.IsNullOrEmpty(interOutDir))
{
interOutDir = Path.GetDirectoryName(Path.Combine(interOutDir, splitpath[0].Remove(0, splitpath[1].Length + 1)));
if (splitpath[0].Length == splitpath[1].Length)
{
interOutDir = Path.GetDirectoryName(Path.Combine(interOutDir, Path.GetFileName(splitpath[0])));
}
else
{
interOutDir = Path.GetDirectoryName(Path.Combine(interOutDir, splitpath[0].Remove(0, splitpath[1].Length + 1)));
}
}
else
{
interOutDir = Path.GetDirectoryName(Path.Combine(Environment.CurrentDirectory, splitpath[0].Remove(0, splitpath[1].Length + 1)));
if (splitpath[0].Length == splitpath[1].Length)
{
interOutDir = Path.GetDirectoryName(Path.Combine(Environment.CurrentDirectory, Path.GetFileName(splitpath[0])));
}
else
{
interOutDir = Path.GetDirectoryName(Path.Combine(Environment.CurrentDirectory, splitpath[0].Remove(0, splitpath[1].Length + 1)));
}
}
// Once we're done, try writing out

View File

@@ -240,6 +240,9 @@ namespace SabreTools.Library.Data
// Base diffs
Against = ReverseCascade << 1,
// Not technically diffs
BaseReplace = Against << 1,
}
/// <summary>

View File

@@ -1177,6 +1177,17 @@ Options:
a second time, this will skip writing it. This can often
speed up the output process.
-bn, --base-name Replace matching item names from base DAT
By default, no item names are changed except when there is a merge
occurring. This flag enables users to define a DAT or set of base
DATs to use as "replacement names" for all input DATs. Note that
the first found instance of an item in the base DAT(s) will be
used and all others will be disgarded.
-bd=, --base-dat= Add a base DAT for replacing
Add a DAT or folder of DATs to the base set to be used in
item name replacement
-gn=, --not-game= Filter by game name
-ngn=, --game-name= Exclude by game name
-rn=, --rom-name= Filter by rom name

View File

@@ -1225,6 +1225,16 @@ namespace SabreTools
"Don't include the date in automatic name",
FeatureType.Flag,
null));
update.AddFeature("base-name", new Feature(
new List<string>() { "-bn", "--base-name" },
"Replace matching item names from a base DAT",
FeatureType.Flag,
null));
update["base-name"].AddFeature("base-dat", new Feature(
new List<string>() { "-bd", "--base-dat" },
"Add a base DAT for replacing",
FeatureType.List,
null));
update.AddFeature("game-name", new Feature(
new List<string>() { "-gn", "--game-name" },
"Filter by game name",

View File

@@ -304,6 +304,10 @@ namespace SabreTools
case "--baddump-col":
showBaddumpColumn = true;
break;
case "-bn":
case "--base-name":
diffMode |= DiffMode.BaseReplace;
break;
case "-c":
case "--cascade":
diffMode |= DiffMode.Cascade;