Add split by total size

This commit is contained in:
Matt Nadareski
2021-02-17 16:47:32 -08:00
parent 1059fa85b6
commit 8e59aa6aa5
4 changed files with 136 additions and 4 deletions

View File

@@ -721,6 +721,15 @@ Features and Options:
first DAT, and everything greater than or equal goes in the first DAT, and everything greater than or equal goes in the
second. second.
-tis, --total-size Split DAT(s) or folder by total game sizes
For a DAT, or set of DATs, allow for splitting based on the combined
sizes of the games, splitting into individual chunks.
-cs=, --chunk-size= Set a chunk size to output
Set the total game size to cut off at for each chunked DAT. It is
recommended to use a sufficiently large size such as 1GB or else
you may run into issues.
-ts, --type Split DAT(s) or folder by file types (rom/disk) -ts, --type Split DAT(s) or folder by file types (rom/disk)
For a DAT, or set of DATs, allow for splitting based on the types of For a DAT, or set of DATs, allow for splitting based on the types of
the files, specifically if the type is a rom or a disk. the files, specifically if the type is a rom or a disk.

View File

@@ -353,6 +353,79 @@ namespace SabreTools.DatTools
return (lessThan, greaterThan); return (lessThan, greaterThan);
} }
/// <summary>
/// Split a DAT by size of Rom
/// </summary>
/// <param name="datFile">Current DatFile object to split</param>
/// <param name="chunkSize">Long value representing the total size to split at</param>
/// <returns>Less Than and Greater Than DatFiles</returns>
public static List<DatFile> SplitByTotalSize(DatFile datFile, long chunkSize)
{
// If the size is invalid, just return
if (chunkSize <= 0)
return new List<DatFile>();
// Create each of the respective output DATs
InternalStopwatch watch = new InternalStopwatch($"Splitting DAT by total size");
// Sort the DatFile by machine name
datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None);
// Get the keys in a known order for easier sorting
var keys = datFile.Items.SortedKeys;
// Get the output list
List<DatFile> datFiles = new List<DatFile>();
// Initialize everything
long currentSize = 0;
long currentIndex = 0;
DatFile currentDat = DatFile.Create(datFile.Header.CloneStandard());
currentDat.Header.FileName += $"_{currentIndex}";
currentDat.Header.Name += $"_{currentIndex}";
currentDat.Header.Description += $"_{currentIndex}";
// Loop through each machine
foreach (string machine in keys)
{
// Get the current machine
var items = datFile.Items[machine];
if (items == null || !items.Any())
continue;
// Get the total size of the current machine
long machineSize = 0;
foreach (var item in items)
{
if (item is Rom rom)
machineSize += rom.Size ?? 0;
}
// If the current machine size makes the current DatFile too big, split
if (currentSize + machineSize > chunkSize)
{
datFiles.Add(currentDat);
currentSize = 0;
currentIndex++;
currentDat = DatFile.Create(datFile.Header.CloneStandard());
currentDat.Header.FileName += $"_{currentIndex}";
currentDat.Header.Name += $"_{currentIndex}";
currentDat.Header.Description += $"_{currentIndex}";
}
// Add the current machine to the current DatFile
currentDat.Items[machine] = items;
currentSize += machineSize;
}
// Add the final DatFile to the list
datFiles.Add(currentDat);
// Then return the list
watch.Stop();
return datFiles;
}
/// <summary> /// <summary>
/// Split a DAT by type of DatItem /// Split a DAT by type of DatItem
/// </summary> /// </summary>

View File

@@ -38,10 +38,11 @@ namespace SabreTools.Features
None = 0x00, None = 0x00,
Extension = 1 << 0, Extension = 1 << 0,
Hash = 1 << 2, Hash = 1 << 1,
Level = 1 << 3, Level = 1 << 2,
Type = 1 << 4, Type = 1 << 3,
Size = 1 << 5, Size = 1 << 4,
TotalSize = 1 << 5,
} }
/// <summary> /// <summary>
@@ -1080,6 +1081,20 @@ namespace SabreTools.Features
} }
} }
internal const string TotalSizeValue = "total-size";
internal static Feature TotalSizeFlag
{
get
{
return new Feature(
TotalSizeValue,
new List<string>() { "-tis", "--total-size" },
"Split DAT(s) or folder by total game sizes",
ParameterType.Flag,
longDescription: "For a DAT, or set of DATs, allow for splitting based on the combined sizes of the games, splitting into individual chunks.");
}
}
internal const string TrimValue = "trim"; internal const string TrimValue = "trim";
internal static Feature TrimFlag internal static Feature TrimFlag
{ {
@@ -1172,6 +1187,20 @@ namespace SabreTools.Features
#region Int64 features #region Int64 features
internal const string ChunkSizeInt64Value = "chunk-size";
internal static Feature ChunkSizeInt64Input
{
get
{
return new Feature(
ChunkSizeInt64Value,
new List<string>() { "-cs", "--chunk-size" },
"Set a chunk size to output",
ParameterType.Int64,
longDescription: "Set the total game size to cut off at for each chunked DAT. It is recommended to use a sufficiently large size such as 1GB or else you may run into issues.");
}
}
internal const string RadixInt64Value = "radix"; internal const string RadixInt64Value = "radix";
internal static Feature RadixInt64Input internal static Feature RadixInt64Input
{ {
@@ -1958,6 +1987,8 @@ Some special strings that can be used:
splittingMode |= SplittingMode.Level; splittingMode |= SplittingMode.Level;
if (GetBoolean(features, SizeValue)) if (GetBoolean(features, SizeValue))
splittingMode |= SplittingMode.Size; splittingMode |= SplittingMode.Size;
if (GetBoolean(features, TotalSizeValue))
splittingMode |= SplittingMode.TotalSize;
if (GetBoolean(features, TypeValue)) if (GetBoolean(features, TypeValue))
splittingMode |= SplittingMode.Type; splittingMode |= SplittingMode.Type;

View File

@@ -39,6 +39,8 @@ namespace SabreTools.Features
this[LevelFlag].AddFeature(BaseFlag); this[LevelFlag].AddFeature(BaseFlag);
AddFeature(SizeFlag); AddFeature(SizeFlag);
this[SizeFlag].AddFeature(RadixInt64Input); this[SizeFlag].AddFeature(RadixInt64Input);
AddFeature(TotalSizeFlag);
this[TotalSizeFlag].AddFeature(ChunkSizeInt64Input);
AddFeature(TypeFlag); AddFeature(TypeFlag);
} }
@@ -120,6 +122,23 @@ namespace SabreTools.Features
watch.Stop(); watch.Stop();
} }
// Total Size splitting
if (splittingMode.HasFlag(SplittingMode.TotalSize))
{
logger.Warning("This feature is not implemented: level-split");
List<DatFile> sizedDats = DatTools.Splitter.SplitByTotalSize(internalDat, GetInt64(features, ChunkSizeInt64Value));
InternalStopwatch watch = new InternalStopwatch("Outputting total-size-split DATs");
// Loop through each type DatFile
Parallel.ForEach(sizedDats, Globals.ParallelOptions, sizedDat =>
{
Writer.Write(sizedDat, OutputDir);
});
watch.Stop();
}
// Type splitting // Type splitting
if (splittingMode.HasFlag(SplittingMode.Type)) if (splittingMode.HasFlag(SplittingMode.Type))
{ {