mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-04-05 22:01:33 +00:00
Port metadata functionality from ST
This commit is contained in:
23
SabreTools.Metadata.DatFiles.Test/ConvertersTests.cs
Normal file
23
SabreTools.Metadata.DatFiles.Test/ConvertersTests.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using SabreTools.Metadata.Tools;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Test
|
||||
{
|
||||
public class ConvertersTests
|
||||
{
|
||||
#region Generators
|
||||
|
||||
[Theory]
|
||||
[InlineData(MergingFlag.None, 12)]
|
||||
[InlineData(NodumpFlag.None, 4)]
|
||||
[InlineData(PackingFlag.None, 8)]
|
||||
public void GenerateToEnumTest<T>(T value, int expected)
|
||||
{
|
||||
var actual = Converters.GenerateToEnum<T>();
|
||||
Assert.Equal(default, value);
|
||||
Assert.Equal(expected, actual.Keys.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
304
SabreTools.Metadata.DatFiles.Test/DatFileTests.Filtering.cs
Normal file
304
SabreTools.Metadata.DatFiles.Test/DatFileTests.Filtering.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatFiles.Formats;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Test
|
||||
{
|
||||
public partial class DatFileTests
|
||||
{
|
||||
#region ExecuteFilters
|
||||
|
||||
[Fact]
|
||||
public void ExecuteFilters_Items()
|
||||
{
|
||||
FilterObject filterObject = new FilterObject("rom.crc", "deadbeef", Operation.NotEquals);
|
||||
FilterRunner filterRunner = new FilterRunner([filterObject]);
|
||||
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetName("rom.bin");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
datItem.SetFieldValue(DatItem.MachineKey, machine);
|
||||
datItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(datItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.ExecuteFilters(filterRunner);
|
||||
|
||||
var actualDatItems = datFile.GetItemsForBucket("machine");
|
||||
DatItem actualRom = Assert.Single(actualDatItems);
|
||||
Assert.Equal(true, actualRom.GetBoolFieldValue(DatItem.RemoveKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExecuteFilters_ItemsDB()
|
||||
{
|
||||
FilterObject filterObject = new FilterObject("rom.crc", "deadbeef", Operation.NotEquals);
|
||||
FilterRunner filterRunner = new FilterRunner([filterObject]);
|
||||
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetName("rom.bin");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
datItem.SetFieldValue(DatItem.MachineKey, machine);
|
||||
datItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
long machineIndex = datFile.AddMachineDB(machine);
|
||||
_ = datFile.AddItemDB(datItem, machineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.ExecuteFilters(filterRunner);
|
||||
|
||||
var actualDatItems = datFile.GetItemsForBucketDB("machine");
|
||||
DatItem actualRom = Assert.Single(actualDatItems).Value;
|
||||
Assert.Equal(true, actualRom.GetBoolFieldValue(DatItem.RemoveKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MachineDescriptionToName
|
||||
|
||||
[Fact]
|
||||
public void MachineDescriptionToName_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine");
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.DescriptionKey, "description");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetFieldValue(DatItem.MachineKey, machine);
|
||||
datItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(datItem, statsOnly: false);
|
||||
|
||||
datFile.MachineDescriptionToName();
|
||||
|
||||
// The name of the bucket is not expected to change
|
||||
DatItem actual = Assert.Single(datFile.GetItemsForBucket("machine"));
|
||||
Machine? actualMachine = actual.GetMachine();
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("description", actualMachine.GetName());
|
||||
Assert.Equal("description", actualMachine.GetStringFieldValue(Data.Models.Metadata.Machine.DescriptionKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MachineDescriptionToName_ItemsDB()
|
||||
{
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine");
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.DescriptionKey, "description");
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
_ = datFile.AddMachineDB(machine);
|
||||
|
||||
datFile.MachineDescriptionToName();
|
||||
|
||||
Machine actualMachine = Assert.Single(datFile.GetMachinesDB()).Value;
|
||||
Assert.Equal("description", actualMachine.GetName());
|
||||
Assert.Equal("description", actualMachine.GetStringFieldValue(Data.Models.Metadata.Machine.DescriptionKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetOneRomPerGame
|
||||
|
||||
[Fact]
|
||||
public void SetOneRomPerGame_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine");
|
||||
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("rom.bin");
|
||||
rom.SetFieldValue(DatItem.MachineKey, machine);
|
||||
rom.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem disk = new Disk();
|
||||
disk.SetName("disk");
|
||||
disk.SetFieldValue(DatItem.MachineKey, machine);
|
||||
disk.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(rom, statsOnly: false);
|
||||
datFile.AddItem(disk, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.SetOneRomPerGame();
|
||||
|
||||
var actualDatItems = datFile.GetItemsForBucket("machine");
|
||||
Assert.Equal(2, actualDatItems.Count);
|
||||
|
||||
DatItem actualRom = Assert.Single(actualDatItems.FindAll(i => i is Rom));
|
||||
Machine? actualRomMachine = actualRom.GetMachine();
|
||||
Assert.NotNull(actualRomMachine);
|
||||
Assert.Equal("machine/rom", actualRomMachine.GetName());
|
||||
|
||||
DatItem actualDisk = Assert.Single(actualDatItems.FindAll(i => i is Disk));
|
||||
Machine? actualDiskMachine = actualDisk.GetMachine();
|
||||
Assert.NotNull(actualDiskMachine);
|
||||
Assert.Equal("machine/disk", actualDiskMachine.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetOneRomPerGame_ItemsDB()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine");
|
||||
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("rom.bin");
|
||||
|
||||
DatItem disk = new Disk();
|
||||
disk.SetName("disk");
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
long machineIndex = datFile.AddMachineDB(machine);
|
||||
_ = datFile.AddItemDB(rom, machineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(disk, machineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.SetOneRomPerGame();
|
||||
|
||||
var actualDatItems = datFile.GetItemsForBucketDB("machine");
|
||||
Assert.Equal(2, actualDatItems.Count);
|
||||
|
||||
var actualRom = Assert.Single(actualDatItems, i => i.Value is Rom);
|
||||
var actualRomMachine = datFile.GetMachineForItemDB(actualRom.Key);
|
||||
Assert.NotNull(actualRomMachine.Value);
|
||||
Assert.Equal("machine/rom", actualRomMachine.Value.GetName());
|
||||
|
||||
var actualDisk = Assert.Single(actualDatItems, i => i.Value is Disk);
|
||||
var actualDiskMachine = datFile.GetMachineForItemDB(actualDisk.Key);
|
||||
Assert.NotNull(actualDiskMachine.Value);
|
||||
Assert.Equal("machine/disk", actualDiskMachine.Value.GetName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetOneGamePerRegion
|
||||
|
||||
[Fact]
|
||||
public void SetOneGamePerRegion_Items()
|
||||
{
|
||||
Machine nowhereMachine = new Machine();
|
||||
nowhereMachine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine (Nowhere)");
|
||||
|
||||
Machine worldMachine = new Machine();
|
||||
worldMachine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine (World)");
|
||||
worldMachine.SetFieldValue(Data.Models.Metadata.Machine.CloneOfKey, "machine (Nowhere)");
|
||||
|
||||
DatItem nowhereRom = new Rom();
|
||||
nowhereRom.SetName("rom.bin");
|
||||
nowhereRom.SetFieldValue(DatItem.MachineKey, nowhereMachine);
|
||||
|
||||
DatItem worldRom = new Rom();
|
||||
worldRom.SetName("rom.nib");
|
||||
worldRom.SetFieldValue(DatItem.MachineKey, worldMachine);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(nowhereRom, statsOnly: false);
|
||||
datFile.AddItem(worldRom, statsOnly: false);
|
||||
|
||||
List<string> regions = ["World", "Nowhere"];
|
||||
datFile.SetOneGamePerRegion(regions);
|
||||
|
||||
Assert.Empty(datFile.GetItemsForBucket("machine (nowhere)"));
|
||||
|
||||
var actualDatItems = datFile.GetItemsForBucket("machine (world)");
|
||||
DatItem actualWorldRom = Assert.Single(actualDatItems);
|
||||
Machine? actualWorldMachine = actualWorldRom.GetMachine();
|
||||
Assert.NotNull(actualWorldMachine);
|
||||
Assert.Equal("machine (World)", actualWorldMachine.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetOneGamePerRegion_ItemsDB()
|
||||
{
|
||||
Machine nowhereMachine = new Machine();
|
||||
nowhereMachine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine (Nowhere)");
|
||||
|
||||
Machine worldMachine = new Machine();
|
||||
worldMachine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "machine (World)");
|
||||
worldMachine.SetFieldValue(Data.Models.Metadata.Machine.CloneOfKey, "machine (Nowhere)");
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
_ = datFile.AddMachineDB(nowhereMachine);
|
||||
_ = datFile.AddMachineDB(worldMachine);
|
||||
|
||||
List<string> regions = ["World", "Nowhere"];
|
||||
datFile.SetOneGamePerRegion(regions);
|
||||
|
||||
var actualWorldMachine = Assert.Single(datFile.GetMachinesDB());
|
||||
Assert.NotNull(actualWorldMachine.Value);
|
||||
Assert.Equal("machine (World)", actualWorldMachine.Value.GetName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region StripSceneDatesFromItems
|
||||
|
||||
[Fact]
|
||||
public void StripSceneDatesFromItems_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "10.10.10-machine-name");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetFieldValue(DatItem.MachineKey, machine);
|
||||
datItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(datItem, statsOnly: false);
|
||||
|
||||
datFile.StripSceneDatesFromItems();
|
||||
|
||||
// The name of the bucket is not expected to change
|
||||
DatItem actual = Assert.Single(datFile.GetItemsForBucket("10.10.10-machine-name"));
|
||||
Machine? actualMachine = actual.GetMachine();
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("machine-name", actualMachine.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StripSceneDatesFromItems_ItemsDB()
|
||||
{
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "10.10.10-machine-name");
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
_ = datFile.AddMachineDB(machine);
|
||||
|
||||
datFile.StripSceneDatesFromItems();
|
||||
|
||||
Machine actualMachine = Assert.Single(datFile.GetMachinesDB()).Value;
|
||||
Assert.Equal("machine-name", actualMachine.GetName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1564
SabreTools.Metadata.DatFiles.Test/DatFileTests.FromMetadata.cs
Normal file
1564
SabreTools.Metadata.DatFiles.Test/DatFileTests.FromMetadata.cs
Normal file
File diff suppressed because it is too large
Load Diff
847
SabreTools.Metadata.DatFiles.Test/DatFileTests.Splitting.cs
Normal file
847
SabreTools.Metadata.DatFiles.Test/DatFileTests.Splitting.cs
Normal file
@@ -0,0 +1,847 @@
|
||||
using SabreTools.Metadata.DatFiles.Formats;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Test
|
||||
{
|
||||
public partial class DatFileTests
|
||||
{
|
||||
#region AddItemsFromChildren
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromChildren_Items_Dedup()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(parentItem, statsOnly: false);
|
||||
datFile.AddItem(matchChildItem, statsOnly: false);
|
||||
datFile.AddItem(noMatchChildItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromChildren(subfolder: true, skipDedup: false);
|
||||
|
||||
Assert.Equal(2, datFile.GetItemsForBucket("parent").Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromChildren_Items_SkipDedup()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(parentItem, statsOnly: false);
|
||||
datFile.AddItem(matchChildItem, statsOnly: false);
|
||||
datFile.AddItem(noMatchChildItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromChildren(subfolder: true, skipDedup: true);
|
||||
|
||||
Assert.Equal(3, datFile.GetItemsForBucket("parent").Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromChildren_ItemsDB_Dedup()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long biosMachineIndex = datFile.AddMachineDB(parentMachine);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(childMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(parentItem, biosMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(matchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(noMatchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromChildren(subfolder: true, skipDedup: false);
|
||||
|
||||
Assert.Equal(2, datFile.GetItemsForBucketDB("parent").Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromChildren_ItemsDB_SkipDedup()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long biosMachineIndex = datFile.AddMachineDB(parentMachine);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(childMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(parentItem, biosMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(matchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(noMatchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromChildren(subfolder: true, skipDedup: true);
|
||||
|
||||
Assert.Equal(3, datFile.GetItemsForBucketDB("parent").Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddItemsFromCloneOfParent
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromCloneOfParent_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(parentItem, statsOnly: false);
|
||||
datFile.AddItem(matchChildItem, statsOnly: false);
|
||||
datFile.AddItem(noMatchChildItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromCloneOfParent();
|
||||
|
||||
Assert.Equal(2, datFile.GetItemsForBucket("child").Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromCloneOfParent_ItemsDB()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long biosMachineIndex = datFile.AddMachineDB(parentMachine);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(childMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(parentItem, biosMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(matchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(noMatchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromCloneOfParent();
|
||||
|
||||
Assert.Equal(2, datFile.GetItemsForBucketDB("child").Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddItemsFromDevices
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, 4)]
|
||||
[InlineData(false, true, 4)]
|
||||
[InlineData(true, false, 3)]
|
||||
[InlineData(true, true, 3)]
|
||||
public void AddItemsFromDevices_Items(bool deviceOnly, bool useSlotOptions, int expected)
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine deviceMachine = new Machine();
|
||||
deviceMachine.SetName("device");
|
||||
deviceMachine.SetFieldValue(Data.Models.Metadata.Machine.IsDeviceKey, true);
|
||||
|
||||
Machine slotOptionMachine = new Machine();
|
||||
slotOptionMachine.SetName("slotoption");
|
||||
|
||||
Machine itemMachine = new Machine();
|
||||
itemMachine.SetName("machine");
|
||||
|
||||
DatItem deviceItem = new Sample();
|
||||
deviceItem.SetName("device_item");
|
||||
deviceItem.SetFieldValue(DatItem.MachineKey, deviceMachine);
|
||||
deviceItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem slotOptionItem = new Sample();
|
||||
slotOptionItem.SetName("slot_option_item");
|
||||
slotOptionItem.SetFieldValue(DatItem.MachineKey, slotOptionMachine);
|
||||
slotOptionItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetName("rom");
|
||||
datItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
datItem.SetFieldValue(DatItem.MachineKey, itemMachine);
|
||||
datItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem deviceRef = new DeviceRef();
|
||||
deviceRef.SetName("device");
|
||||
deviceRef.SetFieldValue(DatItem.MachineKey, itemMachine);
|
||||
deviceRef.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem slotOption = new SlotOption();
|
||||
slotOption.SetName("slotoption");
|
||||
slotOption.SetFieldValue(DatItem.MachineKey, itemMachine);
|
||||
slotOption.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(deviceItem, statsOnly: false);
|
||||
datFile.AddItem(slotOptionItem, statsOnly: false);
|
||||
datFile.AddItem(datItem, statsOnly: false);
|
||||
datFile.AddItem(deviceRef, statsOnly: false);
|
||||
datFile.AddItem(slotOption, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromDevices(deviceOnly, useSlotOptions);
|
||||
|
||||
Assert.Equal(expected, datFile.GetItemsForBucket("machine").Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, 4)]
|
||||
[InlineData(false, true, 4)]
|
||||
[InlineData(true, false, 3)]
|
||||
[InlineData(true, true, 3)]
|
||||
public void AddItemsFromDevices_ItemsDB(bool deviceOnly, bool useSlotOptions, int expected)
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine deviceMachine = new Machine();
|
||||
deviceMachine.SetName("device");
|
||||
deviceMachine.SetFieldValue(Data.Models.Metadata.Machine.IsDeviceKey, true);
|
||||
|
||||
Machine slotOptionMachine = new Machine();
|
||||
slotOptionMachine.SetName("slotoption");
|
||||
|
||||
Machine itemMachine = new Machine();
|
||||
itemMachine.SetName("machine");
|
||||
|
||||
DatItem deviceItem = new Sample();
|
||||
deviceItem.SetName("device_item");
|
||||
|
||||
DatItem slotOptionItem = new Sample();
|
||||
slotOptionItem.SetName("slot_option_item");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetName("rom");
|
||||
datItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
|
||||
DatItem deviceRef = new DeviceRef();
|
||||
deviceRef.SetName("device");
|
||||
|
||||
DatItem slotOption = new SlotOption();
|
||||
slotOption.SetName("slotoption");
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(deviceMachine);
|
||||
long slotOptionMachineIndex = datFile.AddMachineDB(slotOptionMachine);
|
||||
long itemMachineIndex = datFile.AddMachineDB(itemMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(deviceItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(slotOptionItem, slotOptionMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(datItem, itemMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(deviceRef, itemMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(slotOption, itemMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromDevices(deviceOnly, useSlotOptions);
|
||||
|
||||
Assert.Equal(expected, datFile.GetItemsForBucketDB("machine").Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddItemsFromRomOfParent
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromRomOfParent_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(parentItem, statsOnly: false);
|
||||
datFile.AddItem(matchChildItem, statsOnly: false);
|
||||
datFile.AddItem(noMatchChildItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromRomOfParent();
|
||||
|
||||
Assert.Equal(2, datFile.GetItemsForBucket("child").Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItemsFromRomOfParent_ItemsDB()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long biosMachineIndex = datFile.AddMachineDB(parentMachine);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(childMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(parentItem, biosMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(matchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(noMatchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.AddItemsFromRomOfParent();
|
||||
|
||||
Assert.Equal(2, datFile.GetItemsForBucketDB("child").Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemoveBiosAndDeviceSets
|
||||
|
||||
[Fact]
|
||||
public void RemoveBiosAndDeviceSets_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine biosMachine = new Machine();
|
||||
biosMachine.SetName("bios");
|
||||
biosMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
Machine deviceMachine = new Machine();
|
||||
deviceMachine.SetName("device");
|
||||
deviceMachine.SetFieldValue(Data.Models.Metadata.Machine.IsDeviceKey, true);
|
||||
|
||||
DatItem biosItem = new Rom();
|
||||
biosItem.SetFieldValue(DatItem.MachineKey, biosMachine);
|
||||
biosItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem deviceItem = new Rom();
|
||||
deviceItem.SetFieldValue(DatItem.MachineKey, deviceMachine);
|
||||
deviceItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(biosItem, statsOnly: false);
|
||||
datFile.AddItem(deviceItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveBiosAndDeviceSets();
|
||||
|
||||
Assert.Empty(datFile.GetItemsForBucket("bios"));
|
||||
Assert.Empty(datFile.GetItemsForBucket("device"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveBiosAndDeviceSets_ItemsDB()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine biosMachine = new Machine();
|
||||
biosMachine.SetName("bios");
|
||||
biosMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
Machine deviceMachine = new Machine();
|
||||
deviceMachine.SetName("device");
|
||||
deviceMachine.SetFieldValue(Data.Models.Metadata.Machine.IsDeviceKey, true);
|
||||
|
||||
DatItem biosItem = new Rom();
|
||||
DatItem deviceItem = new Rom();
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long biosMachineIndex = datFile.AddMachineDB(biosMachine);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(deviceMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(biosItem, biosMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(deviceItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveBiosAndDeviceSets();
|
||||
|
||||
Assert.Empty(datFile.GetMachinesDB());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemoveItemsFromCloneOfChild
|
||||
|
||||
[Fact]
|
||||
public void RemoveItemsFromCloneOfChild_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
parentMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "romof");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(parentItem, statsOnly: false);
|
||||
datFile.AddItem(matchChildItem, statsOnly: false);
|
||||
datFile.AddItem(noMatchChildItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveItemsFromCloneOfChild();
|
||||
|
||||
Assert.Single(datFile.GetItemsForBucket("parent"));
|
||||
DatItem actual = Assert.Single(datFile.GetItemsForBucket("child"));
|
||||
Machine? actualMachine = actual.GetMachine();
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("child", actualMachine.GetName());
|
||||
Assert.Equal("romof", actualMachine.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveItemsFromCloneOfChild_ItemsDB()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
parentMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "romof");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "parent");
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long biosMachineIndex = datFile.AddMachineDB(parentMachine);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(childMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(parentItem, biosMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(matchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(noMatchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveItemsFromCloneOfChild();
|
||||
|
||||
Assert.Single(datFile.GetItemsForBucketDB("parent"));
|
||||
long actual = Assert.Single(datFile.GetItemsForBucketDB("child")).Key;
|
||||
Machine? actualMachine = datFile.GetMachineForItemDB(actual).Value;
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("child", actualMachine.GetName());
|
||||
Assert.Equal("romof", actualMachine.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemoveItemsFromRomOfChild
|
||||
|
||||
[Fact]
|
||||
public void RemoveItemsFromRomOfChild_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(parentItem, statsOnly: false);
|
||||
datFile.AddItem(matchChildItem, statsOnly: false);
|
||||
datFile.AddItem(noMatchChildItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveItemsFromRomOfChild();
|
||||
|
||||
Assert.Single(datFile.GetItemsForBucket("parent"));
|
||||
DatItem actual = Assert.Single(datFile.GetItemsForBucket("child"));
|
||||
Machine? actualMachine = actual.GetMachine();
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("child", actualMachine.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveItemsFromRomOfChild_ItemsDB()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine parentMachine = new Machine();
|
||||
parentMachine.SetName("parent");
|
||||
|
||||
Machine childMachine = new Machine();
|
||||
childMachine.SetName("child");
|
||||
childMachine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "parent");
|
||||
childMachine.SetFieldValue(Data.Models.Metadata.Machine.IsBiosKey, true);
|
||||
|
||||
DatItem parentItem = new Rom();
|
||||
parentItem.SetName("parent_rom");
|
||||
parentItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
parentItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
parentItem.SetFieldValue(DatItem.MachineKey, parentMachine);
|
||||
parentItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem matchChildItem = new Rom();
|
||||
matchChildItem.SetName("match_child_rom");
|
||||
matchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
matchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
matchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
matchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatItem noMatchChildItem = new Rom();
|
||||
noMatchChildItem.SetName("no_match_child_rom");
|
||||
noMatchChildItem.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
noMatchChildItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "beefdead");
|
||||
noMatchChildItem.SetFieldValue(DatItem.MachineKey, childMachine);
|
||||
noMatchChildItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long biosMachineIndex = datFile.AddMachineDB(parentMachine);
|
||||
long deviceMachineIndex = datFile.AddMachineDB(childMachine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(parentItem, biosMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(matchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
_ = datFile.AddItemDB(noMatchChildItem, deviceMachineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveItemsFromRomOfChild();
|
||||
|
||||
Assert.Single(datFile.GetItemsForBucketDB("parent"));
|
||||
long actual = Assert.Single(datFile.GetItemsForBucketDB("child")).Key;
|
||||
Machine? actualMachine = datFile.GetMachineForItemDB(actual).Value;
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("child", actualMachine.GetName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemoveMachineRelationshipTags
|
||||
|
||||
[Fact]
|
||||
public void RemoveMachineRelationshipTags_Items()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "XXXXXX");
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "XXXXXX");
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.SampleOfKey, "XXXXXX");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetFieldValue(DatItem.MachineKey, machine);
|
||||
datItem.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
datFile.AddItem(datItem, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveMachineRelationshipTags();
|
||||
|
||||
DatItem actualItem = Assert.Single(datFile.GetItemsForBucket("machine"));
|
||||
Machine? actual = actualItem.GetMachine();
|
||||
Assert.NotNull(actual);
|
||||
Assert.Null(actual.GetStringFieldValue(Data.Models.Metadata.Machine.CloneOfKey));
|
||||
Assert.Null(actual.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey));
|
||||
Assert.Null(actual.GetStringFieldValue(Data.Models.Metadata.Machine.SampleOfKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveMachineRelationshipTags_ItemsDB()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, "XXXXXX");
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, "XXXXXX");
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.SampleOfKey, "XXXXXX");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
|
||||
DatFile datFile = new Logiqx(datFile: null, useGame: false);
|
||||
long machineIndex = datFile.AddMachineDB(machine);
|
||||
long sourceIndex = datFile.AddSourceDB(source);
|
||||
_ = datFile.AddItemDB(datItem, machineIndex, sourceIndex, statsOnly: false);
|
||||
|
||||
datFile.BucketBy(ItemKey.Machine);
|
||||
datFile.RemoveMachineRelationshipTags();
|
||||
|
||||
Machine actual = Assert.Single(datFile.GetMachinesDB()).Value;
|
||||
Assert.Null(actual.GetStringFieldValue(Data.Models.Metadata.Machine.CloneOfKey));
|
||||
Assert.Null(actual.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey));
|
||||
Assert.Null(actual.GetStringFieldValue(Data.Models.Metadata.Machine.SampleOfKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1166
SabreTools.Metadata.DatFiles.Test/DatFileTests.ToMetadata.cs
Normal file
1166
SabreTools.Metadata.DatFiles.Test/DatFileTests.ToMetadata.cs
Normal file
File diff suppressed because it is too large
Load Diff
2343
SabreTools.Metadata.DatFiles.Test/DatFileTests.cs
Normal file
2343
SabreTools.Metadata.DatFiles.Test/DatFileTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
211
SabreTools.Metadata.DatFiles.Test/DatHeaderTests.cs
Normal file
211
SabreTools.Metadata.DatFiles.Test/DatHeaderTests.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Test
|
||||
{
|
||||
public class DatHeaderTests
|
||||
{
|
||||
#region CanOpenSpecified
|
||||
|
||||
[Fact]
|
||||
public void CanOpenSpecified_Missing()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<string[]>(Data.Models.Metadata.Header.CanOpenKey, null);
|
||||
Assert.False(header.CanOpenSpecified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOpenSpecified_Empty()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<string[]>(Data.Models.Metadata.Header.CanOpenKey, []);
|
||||
Assert.False(header.CanOpenSpecified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOpenSpecified_Exists()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<string[]>(Data.Models.Metadata.Header.CanOpenKey, ["value"]);
|
||||
Assert.True(header.CanOpenSpecified);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ImagesSpecified
|
||||
|
||||
[Fact]
|
||||
public void ImagesSpecified_Missing()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.Images>(Data.Models.Metadata.Header.ImagesKey, null);
|
||||
Assert.False(header.ImagesSpecified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImagesSpecified_Exists()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.Images>(Data.Models.Metadata.Header.ImagesKey, new());
|
||||
Assert.True(header.ImagesSpecified);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region InfosSpecified
|
||||
|
||||
[Fact]
|
||||
public void InfosSpecified_Missing()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.Infos>(Data.Models.Metadata.Header.InfosKey, null);
|
||||
Assert.False(header.InfosSpecified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InfosSpecified_Exists()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.Infos>(Data.Models.Metadata.Header.InfosKey, new());
|
||||
Assert.True(header.InfosSpecified);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NewDatSpecified
|
||||
|
||||
[Fact]
|
||||
public void NewDatSpecified_Missing()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.NewDat>(Data.Models.Metadata.Header.NewDatKey, null);
|
||||
Assert.False(header.NewDatSpecified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewDatSpecified_Exists()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.NewDat>(Data.Models.Metadata.Header.NewDatKey, new());
|
||||
Assert.True(header.NewDatSpecified);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SearchSpecified
|
||||
|
||||
[Fact]
|
||||
public void SearchSpecified_Missing()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.Search>(Data.Models.Metadata.Header.SearchKey, null);
|
||||
Assert.False(header.SearchSpecified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SearchSpecified_Exists()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue<Data.Models.OfflineList.Search>(Data.Models.Metadata.Header.SearchKey, new());
|
||||
Assert.True(header.SearchSpecified);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Clone
|
||||
|
||||
[Fact]
|
||||
public void CloneTest()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue(Data.Models.Metadata.Header.NameKey, "name");
|
||||
|
||||
object clone = header.Clone();
|
||||
DatHeader? actual = clone as DatHeader;
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal("name", actual.GetStringFieldValue(Data.Models.Metadata.Header.NameKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CloneFormat
|
||||
|
||||
[Fact]
|
||||
public void CloneFormatTest()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.Logiqx);
|
||||
|
||||
object clone = header.Clone();
|
||||
DatHeader? actual = clone as DatHeader;
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(DatFormat.Logiqx, actual.GetFieldValue<DatFormat>(DatHeader.DatFormatKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetInternalClone
|
||||
|
||||
[Fact]
|
||||
public void GetInternalCloneTest()
|
||||
{
|
||||
DatHeader header = new DatHeader();
|
||||
header.SetFieldValue(Data.Models.Metadata.Header.NameKey, "name");
|
||||
|
||||
Data.Models.Metadata.Header actual = header.GetInternalClone();
|
||||
Assert.Equal("name", actual[Data.Models.Metadata.Header.NameKey]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equals
|
||||
|
||||
[Fact]
|
||||
public void Equals_Null_False()
|
||||
{
|
||||
DatHeader self = new DatHeader();
|
||||
DatHeader? other = null;
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_DefaultInternal_True()
|
||||
{
|
||||
DatHeader self = new DatHeader();
|
||||
DatHeader? other = new DatHeader();
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_MismatchedInternal_False()
|
||||
{
|
||||
DatHeader self = new DatHeader();
|
||||
self.SetFieldValue(Data.Models.Metadata.Header.NameKey, "self");
|
||||
|
||||
DatHeader? other = new DatHeader();
|
||||
other.SetFieldValue(Data.Models.Metadata.Header.NameKey, "other");
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_EqualInternal_True()
|
||||
{
|
||||
DatHeader self = new DatHeader();
|
||||
self.SetFieldValue(Data.Models.Metadata.Header.NameKey, "name");
|
||||
|
||||
DatHeader? other = new DatHeader();
|
||||
other.SetFieldValue(Data.Models.Metadata.Header.NameKey, "name");
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
326
SabreTools.Metadata.DatFiles.Test/DatStatisticsTests.cs
Normal file
326
SabreTools.Metadata.DatFiles.Test/DatStatisticsTests.cs
Normal file
@@ -0,0 +1,326 @@
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Test
|
||||
{
|
||||
public class DatStatisticsTests
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
[Fact]
|
||||
public void DefaultConstructorTest()
|
||||
{
|
||||
var stats = new DatStatistics();
|
||||
|
||||
Assert.Null(stats.DisplayName);
|
||||
Assert.Equal(0, stats.MachineCount);
|
||||
Assert.False(stats.IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NamedConstructorTest()
|
||||
{
|
||||
var stats = new DatStatistics("name", isDirectory: true);
|
||||
|
||||
Assert.Equal("name", stats.DisplayName);
|
||||
Assert.Equal(0, stats.MachineCount);
|
||||
Assert.True(stats.IsDirectory);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region End to End
|
||||
|
||||
[Fact]
|
||||
public void AddRemoveStatisticsTest()
|
||||
{
|
||||
// Get items for testing
|
||||
var disk = CreateDisk();
|
||||
var file = CreateFile();
|
||||
var media = CreateMedia();
|
||||
var rom = CreateRom();
|
||||
var sample = CreateSample();
|
||||
|
||||
// Create an empty stats object
|
||||
var stats = new DatStatistics();
|
||||
|
||||
// Validate pre-add values
|
||||
Assert.Equal(0, stats.TotalCount);
|
||||
Assert.Equal(0, stats.TotalSize);
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD2));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD4));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD5));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD128));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD160));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA1));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA256));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA384));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA512));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SpamSum));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Disk));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.File));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Media));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Sample));
|
||||
Assert.Equal(0, stats.GetStatusCount(ItemStatus.Good));
|
||||
|
||||
// AddItemStatistics
|
||||
stats.AddItemStatistics(disk);
|
||||
stats.AddItemStatistics(file);
|
||||
stats.AddItemStatistics(media);
|
||||
stats.AddItemStatistics(rom);
|
||||
stats.AddItemStatistics(sample);
|
||||
|
||||
// Validate post-add values
|
||||
Assert.Equal(5, stats.TotalCount);
|
||||
Assert.Equal(2, stats.TotalSize);
|
||||
Assert.Equal(2, stats.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.MD2));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.MD4));
|
||||
Assert.Equal(4, stats.GetHashCount(HashType.MD5));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.RIPEMD128));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.RIPEMD160));
|
||||
Assert.Equal(4, stats.GetHashCount(HashType.SHA1));
|
||||
Assert.Equal(3, stats.GetHashCount(HashType.SHA256));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.SHA384));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.SHA512));
|
||||
Assert.Equal(2, stats.GetHashCount(HashType.SpamSum));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Disk));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.File));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Media));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Sample));
|
||||
Assert.Equal(2, stats.GetStatusCount(ItemStatus.Good));
|
||||
|
||||
// RemoveItemStatistics
|
||||
stats.RemoveItemStatistics(disk);
|
||||
stats.RemoveItemStatistics(file);
|
||||
stats.RemoveItemStatistics(media);
|
||||
stats.RemoveItemStatistics(rom);
|
||||
stats.RemoveItemStatistics(sample);
|
||||
|
||||
// Validate post-remove values
|
||||
Assert.Equal(0, stats.TotalCount);
|
||||
Assert.Equal(0, stats.TotalSize);
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD2));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD4));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD5));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD128));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD160));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA1));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA256));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA384));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA512));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SpamSum));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Disk));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.File));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Media));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Sample));
|
||||
Assert.Equal(0, stats.GetStatusCount(ItemStatus.Good));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetStatisticsTest()
|
||||
{
|
||||
// Get items for testing
|
||||
var disk = CreateDisk();
|
||||
var file = CreateFile();
|
||||
var media = CreateMedia();
|
||||
var rom = CreateRom();
|
||||
var sample = CreateSample();
|
||||
|
||||
// Create an empty stats object
|
||||
var stats = new DatStatistics();
|
||||
|
||||
// Validate pre-add values
|
||||
Assert.Equal(0, stats.TotalCount);
|
||||
Assert.Equal(0, stats.TotalSize);
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD2));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD4));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD5));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD128));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD160));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA1));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA256));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA384));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA512));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SpamSum));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Disk));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.File));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Media));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Sample));
|
||||
Assert.Equal(0, stats.GetStatusCount(ItemStatus.Good));
|
||||
|
||||
// AddItemStatistics
|
||||
stats.AddItemStatistics(disk);
|
||||
stats.AddItemStatistics(file);
|
||||
stats.AddItemStatistics(media);
|
||||
stats.AddItemStatistics(rom);
|
||||
stats.AddItemStatistics(sample);
|
||||
|
||||
// Validate post-add values
|
||||
Assert.Equal(5, stats.TotalCount);
|
||||
Assert.Equal(2, stats.TotalSize);
|
||||
Assert.Equal(2, stats.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.MD2));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.MD4));
|
||||
Assert.Equal(4, stats.GetHashCount(HashType.MD5));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.RIPEMD128));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.RIPEMD160));
|
||||
Assert.Equal(4, stats.GetHashCount(HashType.SHA1));
|
||||
Assert.Equal(3, stats.GetHashCount(HashType.SHA256));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.SHA384));
|
||||
Assert.Equal(1, stats.GetHashCount(HashType.SHA512));
|
||||
Assert.Equal(2, stats.GetHashCount(HashType.SpamSum));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Disk));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.File));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Media));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(1, stats.GetItemCount(ItemType.Sample));
|
||||
Assert.Equal(2, stats.GetStatusCount(ItemStatus.Good));
|
||||
|
||||
// ResetStatistics
|
||||
stats.ResetStatistics();
|
||||
|
||||
// Validate post-reset values
|
||||
Assert.Equal(0, stats.TotalCount);
|
||||
Assert.Equal(0, stats.TotalSize);
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD2));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD4));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.MD5));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD128));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.RIPEMD160));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA1));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA256));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA384));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SHA512));
|
||||
Assert.Equal(0, stats.GetHashCount(HashType.SpamSum));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Disk));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.File));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Media));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(0, stats.GetItemCount(ItemType.Sample));
|
||||
Assert.Equal(0, stats.GetStatusCount(ItemStatus.Good));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddStatistics
|
||||
|
||||
[Fact]
|
||||
public void AddStatisticsTest()
|
||||
{
|
||||
var rom = CreateRom();
|
||||
var origStats = new DatStatistics();
|
||||
origStats.AddItemStatistics(rom);
|
||||
|
||||
var newStats = new DatStatistics();
|
||||
newStats.AddStatistics(origStats);
|
||||
|
||||
Assert.Equal(1, newStats.TotalCount);
|
||||
Assert.Equal(1, newStats.TotalSize);
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.MD2));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.MD4));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.MD5));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.RIPEMD128));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.RIPEMD160));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.SHA1));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.SHA256));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.SHA384));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.SHA512));
|
||||
Assert.Equal(1, newStats.GetHashCount(HashType.SpamSum));
|
||||
Assert.Equal(1, newStats.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(1, newStats.GetStatusCount(ItemStatus.Good));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Create a Disk for testing
|
||||
/// </summary>
|
||||
private static Disk CreateDisk()
|
||||
{
|
||||
var disk = new Disk();
|
||||
|
||||
disk.SetFieldValue<string?>(Data.Models.Metadata.Disk.StatusKey, ItemStatus.Good.AsStringValue());
|
||||
disk.SetFieldValue<string?>(Data.Models.Metadata.Disk.MD5Key, HashType.MD5.ZeroString);
|
||||
disk.SetFieldValue<string?>(Data.Models.Metadata.Disk.SHA1Key, HashType.SHA1.ZeroString);
|
||||
|
||||
return disk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a File for testing
|
||||
/// </summary>
|
||||
private static File CreateFile()
|
||||
{
|
||||
var file = new File
|
||||
{
|
||||
Size = 1,
|
||||
CRC = HashType.CRC32.ZeroString,
|
||||
MD5 = HashType.MD5.ZeroString,
|
||||
SHA1 = HashType.SHA1.ZeroString,
|
||||
SHA256 = HashType.SHA256.ZeroString
|
||||
};
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Media for testing
|
||||
/// </summary>
|
||||
private static Media CreateMedia()
|
||||
{
|
||||
var media = new Media();
|
||||
|
||||
media.SetFieldValue<string?>(Data.Models.Metadata.Media.MD5Key, HashType.MD5.ZeroString);
|
||||
media.SetFieldValue<string?>(Data.Models.Metadata.Media.SHA1Key, HashType.SHA1.ZeroString);
|
||||
media.SetFieldValue<string?>(Data.Models.Metadata.Media.SHA256Key, HashType.SHA256.ZeroString);
|
||||
media.SetFieldValue<string?>(Data.Models.Metadata.Media.SpamSumKey, HashType.SpamSum.ZeroString);
|
||||
|
||||
return media;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Rom for testing
|
||||
/// </summary>
|
||||
private static Rom CreateRom()
|
||||
{
|
||||
var rom = new Rom();
|
||||
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.StatusKey, ItemStatus.Good.AsStringValue());
|
||||
rom.SetFieldValue<long>(Data.Models.Metadata.Rom.SizeKey, 1);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, HashType.CRC32.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD2Key, HashType.MD2.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD4Key, HashType.MD4.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD5Key, HashType.MD5.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD128Key, HashType.RIPEMD128.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD160Key, HashType.RIPEMD160.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, HashType.SHA1.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA256Key, HashType.SHA256.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA384Key, HashType.SHA384.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA512Key, HashType.SHA512.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SpamSumKey, HashType.SpamSum.ZeroString);
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Sample for testing
|
||||
/// </summary>
|
||||
private static Sample CreateSample() => new();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
114
SabreTools.Metadata.DatFiles.Test/ExtensionsTests.cs
Normal file
114
SabreTools.Metadata.DatFiles.Test/ExtensionsTests.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Test
|
||||
{
|
||||
public class ExtensionsTests
|
||||
{
|
||||
#region String to Enum
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, MergingFlag.None)]
|
||||
[InlineData("none", MergingFlag.None)]
|
||||
[InlineData("split", MergingFlag.Split)]
|
||||
[InlineData("merged", MergingFlag.Merged)]
|
||||
[InlineData("nonmerged", MergingFlag.NonMerged)]
|
||||
[InlineData("unmerged", MergingFlag.NonMerged)]
|
||||
[InlineData("fullmerged", MergingFlag.FullMerged)]
|
||||
[InlineData("device", MergingFlag.DeviceNonMerged)]
|
||||
[InlineData("devicenonmerged", MergingFlag.DeviceNonMerged)]
|
||||
[InlineData("deviceunmerged", MergingFlag.DeviceNonMerged)]
|
||||
[InlineData("full", MergingFlag.FullNonMerged)]
|
||||
[InlineData("fullnonmerged", MergingFlag.FullNonMerged)]
|
||||
[InlineData("fullunmerged", MergingFlag.FullNonMerged)]
|
||||
public void AsMergingFlagTest(string? field, MergingFlag expected)
|
||||
{
|
||||
MergingFlag actual = field.AsMergingFlag();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, NodumpFlag.None)]
|
||||
[InlineData("none", NodumpFlag.None)]
|
||||
[InlineData("obsolete", NodumpFlag.Obsolete)]
|
||||
[InlineData("required", NodumpFlag.Required)]
|
||||
[InlineData("ignore", NodumpFlag.Ignore)]
|
||||
public void AsNodumpFlagTest(string? field, NodumpFlag expected)
|
||||
{
|
||||
NodumpFlag actual = field.AsNodumpFlag();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, PackingFlag.None)]
|
||||
[InlineData("none", PackingFlag.None)]
|
||||
[InlineData("yes", PackingFlag.Zip)]
|
||||
[InlineData("zip", PackingFlag.Zip)]
|
||||
[InlineData("no", PackingFlag.Unzip)]
|
||||
[InlineData("unzip", PackingFlag.Unzip)]
|
||||
[InlineData("partial", PackingFlag.Partial)]
|
||||
[InlineData("flat", PackingFlag.Flat)]
|
||||
[InlineData("fileonly", PackingFlag.FileOnly)]
|
||||
public void AsPackingFlagTest(string? field, PackingFlag expected)
|
||||
{
|
||||
PackingFlag actual = field.AsPackingFlag();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enum to String
|
||||
|
||||
[Theory]
|
||||
[InlineData(MergingFlag.None, true, "none")]
|
||||
[InlineData(MergingFlag.None, false, "none")]
|
||||
[InlineData(MergingFlag.Split, true, "split")]
|
||||
[InlineData(MergingFlag.Split, false, "split")]
|
||||
[InlineData(MergingFlag.Merged, true, "merged")]
|
||||
[InlineData(MergingFlag.Merged, false, "merged")]
|
||||
[InlineData(MergingFlag.NonMerged, true, "unmerged")]
|
||||
[InlineData(MergingFlag.NonMerged, false, "nonmerged")]
|
||||
[InlineData(MergingFlag.FullMerged, true, "fullmerged")]
|
||||
[InlineData(MergingFlag.FullMerged, false, "fullmerged")]
|
||||
[InlineData(MergingFlag.DeviceNonMerged, true, "deviceunmerged")]
|
||||
[InlineData(MergingFlag.DeviceNonMerged, false, "device")]
|
||||
[InlineData(MergingFlag.FullNonMerged, true, "fullunmerged")]
|
||||
[InlineData(MergingFlag.FullNonMerged, false, "full")]
|
||||
public void FromMergingFlagTest(MergingFlag field, bool useSecond, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue(useSecond);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(NodumpFlag.None, "none")]
|
||||
[InlineData(NodumpFlag.Obsolete, "obsolete")]
|
||||
[InlineData(NodumpFlag.Required, "required")]
|
||||
[InlineData(NodumpFlag.Ignore, "ignore")]
|
||||
public void FromNodumpFlagTest(NodumpFlag field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PackingFlag.None, true, "none")]
|
||||
[InlineData(PackingFlag.None, false, "none")]
|
||||
[InlineData(PackingFlag.Zip, true, "yes")]
|
||||
[InlineData(PackingFlag.Zip, false, "zip")]
|
||||
[InlineData(PackingFlag.Unzip, true, "no")]
|
||||
[InlineData(PackingFlag.Unzip, false, "unzip")]
|
||||
[InlineData(PackingFlag.Partial, true, "partial")]
|
||||
[InlineData(PackingFlag.Partial, false, "partial")]
|
||||
[InlineData(PackingFlag.Flat, true, "flat")]
|
||||
[InlineData(PackingFlag.Flat, false, "flat")]
|
||||
[InlineData(PackingFlag.FileOnly, true, "fileonly")]
|
||||
[InlineData(PackingFlag.FileOnly, false, "fileonly")]
|
||||
public void FromPackingFlagTest(PackingFlag field, bool useSecond, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue(useSecond);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1647
SabreTools.Metadata.DatFiles.Test/FormatsTests.cs
Normal file
1647
SabreTools.Metadata.DatFiles.Test/FormatsTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
1025
SabreTools.Metadata.DatFiles.Test/ItemDictionaryDBTests.cs
Normal file
1025
SabreTools.Metadata.DatFiles.Test/ItemDictionaryDBTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
818
SabreTools.Metadata.DatFiles.Test/ItemDictionaryTests.cs
Normal file
818
SabreTools.Metadata.DatFiles.Test/ItemDictionaryTests.cs
Normal file
@@ -0,0 +1,818 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Test
|
||||
{
|
||||
public class ItemDictionaryTests
|
||||
{
|
||||
#region AddItem
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Disk_WithHashes()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem disk = new Disk();
|
||||
disk.SetName("item");
|
||||
disk.SetFieldValue<string?>(Data.Models.Metadata.Disk.SHA1Key, "deadbeef");
|
||||
disk.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
disk.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(disk, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Disk);
|
||||
Assert.Equal("none", actual.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Disk_WithoutHashes()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem disk = new Disk();
|
||||
disk.SetName("item");
|
||||
disk.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
disk.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(disk, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Disk);
|
||||
Assert.Equal("nodump", actual.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_File_WithHashes()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
var file = new File();
|
||||
file.SetName("item");
|
||||
file.SHA1 = "deadbeef";
|
||||
file.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
file.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(file, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is File);
|
||||
//Assert.Equal("none", actual.GetStringFieldValue(File.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_File_WithoutHashes()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem file = new File();
|
||||
file.SetName("item");
|
||||
file.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
file.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(file, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is File);
|
||||
//Assert.Equal("nodump", actual.GetStringFieldValue(File.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Media_WithHashes()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem media = new Media();
|
||||
media.SetName("item");
|
||||
media.SetFieldValue<string?>(Data.Models.Metadata.Media.SHA1Key, "deadbeef");
|
||||
media.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
media.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(media, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Media);
|
||||
//Assert.Equal("none", actual.GetStringFieldValue(Data.Models.Metadata.Media.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Media_WithoutHashes()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem media = new Media();
|
||||
media.SetName("item");
|
||||
media.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
media.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(media, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Media);
|
||||
//Assert.Equal("nodump", actual.GetStringFieldValue(Data.Models.Metadata.Media.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Rom_WithHashesWithSize()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("item");
|
||||
rom.SetFieldValue<long?>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "deadbeef");
|
||||
rom.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
rom.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(rom, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Rom);
|
||||
Assert.Equal(12345, actual.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey));
|
||||
Assert.Equal("deadbeef", actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal("none", actual.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Rom_WithoutHashesWithSize()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("item");
|
||||
rom.SetFieldValue<long?>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
rom.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
rom.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(rom, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Rom);
|
||||
Assert.Equal(12345, actual.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey));
|
||||
Assert.Null(actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal("nodump", actual.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Rom_WithHashesWithoutSize()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("item");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "deadbeef");
|
||||
rom.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
rom.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(rom, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Rom);
|
||||
Assert.Null(actual.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey));
|
||||
Assert.Equal("deadbeef", actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal("none", actual.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_Rom_WithoutHashesWithoutSize()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("item");
|
||||
rom.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
rom.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(rom, statsOnly: false);
|
||||
|
||||
DatItem actual = Assert.Single(dict.GetItemsForBucket("default"));
|
||||
Assert.True(actual is Rom);
|
||||
Assert.Equal(0, actual.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey));
|
||||
Assert.Equal(HashType.SHA1.ZeroString, actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal("none", actual.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_StatsOnly()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetName("item");
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: true);
|
||||
|
||||
Assert.Empty(dict.GetItemsForBucket("default"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_NormalAdd()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
Machine machine = new Machine();
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetName("item");
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: false);
|
||||
|
||||
Assert.Single(dict.GetItemsForBucket("default"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ClearMarked
|
||||
|
||||
[Fact]
|
||||
public void ClearMarkedTest()
|
||||
{
|
||||
// Setup the items
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("game-1");
|
||||
|
||||
DatItem rom1 = new Rom();
|
||||
rom1.SetName("rom-1");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine);
|
||||
|
||||
DatItem rom2 = new Rom();
|
||||
rom2.SetName("rom-2");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
rom2.SetFieldValue<bool?>(DatItem.RemoveKey, true);
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "000000e948edcb4f7704b8af85a77a3339ecce44");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom2.CopyMachineInformation(machine);
|
||||
|
||||
// Setup the dictionary
|
||||
var dict = new ItemDictionary();
|
||||
dict.AddItem(rom1, statsOnly: false);
|
||||
dict.AddItem(rom2, statsOnly: false);
|
||||
|
||||
dict.ClearMarked();
|
||||
Assert.Empty(dict.GetItemsForBucket("default"));
|
||||
List<DatItem> items = dict.GetItemsForBucket("game-1");
|
||||
Assert.Single(items);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetItemsForBucket
|
||||
|
||||
[Fact]
|
||||
public void GetItemsForBucket_NullBucketName()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: false);
|
||||
|
||||
var actual = dict.GetItemsForBucket(null, filter: false);
|
||||
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItemsForBucket_InvalidBucketName()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: false);
|
||||
|
||||
var actual = dict.GetItemsForBucket("INVALID", filter: false);
|
||||
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItemsForBucket_RemovedFilter()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetFieldValue<bool?>(DatItem.RemoveKey, true);
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: false);
|
||||
|
||||
var actual = dict.GetItemsForBucket("machine", filter: true);
|
||||
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItemsForBucket_RemovedNoFilter()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetFieldValue<bool?>(DatItem.RemoveKey, true);
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: false);
|
||||
|
||||
var actual = dict.GetItemsForBucket("machine", filter: false);
|
||||
|
||||
Assert.Single(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItemsForBucket_Standard()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: false);
|
||||
|
||||
var actual = dict.GetItemsForBucket("machine", filter: false);
|
||||
|
||||
Assert.Single(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemoveBucket
|
||||
|
||||
[Fact]
|
||||
public void RemoveBucketTest()
|
||||
{
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("game-1");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetName("rom-1");
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
datItem.CopyMachineInformation(machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
dict.AddItem(datItem, statsOnly: false);
|
||||
|
||||
dict.RemoveBucket("game-1");
|
||||
|
||||
Assert.Empty(dict.GetItemsForBucket("default"));
|
||||
Assert.Empty(dict.GetItemsForBucket("game-1"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemoveItem
|
||||
|
||||
[Fact]
|
||||
public void RemoveItemTest()
|
||||
{
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("game-1");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetName("rom-1");
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
datItem.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
datItem.CopyMachineInformation(machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
dict.AddItem(datItem, statsOnly: false);
|
||||
|
||||
dict.RemoveItem("game-1", (Rom)datItem.Clone(), 0);
|
||||
|
||||
Assert.Empty(dict.GetItemsForBucket("default"));
|
||||
Assert.Empty(dict.GetItemsForBucket("game-1"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BucketBy
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemKey.NULL, 2)]
|
||||
[InlineData(ItemKey.Machine, 2)]
|
||||
[InlineData(ItemKey.CRC, 1)]
|
||||
[InlineData(ItemKey.SHA1, 4)]
|
||||
public void BucketByTest(ItemKey itemKey, int expected)
|
||||
{
|
||||
// Setup the items
|
||||
Machine machine1 = new Machine();
|
||||
machine1.SetName("game-1");
|
||||
|
||||
Machine machine2 = new Machine();
|
||||
machine2.SetName("game-2");
|
||||
|
||||
DatItem rom1 = new Rom();
|
||||
rom1.SetName("rom-1");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine1);
|
||||
|
||||
DatItem rom2 = new Rom();
|
||||
rom2.SetName("rom-2");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "000000e948edcb4f7704b8af85a77a3339ecce44");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine1);
|
||||
|
||||
DatItem rom3 = new Rom();
|
||||
rom3.SetName("rom-3");
|
||||
rom3.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
rom3.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "00000ea4014ce66679e7e17d56ac510f67e39e26");
|
||||
rom3.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine2);
|
||||
|
||||
DatItem rom4 = new Rom();
|
||||
rom4.SetName("rom-4");
|
||||
rom4.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "DEAEEF");
|
||||
rom4.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "00000151d437442e74e5134023fab8bf694a2487");
|
||||
rom4.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine2);
|
||||
|
||||
// Setup the dictionary
|
||||
var dict = new ItemDictionary();
|
||||
dict.AddItem(rom1, statsOnly: false);
|
||||
dict.AddItem(rom2, statsOnly: false);
|
||||
dict.AddItem(rom3, statsOnly: false);
|
||||
dict.AddItem(rom4, statsOnly: false);
|
||||
|
||||
dict.BucketBy(itemKey);
|
||||
Assert.Equal(expected, dict.SortedKeys.Length);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Deduplicate
|
||||
|
||||
[Fact]
|
||||
public void DeduplicateTest()
|
||||
{
|
||||
// Setup the items
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("game-1");
|
||||
|
||||
DatItem rom1 = new Rom();
|
||||
rom1.SetName("rom-1");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine);
|
||||
|
||||
DatItem rom2 = new Rom();
|
||||
rom2.SetName("rom-2");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom2.CopyMachineInformation(machine);
|
||||
|
||||
// Setup the dictionary
|
||||
var dict = new ItemDictionary();
|
||||
dict.AddItem(rom1, statsOnly: false);
|
||||
dict.AddItem(rom2, statsOnly: false);
|
||||
|
||||
dict.Deduplicate();
|
||||
Assert.Equal(1, dict.DatStatistics.TotalCount);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetDuplicateStatus
|
||||
|
||||
[Fact]
|
||||
public void GetDuplicateStatus_NullOther_NoDupe()
|
||||
{
|
||||
var dict = new ItemDictionary();
|
||||
DatItem item = new Rom();
|
||||
DatItem? lastItem = null;
|
||||
var actual = dict.GetDuplicateStatus(item, lastItem);
|
||||
Assert.Equal((DupeType)0x00, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDuplicateStatus_DifferentTypes_NoDupe()
|
||||
{
|
||||
var dict = new ItemDictionary();
|
||||
var rom = new Rom();
|
||||
DatItem? lastItem = new Disk();
|
||||
var actual = dict.GetDuplicateStatus(rom, lastItem);
|
||||
Assert.Equal((DupeType)0x00, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDuplicateStatus_MismatchedHashes_NoDupe()
|
||||
{
|
||||
var dict = new ItemDictionary();
|
||||
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("name-same");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
machineB.SetName("name-same");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetName("same-name");
|
||||
romA.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "BEEFDEAD");
|
||||
romA.SetFieldValue<Source?>(DatItem.SourceKey, new Source(0));
|
||||
romA.CopyMachineInformation(machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetName("same-name");
|
||||
romB.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romB.SetFieldValue<Source?>(DatItem.SourceKey, new Source(1));
|
||||
romB.CopyMachineInformation(machineB);
|
||||
|
||||
var actual = dict.GetDuplicateStatus(romA, romB);
|
||||
Assert.Equal((DupeType)0x00, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDuplicateStatus_DifferentSource_NameMatch_ExternalAll()
|
||||
{
|
||||
var dict = new ItemDictionary();
|
||||
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("name-same");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
machineB.SetName("name-same");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetName("same-name");
|
||||
romA.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romA.SetFieldValue<Source?>(DatItem.SourceKey, new Source(0));
|
||||
romA.CopyMachineInformation(machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetName("same-name");
|
||||
romB.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romB.SetFieldValue<Source?>(DatItem.SourceKey, new Source(1));
|
||||
romB.CopyMachineInformation(machineB);
|
||||
|
||||
var actual = dict.GetDuplicateStatus(romA, romB);
|
||||
Assert.Equal(DupeType.External | DupeType.All, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDuplicateStatus_DifferentSource_NoNameMatch_ExternalHash()
|
||||
{
|
||||
var dict = new ItemDictionary();
|
||||
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("name-same");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
machineB.SetName("not-name-same");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetName("same-name");
|
||||
romA.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romA.SetFieldValue<Source?>(DatItem.SourceKey, new Source(0));
|
||||
romA.CopyMachineInformation(machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetName("same-name");
|
||||
romB.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romB.SetFieldValue<Source?>(DatItem.SourceKey, new Source(1));
|
||||
romB.CopyMachineInformation(machineB);
|
||||
|
||||
var actual = dict.GetDuplicateStatus(romA, romB);
|
||||
Assert.Equal(DupeType.External | DupeType.Hash, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDuplicateStatus_SameSource_NameMatch_InternalAll()
|
||||
{
|
||||
var dict = new ItemDictionary();
|
||||
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("name-same");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
machineB.SetName("name-same");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetName("same-name");
|
||||
romA.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romA.SetFieldValue<Source?>(DatItem.SourceKey, new Source(0));
|
||||
romA.CopyMachineInformation(machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetName("same-name");
|
||||
romB.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romB.SetFieldValue<Source?>(DatItem.SourceKey, new Source(0));
|
||||
romB.CopyMachineInformation(machineB);
|
||||
|
||||
var actual = dict.GetDuplicateStatus(romA, romB);
|
||||
Assert.Equal(DupeType.Internal | DupeType.All, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDuplicateStatus_SameSource_NoNameMatch_InternalHash()
|
||||
{
|
||||
var dict = new ItemDictionary();
|
||||
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("name-same");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
machineB.SetName("not-name-same");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetName("same-name");
|
||||
romA.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romA.SetFieldValue<Source?>(DatItem.SourceKey, new Source(0));
|
||||
romA.CopyMachineInformation(machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetName("same-name");
|
||||
romB.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
romB.SetFieldValue<Source?>(DatItem.SourceKey, new Source(0));
|
||||
romB.CopyMachineInformation(machineB);
|
||||
|
||||
var actual = dict.GetDuplicateStatus(romA, romB);
|
||||
Assert.Equal(DupeType.Internal | DupeType.Hash, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetDuplicates
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, 1)]
|
||||
[InlineData(false, 0)]
|
||||
public void GetDuplicatesTest(bool hasDuplicate, int expected)
|
||||
{
|
||||
// Setup the items
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("game-1");
|
||||
|
||||
DatItem rom1 = new Rom();
|
||||
rom1.SetName("rom-1");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine);
|
||||
|
||||
DatItem rom2 = new Rom();
|
||||
rom2.SetName("rom-2");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "000000e948edcb4f7704b8af85a77a3339ecce44");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom2.CopyMachineInformation(machine);
|
||||
|
||||
// Setup the dictionary
|
||||
var dict = new ItemDictionary();
|
||||
dict.AddItem(rom1, statsOnly: false);
|
||||
dict.AddItem(rom2, statsOnly: false);
|
||||
|
||||
// Setup the test item
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("rom-1");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, hasDuplicate ? "1024" : "2048");
|
||||
rom.CopyMachineInformation(machine);
|
||||
|
||||
var actual = dict.GetDuplicates(rom);
|
||||
Assert.Equal(expected, actual.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasDuplicates
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void HasDuplicatesTest(bool expected)
|
||||
{
|
||||
// Setup the items
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("game-1");
|
||||
|
||||
DatItem rom1 = new Rom();
|
||||
rom1.SetName("rom-1");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom1.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine);
|
||||
|
||||
DatItem rom2 = new Rom();
|
||||
rom2.SetName("rom-2");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "000000e948edcb4f7704b8af85a77a3339ecce44");
|
||||
rom2.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "1024");
|
||||
rom1.CopyMachineInformation(machine);
|
||||
|
||||
// Setup the dictionary
|
||||
var dict = new ItemDictionary();
|
||||
dict.AddItem("game-1", rom1);
|
||||
dict.AddItem("game-1", rom2);
|
||||
|
||||
// Setup the test item
|
||||
DatItem rom = new Rom();
|
||||
rom.SetName("rom-1");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, "0000000fbbb37f8488100b1b4697012de631a5e6");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, expected ? "1024" : "2048");
|
||||
rom1.CopyMachineInformation(machine);
|
||||
|
||||
bool actual = dict.HasDuplicates(rom);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RecalculateStats
|
||||
|
||||
[Fact]
|
||||
public void RecalculateStatsTest()
|
||||
{
|
||||
Source source = new Source(0, source: null);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetName("machine");
|
||||
|
||||
DatItem item = new Rom();
|
||||
item.SetName("rom");
|
||||
item.SetFieldValue<long?>(Data.Models.Metadata.Rom.SizeKey, 12345);
|
||||
item.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, "deadbeef");
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
item.SetFieldValue<Machine?>(DatItem.MachineKey, machine);
|
||||
|
||||
var dict = new ItemDictionary();
|
||||
_ = dict.AddItem(item, statsOnly: false);
|
||||
|
||||
Assert.Equal(1, dict.DatStatistics.TotalCount);
|
||||
Assert.Equal(1, dict.DatStatistics.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(12345, dict.DatStatistics.TotalSize);
|
||||
Assert.Equal(1, dict.DatStatistics.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(0, dict.DatStatistics.GetHashCount(HashType.MD5));
|
||||
|
||||
item.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD5Key, "deadbeef");
|
||||
|
||||
dict.RecalculateStats();
|
||||
|
||||
Assert.Equal(1, dict.DatStatistics.TotalCount);
|
||||
Assert.Equal(1, dict.DatStatistics.GetItemCount(ItemType.Rom));
|
||||
Assert.Equal(12345, dict.DatStatistics.TotalSize);
|
||||
Assert.Equal(1, dict.DatStatistics.GetHashCount(HashType.CRC32));
|
||||
Assert.Equal(1, dict.DatStatistics.GetHashCount(HashType.MD5));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="[2.0.0]" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Metadata\SabreTools.Metadata.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Metadata.DatFiles\SabreTools.Metadata.DatFiles.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Metadata.DatItems\SabreTools.Metadata.DatItems.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
801
SabreTools.Metadata.DatFiles/DatFile.Filtering.cs
Normal file
801
SabreTools.Metadata.DatFiles/DatFile.Filtering.cs
Normal file
@@ -0,0 +1,801 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
#pragma warning disable IDE0057 // Use range operator
|
||||
#pragma warning disable IDE0059 // Unnecessary assignment of a value
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
public partial class DatFile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Scene name Regex pattern
|
||||
/// </summary>
|
||||
private const string SceneNamePattern = @"([0-9]{2}\.[0-9]{2}\.[0-9]{2}-)(.*?-.*?)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filtering
|
||||
|
||||
/// <summary>
|
||||
/// Execute all filters in a filter runner on the items in the dictionary
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Preconfigured filter runner to use</param>
|
||||
public void ExecuteFilters(FilterRunner filterRunner)
|
||||
{
|
||||
ExecuteFiltersImpl(filterRunner);
|
||||
ExecuteFiltersImplDB(filterRunner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use game descriptions as names, updating cloneof/romof/sampleof
|
||||
/// </summary>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
public void MachineDescriptionToName(bool throwOnError = false)
|
||||
{
|
||||
MachineDescriptionToNameImpl(throwOnError);
|
||||
MachineDescriptionToNameImplDB(throwOnError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that all roms are in their own game (or at least try to ensure)
|
||||
/// </summary>
|
||||
public void SetOneRomPerGame()
|
||||
{
|
||||
SetOneRomPerGameImpl();
|
||||
SetOneRomPerGameImplDB();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter a DAT using 1G1R logic given an ordered set of regions
|
||||
/// </summary>
|
||||
/// <param name="regionList">List of regions in order of priority</param>
|
||||
/// <remarks>
|
||||
/// In the most technical sense, the way that the region list is being used does not
|
||||
/// confine its values to be just regions. Since it's essentially acting like a
|
||||
/// specialized version of the machine name filter, anything that is usually encapsulated
|
||||
/// in parenthesis would be matched on, including disc numbers, languages, editions,
|
||||
/// and anything else commonly used. Please note that, unlike other existing 1G1R
|
||||
/// solutions, this does not have the ability to contain custom mappings of parent
|
||||
/// to clone sets based on name, nor does it have the ability to match on the
|
||||
/// Release DatItem type.
|
||||
/// </remarks>
|
||||
public void SetOneGamePerRegion(List<string> regionList)
|
||||
{
|
||||
SetOneGamePerRegionImpl(regionList);
|
||||
SetOneGamePerRegionImplDB(regionList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strip the dates from the beginning of scene-style set names
|
||||
/// </summary>
|
||||
public void StripSceneDatesFromItems()
|
||||
{
|
||||
StripSceneDatesFromItemsImpl();
|
||||
StripSceneDatesFromItemsImplDB();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filtering Implementations
|
||||
|
||||
/// <summary>
|
||||
/// Create machine to description mapping dictionary
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private IDictionary<string, string> CreateMachineToDescriptionMapping()
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
ConcurrentDictionary<string, string> mapping = new();
|
||||
#else
|
||||
Dictionary<string, string> mapping = [];
|
||||
#endif
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(Items.SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in Items.SortedKeys)
|
||||
#endif
|
||||
{
|
||||
var items = GetItemsForBucket(key);
|
||||
if (items is null)
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
return;
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
// Get the current machine
|
||||
var machine = item.GetMachine();
|
||||
if (machine is null)
|
||||
continue;
|
||||
|
||||
// Get the values to check against
|
||||
string? machineName = machine.GetName();
|
||||
string? machineDesc = machine.GetStringFieldValue(Data.Models.Metadata.Machine.DescriptionKey);
|
||||
if (machineName is null || machineDesc is null)
|
||||
continue;
|
||||
|
||||
// Adjust the description
|
||||
machineDesc = machineDesc.Replace('/', '_').Replace("\"", "''").Replace(":", " -");
|
||||
if (machineName == machineDesc)
|
||||
continue;
|
||||
|
||||
// If the key mapping doesn't exist, add it
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
mapping.TryAdd(machineName, machineDesc);
|
||||
#else
|
||||
if (!mapping.ContainsKey(machineName))
|
||||
mapping[machineName] = machineDesc;
|
||||
#endif
|
||||
}
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create machine to description mapping dictionary
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private Dictionary<string, string> CreateMachineToDescriptionMappingDB()
|
||||
{
|
||||
Dictionary<string, string> mapping = [];
|
||||
foreach (var machine in GetMachinesDB())
|
||||
{
|
||||
// Get the current machine
|
||||
if (machine.Value is null)
|
||||
continue;
|
||||
|
||||
// Get the values to check against
|
||||
string? machineName = machine.Value.GetName();
|
||||
string? machineDesc = machine.Value.GetStringFieldValue(Data.Models.Metadata.Machine.DescriptionKey);
|
||||
if (machineName is null || machineDesc is null)
|
||||
continue;
|
||||
|
||||
// Adjust the description
|
||||
machineDesc = machineDesc.Replace('/', '_').Replace("\"", "''").Replace(":", " -");
|
||||
if (machineName == machineDesc)
|
||||
continue;
|
||||
|
||||
// If the key mapping doesn't exist, add it
|
||||
if (!mapping.ContainsKey(machineName))
|
||||
mapping[machineName] = machineDesc;
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute all filters in a filter runner on the items in the dictionary
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Preconfigured filter runner to use</param>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private void ExecuteFiltersImpl(FilterRunner filterRunner)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(Items.SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in Items.SortedKeys)
|
||||
#endif
|
||||
{
|
||||
ExecuteFilterOnBucket(filterRunner, key);
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute all filters in a filter runner on the items in the dictionary
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Preconfigured filter runner to use</param>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void ExecuteFiltersImplDB(FilterRunner filterRunner)
|
||||
{
|
||||
List<string> keys = [.. ItemsDB.SortedKeys];
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(keys, key =>
|
||||
#else
|
||||
foreach (var key in keys)
|
||||
#endif
|
||||
{
|
||||
ExecuteFilterOnBucketDB(filterRunner, key);
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute all filters in a filter runner on a single bucket
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Preconfigured filter runner to use</param>
|
||||
/// <param name="bucketName">Name of the bucket to filter on</param>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private void ExecuteFilterOnBucket(FilterRunner filterRunner, string bucketName)
|
||||
{
|
||||
List<DatItem>? items = GetItemsForBucket(bucketName);
|
||||
if (items is null)
|
||||
return;
|
||||
|
||||
// Filter all items in the current key
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!item.PassesFilter(filterRunner))
|
||||
item.SetFieldValue<bool?>(DatItem.RemoveKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute all filters in a filter runner on a single bucket
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Preconfigured filter runner to use</param>
|
||||
/// <param name="bucketName">Name of the bucket to filter on</param>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void ExecuteFilterOnBucketDB(FilterRunner filterRunner, string bucketName)
|
||||
{
|
||||
var items = GetItemsForBucketDB(bucketName);
|
||||
if (items is null)
|
||||
return;
|
||||
|
||||
// Filter all items in the current key
|
||||
List<long> newItems = [];
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!item.Value.PassesFilterDB(filterRunner))
|
||||
item.Value.SetFieldValue<bool?>(DatItem.RemoveKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use game descriptions as names, updating cloneof/romof/sampleof
|
||||
/// </summary>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private void MachineDescriptionToNameImpl(bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// First we want to get a mapping for all games to description
|
||||
var mapping = CreateMachineToDescriptionMapping();
|
||||
|
||||
// Now we loop through every item and update accordingly
|
||||
UpdateMachineNamesFromDescriptions(mapping);
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Warning(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use game descriptions as names, updating cloneof/romof/sampleof
|
||||
/// </summary>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void MachineDescriptionToNameImplDB(bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// First we want to get a mapping for all games to description
|
||||
var mapping = CreateMachineToDescriptionMappingDB();
|
||||
|
||||
// Now we loop through every item and update accordingly
|
||||
UpdateMachineNamesFromDescriptionsDB(mapping);
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Warning(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter a DAT using 1G1R logic given an ordered set of regions
|
||||
/// </summary>
|
||||
/// <param name="regionList">List of regions in order of priority</param>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private void SetOneGamePerRegionImpl(List<string> regionList)
|
||||
{
|
||||
// For sake of ease, the first thing we want to do is bucket by game
|
||||
BucketBy(ItemKey.Machine, norename: true);
|
||||
|
||||
// Then we want to get a mapping of all machines to parents
|
||||
Dictionary<string, List<string>> parents = [];
|
||||
foreach (string key in Items.SortedKeys)
|
||||
{
|
||||
DatItem item = GetItemsForBucket(key)[0];
|
||||
|
||||
// Get machine information
|
||||
Machine? machine = item.GetMachine();
|
||||
string? machineName = machine?.GetName()?.ToLowerInvariant();
|
||||
if (machine is null || machineName is null)
|
||||
continue;
|
||||
|
||||
// Get the string values
|
||||
string? cloneOf = machine.GetStringFieldValue(Data.Models.Metadata.Machine.CloneOfKey)?.ToLowerInvariant();
|
||||
string? romOf = machine.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey)?.ToLowerInvariant();
|
||||
|
||||
// Match on CloneOf first
|
||||
if (!string.IsNullOrEmpty(cloneOf))
|
||||
{
|
||||
if (!parents.ContainsKey(cloneOf!))
|
||||
parents.Add(cloneOf!, []);
|
||||
|
||||
parents[cloneOf!].Add(machineName);
|
||||
}
|
||||
|
||||
// Then by RomOf
|
||||
else if (!string.IsNullOrEmpty(romOf))
|
||||
{
|
||||
if (!parents.ContainsKey(romOf!))
|
||||
parents.Add(romOf!, []);
|
||||
|
||||
parents[romOf!].Add(machineName);
|
||||
}
|
||||
|
||||
// Otherwise, treat it as a parent
|
||||
else
|
||||
{
|
||||
if (!parents.ContainsKey(machineName))
|
||||
parents.Add(machineName, []);
|
||||
|
||||
parents[machineName].Add(machineName);
|
||||
}
|
||||
}
|
||||
|
||||
// Once we have the full list of mappings, filter out games to keep
|
||||
foreach (string key in parents.Keys)
|
||||
{
|
||||
// Find the first machine that matches the regions in order, if possible
|
||||
string? machine = default;
|
||||
foreach (string region in regionList)
|
||||
{
|
||||
machine = parents[key].Find(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase));
|
||||
if (machine != default)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we didn't get a match, use the parent
|
||||
if (machine == default)
|
||||
machine = key;
|
||||
|
||||
// Remove the key from the list
|
||||
parents[key].Remove(machine);
|
||||
|
||||
// Remove the rest of the items from this key
|
||||
parents[key].ForEach(k => RemoveBucket(k));
|
||||
}
|
||||
|
||||
// Finally, strip out the parent tags
|
||||
RemoveMachineRelationshipTagsImpl();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter a DAT using 1G1R logic given an ordered set of regions
|
||||
/// </summary>
|
||||
/// <param name="regionList">List of regions in order of priority</param>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void SetOneGamePerRegionImplDB(List<string> regionList)
|
||||
{
|
||||
// Then we want to get a mapping of all machines to parents
|
||||
Dictionary<string, List<string>> parents = [];
|
||||
foreach (var machine in GetMachinesDB())
|
||||
{
|
||||
if (machine.Value is null)
|
||||
continue;
|
||||
|
||||
// Get machine information
|
||||
Machine? machineObj = machine.Value;
|
||||
string? machineName = machineObj?.GetName()?.ToLowerInvariant();
|
||||
if (machineObj is null || machineName is null)
|
||||
continue;
|
||||
|
||||
// Get the string values
|
||||
string? cloneOf = machineObj.GetStringFieldValue(Data.Models.Metadata.Machine.CloneOfKey)?.ToLowerInvariant();
|
||||
string? romOf = machineObj.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey)?.ToLowerInvariant();
|
||||
|
||||
// Match on CloneOf first
|
||||
if (!string.IsNullOrEmpty(cloneOf))
|
||||
{
|
||||
if (!parents.ContainsKey(cloneOf!))
|
||||
parents.Add(cloneOf!, []);
|
||||
|
||||
parents[cloneOf!].Add(machineName);
|
||||
}
|
||||
|
||||
// Then by RomOf
|
||||
else if (!string.IsNullOrEmpty(romOf))
|
||||
{
|
||||
if (!parents.ContainsKey(romOf!))
|
||||
parents.Add(romOf!, []);
|
||||
|
||||
parents[romOf!].Add(machineName);
|
||||
}
|
||||
|
||||
// Otherwise, treat it as a parent
|
||||
else
|
||||
{
|
||||
if (!parents.ContainsKey(machineName))
|
||||
parents.Add(machineName, []);
|
||||
|
||||
parents[machineName].Add(machineName);
|
||||
}
|
||||
}
|
||||
|
||||
// Once we have the full list of mappings, filter out games to keep
|
||||
foreach (string key in parents.Keys)
|
||||
{
|
||||
// Find the first machine that matches the regions in order, if possible
|
||||
string? machine = default;
|
||||
foreach (string region in regionList)
|
||||
{
|
||||
machine = parents[key].Find(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase));
|
||||
if (machine != default)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we didn't get a match, use the parent
|
||||
if (machine == default)
|
||||
machine = key;
|
||||
|
||||
// Remove the key from the list
|
||||
parents[key].Remove(machine);
|
||||
|
||||
// Remove the rest of the items from this key
|
||||
parents[key].ForEach(k => RemoveMachineDB(k));
|
||||
}
|
||||
|
||||
// Finally, strip out the parent tags
|
||||
RemoveMachineRelationshipTagsImplDB();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that all roms are in their own game (or at least try to ensure)
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private void SetOneRomPerGameImpl()
|
||||
{
|
||||
// For each rom, we want to update the game to be "<game name>/<rom name>"
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(Items.SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in Items.SortedKeys)
|
||||
#endif
|
||||
{
|
||||
var items = GetItemsForBucket(key);
|
||||
if (items is null)
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
return;
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
SetOneRomPerGameImpl(items[i]);
|
||||
}
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set internal names to match One Rom Per Game (ORPG) logic
|
||||
/// </summary>
|
||||
/// <param name="datItem">DatItem to run logic on</param>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private static void SetOneRomPerGameImpl(DatItem datItem)
|
||||
{
|
||||
// If the item name is null
|
||||
string? itemName = datItem.GetName();
|
||||
if (itemName is null)
|
||||
return;
|
||||
|
||||
// Get the current machine
|
||||
var machine = datItem.GetMachine();
|
||||
if (machine is null)
|
||||
return;
|
||||
|
||||
// Clone current machine to avoid conflict
|
||||
machine = (Machine)machine.Clone();
|
||||
|
||||
// Reassign the item to the new machine
|
||||
datItem.SetFieldValue(DatItem.MachineKey, machine);
|
||||
|
||||
// Remove extensions from File and Rom items
|
||||
if (datItem is DatItems.Formats.File || datItem is Rom)
|
||||
{
|
||||
string[] splitname = itemName.Split('.');
|
||||
itemName = machine.GetName()
|
||||
+ $"/{string.Join(".", splitname, 0, splitname.Length > 1 ? splitname.Length - 1 : 1)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
itemName = machine.GetName() + $"/{itemName}";
|
||||
}
|
||||
|
||||
// Strip off "Default" prefix only for ORPG
|
||||
if (itemName.StartsWith("Default"))
|
||||
itemName = itemName.Substring("Default".Length + 1);
|
||||
|
||||
machine.SetName(itemName);
|
||||
datItem.SetName(Path.GetFileName(datItem.GetName()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that all roms are in their own game (or at least try to ensure)
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void SetOneRomPerGameImplDB()
|
||||
{
|
||||
// For each rom, we want to update the game to be "<game name>/<rom name>"
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(ItemsDB.SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in ItemsDB.SortedKeys)
|
||||
#endif
|
||||
{
|
||||
var items = GetItemsForBucketDB(key);
|
||||
if (items is null)
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
return;
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
SetOneRomPerGameImplDB(item);
|
||||
}
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set internal names to match One Rom Per Game (ORPG) logic
|
||||
/// </summary>
|
||||
/// <param name="datItem">DatItem to run logic on</param>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void SetOneRomPerGameImplDB(KeyValuePair<long, DatItem> datItem)
|
||||
{
|
||||
// If the item name is null
|
||||
string? itemName = datItem.Value.GetName();
|
||||
if (datItem.Key < 0 || itemName is null)
|
||||
return;
|
||||
|
||||
// Get the current machine
|
||||
var machine = GetMachineForItemDB(datItem.Key);
|
||||
if (machine.Value is null)
|
||||
return;
|
||||
|
||||
// Clone current machine to avoid conflict
|
||||
long newMachineIndex = AddMachineDB((Machine)machine.Value.Clone());
|
||||
machine = new KeyValuePair<long, Machine?>(newMachineIndex, ItemsDB.GetMachine(newMachineIndex));
|
||||
if (machine.Value is null)
|
||||
return;
|
||||
|
||||
// Reassign the item to the new machine
|
||||
ItemsDB.RemapDatItemToMachine(datItem.Key, newMachineIndex);
|
||||
|
||||
// Remove extensions from File and Rom items
|
||||
if (datItem.Value is DatItems.Formats.File || datItem.Value is Rom)
|
||||
{
|
||||
string[] splitname = itemName.Split('.');
|
||||
itemName = machine.Value.GetName()
|
||||
+ $"/{string.Join(".", splitname, 0, splitname.Length > 1 ? splitname.Length - 1 : 1)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
itemName = machine.Value.GetName() + $"/{itemName}";
|
||||
}
|
||||
|
||||
// Strip off "Default" prefix only for ORPG
|
||||
if (itemName.StartsWith("Default"))
|
||||
itemName = itemName.Substring("Default".Length + 1);
|
||||
|
||||
machine.Value.SetName(itemName);
|
||||
datItem.Value.SetName(Path.GetFileName(datItem.Value.GetName()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strip the dates from the beginning of scene-style set names
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private void StripSceneDatesFromItemsImpl()
|
||||
{
|
||||
// Now process all of the roms
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(Items.SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in Items.SortedKeys)
|
||||
#endif
|
||||
{
|
||||
var items = GetItemsForBucket(key);
|
||||
if (items is null)
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
return;
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
// Get the current machine
|
||||
var machine = item.GetMachine();
|
||||
if (machine is null)
|
||||
continue;
|
||||
|
||||
// Get the values to check against
|
||||
string? machineName = machine.GetName();
|
||||
string? machineDesc = machine.GetStringFieldValue(Data.Models.Metadata.Machine.DescriptionKey);
|
||||
|
||||
if (machineName is not null && Regex.IsMatch(machineName, SceneNamePattern))
|
||||
item.GetMachine()!.SetName(Regex.Replace(machineName, SceneNamePattern, "$2"));
|
||||
|
||||
if (machineDesc is not null && Regex.IsMatch(machineDesc, SceneNamePattern))
|
||||
item.GetMachine()!.SetFieldValue<string?>(Data.Models.Metadata.Machine.DescriptionKey, Regex.Replace(machineDesc, SceneNamePattern, "$2"));
|
||||
}
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strip the dates from the beginning of scene-style set names
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void StripSceneDatesFromItemsImplDB()
|
||||
{
|
||||
// Now process all of the machines
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(GetMachinesDB(), machine =>
|
||||
#else
|
||||
foreach (var machine in GetMachinesDB())
|
||||
#endif
|
||||
{
|
||||
// Get the current machine
|
||||
if (machine.Value is null)
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
return;
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Get the values to check against
|
||||
string? machineName = machine.Value.GetName();
|
||||
string? machineDesc = machine.Value.GetStringFieldValue(Data.Models.Metadata.Machine.DescriptionKey);
|
||||
|
||||
if (machineName is not null && Regex.IsMatch(machineName, SceneNamePattern))
|
||||
machine.Value.SetName(Regex.Replace(machineName, SceneNamePattern, "$2"));
|
||||
|
||||
if (machineDesc is not null && Regex.IsMatch(machineDesc, SceneNamePattern))
|
||||
machine.Value.SetFieldValue<string?>(Data.Models.Metadata.Machine.DescriptionKey, Regex.Replace(machineDesc, SceneNamePattern, "$2"));
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update machine names from descriptions according to mappings
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="Items"/></remarks>
|
||||
private void UpdateMachineNamesFromDescriptions(IDictionary<string, string> mapping)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(Items.SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in Items.SortedKeys)
|
||||
#endif
|
||||
{
|
||||
var items = GetItemsForBucket(key);
|
||||
if (items is null)
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
return;
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
// Get the current machine
|
||||
var machine = item.GetMachine();
|
||||
if (machine is null)
|
||||
continue;
|
||||
|
||||
// Get the values to check against
|
||||
string? machineName = machine.GetName();
|
||||
string? cloneOf = machine.GetStringFieldValue(Data.Models.Metadata.Machine.CloneOfKey);
|
||||
string? romOf = machine.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey);
|
||||
string? sampleOf = machine.GetStringFieldValue(Data.Models.Metadata.Machine.SampleOfKey);
|
||||
|
||||
// Update machine name
|
||||
if (machineName is not null && mapping.ContainsKey(machineName))
|
||||
machine.SetName(mapping[machineName]);
|
||||
|
||||
// Update cloneof
|
||||
if (cloneOf is not null && mapping.ContainsKey(cloneOf))
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, mapping[cloneOf]);
|
||||
|
||||
// Update romof
|
||||
if (romOf is not null && mapping.ContainsKey(romOf))
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, mapping[romOf]);
|
||||
|
||||
// Update sampleof
|
||||
if (sampleOf is not null && mapping.ContainsKey(sampleOf))
|
||||
machine.SetFieldValue<string?>(Data.Models.Metadata.Machine.SampleOfKey, mapping[sampleOf]);
|
||||
}
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update machine names from descriptions according to mappings
|
||||
/// </summary>
|
||||
/// <remarks>Applies to <see cref="ItemsDB"/></remarks>
|
||||
private void UpdateMachineNamesFromDescriptionsDB(Dictionary<string, string> mapping)
|
||||
{
|
||||
foreach (var machine in GetMachinesDB())
|
||||
{
|
||||
// Get the current machine
|
||||
if (machine.Value is null)
|
||||
continue;
|
||||
|
||||
// Get the values to check against
|
||||
string? machineName = machine.Value.GetName();
|
||||
string? cloneOf = machine.Value.GetStringFieldValue(Data.Models.Metadata.Machine.CloneOfKey);
|
||||
string? romOf = machine.Value.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey);
|
||||
string? sampleOf = machine.Value.GetStringFieldValue(Data.Models.Metadata.Machine.SampleOfKey);
|
||||
|
||||
// Update machine name
|
||||
if (machineName is not null && mapping.ContainsKey(machineName))
|
||||
machine.Value.SetName(mapping[machineName]);
|
||||
|
||||
// Update cloneof
|
||||
if (cloneOf is not null && mapping.ContainsKey(cloneOf))
|
||||
machine.Value.SetFieldValue<string?>(Data.Models.Metadata.Machine.CloneOfKey, mapping[cloneOf]);
|
||||
|
||||
// Update romof
|
||||
if (romOf is not null && mapping.ContainsKey(romOf))
|
||||
machine.Value.SetFieldValue<string?>(Data.Models.Metadata.Machine.RomOfKey, mapping[romOf]);
|
||||
|
||||
// Update sampleof
|
||||
if (sampleOf is not null && mapping.ContainsKey(sampleOf))
|
||||
machine.Value.SetFieldValue<string?>(Data.Models.Metadata.Machine.SampleOfKey, mapping[sampleOf]);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
715
SabreTools.Metadata.DatFiles/DatFile.FromMetadata.cs
Normal file
715
SabreTools.Metadata.DatFiles/DatFile.FromMetadata.cs
Normal file
@@ -0,0 +1,715 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
#pragma warning disable IDE0056 // Use index operator
|
||||
#pragma warning disable IDE0060 // Remove unused parameter
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
public partial class DatFile
|
||||
{
|
||||
#region From Metadata
|
||||
|
||||
/// <summary>
|
||||
/// Convert metadata information
|
||||
/// </summary>
|
||||
/// <param name="item">Metadata file to convert</param>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <param name="indexId">Index ID for the DAT</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
internal void ConvertFromMetadata(Data.Models.Metadata.MetadataFile? item,
|
||||
string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If the metadata file is invalid, we can't do anything
|
||||
if (item is null || item.Count == 0)
|
||||
return;
|
||||
|
||||
// Create an internal source and add to the dictionary
|
||||
var source = new Source(indexId, filename);
|
||||
// long sourceIndex = AddSourceDB(source);
|
||||
|
||||
// Get the header from the metadata
|
||||
var header = item.Read<Data.Models.Metadata.Header>(Data.Models.Metadata.MetadataFile.HeaderKey);
|
||||
if (header is not null)
|
||||
ConvertHeader(header, keep);
|
||||
|
||||
// Get the machines from the metadata
|
||||
var machines = item.ReadItemArray<Data.Models.Metadata.Machine>(Data.Models.Metadata.MetadataFile.MachineKey);
|
||||
if (machines is not null)
|
||||
ConvertMachines(machines, source, sourceIndex: 0, statsOnly, filterRunner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert header information
|
||||
/// </summary>
|
||||
/// <param name="item">Header to convert</param>
|
||||
/// <param name="keep">True if full pathnames are to be kept, false otherwise</param>
|
||||
private void ConvertHeader(Data.Models.Metadata.Header? item, bool keep)
|
||||
{
|
||||
// If the header is invalid, we can't do anything
|
||||
if (item is null || item.Count == 0)
|
||||
return;
|
||||
|
||||
// Create an internal header
|
||||
var header = new DatHeader(item);
|
||||
|
||||
// Convert subheader values
|
||||
if (item.ContainsKey(Data.Models.Metadata.Header.CanOpenKey))
|
||||
{
|
||||
var canOpen = item.Read<Data.Models.OfflineList.CanOpen>(Data.Models.Metadata.Header.CanOpenKey);
|
||||
if (canOpen?.Extension is not null)
|
||||
Header.SetFieldValue<string[]?>(Data.Models.Metadata.Header.CanOpenKey, canOpen.Extension);
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Header.ImagesKey))
|
||||
{
|
||||
var images = item.Read<Data.Models.OfflineList.Images>(Data.Models.Metadata.Header.ImagesKey);
|
||||
Header.SetFieldValue<Data.Models.OfflineList.Images?>(Data.Models.Metadata.Header.ImagesKey, images);
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Header.InfosKey))
|
||||
{
|
||||
var infos = item.Read<Data.Models.OfflineList.Infos>(Data.Models.Metadata.Header.InfosKey);
|
||||
Header.SetFieldValue<Data.Models.OfflineList.Infos?>(Data.Models.Metadata.Header.InfosKey, infos);
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Header.NewDatKey))
|
||||
{
|
||||
var newDat = item.Read<Data.Models.OfflineList.NewDat>(Data.Models.Metadata.Header.NewDatKey);
|
||||
Header.SetFieldValue<Data.Models.OfflineList.NewDat?>(Data.Models.Metadata.Header.NewDatKey, newDat);
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Header.SearchKey))
|
||||
{
|
||||
var search = item.Read<Data.Models.OfflineList.Search>(Data.Models.Metadata.Header.SearchKey);
|
||||
Header.SetFieldValue<Data.Models.OfflineList.Search?>(Data.Models.Metadata.Header.SearchKey, search);
|
||||
}
|
||||
|
||||
// Selectively set all possible fields -- TODO: Figure out how to make this less manual
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.AuthorKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.AuthorKey, header.GetStringFieldValue(Data.Models.Metadata.Header.AuthorKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.BiosModeKey).AsMergingFlag() == MergingFlag.None)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.BiosModeKey, header.GetStringFieldValue(Data.Models.Metadata.Header.BiosModeKey).AsMergingFlag().AsStringValue());
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.BuildKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.BuildKey, header.GetStringFieldValue(Data.Models.Metadata.Header.BuildKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.CategoryKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.CategoryKey, header.GetStringFieldValue(Data.Models.Metadata.Header.CategoryKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.CommentKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.CommentKey, header.GetStringFieldValue(Data.Models.Metadata.Header.CommentKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.DateKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.DateKey, header.GetStringFieldValue(Data.Models.Metadata.Header.DateKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.DatVersionKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.DatVersionKey, header.GetStringFieldValue(Data.Models.Metadata.Header.DatVersionKey));
|
||||
if (Header.GetBoolFieldValue(Data.Models.Metadata.Header.DebugKey) is null)
|
||||
Header.SetFieldValue(Data.Models.Metadata.Header.DebugKey, header.GetBoolFieldValue(Data.Models.Metadata.Header.DebugKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.DescriptionKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.DescriptionKey, header.GetStringFieldValue(Data.Models.Metadata.Header.DescriptionKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.EmailKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.EmailKey, header.GetStringFieldValue(Data.Models.Metadata.Header.EmailKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.EmulatorVersionKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.EmulatorVersionKey, header.GetStringFieldValue(Data.Models.Metadata.Header.EmulatorVersionKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.ForceMergingKey).AsMergingFlag() == MergingFlag.None)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.ForceMergingKey, header.GetStringFieldValue(Data.Models.Metadata.Header.ForceMergingKey).AsMergingFlag().AsStringValue());
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.ForceNodumpKey).AsNodumpFlag() == NodumpFlag.None)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.ForceNodumpKey, header.GetStringFieldValue(Data.Models.Metadata.Header.ForceNodumpKey).AsNodumpFlag().AsStringValue());
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.ForcePackingKey).AsPackingFlag() == PackingFlag.None)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.ForcePackingKey, header.GetStringFieldValue(Data.Models.Metadata.Header.ForcePackingKey).AsPackingFlag().AsStringValue());
|
||||
if (Header.GetBoolFieldValue(Data.Models.Metadata.Header.ForceZippingKey) is null)
|
||||
Header.SetFieldValue(Data.Models.Metadata.Header.ForceZippingKey, header.GetBoolFieldValue(Data.Models.Metadata.Header.ForceZippingKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.HeaderKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.HeaderKey, header.GetStringFieldValue(Data.Models.Metadata.Header.HeaderKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.HomepageKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.HomepageKey, header.GetStringFieldValue(Data.Models.Metadata.Header.HomepageKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.IdKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.IdKey, header.GetStringFieldValue(Data.Models.Metadata.Header.IdKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.ImFolderKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.ImFolderKey, header.GetStringFieldValue(Data.Models.Metadata.Header.ImFolderKey));
|
||||
if (Header.GetBoolFieldValue(Data.Models.Metadata.Header.LockBiosModeKey) is null)
|
||||
Header.SetFieldValue(Data.Models.Metadata.Header.LockBiosModeKey, header.GetBoolFieldValue(Data.Models.Metadata.Header.LockBiosModeKey));
|
||||
if (Header.GetBoolFieldValue(Data.Models.Metadata.Header.LockRomModeKey) is null)
|
||||
Header.SetFieldValue(Data.Models.Metadata.Header.LockRomModeKey, header.GetBoolFieldValue(Data.Models.Metadata.Header.LockRomModeKey));
|
||||
if (Header.GetBoolFieldValue(Data.Models.Metadata.Header.LockSampleModeKey) is null)
|
||||
Header.SetFieldValue(Data.Models.Metadata.Header.LockSampleModeKey, header.GetBoolFieldValue(Data.Models.Metadata.Header.LockSampleModeKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.MameConfigKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.MameConfigKey, header.GetStringFieldValue(Data.Models.Metadata.Header.MameConfigKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.NameKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.NameKey, header.GetStringFieldValue(Data.Models.Metadata.Header.NameKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.NotesKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.NotesKey, header.GetStringFieldValue(Data.Models.Metadata.Header.NotesKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.PluginKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.PluginKey, header.GetStringFieldValue(Data.Models.Metadata.Header.PluginKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.RefNameKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.RefNameKey, header.GetStringFieldValue(Data.Models.Metadata.Header.RefNameKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.RomModeKey).AsMergingFlag() == MergingFlag.None)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.RomModeKey, header.GetStringFieldValue(Data.Models.Metadata.Header.RomModeKey).AsMergingFlag().AsStringValue());
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.RomTitleKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.RomTitleKey, header.GetStringFieldValue(Data.Models.Metadata.Header.RomTitleKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.RootDirKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.RootDirKey, header.GetStringFieldValue(Data.Models.Metadata.Header.RootDirKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.SampleModeKey).AsMergingFlag() == MergingFlag.None)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.SampleModeKey, header.GetStringFieldValue(Data.Models.Metadata.Header.SampleModeKey).AsMergingFlag().AsStringValue());
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.SchemaLocationKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.SchemaLocationKey, header.GetStringFieldValue(Data.Models.Metadata.Header.SchemaLocationKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.ScreenshotsHeightKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.ScreenshotsHeightKey, header.GetStringFieldValue(Data.Models.Metadata.Header.ScreenshotsHeightKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.ScreenshotsWidthKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.ScreenshotsWidthKey, header.GetStringFieldValue(Data.Models.Metadata.Header.ScreenshotsWidthKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.SystemKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.SystemKey, header.GetStringFieldValue(Data.Models.Metadata.Header.SystemKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.TimestampKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.TimestampKey, header.GetStringFieldValue(Data.Models.Metadata.Header.TimestampKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.TypeKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.TypeKey, header.GetStringFieldValue(Data.Models.Metadata.Header.TypeKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.UrlKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.UrlKey, header.GetStringFieldValue(Data.Models.Metadata.Header.UrlKey));
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.VersionKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.VersionKey, header.GetStringFieldValue(Data.Models.Metadata.Header.VersionKey));
|
||||
|
||||
// Handle implied SuperDAT
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.NameKey)?.Contains(" - SuperDAT") == true && keep)
|
||||
{
|
||||
if (Header.GetStringFieldValue(Data.Models.Metadata.Header.TypeKey) is null)
|
||||
Header.SetFieldValue<string?>(Data.Models.Metadata.Header.TypeKey, "SuperDAT");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert machines information
|
||||
/// </summary>
|
||||
/// <param name="items">Machine array to convert</param>
|
||||
/// <param name="source">Source to use with the converted items</param>
|
||||
/// <param name="sourceIndex">Index of the Source to use with the converted items</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ConvertMachines(Data.Models.Metadata.Machine[]? items,
|
||||
Source source,
|
||||
long sourceIndex,
|
||||
bool statsOnly,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If the array is invalid, we can't do anything
|
||||
if (items is null || items.Length == 0)
|
||||
return;
|
||||
|
||||
// Loop through the machines and add
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(items, machine =>
|
||||
#else
|
||||
foreach (var machine in items)
|
||||
#endif
|
||||
{
|
||||
ConvertMachine(machine, source, sourceIndex, statsOnly, filterRunner);
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert machine information
|
||||
/// </summary>
|
||||
/// <param name="item">Machine to convert</param>
|
||||
/// <param name="source">Source to use with the converted items</param>
|
||||
/// <param name="sourceIndex">Index of the Source to use with the converted items</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ConvertMachine(Data.Models.Metadata.Machine? item,
|
||||
Source source,
|
||||
long sourceIndex,
|
||||
bool statsOnly,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If the machine is invalid, we can't do anything
|
||||
if (item is null || item.Count == 0)
|
||||
return;
|
||||
|
||||
// If the machine doesn't pass the filter
|
||||
if (filterRunner is not null && !filterRunner.Run(item))
|
||||
return;
|
||||
|
||||
// Create an internal machine and add to the dictionary
|
||||
var machine = new Machine(item);
|
||||
// long machineIndex = AddMachineDB(machine);
|
||||
|
||||
// Convert items in the machine
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.AdjusterKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Adjuster>(Data.Models.Metadata.Machine.AdjusterKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Adjuster(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.ArchiveKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Archive>(Data.Models.Metadata.Machine.ArchiveKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Archive(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.BiosSetKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.BiosSet>(Data.Models.Metadata.Machine.BiosSetKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new BiosSet(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.ChipKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Chip>(Data.Models.Metadata.Machine.ChipKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Chip(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.ConfigurationKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Configuration>(Data.Models.Metadata.Machine.ConfigurationKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Configuration(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.DeviceKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Device>(Data.Models.Metadata.Machine.DeviceKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Device(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.DeviceRefKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.DeviceRef>(Data.Models.Metadata.Machine.DeviceRefKey) ?? [];
|
||||
// Do not filter these due to later use
|
||||
Array.ForEach(items, item =>
|
||||
{
|
||||
var datItem = new DeviceRef(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.DipSwitchKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.DipSwitch>(Data.Models.Metadata.Machine.DipSwitchKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new DipSwitch(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.DiskKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Disk>(Data.Models.Metadata.Machine.DiskKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Disk(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.DisplayKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Display>(Data.Models.Metadata.Machine.DisplayKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Display(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.DriverKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Driver>(Data.Models.Metadata.Machine.DriverKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Driver(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.DumpKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Dump>(Data.Models.Metadata.Machine.DumpKey) ?? [];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var datItem = new Rom(items[i], machine, source, i);
|
||||
if (datItem.GetName() is not null)
|
||||
{
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.FeatureKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Feature>(Data.Models.Metadata.Machine.FeatureKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Feature(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.InfoKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Info>(Data.Models.Metadata.Machine.InfoKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Info(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.InputKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Input>(Data.Models.Metadata.Machine.InputKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Input(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.MediaKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Media>(Data.Models.Metadata.Machine.MediaKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Media(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.PartKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Part>(Data.Models.Metadata.Machine.PartKey) ?? [];
|
||||
ProcessItems(items, machine, machineIndex: 0, source, sourceIndex, statsOnly, filterRunner);
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.PortKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Port>(Data.Models.Metadata.Machine.PortKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Port(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.RamOptionKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.RamOption>(Data.Models.Metadata.Machine.RamOptionKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new RamOption(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.ReleaseKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Release>(Data.Models.Metadata.Machine.ReleaseKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Release(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.RomKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Rom>(Data.Models.Metadata.Machine.RomKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Rom(item, machine, source);
|
||||
datItem.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
datItem.CopyMachineInformation(machine);
|
||||
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.SampleKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Sample>(Data.Models.Metadata.Machine.SampleKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Sample(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.SharedFeatKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.SharedFeat>(Data.Models.Metadata.Machine.SharedFeatKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new SharedFeat(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.SlotKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Slot>(Data.Models.Metadata.Machine.SlotKey) ?? [];
|
||||
// Do not filter these due to later use
|
||||
Array.ForEach(items, item =>
|
||||
{
|
||||
var datItem = new Slot(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.SoftwareListKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.SoftwareList>(Data.Models.Metadata.Machine.SoftwareListKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new SoftwareList(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.SoundKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Sound>(Data.Models.Metadata.Machine.SoundKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Sound(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.ContainsKey(Data.Models.Metadata.Machine.VideoKey))
|
||||
{
|
||||
var items = item.ReadItemArray<Data.Models.Metadata.Video>(Data.Models.Metadata.Machine.VideoKey) ?? [];
|
||||
var filtered = filterRunner is null ? items : Array.FindAll(items, i => filterRunner.Run(item));
|
||||
Array.ForEach(filtered, item =>
|
||||
{
|
||||
var datItem = new Display(item, machine, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
// AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Part information
|
||||
/// </summary>
|
||||
/// <param name="items">Array of internal items to convert</param>
|
||||
/// <param name="machine">Machine to use with the converted items</param>
|
||||
/// <param name="machineIndex">Index of the Machine to use with the converted items</param>
|
||||
/// <param name="source">Source to use with the converted items</param>
|
||||
/// <param name="sourceIndex">Index of the Source to use with the converted items</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ProcessItems(Data.Models.Metadata.Part[] items,
|
||||
Machine machine,
|
||||
long machineIndex,
|
||||
Source source,
|
||||
long sourceIndex,
|
||||
bool statsOnly,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If the array is null or empty, return without processing
|
||||
if (items.Length == 0)
|
||||
return;
|
||||
|
||||
// Loop through the items and add
|
||||
foreach (var item in items)
|
||||
{
|
||||
var partItem = new Part(item, machine, source);
|
||||
|
||||
// Handle subitems
|
||||
var dataAreas = item.ReadItemArray<Data.Models.Metadata.DataArea>(Data.Models.Metadata.Part.DataAreaKey);
|
||||
if (dataAreas is not null)
|
||||
{
|
||||
foreach (var dataArea in dataAreas)
|
||||
{
|
||||
var dataAreaItem = new DataArea(dataArea, machine, source);
|
||||
var roms = dataArea.ReadItemArray<Data.Models.Metadata.Rom>(Data.Models.Metadata.DataArea.RomKey);
|
||||
if (roms is null)
|
||||
continue;
|
||||
|
||||
// Handle "offset" roms
|
||||
List<Rom> addRoms = [];
|
||||
foreach (var rom in roms)
|
||||
{
|
||||
// If the item doesn't pass the filter
|
||||
if (filterRunner is not null && !filterRunner.Run(rom))
|
||||
continue;
|
||||
|
||||
// Convert the item
|
||||
var romItem = new Rom(rom, machine, source);
|
||||
long? size = romItem.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey);
|
||||
|
||||
// If the rom is a continue or ignore
|
||||
string? loadFlag = rom.ReadString(Data.Models.Metadata.Rom.LoadFlagKey);
|
||||
if (loadFlag is not null
|
||||
&& (loadFlag.Equals("continue", StringComparison.OrdinalIgnoreCase)
|
||||
|| loadFlag.Equals("ignore", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var lastRom = addRoms[addRoms.Count - 1];
|
||||
long? lastSize = lastRom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey);
|
||||
lastRom.SetFieldValue(Data.Models.Metadata.Rom.SizeKey, lastSize + size);
|
||||
continue;
|
||||
}
|
||||
|
||||
romItem.SetFieldValue<DataArea?>(Rom.DataAreaKey, dataAreaItem);
|
||||
romItem.SetFieldValue<Part?>(Rom.PartKey, partItem);
|
||||
|
||||
addRoms.Add(romItem);
|
||||
}
|
||||
|
||||
// Add all of the adjusted roms
|
||||
foreach (var romItem in addRoms)
|
||||
{
|
||||
AddItem(romItem, statsOnly);
|
||||
// AddItemDB(romItem, machineIndex, sourceIndex, statsOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var diskAreas = item.ReadItemArray<Data.Models.Metadata.DiskArea>(Data.Models.Metadata.Part.DiskAreaKey);
|
||||
if (diskAreas is not null)
|
||||
{
|
||||
foreach (var diskArea in diskAreas)
|
||||
{
|
||||
var diskAreaitem = new DiskArea(diskArea, machine, source);
|
||||
var disks = diskArea.ReadItemArray<Data.Models.Metadata.Disk>(Data.Models.Metadata.DiskArea.DiskKey);
|
||||
if (disks is null)
|
||||
continue;
|
||||
|
||||
foreach (var disk in disks)
|
||||
{
|
||||
// If the item doesn't pass the filter
|
||||
if (filterRunner is not null && !filterRunner.Run(disk))
|
||||
continue;
|
||||
|
||||
var diskItem = new Disk(disk, machine, source);
|
||||
diskItem.SetFieldValue<DiskArea?>(Disk.DiskAreaKey, diskAreaitem);
|
||||
diskItem.SetFieldValue<Part?>(Disk.PartKey, partItem);
|
||||
|
||||
AddItem(diskItem, statsOnly);
|
||||
// AddItemDB(diskItem, machineIndex, sourceIndex, statsOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var dipSwitches = item.ReadItemArray<Data.Models.Metadata.DipSwitch>(Data.Models.Metadata.Part.DipSwitchKey);
|
||||
if (dipSwitches is not null)
|
||||
{
|
||||
foreach (var dipSwitch in dipSwitches)
|
||||
{
|
||||
// If the item doesn't pass the filter
|
||||
if (filterRunner is not null && !filterRunner.Run(dipSwitch))
|
||||
continue;
|
||||
|
||||
var dipSwitchItem = new DipSwitch(dipSwitch, machine, source);
|
||||
dipSwitchItem.SetFieldValue<Part?>(DipSwitch.PartKey, partItem);
|
||||
|
||||
AddItem(dipSwitchItem, statsOnly);
|
||||
// AddItemDB(dipSwitchItem, machineIndex, sourceIndex, statsOnly);
|
||||
}
|
||||
}
|
||||
|
||||
var partFeatures = item.ReadItemArray<Data.Models.Metadata.Feature>(Data.Models.Metadata.Part.FeatureKey);
|
||||
if (partFeatures is not null)
|
||||
{
|
||||
foreach (var partFeature in partFeatures)
|
||||
{
|
||||
// If the item doesn't pass the filter
|
||||
if (filterRunner is not null && !filterRunner.Run(partFeature))
|
||||
continue;
|
||||
|
||||
var partFeatureItem = new PartFeature(partFeature);
|
||||
partFeatureItem.SetFieldValue<Part?>(DipSwitch.PartKey, partItem);
|
||||
partFeatureItem.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
partFeatureItem.CopyMachineInformation(machine);
|
||||
|
||||
AddItem(partFeatureItem, statsOnly);
|
||||
// AddItemDB(partFeatureItem, machineIndex, sourceIndex, statsOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1506
SabreTools.Metadata.DatFiles/DatFile.Splitting.cs
Normal file
1506
SabreTools.Metadata.DatFiles/DatFile.Splitting.cs
Normal file
File diff suppressed because it is too large
Load Diff
1107
SabreTools.Metadata.DatFiles/DatFile.ToMetadata.cs
Normal file
1107
SabreTools.Metadata.DatFiles/DatFile.ToMetadata.cs
Normal file
File diff suppressed because it is too large
Load Diff
1252
SabreTools.Metadata.DatFiles/DatFile.cs
Normal file
1252
SabreTools.Metadata.DatFiles/DatFile.cs
Normal file
File diff suppressed because it is too large
Load Diff
229
SabreTools.Metadata.DatFiles/DatHeader.cs
Normal file
229
SabreTools.Metadata.DatFiles/DatHeader.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents all possible DAT header information
|
||||
/// </summary>
|
||||
[JsonObject("header"), XmlRoot("header")]
|
||||
public sealed class DatHeader : ModelBackedItem<Data.Models.Metadata.Header>, ICloneable
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Read or write format
|
||||
/// </summary>
|
||||
public const string DatFormatKey = "DATFORMAT";
|
||||
|
||||
/// <summary>
|
||||
/// External name of the DAT
|
||||
/// </summary>
|
||||
public const string FileNameKey = "FILENAME";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
[JsonIgnore]
|
||||
public bool CanOpenSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var canOpen = GetStringArrayFieldValue(Data.Models.Metadata.Header.CanOpenKey);
|
||||
return canOpen is not null && canOpen.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ImagesSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFieldValue<Data.Models.OfflineList.Images?>(Data.Models.Metadata.Header.ImagesKey) is not null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool InfosSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFieldValue<Data.Models.OfflineList.Infos?>(Data.Models.Metadata.Header.InfosKey) is not null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool NewDatSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFieldValue<Data.Models.OfflineList.NewDat?>(Data.Models.Metadata.Header.NewDatKey) is not null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool SearchSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFieldValue<Data.Models.OfflineList.Search?>(Data.Models.Metadata.Header.SearchKey) is not null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DatHeader() { }
|
||||
|
||||
public DatHeader(Data.Models.Metadata.Header header)
|
||||
{
|
||||
// Create a new internal model
|
||||
_internal = [];
|
||||
|
||||
// Get all fields to automatically copy without processing
|
||||
var nonItemFields = TypeHelper.GetConstants(typeof(Data.Models.Metadata.Header));
|
||||
if (nonItemFields is not null)
|
||||
{
|
||||
// Populate the internal machine from non-filter fields
|
||||
foreach (string fieldName in nonItemFields)
|
||||
{
|
||||
if (header.ContainsKey(fieldName))
|
||||
_internal[fieldName] = header[fieldName];
|
||||
}
|
||||
}
|
||||
|
||||
// Get all fields specific to the DatFiles implementation
|
||||
var nonStandardFields = TypeHelper.GetConstants(typeof(DatHeader));
|
||||
if (nonStandardFields is not null)
|
||||
{
|
||||
// Populate the internal machine from filter fields
|
||||
foreach (string fieldName in nonStandardFields)
|
||||
{
|
||||
if (header.ContainsKey(fieldName))
|
||||
_internal[fieldName] = header[fieldName];
|
||||
}
|
||||
}
|
||||
|
||||
// Get all no-filter fields
|
||||
if (header.ContainsKey(Data.Models.Metadata.Header.CanOpenKey))
|
||||
_internal[Data.Models.Metadata.Header.CanOpenKey] = header[Data.Models.Metadata.Header.CanOpenKey];
|
||||
if (header.ContainsKey(Data.Models.Metadata.Header.ImagesKey))
|
||||
_internal[Data.Models.Metadata.Header.ImagesKey] = header[Data.Models.Metadata.Header.ImagesKey];
|
||||
if (header.ContainsKey(Data.Models.Metadata.Header.InfosKey))
|
||||
_internal[Data.Models.Metadata.Header.InfosKey] = header[Data.Models.Metadata.Header.InfosKey];
|
||||
if (header.ContainsKey(Data.Models.Metadata.Header.NewDatKey))
|
||||
_internal[Data.Models.Metadata.Header.NewDatKey] = header[Data.Models.Metadata.Header.NewDatKey];
|
||||
if (header.ContainsKey(Data.Models.Metadata.Header.SearchKey))
|
||||
_internal[Data.Models.Metadata.Header.SearchKey] = header[Data.Models.Metadata.Header.SearchKey];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clone the current header
|
||||
/// </summary>
|
||||
public object Clone() => new DatHeader(GetInternalClone());
|
||||
|
||||
/// <summary>
|
||||
/// Clone just the format from the current header
|
||||
/// </summary>
|
||||
public DatHeader CloneFormat()
|
||||
{
|
||||
var header = new DatHeader();
|
||||
|
||||
header.SetFieldValue(DatFormatKey, GetFieldValue<DatFormat>(DatFormatKey));
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a clone of the current internal model
|
||||
/// </summary>
|
||||
public Data.Models.Metadata.Header GetInternalClone()
|
||||
{
|
||||
var header = (_internal.Clone() as Data.Models.Metadata.Header)!;
|
||||
|
||||
// Remove fields with default values
|
||||
if (header.ReadString(Data.Models.Metadata.Header.ForceMergingKey).AsMergingFlag() == MergingFlag.None)
|
||||
header.Remove(Data.Models.Metadata.Header.ForceMergingKey);
|
||||
if (header.ReadString(Data.Models.Metadata.Header.ForceNodumpKey).AsNodumpFlag() == NodumpFlag.None)
|
||||
header.Remove(Data.Models.Metadata.Header.ForceNodumpKey);
|
||||
if (header.ReadString(Data.Models.Metadata.Header.ForcePackingKey).AsPackingFlag() == PackingFlag.None)
|
||||
header.Remove(Data.Models.Metadata.Header.ForcePackingKey);
|
||||
if (header.ReadString(Data.Models.Metadata.Header.BiosModeKey).AsMergingFlag() == MergingFlag.None)
|
||||
header.Remove(Data.Models.Metadata.Header.BiosModeKey);
|
||||
if (header.ReadString(Data.Models.Metadata.Header.RomModeKey).AsMergingFlag() == MergingFlag.None)
|
||||
header.Remove(Data.Models.Metadata.Header.RomModeKey);
|
||||
if (header.ReadString(Data.Models.Metadata.Header.SampleModeKey).AsMergingFlag() == MergingFlag.None)
|
||||
header.Remove(Data.Models.Metadata.Header.SampleModeKey);
|
||||
|
||||
// Convert subheader values
|
||||
if (CanOpenSpecified)
|
||||
header[Data.Models.Metadata.Header.CanOpenKey] = new Data.Models.OfflineList.CanOpen { Extension = GetStringArrayFieldValue(Data.Models.Metadata.Header.CanOpenKey) };
|
||||
if (ImagesSpecified)
|
||||
header[Data.Models.Metadata.Header.ImagesKey] = GetFieldValue<Data.Models.OfflineList.Images>(Data.Models.Metadata.Header.ImagesKey);
|
||||
if (InfosSpecified)
|
||||
header[Data.Models.Metadata.Header.InfosKey] = GetFieldValue<Data.Models.OfflineList.Infos>(Data.Models.Metadata.Header.InfosKey);
|
||||
if (NewDatSpecified)
|
||||
header[Data.Models.Metadata.Header.NewDatKey] = GetFieldValue<Data.Models.OfflineList.NewDat>(Data.Models.Metadata.Header.NewDatKey);
|
||||
if (SearchSpecified)
|
||||
header[Data.Models.Metadata.Header.SearchKey] = GetFieldValue<Data.Models.OfflineList.Search>(Data.Models.Metadata.Header.SearchKey);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(ModelBackedItem? other)
|
||||
{
|
||||
// If other is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// If the type is mismatched
|
||||
if (other is not DatHeader otherItem)
|
||||
return false;
|
||||
|
||||
// Compare internal models
|
||||
return _internal.EqualTo(otherItem._internal);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(ModelBackedItem<Data.Models.Metadata.Header>? other)
|
||||
{
|
||||
// If other is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// If the type is mismatched
|
||||
if (other is not DatHeader otherItem)
|
||||
return false;
|
||||
|
||||
// Compare internal models
|
||||
return _internal.EqualTo(otherItem._internal);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Runs a filter and determines if it passes or not
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Filter runner to use for checking</param>
|
||||
/// <returns>True if the Machine passes the filter, false otherwise</returns>
|
||||
public bool PassesFilter(FilterRunner filterRunner) => filterRunner.Run(_internal);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
88
SabreTools.Metadata.DatFiles/DatModifiers.cs
Normal file
88
SabreTools.Metadata.DatFiles/DatModifiers.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents various modifiers that can be applied to a DAT
|
||||
/// </summary>
|
||||
public sealed class DatModifiers : ICloneable
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Text to prepend to all outputted lines
|
||||
/// </summary>
|
||||
public string? Prefix { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Text to append to all outputted lines
|
||||
/// </summary>
|
||||
public string? Postfix { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Add a new extension to all items
|
||||
/// </summary>
|
||||
public string? AddExtension { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Remove all item extensions
|
||||
/// </summary>
|
||||
public bool RemoveExtension { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Replace all item extensions
|
||||
/// </summary>
|
||||
public string? ReplaceExtension { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Output the machine name before the item name
|
||||
/// </summary>
|
||||
public bool GameName { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Wrap quotes around the entire line, sans prefix and postfix
|
||||
/// </summary>
|
||||
public bool Quotes { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Use the item name instead of machine name on output
|
||||
/// </summary>
|
||||
public bool UseRomName { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Input depot information
|
||||
/// </summary>
|
||||
public DepotInformation? InputDepot { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Output depot information
|
||||
/// </summary>
|
||||
public DepotInformation? OutputDepot { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clone the current modifiers
|
||||
/// </summary>
|
||||
public object Clone()
|
||||
{
|
||||
return new DatModifiers
|
||||
{
|
||||
Prefix = this.Prefix,
|
||||
Postfix = this.Postfix,
|
||||
AddExtension = this.AddExtension,
|
||||
RemoveExtension = this.RemoveExtension,
|
||||
ReplaceExtension = this.ReplaceExtension,
|
||||
GameName = this.GameName,
|
||||
Quotes = this.Quotes,
|
||||
UseRomName = this.UseRomName,
|
||||
InputDepot = (DepotInformation?)this.InputDepot?.Clone(),
|
||||
OutputDepot = (DepotInformation?)this.OutputDepot?.Clone(),
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
725
SabreTools.Metadata.DatFiles/DatStatistics.cs
Normal file
725
SabreTools.Metadata.DatFiles/DatStatistics.cs
Normal file
@@ -0,0 +1,725 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Statistics wrapper for outputting
|
||||
/// </summary>
|
||||
public class DatStatistics
|
||||
{
|
||||
#region Private instance variables
|
||||
|
||||
/// <summary>
|
||||
/// Number of items for each hash type
|
||||
/// </summary>
|
||||
private readonly Dictionary<HashType, long> _hashCounts = [];
|
||||
|
||||
/// <summary>
|
||||
/// Number of items for each item type
|
||||
/// </summary>
|
||||
private readonly Dictionary<ItemType, long> _itemCounts = [];
|
||||
|
||||
/// <summary>
|
||||
/// Number of items for each item status
|
||||
/// </summary>
|
||||
private readonly Dictionary<ItemStatus, long> _statusCounts = [];
|
||||
|
||||
/// <summary>
|
||||
/// Lock for statistics calculation
|
||||
/// </summary>
|
||||
private readonly object statsLock = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Overall item count
|
||||
/// </summary>
|
||||
public long TotalCount { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Number of machines
|
||||
/// </summary>
|
||||
/// <remarks>Special count only used by statistics output</remarks>
|
||||
public long GameCount { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Total uncompressed size
|
||||
/// </summary>
|
||||
public long TotalSize { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Number of items with the remove flag
|
||||
/// </summary>
|
||||
public long RemovedCount { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Name to display on output
|
||||
/// </summary>
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total machine count to use on output
|
||||
/// </summary>
|
||||
public long MachineCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if statistics are for a directory or not
|
||||
/// </summary>
|
||||
public readonly bool IsDirectory;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public DatStatistics()
|
||||
{
|
||||
DisplayName = null;
|
||||
MachineCount = 0;
|
||||
IsDirectory = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for aggregate data
|
||||
/// </summary>
|
||||
public DatStatistics(string? displayName, bool isDirectory)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
MachineCount = 0;
|
||||
IsDirectory = isDirectory;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accessors
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given DatItem
|
||||
/// </summary>
|
||||
/// <param name="item">Item to add info from</param>
|
||||
public void AddItemStatistics(DatItem item)
|
||||
{
|
||||
lock (statsLock)
|
||||
{
|
||||
// No matter what the item is, we increment the count
|
||||
TotalCount++;
|
||||
|
||||
// Increment removal count
|
||||
if (item.GetBoolFieldValue(DatItem.RemoveKey) == true)
|
||||
RemovedCount++;
|
||||
|
||||
// Increment the item count for the type
|
||||
AddItemCount(item.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType());
|
||||
|
||||
// Some item types require special processing
|
||||
switch (item)
|
||||
{
|
||||
case Disk disk:
|
||||
AddItemStatistics(disk);
|
||||
break;
|
||||
case File file:
|
||||
AddItemStatistics(file);
|
||||
break;
|
||||
case Media media:
|
||||
AddItemStatistics(media);
|
||||
break;
|
||||
case Rom rom:
|
||||
AddItemStatistics(rom);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given DatItem
|
||||
/// </summary>
|
||||
/// <param name="item">Item to add info from</param>
|
||||
public void AddItemStatistics(Data.Models.Metadata.DatItem item)
|
||||
{
|
||||
lock (statsLock)
|
||||
{
|
||||
// No matter what the item is, we increment the count
|
||||
TotalCount++;
|
||||
|
||||
// Increment removal count
|
||||
if (item.ReadBool(DatItem.RemoveKey) == true)
|
||||
RemovedCount++;
|
||||
|
||||
// Increment the item count for the type
|
||||
AddItemCount(item.ReadString(Data.Models.Metadata.DatItem.TypeKey).AsItemType());
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Some item types require special processing
|
||||
switch (item)
|
||||
{
|
||||
case Data.Models.Metadata.Disk disk:
|
||||
AddItemStatistics(disk);
|
||||
break;
|
||||
case Data.Models.Metadata.Media media:
|
||||
AddItemStatistics(media);
|
||||
break;
|
||||
case Data.Models.Metadata.Rom rom:
|
||||
AddItemStatistics(rom);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add statistics from another DatStatistics object
|
||||
/// </summary>
|
||||
/// <param name="stats">DatStatistics object to add from</param>
|
||||
public void AddStatistics(DatStatistics stats)
|
||||
{
|
||||
TotalCount += stats.TotalCount;
|
||||
|
||||
// Loop through and add stats for all items
|
||||
foreach (var itemCountKvp in stats._itemCounts)
|
||||
{
|
||||
AddItemCount(itemCountKvp.Key, itemCountKvp.Value);
|
||||
}
|
||||
|
||||
GameCount += stats.GameCount;
|
||||
|
||||
TotalSize += stats.TotalSize;
|
||||
|
||||
// Individual hash counts
|
||||
foreach (var hashCountKvp in stats._hashCounts)
|
||||
{
|
||||
AddHashCount(hashCountKvp.Key, hashCountKvp.Value);
|
||||
}
|
||||
|
||||
// Individual status counts
|
||||
foreach (var statusCountKvp in stats._statusCounts)
|
||||
{
|
||||
AddStatusCount(statusCountKvp.Key, statusCountKvp.Value);
|
||||
}
|
||||
|
||||
RemovedCount += stats.RemovedCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the item count for a given hash type, defaulting to 0 if it does not exist
|
||||
/// </summary>
|
||||
/// <param name="hashType">Hash type to retrieve</param>
|
||||
/// <returns>The number of items with that hash, if it exists</returns>
|
||||
public long GetHashCount(HashType hashType)
|
||||
{
|
||||
lock (_hashCounts)
|
||||
{
|
||||
if (!_hashCounts.TryGetValue(hashType, out long value))
|
||||
return 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the item count for a given item type, defaulting to 0 if it does not exist
|
||||
/// </summary>
|
||||
/// <param name="itemType">Item type to retrieve</param>
|
||||
/// <returns>The number of items of that type, if it exists</returns>
|
||||
public long GetItemCount(ItemType itemType)
|
||||
{
|
||||
lock (_itemCounts)
|
||||
{
|
||||
if (!_itemCounts.TryGetValue(itemType, out long value))
|
||||
return 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the item count for a given item status, defaulting to 0 if it does not exist
|
||||
/// </summary>
|
||||
/// <param name="itemStatus">Item status to retrieve</param>
|
||||
/// <returns>The number of items of that type, if it exists</returns>
|
||||
public long GetStatusCount(ItemStatus itemStatus)
|
||||
{
|
||||
lock (_statusCounts)
|
||||
{
|
||||
if (!_statusCounts.TryGetValue(itemStatus, out long value))
|
||||
return 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a DatItem
|
||||
/// </summary>
|
||||
/// <param name="item">Item to remove info for</param>
|
||||
public void RemoveItemStatistics(DatItem item)
|
||||
{
|
||||
// If we have a null item, we can't do anything
|
||||
if (item is null)
|
||||
return;
|
||||
|
||||
lock (statsLock)
|
||||
{
|
||||
// No matter what the item is, we decrease the count
|
||||
TotalCount--;
|
||||
|
||||
// Decrement removal count
|
||||
if (item.GetBoolFieldValue(DatItem.RemoveKey) == true)
|
||||
RemovedCount--;
|
||||
|
||||
// Decrement the item count for the type
|
||||
RemoveItemCount(item.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType());
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Some item types require special processing
|
||||
switch (item)
|
||||
{
|
||||
case Disk disk:
|
||||
RemoveItemStatistics(disk);
|
||||
break;
|
||||
case File file:
|
||||
RemoveItemStatistics(file);
|
||||
break;
|
||||
case Media media:
|
||||
RemoveItemStatistics(media);
|
||||
break;
|
||||
case Rom rom:
|
||||
RemoveItemStatistics(rom);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a DatItem
|
||||
/// </summary>
|
||||
/// <param name="item">Item to remove info for</param>
|
||||
public void RemoveItemStatistics(Data.Models.Metadata.DatItem item)
|
||||
{
|
||||
// If we have a null item, we can't do anything
|
||||
if (item is null)
|
||||
return;
|
||||
|
||||
lock (statsLock)
|
||||
{
|
||||
// No matter what the item is, we decrease the count
|
||||
TotalCount--;
|
||||
|
||||
// Decrement removal count
|
||||
if (item.ReadBool(DatItem.RemoveKey) == true)
|
||||
RemovedCount--;
|
||||
|
||||
// Decrement the item count for the type
|
||||
RemoveItemCount(item.ReadString(Data.Models.Metadata.DatItem.TypeKey).AsItemType());
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Some item types require special processing
|
||||
switch (item)
|
||||
{
|
||||
case Data.Models.Metadata.Disk disk:
|
||||
RemoveItemStatistics(disk);
|
||||
break;
|
||||
case Data.Models.Metadata.Media media:
|
||||
RemoveItemStatistics(media);
|
||||
break;
|
||||
case Data.Models.Metadata.Rom rom:
|
||||
RemoveItemStatistics(rom);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset all statistics
|
||||
/// </summary>
|
||||
public void ResetStatistics()
|
||||
{
|
||||
_hashCounts.Clear();
|
||||
_itemCounts.Clear();
|
||||
_statusCounts.Clear();
|
||||
|
||||
TotalCount = 0;
|
||||
GameCount = 0;
|
||||
TotalSize = 0;
|
||||
RemovedCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increment the hash count for a given hash type
|
||||
/// </summary>
|
||||
/// <param name="hashType">Hash type to increment</param>
|
||||
/// <param name="interval">Amount to increment by, defaults to 1</param>
|
||||
private void AddHashCount(HashType hashType, long interval = 1)
|
||||
{
|
||||
lock (_hashCounts)
|
||||
{
|
||||
if (!_hashCounts.ContainsKey(hashType))
|
||||
_hashCounts[hashType] = 0;
|
||||
|
||||
_hashCounts[hashType] += interval;
|
||||
if (_hashCounts[hashType] < 0)
|
||||
_hashCounts[hashType] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increment the item count for a given item type
|
||||
/// </summary>
|
||||
/// <param name="itemType">Item type to increment</param>
|
||||
/// <param name="interval">Amount to increment by, defaults to 1</param>
|
||||
private void AddItemCount(ItemType itemType, long interval = 1)
|
||||
{
|
||||
lock (_itemCounts)
|
||||
{
|
||||
if (!_itemCounts.ContainsKey(itemType))
|
||||
_itemCounts[itemType] = 0;
|
||||
|
||||
_itemCounts[itemType] += interval;
|
||||
if (_itemCounts[itemType] < 0)
|
||||
_itemCounts[itemType] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given Disk
|
||||
/// </summary>
|
||||
/// <param name="disk">Item to add info from</param>
|
||||
private void AddItemStatistics(Disk disk)
|
||||
{
|
||||
if (disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
AddHashCount(HashType.MD5, string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA1, string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)) ? 0 : 1);
|
||||
}
|
||||
|
||||
AddStatusCount(ItemStatus.BadDump, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Good, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Nodump, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Verified, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given Disk
|
||||
/// </summary>
|
||||
/// <param name="disk">Item to add info from</param>
|
||||
private void AddItemStatistics(Data.Models.Metadata.Disk disk)
|
||||
{
|
||||
if (disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
AddHashCount(HashType.MD5, string.IsNullOrEmpty(disk.ReadString(Data.Models.Metadata.Disk.MD5Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA1, string.IsNullOrEmpty(disk.ReadString(Data.Models.Metadata.Disk.SHA1Key)) ? 0 : 1);
|
||||
}
|
||||
|
||||
AddStatusCount(ItemStatus.BadDump, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Good, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Nodump, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Verified, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given File
|
||||
/// </summary>
|
||||
/// <param name="file">Item to add info from</param>
|
||||
private void AddItemStatistics(File file)
|
||||
{
|
||||
TotalSize += file.Size ?? 0;
|
||||
AddHashCount(HashType.CRC32, string.IsNullOrEmpty(file.CRC) ? 0 : 1);
|
||||
AddHashCount(HashType.MD5, string.IsNullOrEmpty(file.MD5) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA1, string.IsNullOrEmpty(file.SHA1) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA256, string.IsNullOrEmpty(file.SHA256) ? 0 : 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given Media
|
||||
/// </summary>
|
||||
/// <param name="media">Item to add info from</param>
|
||||
private void AddItemStatistics(Media media)
|
||||
{
|
||||
AddHashCount(HashType.MD5, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA1, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA256, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SpamSum, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given Media
|
||||
/// </summary>
|
||||
/// <param name="media">Item to add info from</param>
|
||||
private void AddItemStatistics(Data.Models.Metadata.Media media)
|
||||
{
|
||||
AddHashCount(HashType.MD5, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.MD5Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA1, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.SHA1Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA256, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.SHA256Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SpamSum, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given Rom
|
||||
/// </summary>
|
||||
/// <param name="rom">Item to add info from</param>
|
||||
private void AddItemStatistics(Rom rom)
|
||||
{
|
||||
if (rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
TotalSize += rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) ?? 0;
|
||||
AddHashCount(HashType.CRC32, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)) ? 0 : 1);
|
||||
AddHashCount(HashType.MD2, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.MD4, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.MD5, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.RIPEMD128, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.RIPEMD160, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA1, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA256, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
AddStatusCount(ItemStatus.BadDump, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Good, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Nodump, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Verified, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to the statistics for a given Rom
|
||||
/// </summary>
|
||||
/// <param name="rom">Item to add info from</param>
|
||||
private void AddItemStatistics(Data.Models.Metadata.Rom rom)
|
||||
{
|
||||
if (rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
TotalSize += rom.ReadLong(Data.Models.Metadata.Rom.SizeKey) ?? 0;
|
||||
AddHashCount(HashType.CRC32, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.CRCKey)) ? 0 : 1);
|
||||
AddHashCount(HashType.MD2, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.MD2Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.MD4, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.MD4Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.MD5, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.MD5Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.RIPEMD128, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.RIPEMD128Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.RIPEMD160, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.RIPEMD160Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA1, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA1Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA256, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA256Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA384Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA512Key)) ? 0 : 1);
|
||||
AddHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
AddStatusCount(ItemStatus.BadDump, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Good, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Nodump, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
AddStatusCount(ItemStatus.Verified, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increment the item count for a given item status
|
||||
/// </summary>
|
||||
/// <param name="itemStatus">Item type to increment</param>
|
||||
/// <param name="interval">Amount to increment by, defaults to 1</param>
|
||||
private void AddStatusCount(ItemStatus itemStatus, long interval = 1)
|
||||
{
|
||||
lock (_statusCounts)
|
||||
{
|
||||
if (!_statusCounts.ContainsKey(itemStatus))
|
||||
_statusCounts[itemStatus] = 0;
|
||||
|
||||
_statusCounts[itemStatus] += interval;
|
||||
if (_statusCounts[itemStatus] < 0)
|
||||
_statusCounts[itemStatus] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrement the hash count for a given hash type
|
||||
/// </summary>
|
||||
/// <param name="hashType">Hash type to increment</param>
|
||||
/// <param name="interval">Amount to increment by, defaults to 1</param>
|
||||
private void RemoveHashCount(HashType hashType, long interval = 1)
|
||||
{
|
||||
lock (_hashCounts)
|
||||
{
|
||||
if (!_hashCounts.ContainsKey(hashType))
|
||||
return;
|
||||
|
||||
_hashCounts[hashType] -= interval;
|
||||
if (_hashCounts[hashType] < 0)
|
||||
_hashCounts[hashType] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrement the item count for a given item type
|
||||
/// </summary>
|
||||
/// <param name="itemType">Item type to decrement</param>
|
||||
/// <param name="interval">Amount to increment by, defaults to 1</param>
|
||||
private void RemoveItemCount(ItemType itemType, long interval = 1)
|
||||
{
|
||||
lock (_itemCounts)
|
||||
{
|
||||
if (!_itemCounts.ContainsKey(itemType))
|
||||
return;
|
||||
|
||||
_itemCounts[itemType] -= interval;
|
||||
if (_itemCounts[itemType] < 0)
|
||||
_itemCounts[itemType] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a Disk
|
||||
/// </summary>
|
||||
/// <param name="disk">Item to remove info for</param>
|
||||
private void RemoveItemStatistics(Disk disk)
|
||||
{
|
||||
if (disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)) ? 0 : 1);
|
||||
}
|
||||
|
||||
RemoveStatusCount(ItemStatus.BadDump, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Good, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Nodump, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Verified, disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a Disk
|
||||
/// </summary>
|
||||
/// <param name="disk">Item to remove info for</param>
|
||||
private void RemoveItemStatistics(Data.Models.Metadata.Disk disk)
|
||||
{
|
||||
if (disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(disk.ReadString(Data.Models.Metadata.Disk.MD5Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(disk.ReadString(Data.Models.Metadata.Disk.SHA1Key)) ? 0 : 1);
|
||||
}
|
||||
|
||||
RemoveStatusCount(ItemStatus.BadDump, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Good, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Nodump, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Verified, disk.ReadString(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a File
|
||||
/// </summary>
|
||||
/// <param name="file">Item to remove info for</param>
|
||||
private void RemoveItemStatistics(File file)
|
||||
{
|
||||
TotalSize -= file.Size ?? 0;
|
||||
RemoveHashCount(HashType.CRC32, string.IsNullOrEmpty(file.CRC) ? 0 : 1);
|
||||
RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(file.MD5) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(file.SHA1) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA256, string.IsNullOrEmpty(file.SHA256) ? 0 : 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a Media
|
||||
/// </summary>
|
||||
/// <param name="media">Item to remove info for</param>
|
||||
private void RemoveItemStatistics(Media media)
|
||||
{
|
||||
RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA256, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SpamSum, string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a Media
|
||||
/// </summary>
|
||||
/// <param name="media">Item to remove info for</param>
|
||||
private void RemoveItemStatistics(Data.Models.Metadata.Media media)
|
||||
{
|
||||
RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.MD5Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.SHA1Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA256, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.SHA256Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SpamSum, string.IsNullOrEmpty(media.ReadString(Data.Models.Metadata.Media.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a Rom
|
||||
/// </summary>
|
||||
/// <param name="rom">Item to remove info for</param>
|
||||
private void RemoveItemStatistics(Rom rom)
|
||||
{
|
||||
if (rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
TotalSize -= rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) ?? 0;
|
||||
RemoveHashCount(HashType.CRC32, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.MD2, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.MD4, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.RIPEMD128, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.RIPEMD160, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA256, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
RemoveStatusCount(ItemStatus.BadDump, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Good, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Nodump, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Verified, rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove from the statistics given a Rom
|
||||
/// </summary>
|
||||
/// <param name="rom">Item to remove info for</param>
|
||||
private void RemoveItemStatistics(Data.Models.Metadata.Rom rom)
|
||||
{
|
||||
if (rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() != ItemStatus.Nodump)
|
||||
{
|
||||
TotalSize -= rom.ReadLong(Data.Models.Metadata.Rom.SizeKey) ?? 0;
|
||||
RemoveHashCount(HashType.CRC32, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.CRCKey)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.MD2, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.MD2Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.MD4, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.MD4Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.MD5Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.RIPEMD128, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.RIPEMD128Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.RIPEMD160, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.RIPEMD160Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA1Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA256, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA256Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA384Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SHA512Key)) ? 0 : 1);
|
||||
RemoveHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.ReadString(Data.Models.Metadata.Rom.SpamSumKey)) ? 0 : 1);
|
||||
}
|
||||
|
||||
RemoveStatusCount(ItemStatus.BadDump, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.BadDump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Good, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Good ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Nodump, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Nodump ? 1 : 0);
|
||||
RemoveStatusCount(ItemStatus.Verified, rom.ReadString(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Verified ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrement the item count for a given item status
|
||||
/// </summary>
|
||||
/// <param name="itemStatus">Item type to decrement</param>
|
||||
/// <param name="interval">Amount to increment by, defaults to 1</param>
|
||||
private void RemoveStatusCount(ItemStatus itemStatus, long interval = 1)
|
||||
{
|
||||
lock (_statusCounts)
|
||||
{
|
||||
if (!_statusCounts.ContainsKey(itemStatus))
|
||||
return;
|
||||
|
||||
_statusCounts[itemStatus] -= interval;
|
||||
if (_statusCounts[itemStatus] < 0)
|
||||
_statusCounts[itemStatus] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
66
SabreTools.Metadata.DatFiles/DepotInformation.cs
Normal file
66
SabreTools.Metadata.DatFiles/DepotInformation.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using SabreTools.Hashing;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Depot information wrapper
|
||||
/// </summary>
|
||||
public class DepotInformation : ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// Name or path of the Depot
|
||||
/// </summary>
|
||||
public string? Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use this Depot or not
|
||||
/// </summary>
|
||||
public bool IsActive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Depot byte-depth
|
||||
/// </summary>
|
||||
public int Depth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="isActive">Set active state</param>
|
||||
/// <param name="depth">Set depth between 0 and SHA-1's byte length</param>
|
||||
public DepotInformation(bool isActive, int depth)
|
||||
: this(null, isActive, depth)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Identifier for the depot</param>
|
||||
/// <param name="isActive">Set active state</param>
|
||||
/// <param name="depth">Set depth between 0 and SHA-1's byte length</param>
|
||||
public DepotInformation(string? name, bool isActive, int depth)
|
||||
{
|
||||
Name = name;
|
||||
IsActive = isActive;
|
||||
Depth = depth;
|
||||
|
||||
// Limit depth value
|
||||
if (Depth == int.MinValue)
|
||||
Depth = 4;
|
||||
else if (Depth < 0)
|
||||
Depth = 0;
|
||||
else if (Depth > HashType.SHA1.ZeroBytes.Length)
|
||||
Depth = HashType.SHA1.ZeroBytes.Length;
|
||||
}
|
||||
|
||||
#region Cloning
|
||||
|
||||
/// <summary>
|
||||
/// Clone the current object
|
||||
/// </summary>
|
||||
public object Clone() => new DepotInformation(Name, IsActive, Depth);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
267
SabreTools.Metadata.DatFiles/Enums.cs
Normal file
267
SabreTools.Metadata.DatFiles/Enums.cs
Normal file
@@ -0,0 +1,267 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// DAT output formats
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DatFormat : ulong
|
||||
{
|
||||
#region XML Formats
|
||||
|
||||
/// <summary>
|
||||
/// Logiqx XML (using machine)
|
||||
/// </summary>
|
||||
Logiqx = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Logiqx XML (using game)
|
||||
/// </summary>
|
||||
LogiqxDeprecated = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// MAME Softare List XML
|
||||
/// </summary>
|
||||
SoftwareList = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// MAME Listxml output
|
||||
/// </summary>
|
||||
Listxml = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// OfflineList XML
|
||||
/// </summary>
|
||||
OfflineList = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// SabreDAT XML
|
||||
/// </summary>
|
||||
SabreXML = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// openMSX Software List XML
|
||||
/// </summary>
|
||||
OpenMSX = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Archive.org file list XML
|
||||
/// </summary>
|
||||
ArchiveDotOrg = 1 << 7,
|
||||
|
||||
#endregion
|
||||
|
||||
#region Propietary Formats
|
||||
|
||||
/// <summary>
|
||||
/// ClrMamePro custom
|
||||
/// </summary>
|
||||
ClrMamePro = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// RomCenter INI-based
|
||||
/// </summary>
|
||||
RomCenter = 1 << 9,
|
||||
|
||||
/// <summary>
|
||||
/// DOSCenter custom
|
||||
/// </summary>
|
||||
DOSCenter = 1 << 10,
|
||||
|
||||
/// <summary>
|
||||
/// AttractMode custom
|
||||
/// </summary>
|
||||
AttractMode = 1 << 11,
|
||||
|
||||
#endregion
|
||||
|
||||
#region Standardized Text Formats
|
||||
|
||||
/// <summary>
|
||||
/// ClrMamePro missfile
|
||||
/// </summary>
|
||||
MissFile = 1 << 12,
|
||||
|
||||
/// <summary>
|
||||
/// Comma-Separated Values (standardized)
|
||||
/// </summary>
|
||||
CSV = 1 << 13,
|
||||
|
||||
/// <summary>
|
||||
/// Semicolon-Separated Values (standardized)
|
||||
/// </summary>
|
||||
SSV = 1 << 14,
|
||||
|
||||
/// <summary>
|
||||
/// Tab-Separated Values (standardized)
|
||||
/// </summary>
|
||||
TSV = 1 << 15,
|
||||
|
||||
/// <summary>
|
||||
/// MAME Listrom output
|
||||
/// </summary>
|
||||
Listrom = 1 << 16,
|
||||
|
||||
/// <summary>
|
||||
/// Everdrive Packs SMDB
|
||||
/// </summary>
|
||||
EverdriveSMDB = 1 << 17,
|
||||
|
||||
/// <summary>
|
||||
/// SabreJSON
|
||||
/// </summary>
|
||||
SabreJSON = 1 << 18,
|
||||
|
||||
#endregion
|
||||
|
||||
#region SFV-similar Formats
|
||||
|
||||
/// <summary>
|
||||
/// CRC32 hash list
|
||||
/// </summary>
|
||||
RedumpSFV = 1 << 19,
|
||||
|
||||
/// <summary>
|
||||
/// MD2 hash list
|
||||
/// </summary>
|
||||
RedumpMD2 = 1 << 20,
|
||||
|
||||
/// <summary>
|
||||
/// MD4 hash list
|
||||
/// </summary>
|
||||
RedumpMD4 = 1 << 21,
|
||||
|
||||
/// <summary>
|
||||
/// MD5 hash list
|
||||
/// </summary>
|
||||
RedumpMD5 = 1 << 22,
|
||||
|
||||
/// <summary>
|
||||
/// RIPEMD128 hash list
|
||||
/// </summary>
|
||||
RedumpRIPEMD128 = 1 << 23,
|
||||
|
||||
/// <summary>
|
||||
/// RIPEMD160 hash list
|
||||
/// </summary>
|
||||
RedumpRIPEMD160 = 1 << 24,
|
||||
|
||||
/// <summary>
|
||||
/// SHA-1 hash list
|
||||
/// </summary>
|
||||
RedumpSHA1 = 1 << 25,
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 hash list
|
||||
/// </summary>
|
||||
RedumpSHA256 = 1 << 26,
|
||||
|
||||
/// <summary>
|
||||
/// SHA-384 hash list
|
||||
/// </summary>
|
||||
RedumpSHA384 = 1 << 27,
|
||||
|
||||
/// <summary>
|
||||
/// SHA-512 hash list
|
||||
/// </summary>
|
||||
RedumpSHA512 = 1 << 28,
|
||||
|
||||
/// <summary>
|
||||
/// SpamSum hash list
|
||||
/// </summary>
|
||||
RedumpSpamSum = 1 << 29,
|
||||
|
||||
#endregion
|
||||
|
||||
// Specialty combinations
|
||||
ALL = ulong.MaxValue,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines merging tag handling for DAT output
|
||||
/// </summary>
|
||||
public enum MergingFlag
|
||||
{
|
||||
[Mapping("none")]
|
||||
None = 0,
|
||||
|
||||
[Mapping("split")]
|
||||
Split,
|
||||
|
||||
[Mapping("merged")]
|
||||
Merged,
|
||||
|
||||
[Mapping("nonmerged", "unmerged")]
|
||||
NonMerged,
|
||||
|
||||
/// <remarks>This is not usually defined for Merging flags</remarks>
|
||||
[Mapping("fullmerged")]
|
||||
FullMerged,
|
||||
|
||||
/// <remarks>This is not usually defined for Merging flags</remarks>
|
||||
[Mapping("device", "deviceunmerged", "devicenonmerged")]
|
||||
DeviceNonMerged,
|
||||
|
||||
/// <remarks>This is not usually defined for Merging flags</remarks>
|
||||
[Mapping("full", "fullunmerged", "fullnonmerged")]
|
||||
FullNonMerged,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines nodump tag handling for DAT output
|
||||
/// </summary>
|
||||
public enum NodumpFlag
|
||||
{
|
||||
[Mapping("none")]
|
||||
None = 0,
|
||||
|
||||
[Mapping("obsolete")]
|
||||
Obsolete,
|
||||
|
||||
[Mapping("required")]
|
||||
Required,
|
||||
|
||||
[Mapping("ignore")]
|
||||
Ignore,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines packing tag handling for DAT output
|
||||
/// </summary>
|
||||
public enum PackingFlag
|
||||
{
|
||||
[Mapping("none")]
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Force all sets to be in archives, except disk and media
|
||||
/// </summary>
|
||||
[Mapping("zip", "yes")]
|
||||
Zip,
|
||||
|
||||
/// <summary>
|
||||
/// Force all sets to be extracted into subfolders
|
||||
/// </summary>
|
||||
[Mapping("unzip", "no")]
|
||||
Unzip,
|
||||
|
||||
/// <summary>
|
||||
/// Force sets with single items to be extracted to the parent folder
|
||||
/// </summary>
|
||||
[Mapping("partial")]
|
||||
Partial,
|
||||
|
||||
/// <summary>
|
||||
/// Force all sets to be extracted to the parent folder
|
||||
/// </summary>
|
||||
[Mapping("flat")]
|
||||
Flat,
|
||||
|
||||
/// <summary>
|
||||
/// Force all sets to have all archives treated as files
|
||||
/// </summary>
|
||||
[Mapping("fileonly")]
|
||||
FileOnly,
|
||||
}
|
||||
}
|
||||
172
SabreTools.Metadata.DatFiles/Extensions.cs
Normal file
172
SabreTools.Metadata.DatFiles/Extensions.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
#region Private Maps
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for MergingFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, MergingFlag> _toMergingFlagMap = Converters.GenerateToEnum<MergingFlag>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for MergingFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<MergingFlag, string> _fromMergingFlagMap = Converters.GenerateToString<MergingFlag>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for MergingFlag (secondary)
|
||||
/// </summary>
|
||||
private static readonly Dictionary<MergingFlag, string> _fromMergingFlagSecondaryMap = Converters.GenerateToString<MergingFlag>(useSecond: true);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for NodumpFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, NodumpFlag> _toNodumpFlagMap = Converters.GenerateToEnum<NodumpFlag>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for NodumpFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<NodumpFlag, string> _fromNodumpFlagMap = Converters.GenerateToString<NodumpFlag>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for PackingFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, PackingFlag> _toPackingFlagMap = Converters.GenerateToEnum<PackingFlag>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for PackingFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<PackingFlag, string> _fromPackingFlagMap = Converters.GenerateToString<PackingFlag>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for PackingFlag (secondary)
|
||||
/// </summary>
|
||||
private static readonly Dictionary<PackingFlag, string> _fromPackingFlagSecondaryMap = Converters.GenerateToString<PackingFlag>(useSecond: true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region String to Enum
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static MergingFlag AsMergingFlag(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toMergingFlagMap.ContainsKey(value))
|
||||
return _toMergingFlagMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static NodumpFlag AsNodumpFlag(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toNodumpFlagMap.ContainsKey(value))
|
||||
return _toNodumpFlagMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static PackingFlag AsPackingFlag(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toPackingFlagMap.ContainsKey(value))
|
||||
return _toPackingFlagMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enum to String
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this MergingFlag value, bool useSecond = false)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (!useSecond && _fromMergingFlagMap.ContainsKey(value))
|
||||
return _fromMergingFlagMap[value];
|
||||
else if (useSecond && _fromMergingFlagSecondaryMap.ContainsKey(value))
|
||||
return _fromMergingFlagSecondaryMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this NodumpFlag value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromNodumpFlagMap.ContainsKey(value))
|
||||
return _fromNodumpFlagMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this PackingFlag value, bool useSecond = false)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (!useSecond && _fromPackingFlagMap.ContainsKey(value))
|
||||
return _fromPackingFlagMap[value];
|
||||
else if (useSecond && _fromPackingFlagSecondaryMap.ContainsKey(value))
|
||||
return _fromPackingFlagSecondaryMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
25
SabreTools.Metadata.DatFiles/Formats/ArchiveDotOrg.cs
Normal file
25
SabreTools.Metadata.DatFiles/Formats/ArchiveDotOrg.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using SabreTools.Metadata.DatItems;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Archive.org file list
|
||||
/// </summary>
|
||||
public sealed class ArchiveDotOrg : SerializableDatFile<Data.Models.ArchiveDotOrg.Files, Serialization.Readers.ArchiveDotOrg, Serialization.Writers.ArchiveDotOrg, Serialization.CrossModel.ArchiveDotOrg>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public ArchiveDotOrg(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.ArchiveDotOrg);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
SabreTools.Metadata.DatFiles/Formats/AttractMode.cs
Normal file
38
SabreTools.Metadata.DatFiles/Formats/AttractMode.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an AttractMode DAT
|
||||
/// </summary>
|
||||
public sealed class AttractMode : SerializableDatFile<Data.Models.AttractMode.MetadataFile, Serialization.Readers.AttractMode, Serialization.Writers.AttractMode, Serialization.CrossModel.AttractMode>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public AttractMode(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.AttractMode);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
203
SabreTools.Metadata.DatFiles/Formats/ClrMamePro.cs
Normal file
203
SabreTools.Metadata.DatFiles/Formats/ClrMamePro.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ClrMamePro DAT
|
||||
/// </summary>
|
||||
public sealed class ClrMamePro : SerializableDatFile<Data.Models.ClrMamePro.MetadataFile, Serialization.Readers.ClrMamePro, Serialization.Writers.ClrMamePro, Serialization.CrossModel.ClrMamePro>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Archive,
|
||||
ItemType.BiosSet,
|
||||
ItemType.Chip,
|
||||
ItemType.DipSwitch,
|
||||
ItemType.Disk,
|
||||
ItemType.Display,
|
||||
ItemType.Driver,
|
||||
ItemType.Input,
|
||||
ItemType.Media,
|
||||
ItemType.Release,
|
||||
ItemType.Rom,
|
||||
ItemType.Sample,
|
||||
ItemType.Sound,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public ClrMamePro(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.ClrMamePro);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Deserialize the input file
|
||||
var metadataFile = new Serialization.Readers.ClrMamePro().Deserialize(filename, quotes: true);
|
||||
var metadata = new Serialization.CrossModel.ClrMamePro().Serialize(metadataFile);
|
||||
|
||||
// Convert to the internal format
|
||||
ConvertFromMetadata(metadata, filename, indexId, keep, statsOnly, filterRunner);
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
string message = $"'{filename}' - An error occurred during parsing";
|
||||
_logger.Error(ex, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Release release:
|
||||
if (string.IsNullOrEmpty(release.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Release.NameKey);
|
||||
if (string.IsNullOrEmpty(release.GetStringFieldValue(Data.Models.Metadata.Release.RegionKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Release.RegionKey);
|
||||
break;
|
||||
|
||||
case BiosSet biosset:
|
||||
if (string.IsNullOrEmpty(biosset.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.BiosSet.NameKey);
|
||||
if (string.IsNullOrEmpty(biosset.GetStringFieldValue(Data.Models.Metadata.BiosSet.DescriptionKey)))
|
||||
missingFields.Add(Data.Models.Metadata.BiosSet.DescriptionKey);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue("MD2"))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue("MD4"))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Disk disk:
|
||||
if (string.IsNullOrEmpty(disk.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Disk.NameKey);
|
||||
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key))
|
||||
&& string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Disk.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Sample sample:
|
||||
if (string.IsNullOrEmpty(sample.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Sample.NameKey);
|
||||
break;
|
||||
|
||||
case Archive archive:
|
||||
if (string.IsNullOrEmpty(archive.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Archive.NameKey);
|
||||
break;
|
||||
|
||||
case Chip chip:
|
||||
if (chip.GetStringFieldValue(Data.Models.Metadata.Chip.ChipTypeKey).AsChipType() == ChipType.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Chip.ChipTypeKey);
|
||||
if (string.IsNullOrEmpty(chip.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Chip.NameKey);
|
||||
break;
|
||||
|
||||
case Display display:
|
||||
if (display.GetStringFieldValue(Data.Models.Metadata.Display.DisplayTypeKey).AsDisplayType() == DisplayType.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Display.DisplayTypeKey);
|
||||
if (display.GetInt64FieldValue(Data.Models.Metadata.Display.RotateKey) is null)
|
||||
missingFields.Add(Data.Models.Metadata.Display.RotateKey);
|
||||
break;
|
||||
|
||||
case Sound sound:
|
||||
if (sound.GetInt64FieldValue(Data.Models.Metadata.Sound.ChannelsKey) is null)
|
||||
missingFields.Add(Data.Models.Metadata.Sound.ChannelsKey);
|
||||
break;
|
||||
|
||||
case Input input:
|
||||
if (input.GetInt64FieldValue(Data.Models.Metadata.Input.PlayersKey) is null)
|
||||
missingFields.Add(Data.Models.Metadata.Input.PlayersKey);
|
||||
if (!input.ControlsSpecified)
|
||||
missingFields.Add(Data.Models.Metadata.Input.ControlKey);
|
||||
break;
|
||||
|
||||
case DipSwitch dipswitch:
|
||||
if (string.IsNullOrEmpty(dipswitch.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.DipSwitch.NameKey);
|
||||
break;
|
||||
|
||||
case Driver driver:
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.StatusKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.StatusKey);
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.EmulationKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.EmulationKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
|
||||
// Serialize the input file
|
||||
var metadata = ConvertToMetadata(ignoreblanks);
|
||||
var metadataFile = new Serialization.CrossModel.ClrMamePro().Deserialize(metadata);
|
||||
if (!new Serialization.Writers.ClrMamePro().SerializeFile(metadataFile, outfile, quotes: true))
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be written! See the log for more details.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
SabreTools.Metadata.DatFiles/Formats/DosCenter.cs
Normal file
55
SabreTools.Metadata.DatFiles/Formats/DosCenter.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of a DosCenter DAT
|
||||
/// </summary>
|
||||
public sealed class DosCenter : SerializableDatFile<Data.Models.DosCenter.MetadataFile, Serialization.Readers.DosCenter, Serialization.Writers.DosCenter, Serialization.CrossModel.DosCenter>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public DosCenter(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.DOSCenter);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
// if (string.IsNullOrEmpty(rom.Date))
|
||||
// missingFields.Add(Data.Models.Metadata.Rom.DateKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.CRCKey);
|
||||
// if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)))
|
||||
// missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
SabreTools.Metadata.DatFiles/Formats/EverdriveSmdb.cs
Normal file
55
SabreTools.Metadata.DatFiles/Formats/EverdriveSmdb.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of an Everdrive SMDB file
|
||||
/// </summary>
|
||||
public sealed class EverdriveSMDB : SerializableDatFile<Data.Models.EverdriveSMDB.MetadataFile, Serialization.Readers.EverdriveSMDB, Serialization.Writers.EverdriveSMDB, Serialization.CrossModel.EverdriveSMDB>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public EverdriveSMDB(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.EverdriveSMDB);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA256Key);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.MD5Key);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.CRCKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
601
SabreTools.Metadata.DatFiles/Formats/Hashfile.cs
Normal file
601
SabreTools.Metadata.DatFiles/Formats/Hashfile.cs
Normal file
@@ -0,0 +1,601 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using SabreTools.Hashing;
|
||||
|
||||
#pragma warning disable IDE0290 // Use primary constructor
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a hashfile such as an SFV, MD5, or SHA-1 file
|
||||
/// </summary>
|
||||
public abstract class Hashfile : SerializableDatFile<Data.Models.Hashfile.Hashfile, Serialization.Readers.Hashfile, Serialization.Writers.Hashfile, Serialization.CrossModel.Hashfile>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
// Private instance variables specific to Hashfile DATs
|
||||
protected HashType _hash;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Hashfile(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Deserialize the input file
|
||||
var hashfile = new Serialization.Readers.Hashfile().Deserialize(filename, _hash);
|
||||
var metadata = new Serialization.CrossModel.Hashfile().Serialize(hashfile);
|
||||
|
||||
// Convert to the internal format
|
||||
ConvertFromMetadata(metadata, filename, indexId, keep, statsOnly, filterRunner);
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
string message = $"'{filename}' - An error occurred during parsing";
|
||||
_logger.Error(ex, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
|
||||
// Serialize the input file
|
||||
var metadata = ConvertToMetadata(ignoreblanks);
|
||||
var hashfile = new Serialization.CrossModel.Hashfile().Deserialize(metadata, _hash);
|
||||
if (!new Serialization.Writers.Hashfile().SerializeFile(hashfile, outfile, _hash))
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be written! See the log for more details.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an SFV (CRC-32) hashfile
|
||||
/// </summary>
|
||||
public sealed class SfvFile : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SfvFile(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.CRC32;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpSFV);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.CRCKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an MD2 hashfile
|
||||
/// </summary>
|
||||
public sealed class Md2File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Md2File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.MD2;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpMD2);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.MD2Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an MD4 hashfile
|
||||
/// </summary>
|
||||
public sealed class Md4File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Md4File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.MD4;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpMD4);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.MD4Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an MD5 hashfile
|
||||
/// </summary>
|
||||
public sealed class Md5File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Disk,
|
||||
ItemType.Media,
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Md5File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.MD5;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpMD5);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Disk disk:
|
||||
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Disk.MD5Key);
|
||||
break;
|
||||
|
||||
case Media medium:
|
||||
if (string.IsNullOrEmpty(medium.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Media.MD5Key);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.MD5Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an RIPEMD128 hashfile
|
||||
/// </summary>
|
||||
public sealed class RipeMD128File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public RipeMD128File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.RIPEMD128;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpRIPEMD128);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.RIPEMD128Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an RIPEMD160 hashfile
|
||||
/// </summary>
|
||||
public sealed class RipeMD160File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public RipeMD160File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.RIPEMD160;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpRIPEMD160);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.RIPEMD160Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an SHA-1 hashfile
|
||||
/// </summary>
|
||||
public sealed class Sha1File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Disk,
|
||||
ItemType.Media,
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Sha1File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.SHA1;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpSHA1);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Disk disk:
|
||||
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Disk.SHA1Key);
|
||||
break;
|
||||
|
||||
case Media medium:
|
||||
if (string.IsNullOrEmpty(medium.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Media.SHA1Key);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an SHA-256 hashfile
|
||||
/// </summary>
|
||||
public sealed class Sha256File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Media,
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Sha256File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.SHA256;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpSHA256);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Media medium:
|
||||
if (string.IsNullOrEmpty(medium.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Media.SHA256Key);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA256Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an SHA-384 hashfile
|
||||
/// </summary>
|
||||
public sealed class Sha384File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Sha384File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.SHA384;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpSHA384);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA384Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an SHA-512 hashfile
|
||||
/// </summary>
|
||||
public sealed class Sha512File : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Sha512File(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.SHA512;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpSHA512);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA512Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an SpamSum hashfile
|
||||
/// </summary>
|
||||
public sealed class SpamSumFile : Hashfile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Media,
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SpamSumFile(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_hash = HashType.SpamSum;
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RedumpSpamSum);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Media medium:
|
||||
if (string.IsNullOrEmpty(medium.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Media.SpamSumKey);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SpamSumKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
SabreTools.Metadata.DatFiles/Formats/Listrom.cs
Normal file
63
SabreTools.Metadata.DatFiles/Formats/Listrom.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a MAME Listrom file
|
||||
/// </summary>
|
||||
public sealed class Listrom : SerializableDatFile<Data.Models.Listrom.MetadataFile, Serialization.Readers.Listrom, Serialization.Writers.Listrom, Serialization.CrossModel.Listrom>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Disk,
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Listrom(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.Listrom);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Disk disk:
|
||||
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key))
|
||||
&& string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Disk.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.CRCKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
397
SabreTools.Metadata.DatFiles/Formats/Listxml.cs
Normal file
397
SabreTools.Metadata.DatFiles/Formats/Listxml.cs
Normal file
@@ -0,0 +1,397 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a MAME/M1 XML DAT
|
||||
/// </summary>
|
||||
public sealed class Listxml : SerializableDatFile<Data.Models.Listxml.Mame, Serialization.Readers.Listxml, Serialization.Writers.Listxml, Serialization.CrossModel.Listxml>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// DTD for original MAME XML DATs
|
||||
/// </summary>
|
||||
internal const string MAMEDTD = @"<!DOCTYPE mame [
|
||||
<!ELEMENT mame (machine+)>
|
||||
<!ATTLIST mame build CDATA #IMPLIED>
|
||||
<!ATTLIST mame debug (yes|no) ""no"">
|
||||
<!ATTLIST mame mameconfig CDATA #REQUIRED>
|
||||
<!ELEMENT machine (description, year?, manufacturer?, biosset*, rom*, disk*, device_ref*, sample*, chip*, display*, sound?, input?, dipswitch*, configuration*, port*, adjuster*, driver?, feature*, device*, slot*, softwarelist*, ramoption*)>
|
||||
<!ATTLIST machine name CDATA #REQUIRED>
|
||||
<!ATTLIST machine sourcefile CDATA #IMPLIED>
|
||||
<!ATTLIST machine isbios (yes|no) ""no"">
|
||||
<!ATTLIST machine isdevice (yes|no) ""no"">
|
||||
<!ATTLIST machine ismechanical (yes|no) ""no"">
|
||||
<!ATTLIST machine runnable (yes|no) ""yes"">
|
||||
<!ATTLIST machine cloneof CDATA #IMPLIED>
|
||||
<!ATTLIST machine romof CDATA #IMPLIED>
|
||||
<!ATTLIST machine sampleof CDATA #IMPLIED>
|
||||
<!ELEMENT description (#PCDATA)>
|
||||
<!ELEMENT year (#PCDATA)>
|
||||
<!ELEMENT manufacturer (#PCDATA)>
|
||||
<!ELEMENT biosset EMPTY>
|
||||
<!ATTLIST biosset name CDATA #REQUIRED>
|
||||
<!ATTLIST biosset description CDATA #REQUIRED>
|
||||
<!ATTLIST biosset default (yes|no) ""no"">
|
||||
<!ELEMENT rom EMPTY>
|
||||
<!ATTLIST rom name CDATA #REQUIRED>
|
||||
<!ATTLIST rom bios CDATA #IMPLIED>
|
||||
<!ATTLIST rom size CDATA #REQUIRED>
|
||||
<!ATTLIST rom crc CDATA #IMPLIED>
|
||||
<!ATTLIST rom sha1 CDATA #IMPLIED>
|
||||
<!ATTLIST rom merge CDATA #IMPLIED>
|
||||
<!ATTLIST rom region CDATA #IMPLIED>
|
||||
<!ATTLIST rom offset CDATA #IMPLIED>
|
||||
<!ATTLIST rom status (baddump|nodump|good) ""good"">
|
||||
<!ATTLIST rom optional (yes|no) ""no"">
|
||||
<!ELEMENT disk EMPTY>
|
||||
<!ATTLIST disk name CDATA #REQUIRED>
|
||||
<!ATTLIST disk sha1 CDATA #IMPLIED>
|
||||
<!ATTLIST disk merge CDATA #IMPLIED>
|
||||
<!ATTLIST disk region CDATA #IMPLIED>
|
||||
<!ATTLIST disk index CDATA #IMPLIED>
|
||||
<!ATTLIST disk writable (yes|no) ""no"">
|
||||
<!ATTLIST disk status (baddump|nodump|good) ""good"">
|
||||
<!ATTLIST disk optional (yes|no) ""no"">
|
||||
<!ELEMENT device_ref EMPTY>
|
||||
<!ATTLIST device_ref name CDATA #REQUIRED>
|
||||
<!ELEMENT sample EMPTY>
|
||||
<!ATTLIST sample name CDATA #REQUIRED>
|
||||
<!ELEMENT chip EMPTY>
|
||||
<!ATTLIST chip name CDATA #REQUIRED>
|
||||
<!ATTLIST chip tag CDATA #IMPLIED>
|
||||
<!ATTLIST chip type (cpu|audio) #REQUIRED>
|
||||
<!ATTLIST chip clock CDATA #IMPLIED>
|
||||
<!ELEMENT display EMPTY>
|
||||
<!ATTLIST display tag CDATA #IMPLIED>
|
||||
<!ATTLIST display type (raster|vector|lcd|svg|unknown) #REQUIRED>
|
||||
<!ATTLIST display rotate (0|90|180|270) #IMPLIED>
|
||||
<!ATTLIST display flipx (yes|no) ""no"">
|
||||
<!ATTLIST display width CDATA #IMPLIED>
|
||||
<!ATTLIST display height CDATA #IMPLIED>
|
||||
<!ATTLIST display refresh CDATA #REQUIRED>
|
||||
<!ATTLIST display pixclock CDATA #IMPLIED>
|
||||
<!ATTLIST display htotal CDATA #IMPLIED>
|
||||
<!ATTLIST display hbend CDATA #IMPLIED>
|
||||
<!ATTLIST display hbstart CDATA #IMPLIED>
|
||||
<!ATTLIST display vtotal CDATA #IMPLIED>
|
||||
<!ATTLIST display vbend CDATA #IMPLIED>
|
||||
<!ATTLIST display vbstart CDATA #IMPLIED>
|
||||
<!ELEMENT sound EMPTY>
|
||||
<!ATTLIST sound channels CDATA #REQUIRED>
|
||||
<!ELEMENT condition EMPTY>
|
||||
<!ATTLIST condition tag CDATA #REQUIRED>
|
||||
<!ATTLIST condition mask CDATA #REQUIRED>
|
||||
<!ATTLIST condition relation (eq|ne|gt|le|lt|ge) #REQUIRED>
|
||||
<!ATTLIST condition value CDATA #REQUIRED>
|
||||
<!ELEMENT input (control*)>
|
||||
<!ATTLIST input service (yes|no) ""no"">
|
||||
<!ATTLIST input tilt (yes|no) ""no"">
|
||||
<!ATTLIST input players CDATA #REQUIRED>
|
||||
<!ATTLIST input coins CDATA #IMPLIED>
|
||||
<!ELEMENT control EMPTY>
|
||||
<!ATTLIST control type CDATA #REQUIRED>
|
||||
<!ATTLIST control player CDATA #IMPLIED>
|
||||
<!ATTLIST control buttons CDATA #IMPLIED>
|
||||
<!ATTLIST control reqbuttons CDATA #IMPLIED>
|
||||
<!ATTLIST control minimum CDATA #IMPLIED>
|
||||
<!ATTLIST control maximum CDATA #IMPLIED>
|
||||
<!ATTLIST control sensitivity CDATA #IMPLIED>
|
||||
<!ATTLIST control keydelta CDATA #IMPLIED>
|
||||
<!ATTLIST control reverse (yes|no) ""no"">
|
||||
<!ATTLIST control ways CDATA #IMPLIED>
|
||||
<!ATTLIST control ways2 CDATA #IMPLIED>
|
||||
<!ATTLIST control ways3 CDATA #IMPLIED>
|
||||
<!ELEMENT dipswitch (condition?, diplocation*, dipvalue*)>
|
||||
<!ATTLIST dipswitch name CDATA #REQUIRED>
|
||||
<!ATTLIST dipswitch tag CDATA #REQUIRED>
|
||||
<!ATTLIST dipswitch mask CDATA #REQUIRED>
|
||||
<!ELEMENT diplocation EMPTY>
|
||||
<!ATTLIST diplocation name CDATA #REQUIRED>
|
||||
<!ATTLIST diplocation number CDATA #REQUIRED>
|
||||
<!ATTLIST diplocation inverted (yes|no) ""no"">
|
||||
<!ELEMENT dipvalue (condition?)>
|
||||
<!ATTLIST dipvalue name CDATA #REQUIRED>
|
||||
<!ATTLIST dipvalue value CDATA #REQUIRED>
|
||||
<!ATTLIST dipvalue default (yes|no) ""no"">
|
||||
<!ELEMENT configuration (condition?, conflocation*, confsetting*)>
|
||||
<!ATTLIST configuration name CDATA #REQUIRED>
|
||||
<!ATTLIST configuration tag CDATA #REQUIRED>
|
||||
<!ATTLIST configuration mask CDATA #REQUIRED>
|
||||
<!ELEMENT conflocation EMPTY>
|
||||
<!ATTLIST conflocation name CDATA #REQUIRED>
|
||||
<!ATTLIST conflocation number CDATA #REQUIRED>
|
||||
<!ATTLIST conflocation inverted (yes|no) ""no"">
|
||||
<!ELEMENT confsetting (condition?)>
|
||||
<!ATTLIST confsetting name CDATA #REQUIRED>
|
||||
<!ATTLIST confsetting value CDATA #REQUIRED>
|
||||
<!ATTLIST confsetting default (yes|no) ""no"">
|
||||
<!ELEMENT port (analog*)>
|
||||
<!ATTLIST port tag CDATA #REQUIRED>
|
||||
<!ELEMENT analog EMPTY>
|
||||
<!ATTLIST analog mask CDATA #REQUIRED>
|
||||
<!ELEMENT adjuster (condition?)>
|
||||
<!ATTLIST adjuster name CDATA #REQUIRED>
|
||||
<!ATTLIST adjuster default CDATA #REQUIRED>
|
||||
<!ELEMENT driver EMPTY>
|
||||
<!ATTLIST driver status (good|imperfect|preliminary) #REQUIRED>
|
||||
<!ATTLIST driver emulation (good|imperfect|preliminary) #REQUIRED>
|
||||
<!ATTLIST driver cocktail (good|imperfect|preliminary) #IMPLIED>
|
||||
<!ATTLIST driver savestate (supported|unsupported) #REQUIRED>
|
||||
<!ATTLIST driver requiresartwork (yes|no) ""no"">
|
||||
<!ATTLIST driver unofficial (yes|no) ""no"">
|
||||
<!ATTLIST driver nosoundhardware (yes|no) ""no"">
|
||||
<!ATTLIST driver incomplete (yes|no) ""no"">
|
||||
<!ELEMENT feature EMPTY>
|
||||
<!ATTLIST feature type (protection|timing|graphics|palette|sound|capture|camera|microphone|controls|keyboard|mouse|media|disk|printer|tape|punch|drum|rom|comms|lan|wan) #REQUIRED>
|
||||
<!ATTLIST feature status (unemulated|imperfect) #IMPLIED>
|
||||
<!ATTLIST feature overall (unemulated|imperfect) #IMPLIED>
|
||||
<!ELEMENT device (instance?, extension*)>
|
||||
<!ATTLIST device type CDATA #REQUIRED>
|
||||
<!ATTLIST device tag CDATA #IMPLIED>
|
||||
<!ATTLIST device fixed_image CDATA #IMPLIED>
|
||||
<!ATTLIST device mandatory CDATA #IMPLIED>
|
||||
<!ATTLIST device interface CDATA #IMPLIED>
|
||||
<!ELEMENT instance EMPTY>
|
||||
<!ATTLIST instance name CDATA #REQUIRED>
|
||||
<!ATTLIST instance briefname CDATA #REQUIRED>
|
||||
<!ELEMENT extension EMPTY>
|
||||
<!ATTLIST extension name CDATA #REQUIRED>
|
||||
<!ELEMENT slot (slotoption*)>
|
||||
<!ATTLIST slot name CDATA #REQUIRED>
|
||||
<!ELEMENT slotoption EMPTY>
|
||||
<!ATTLIST slotoption name CDATA #REQUIRED>
|
||||
<!ATTLIST slotoption devname CDATA #REQUIRED>
|
||||
<!ATTLIST slotoption default (yes|no) ""no"">
|
||||
<!ELEMENT softwarelist EMPTY>
|
||||
<!ATTLIST softwarelist tag CDATA #REQUIRED>
|
||||
<!ATTLIST softwarelist name CDATA #REQUIRED>
|
||||
<!ATTLIST softwarelist status (original|compatible) #REQUIRED>
|
||||
<!ATTLIST softwarelist filter CDATA #IMPLIED>
|
||||
<!ELEMENT ramoption (#PCDATA)>
|
||||
<!ATTLIST ramoption name CDATA #REQUIRED>
|
||||
<!ATTLIST ramoption default CDATA #IMPLIED>
|
||||
]>
|
||||
";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Adjuster,
|
||||
ItemType.BiosSet,
|
||||
ItemType.Chip,
|
||||
ItemType.Condition,
|
||||
ItemType.Configuration,
|
||||
ItemType.Device,
|
||||
ItemType.DeviceRef,
|
||||
ItemType.DipSwitch,
|
||||
ItemType.Disk,
|
||||
ItemType.Display,
|
||||
ItemType.Driver,
|
||||
ItemType.Feature,
|
||||
ItemType.Input,
|
||||
ItemType.Port,
|
||||
ItemType.RamOption,
|
||||
ItemType.Rom,
|
||||
ItemType.Sample,
|
||||
ItemType.Slot,
|
||||
ItemType.SoftwareList,
|
||||
ItemType.Sound,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Listxml(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.Listxml);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Deserialize the input file
|
||||
var mame = new Serialization.Readers.Listxml().Deserialize(filename);
|
||||
Data.Models.Metadata.MetadataFile? metadata;
|
||||
if (mame is null)
|
||||
{
|
||||
var m1 = new Serialization.Readers.M1().Deserialize(filename);
|
||||
metadata = new Serialization.CrossModel.M1().Serialize(m1);
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata = new Serialization.CrossModel.Listxml().Serialize(mame);
|
||||
}
|
||||
|
||||
// Convert to the internal format
|
||||
ConvertFromMetadata(metadata, filename, indexId, keep, statsOnly, filterRunner);
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
string message = $"'{filename}' - An error occurred during parsing";
|
||||
_logger.Error(ex, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case BiosSet biosset:
|
||||
if (string.IsNullOrEmpty(biosset.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.BiosSet.NameKey);
|
||||
if (string.IsNullOrEmpty(biosset.GetStringFieldValue(Data.Models.Metadata.BiosSet.DescriptionKey)))
|
||||
missingFields.Add(Data.Models.Metadata.BiosSet.DescriptionKey);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Disk disk:
|
||||
if (string.IsNullOrEmpty(disk.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Disk.NameKey);
|
||||
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key))
|
||||
&& string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Disk.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DeviceRef deviceref:
|
||||
if (string.IsNullOrEmpty(deviceref.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.DeviceRef.NameKey);
|
||||
break;
|
||||
|
||||
case Sample sample:
|
||||
if (string.IsNullOrEmpty(sample.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Sample.NameKey);
|
||||
break;
|
||||
|
||||
case Chip chip:
|
||||
if (string.IsNullOrEmpty(chip.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Chip.NameKey);
|
||||
if (chip.GetStringFieldValue(Data.Models.Metadata.Chip.ChipTypeKey).AsChipType() == ChipType.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Chip.ChipTypeKey);
|
||||
break;
|
||||
|
||||
case Display display:
|
||||
if (display.GetStringFieldValue(Data.Models.Metadata.Display.DisplayTypeKey).AsDisplayType() == DisplayType.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Display.DisplayTypeKey);
|
||||
if (display.GetDoubleFieldValue(Data.Models.Metadata.Display.RefreshKey) is null)
|
||||
missingFields.Add(Data.Models.Metadata.Display.RefreshKey);
|
||||
break;
|
||||
|
||||
case Sound sound:
|
||||
if (sound.GetInt64FieldValue(Data.Models.Metadata.Sound.ChannelsKey) is null)
|
||||
missingFields.Add(Data.Models.Metadata.Sound.ChannelsKey);
|
||||
break;
|
||||
|
||||
case Input input:
|
||||
if (input.GetInt64FieldValue(Data.Models.Metadata.Input.PlayersKey) is null)
|
||||
missingFields.Add(Data.Models.Metadata.Input.PlayersKey);
|
||||
break;
|
||||
|
||||
case DipSwitch dipswitch:
|
||||
if (string.IsNullOrEmpty(dipswitch.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.DipSwitch.NameKey);
|
||||
if (string.IsNullOrEmpty(dipswitch.GetStringFieldValue(Data.Models.Metadata.DipSwitch.TagKey)))
|
||||
missingFields.Add(Data.Models.Metadata.DipSwitch.TagKey);
|
||||
break;
|
||||
|
||||
case Configuration configuration:
|
||||
if (string.IsNullOrEmpty(configuration.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Configuration.NameKey);
|
||||
if (string.IsNullOrEmpty(configuration.GetStringFieldValue(Data.Models.Metadata.Configuration.TagKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Configuration.TagKey);
|
||||
break;
|
||||
|
||||
case Port port:
|
||||
if (string.IsNullOrEmpty(port.GetStringFieldValue(Data.Models.Metadata.Port.TagKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Port.TagKey);
|
||||
break;
|
||||
|
||||
case Adjuster adjuster:
|
||||
if (string.IsNullOrEmpty(adjuster.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Adjuster.NameKey);
|
||||
break;
|
||||
|
||||
case Driver driver:
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.StatusKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.StatusKey);
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.EmulationKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.EmulationKey);
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.CocktailKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.CocktailKey);
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.SaveStateKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.SaveStateKey);
|
||||
break;
|
||||
|
||||
case Feature feature:
|
||||
if (feature.GetStringFieldValue(Data.Models.Metadata.Feature.FeatureTypeKey).AsFeatureType() == FeatureType.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Feature.FeatureTypeKey);
|
||||
break;
|
||||
|
||||
case Device device:
|
||||
if (device.GetStringFieldValue(Data.Models.Metadata.Device.DeviceTypeKey).AsDeviceType() == DeviceType.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Device.DeviceTypeKey);
|
||||
break;
|
||||
|
||||
case Slot slot:
|
||||
if (string.IsNullOrEmpty(slot.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Slot.NameKey);
|
||||
break;
|
||||
|
||||
case DatItems.Formats.SoftwareList softwarelist:
|
||||
if (string.IsNullOrEmpty(softwarelist.GetStringFieldValue(Data.Models.Metadata.SoftwareList.TagKey)))
|
||||
missingFields.Add(Data.Models.Metadata.SoftwareList.TagKey);
|
||||
if (string.IsNullOrEmpty(softwarelist.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.SoftwareList.NameKey);
|
||||
if (softwarelist.GetStringFieldValue(Data.Models.Metadata.SoftwareList.StatusKey).AsSoftwareListStatus() == SoftwareListStatus.None)
|
||||
missingFields.Add(Data.Models.Metadata.SoftwareList.StatusKey);
|
||||
break;
|
||||
|
||||
case RamOption ramoption:
|
||||
if (string.IsNullOrEmpty(ramoption.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.RamOption.NameKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
404
SabreTools.Metadata.DatFiles/Formats/Logiqx.cs
Normal file
404
SabreTools.Metadata.DatFiles/Formats/Logiqx.cs
Normal file
@@ -0,0 +1,404 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Logiqx-derived DAT
|
||||
/// </summary>
|
||||
public sealed class Logiqx : SerializableDatFile<Data.Models.Logiqx.Datafile, Serialization.Readers.Logiqx, Serialization.Writers.Logiqx, Serialization.CrossModel.Logiqx>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// DTD for original Logiqx DATs
|
||||
/// </summary>
|
||||
/// <remarks>This has been edited to reflect actual current standards</remarks>
|
||||
internal const string LogiqxDTD = @"<!--
|
||||
ROM Management Datafile - DTD
|
||||
|
||||
For further information, see: http://www.logiqx.com/
|
||||
|
||||
This DTD module is identified by the PUBLIC and SYSTEM identifiers:
|
||||
|
||||
PUBLIC "" -//Logiqx//DTD ROM Management Datafile//EN""
|
||||
SYSTEM ""http://www.logiqx.com/Dats/datafile.dtd""
|
||||
|
||||
$Revision: 1.5 $
|
||||
$Date: 2008/10/28 21:39:16 $
|
||||
|
||||
-->
|
||||
|
||||
<!ELEMENT datafile(header?, game*, machine*)>
|
||||
<!ATTLIST datafile build CDATA #IMPLIED>
|
||||
<!ATTLIST datafile debug (yes|no) ""no"">
|
||||
<!ELEMENT header (id?, name, description, rootdir?, type?, category?, version, date?, author, email?, homepage?, url?, comment?, clrmamepro?, romcenter?)>
|
||||
<!ELEMENT id (#PCDATA)>
|
||||
<!ELEMENT name(#PCDATA)>
|
||||
<!ELEMENT description (#PCDATA)>
|
||||
<!ELEMENT rootdir (#PCDATA)>
|
||||
<!ELEMENT type (#PCDATA)>
|
||||
<!ELEMENT category (#PCDATA)>
|
||||
<!ELEMENT version (#PCDATA)>
|
||||
<!ELEMENT date (#PCDATA)>
|
||||
<!ELEMENT author (#PCDATA)>
|
||||
<!ELEMENT email (#PCDATA)>
|
||||
<!ELEMENT homepage (#PCDATA)>
|
||||
<!ELEMENT url (#PCDATA)>
|
||||
<!ELEMENT comment (#PCDATA)>
|
||||
<!ELEMENT clrmamepro EMPTY>
|
||||
<!ATTLIST clrmamepro header CDATA #IMPLIED>
|
||||
<!ATTLIST clrmamepro forcemerging (none|split|merged|nonmerged|fullmerged|device|full) ""split"">
|
||||
<!ATTLIST clrmamepro forcenodump(obsolete|required|ignore) ""obsolete"">
|
||||
<!ATTLIST clrmamepro forcepacking(zip|unzip) ""zip"">
|
||||
<!ELEMENT romcenter EMPTY>
|
||||
<!ATTLIST romcenter plugin CDATA #IMPLIED>
|
||||
<!ATTLIST romcenter rommode (none|split|merged|unmerged|fullmerged|device|full) ""split"">
|
||||
<!ATTLIST romcenter biosmode (none|split|merged|unmerged|fullmerged|device|full) ""split"">
|
||||
<!ATTLIST romcenter samplemode (none|split|merged|unmerged|fullmerged|device|full) ""merged"">
|
||||
<!ATTLIST romcenter lockrommode(yes|no) ""no"">
|
||||
<!ATTLIST romcenter lockbiosmode(yes|no) ""no"">
|
||||
<!ATTLIST romcenter locksamplemode(yes|no) ""no"">
|
||||
<!ELEMENT game (comment*, description, year?, manufacturer?, publisher?, category?, trurip?, release*, biosset*, rom*, disk*, media*, sample*, archive*)>
|
||||
<!ATTLIST game name CDATA #REQUIRED>
|
||||
<!ATTLIST game sourcefile CDATA #IMPLIED>
|
||||
<!ATTLIST game isbios (yes|no) ""no"">
|
||||
<!ATTLIST game cloneof CDATA #IMPLIED>
|
||||
<!ATTLIST game romof CDATA #IMPLIED>
|
||||
<!ATTLIST game sampleof CDATA #IMPLIED>
|
||||
<!ATTLIST game board CDATA #IMPLIED>
|
||||
<!ATTLIST game rebuildto CDATA #IMPLIED>
|
||||
<!ATTLIST game id CDATA #IMPLIED>
|
||||
<!ATTLIST game cloneofid CDATA #IMPLIED>
|
||||
<!ATTLIST game runnable (no|partial|yes) ""no"" #IMPLIED>
|
||||
<!ELEMENT year (#PCDATA)>
|
||||
<!ELEMENT manufacturer (#PCDATA)>
|
||||
<!ELEMENT publisher (#PCDATA)>
|
||||
<!ELEMENT trurip (titleid?, publisher?, developer?, year?, genre?, subgenre?, ratings?, score?, players?, enabled?, crc?, source?, cloneof?, relatedto?)>
|
||||
<!ELEMENT titleid (#PCDATA)>
|
||||
<!ELEMENT developer (#PCDATA)>
|
||||
<!ELEMENT year (#PCDATA)>
|
||||
<!ELEMENT genre (#PCDATA)>
|
||||
<!ELEMENT subgenre (#PCDATA)>
|
||||
<!ELEMENT ratings (#PCDATA)>
|
||||
<!ELEMENT score (#PCDATA)>
|
||||
<!ELEMENT players (#PCDATA)>
|
||||
<!ELEMENT enabled (#PCDATA)>
|
||||
<!ELEMENT crc (#PCDATA)>
|
||||
<!ELEMENT source (#PCDATA)>
|
||||
<!ELEMENT cloneof (#PCDATA)>
|
||||
<!ELEMENT relatedto (#PCDATA)>
|
||||
<!ELEMENT release EMPTY>
|
||||
<!ATTLIST release name CDATA #REQUIRED>
|
||||
<!ATTLIST release region CDATA #REQUIRED>
|
||||
<!ATTLIST release language CDATA #IMPLIED>
|
||||
<!ATTLIST release date CDATA #IMPLIED>
|
||||
<!ATTLIST release default (yes|no) ""no"">
|
||||
<!ELEMENT biosset EMPTY>
|
||||
<!ATTLIST biosset name CDATA #REQUIRED>
|
||||
<!ATTLIST biosset description CDATA #REQUIRED>
|
||||
<!ATTLIST biosset default (yes|no) ""no"">
|
||||
<!ELEMENT rom EMPTY>
|
||||
<!ATTLIST rom name CDATA #REQUIRED>
|
||||
<!ATTLIST rom size CDATA #REQUIRED>
|
||||
<!ATTLIST rom crc CDATA #IMPLIED>
|
||||
<!ATTLIST rom md5 CDATA #IMPLIED>
|
||||
<!ATTLIST rom sha1 CDATA #IMPLIED>
|
||||
<!ATTLIST rom sha256 CDATA #IMPLIED>
|
||||
<!ATTLIST rom sha384 CDATA #IMPLIED>
|
||||
<!ATTLIST rom sha512 CDATA #IMPLIED>
|
||||
<!ATTLIST rom spamsum CDATA #IMPLIED>
|
||||
<!ATTLIST rom xxh3_64 CDATA #IMPLIED>
|
||||
<!ATTLIST rom xxh3_128 CDATA #IMPLIED>
|
||||
<!ATTLIST rom merge CDATA #IMPLIED>
|
||||
<!ATTLIST rom status (baddump|nodump|good|verified) ""good"">
|
||||
<!ATTLIST rom serial CDATA #IMPLIED>
|
||||
<!ATTLIST rom header CDATA #IMPLIED>
|
||||
<!ATTLIST rom date CDATA #IMPLIED>
|
||||
<!ATTLIST rom inverted CDATA #IMPLIED>
|
||||
<!ATTLIST rom mia CDATA #IMPLIED>
|
||||
<!ELEMENT disk EMPTY>
|
||||
<!ATTLIST disk name CDATA #REQUIRED>
|
||||
<!ATTLIST disk md5 CDATA #IMPLIED>
|
||||
<!ATTLIST disk sha1 CDATA #IMPLIED>
|
||||
<!ATTLIST disk merge CDATA #IMPLIED>
|
||||
<!ATTLIST disk status (baddump|nodump|good|verified) ""good"">
|
||||
<!ELEMENT media EMPTY>
|
||||
<!ATTLIST media name CDATA #REQUIRED>
|
||||
<!ATTLIST media md5 CDATA #IMPLIED>
|
||||
<!ATTLIST media sha1 CDATA #IMPLIED>
|
||||
<!ATTLIST media sha256 CDATA #IMPLIED>
|
||||
<!ATTLIST media spamsum CDATA #IMPLIED>
|
||||
<!ELEMENT sample EMPTY>
|
||||
<!ATTLIST sample name CDATA #REQUIRED>
|
||||
<!ELEMENT archive EMPTY>
|
||||
<!ATTLIST archive name CDATA #REQUIRED>
|
||||
<!ELEMENT machine (comment*, description, year?, manufacturer?, publisher?, category?, trurip?, release*, biosset*, rom*, disk*, media*, sample*, archive*)>
|
||||
<!ATTLIST game name CDATA #REQUIRED>
|
||||
<!ATTLIST game sourcefile CDATA #IMPLIED>
|
||||
<!ATTLIST game isbios (yes|no) ""no"">
|
||||
<!ATTLIST game cloneof CDATA #IMPLIED>
|
||||
<!ATTLIST game romof CDATA #IMPLIED>
|
||||
<!ATTLIST game sampleof CDATA #IMPLIED>
|
||||
<!ATTLIST game board CDATA #IMPLIED>
|
||||
<!ATTLIST game rebuildto CDATA #IMPLIED>
|
||||
<!ATTLIST game id CDATA #IMPLIED>
|
||||
<!ATTLIST game cloneofid CDATA #IMPLIED>
|
||||
<!ATTLIST game runnable (no|partial|yes) ""no"" #IMPLIED>
|
||||
<!ELEMENT dir (game*, machine*)>
|
||||
<!ATTLIST dir name CDATA #REQUIRED>
|
||||
";
|
||||
|
||||
/// <summary>
|
||||
/// XSD for No-Intro Logiqx-derived DATs
|
||||
/// </summary>
|
||||
internal const string NoIntroXSD = @"<?xml version=""1.0"" encoding=""UTF-8""?>
|
||||
<xs:schema attributeFormDefault=""unqualified"" elementFormDefault=""qualified"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
|
||||
<xs:element name=""datafile"">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name=""header"">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name=""id"" type=""xs:int""/>
|
||||
<xs:element name=""name"" type=""xs:string""/>
|
||||
<xs:element name=""description"" type=""xs:string""/>
|
||||
<xs:element name=""version"" type=""xs:string""/>
|
||||
<xs:element name=""author"" type=""xs:string""/>
|
||||
<xs:element name=""homepage"" type=""xs:string""/>
|
||||
<xs:element name=""url"" type=""xs:string""/>
|
||||
<xs:element name=""clrmamepro"">
|
||||
<xs:complexType>
|
||||
<xs:attribute name=""forcenodump"" default=""obsolete"" use=""optional"">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base=""xs:token"">
|
||||
<xs:enumeration value=""obsolete""/>
|
||||
<xs:enumeration value=""required""/>
|
||||
<xs:enumeration value=""ignore""/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name=""header"" type=""xs:string"" use=""optional""/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name=""romcenter"" minOccurs=""0"">
|
||||
<xs:complexType>
|
||||
<xs:attribute name=""plugin"" type=""xs:string"" use=""optional""/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element maxOccurs=""unbounded"" name=""game"">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name=""description"" type=""xs:string""/>
|
||||
<xs:element name=""rom"">
|
||||
<xs:complexType>
|
||||
<xs:attribute name=""name"" type=""xs:string"" use=""required""/>
|
||||
<xs:attribute name=""size"" type=""xs:unsignedInt"" use=""required""/>
|
||||
<xs:attribute name=""crc"" type=""xs:string"" use=""required""/>
|
||||
<xs:attribute name=""md5"" type=""xs:string"" use=""required""/>
|
||||
<xs:attribute name=""sha1"" type=""xs:string"" use=""required""/>
|
||||
<xs:attribute name=""sha256"" type=""xs:string"" use=""optional""/>
|
||||
<xs:attribute name=""status"" type=""xs:string"" use=""optional""/>
|
||||
<xs:attribute name=""serial"" type=""xs:string"" use=""optional""/>
|
||||
<xs:attribute name=""header"" type=""xs:string"" use=""optional""/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name=""name"" type=""xs:string"" use=""required""/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Archive,
|
||||
ItemType.BiosSet,
|
||||
ItemType.DeviceRef,
|
||||
ItemType.Disk,
|
||||
ItemType.Driver,
|
||||
ItemType.Media,
|
||||
ItemType.Release,
|
||||
ItemType.Rom,
|
||||
ItemType.Sample,
|
||||
ItemType.SoftwareList,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if game should be used instead of machine
|
||||
/// </summary>
|
||||
private readonly bool _useGame;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
/// <param name="useGame">True if the output uses "game", false if the output uses "machine"</param>
|
||||
public Logiqx(DatFile? datFile, bool useGame) : base(datFile)
|
||||
{
|
||||
_useGame = useGame;
|
||||
if (useGame)
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.LogiqxDeprecated);
|
||||
else
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.Logiqx);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Release release:
|
||||
if (string.IsNullOrEmpty(release.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Release.NameKey);
|
||||
if (string.IsNullOrEmpty(release.GetStringFieldValue(Data.Models.Metadata.Release.RegionKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Release.RegionKey);
|
||||
break;
|
||||
|
||||
case BiosSet biosset:
|
||||
if (string.IsNullOrEmpty(biosset.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.BiosSet.NameKey);
|
||||
if (string.IsNullOrEmpty(biosset.GetStringFieldValue(Data.Models.Metadata.BiosSet.DescriptionKey)))
|
||||
missingFields.Add(Data.Models.Metadata.BiosSet.DescriptionKey);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue("MD2"))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue("MD4"))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Disk disk:
|
||||
if (string.IsNullOrEmpty(disk.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Disk.NameKey);
|
||||
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key))
|
||||
&& string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Disk.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Media media:
|
||||
if (string.IsNullOrEmpty(media.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Media.NameKey);
|
||||
if (string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Media.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DeviceRef deviceref:
|
||||
if (string.IsNullOrEmpty(deviceref.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.DeviceRef.NameKey);
|
||||
break;
|
||||
|
||||
case Sample sample:
|
||||
if (string.IsNullOrEmpty(sample.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Sample.NameKey);
|
||||
break;
|
||||
|
||||
case Archive archive:
|
||||
if (string.IsNullOrEmpty(archive.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Archive.NameKey);
|
||||
break;
|
||||
|
||||
case Driver driver:
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.StatusKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.StatusKey);
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.EmulationKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.EmulationKey);
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.CocktailKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.CocktailKey);
|
||||
if (driver.GetStringFieldValue(Data.Models.Metadata.Driver.SaveStateKey).AsSupportStatus() == SupportStatus.NULL)
|
||||
missingFields.Add(Data.Models.Metadata.Driver.SaveStateKey);
|
||||
break;
|
||||
|
||||
case DatItems.Formats.SoftwareList softwarelist:
|
||||
if (string.IsNullOrEmpty(softwarelist.GetStringFieldValue(Data.Models.Metadata.SoftwareList.TagKey)))
|
||||
missingFields.Add(Data.Models.Metadata.SoftwareList.TagKey);
|
||||
if (string.IsNullOrEmpty(softwarelist.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.SoftwareList.NameKey);
|
||||
if (softwarelist.GetStringFieldValue(Data.Models.Metadata.SoftwareList.StatusKey).AsSoftwareListStatus() == SoftwareListStatus.None)
|
||||
missingFields.Add(Data.Models.Metadata.SoftwareList.StatusKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
|
||||
// Serialize the input file
|
||||
var metadata = ConvertToMetadata(ignoreblanks);
|
||||
var datafile = new Serialization.CrossModel.Logiqx().Deserialize(metadata, _useGame);
|
||||
|
||||
// TODO: Reenable doctype writing
|
||||
// Only write the doctype if we don't have No-Intro data
|
||||
bool success;
|
||||
if (string.IsNullOrEmpty(Header.GetStringFieldValue(Data.Models.Metadata.Header.IdKey)))
|
||||
success = new Serialization.Writers.Logiqx().Serialize(datafile, outfile, null, null, null, null);
|
||||
else
|
||||
success = new Serialization.Writers.Logiqx().Serialize(datafile, outfile, null, null, null, null);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be written! See the log for more details.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
214
SabreTools.Metadata.DatFiles/Formats/Missfile.cs
Normal file
214
SabreTools.Metadata.DatFiles/Formats/Missfile.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Missfile
|
||||
/// </summary>
|
||||
public sealed class Missfile : DatFile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> Enum.GetValues(typeof(ItemType)) as ItemType[] ?? [];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public Missfile(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.MissFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>There is no consistent way to parse a missfile</remarks>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
// TODO: Check required fields
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
FileStream fs = File.Create(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs is null)
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamWriter sw = new(fs, new UTF8Encoding(false));
|
||||
|
||||
// Write out each of the machines and roms
|
||||
string? lastgame = null;
|
||||
|
||||
// Use a sorted list of games to output
|
||||
foreach (string key in Items.SortedKeys)
|
||||
{
|
||||
List<DatItem> datItems = GetItemsForBucket(key, filter: true);
|
||||
|
||||
// If this machine doesn't contain any writable items, skip
|
||||
if (!ContainsWritable(datItems))
|
||||
continue;
|
||||
|
||||
// Resolve the names in the block
|
||||
datItems = ResolveNames(datItems);
|
||||
|
||||
for (int index = 0; index < datItems.Count; index++)
|
||||
{
|
||||
DatItem datItem = datItems[index];
|
||||
|
||||
// Check for a "null" item
|
||||
datItem = ProcessNullifiedItem(datItem);
|
||||
|
||||
// Write out the item if we're using machine names or we're not ignoring
|
||||
if (!Modifiers.UseRomName || !ShouldIgnore(datItem, ignoreblanks))
|
||||
WriteDatItem(sw, datItem, lastgame);
|
||||
|
||||
// Set the new data to compare against
|
||||
lastgame = datItem.GetMachine()!.GetName();
|
||||
}
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
sw.Dispose();
|
||||
fs.Dispose();
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFileDB(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
FileStream fs = File.Create(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs is null)
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamWriter sw = new(fs, new UTF8Encoding(false));
|
||||
|
||||
// Write out each of the machines and roms
|
||||
string? lastgame = null;
|
||||
|
||||
// Use a sorted list of games to output
|
||||
foreach (string key in ItemsDB.SortedKeys)
|
||||
{
|
||||
// If this machine doesn't contain any writable items, skip
|
||||
var itemsDict = GetItemsForBucketDB(key, filter: true);
|
||||
if (itemsDict is null || !ContainsWritable([.. itemsDict.Values]))
|
||||
continue;
|
||||
|
||||
// Resolve the names in the block
|
||||
var items = ResolveNamesDB([.. itemsDict]);
|
||||
|
||||
foreach (var kvp in items)
|
||||
{
|
||||
// Check for a "null" item
|
||||
var datItem = new KeyValuePair<long, DatItem>(kvp.Key, ProcessNullifiedItem(kvp.Value));
|
||||
|
||||
// Get the machine for the item
|
||||
var machine = GetMachineForItemDB(datItem.Key);
|
||||
|
||||
// Write out the item if we're using machine names or we're not ignoring
|
||||
if (!Modifiers.UseRomName || !ShouldIgnore(datItem.Value, ignoreblanks))
|
||||
WriteDatItemDB(sw, datItem, lastgame);
|
||||
|
||||
// Set the new data to compare against
|
||||
lastgame = machine.Value!.GetName();
|
||||
}
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
sw.Dispose();
|
||||
fs.Dispose();
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DatItem using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
/// <param name="lastgame">The name of the last game to be output</param>
|
||||
private void WriteDatItem(StreamWriter sw, DatItem datItem, string? lastgame)
|
||||
{
|
||||
var machine = datItem.GetMachine();
|
||||
WriteDatItemImpl(sw, datItem, machine!, lastgame);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DatItem using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
/// <param name="lastgame">The name of the last game to be output</param>
|
||||
private void WriteDatItemDB(StreamWriter sw, KeyValuePair<long, DatItem> datItem, string? lastgame)
|
||||
{
|
||||
var machine = GetMachineForItemDB(datItem.Key).Value;
|
||||
WriteDatItemImpl(sw, datItem.Value, machine!, lastgame);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DatItem using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
/// <param name="machine">Machine object representing the set the item is in</param>
|
||||
/// <param name="lastgame">The name of the last game to be output</param>
|
||||
private void WriteDatItemImpl(StreamWriter sw, DatItem datItem, Machine machine, string? lastgame)
|
||||
{
|
||||
// Process the item name
|
||||
ProcessItemName(datItem, machine, forceRemoveQuotes: false, forceRomName: false);
|
||||
|
||||
// Romba mode automatically uses item name
|
||||
if (Modifiers.OutputDepot?.IsActive == true || Modifiers.UseRomName)
|
||||
sw.Write($"{datItem.GetName() ?? string.Empty}\n");
|
||||
else if (!Modifiers.UseRomName && machine!.GetName() != lastgame)
|
||||
sw.Write($"{machine!.GetName() ?? string.Empty}\n");
|
||||
|
||||
sw.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
47
SabreTools.Metadata.DatFiles/Formats/OfflineList.cs
Normal file
47
SabreTools.Metadata.DatFiles/Formats/OfflineList.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an OfflineList XML DAT
|
||||
/// </summary>
|
||||
public sealed class OfflineList : SerializableDatFile<Data.Models.OfflineList.Dat, Serialization.Readers.OfflineList, Serialization.Writers.OfflineList, Serialization.CrossModel.OfflineList>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public OfflineList(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.OfflineList);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.CRCKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
88
SabreTools.Metadata.DatFiles/Formats/OpenMSX.cs
Normal file
88
SabreTools.Metadata.DatFiles/Formats/OpenMSX.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an openMSX softawre list XML DAT
|
||||
/// </summary>
|
||||
public sealed class OpenMSX : SerializableDatFile<Data.Models.OpenMSX.SoftwareDb, Serialization.Readers.OpenMSX, Serialization.Writers.OpenMSX, Serialization.CrossModel.OpenMSX>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// DTD for original openMSX DATs
|
||||
/// </summary>
|
||||
internal const string OpenMSXDTD = @"<!ELEMENT softwaredb (person*)>
|
||||
<!ELEMENT software (title, genmsxid?, system, company,year,country,dump)>
|
||||
<!ELEMENT title (#PCDATA)>
|
||||
<!ELEMENT genmsxid (#PCDATA)>
|
||||
<!ELEMENT system (#PCDATA)>
|
||||
<!ELEMENT company (#PCDATA)>
|
||||
<!ELEMENT year (#PCDATA)>
|
||||
<!ELEMENT country (#PCDATA)>
|
||||
<!ELEMENT dump (#PCDATA)>
|
||||
";
|
||||
|
||||
internal const string OpenMSXCredits = @"<!-- Credits -->
|
||||
<![CDATA[
|
||||
The softwaredb.xml file contains information about rom mapper types
|
||||
|
||||
- Copyright 2003 Nicolas Beyaert (Initial Database)
|
||||
- Copyright 2004-2013 BlueMSX Team
|
||||
- Copyright 2005-2023 openMSX Team
|
||||
- Generation MSXIDs by www.generation-msx.nl
|
||||
|
||||
- Thanks go out to:
|
||||
- - Generation MSX/Sylvester for the incredible source of information
|
||||
- p_gimeno and diedel for their help adding and valdiating ROM additions
|
||||
- GDX for additional ROM info and validations and corrections
|
||||
|
||||
|
||||
]]>";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public OpenMSX(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.OpenMSX);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
SabreTools.Metadata.DatFiles/Formats/RomCenter.cs
Normal file
51
SabreTools.Metadata.DatFiles/Formats/RomCenter.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a RomCenter INI file
|
||||
/// </summary>
|
||||
public sealed class RomCenter : SerializableDatFile<Data.Models.RomCenter.MetadataFile, Serialization.Readers.RomCenter, Serialization.Writers.RomCenter, Serialization.CrossModel.RomCenter>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public RomCenter(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.RomCenter);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Rom rom:
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.CRCKey);
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
719
SabreTools.Metadata.DatFiles/Formats/SabreJSON.cs
Normal file
719
SabreTools.Metadata.DatFiles/Formats/SabreJSON.cs
Normal file
@@ -0,0 +1,719 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of a reference SabreDAT JSON
|
||||
/// </summary>
|
||||
/// TODO: Transform this into direct serialization and deserialization of the Metadata type
|
||||
public sealed class SabreJSON : DatFile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> Enum.GetValues(typeof(ItemType)) as ItemType[] ?? [];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SabreJSON(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.SabreJSON);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
var fs = System.IO.File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
var sr = new StreamReader(fs, new UTF8Encoding(false));
|
||||
var jtr = new JsonTextReader(sr);
|
||||
var source = new Source(indexId, filename);
|
||||
// long sourceIndex = AddSourceDB(source);
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (jtr is null)
|
||||
return;
|
||||
|
||||
// Otherwise, read the file to the end
|
||||
try
|
||||
{
|
||||
jtr.Read();
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
// Skip everything not a property name
|
||||
if (jtr.TokenType != JsonToken.PropertyName)
|
||||
{
|
||||
jtr.Read();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (jtr.Value)
|
||||
{
|
||||
// Header value
|
||||
case "header":
|
||||
ReadHeader(jtr);
|
||||
jtr.Read();
|
||||
break;
|
||||
|
||||
// Machine array
|
||||
case "machines":
|
||||
ReadMachines(jtr, statsOnly, source, sourceIndex: 0, filterRunner);
|
||||
jtr.Read();
|
||||
break;
|
||||
|
||||
default:
|
||||
jtr.Read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Warning($"Exception found while parsing '{filename}': {ex}");
|
||||
}
|
||||
|
||||
jtr.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read header information
|
||||
/// </summary>
|
||||
/// <param name="jtr">JsonTextReader to use to parse the header</param>
|
||||
private void ReadHeader(JsonTextReader jtr)
|
||||
{
|
||||
// If the reader is invalid, skip
|
||||
if (jtr is null)
|
||||
return;
|
||||
|
||||
// Read in the header and apply any new fields
|
||||
jtr.Read();
|
||||
JsonSerializer js = new();
|
||||
DatHeader? header = js.Deserialize<DatHeader>(jtr);
|
||||
SetHeader(header);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read machine array information
|
||||
/// </summary>
|
||||
/// <param name="jtr">JsonTextReader to use to parse the machine</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="source">Source representing the DAT</param>
|
||||
/// <param name="sourceIndex">Index of the Source representing the DAT</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ReadMachines(JsonTextReader jtr, bool statsOnly, Source source, long sourceIndex, FilterRunner? filterRunner)
|
||||
{
|
||||
// If the reader is invalid, skip
|
||||
if (jtr is null)
|
||||
return;
|
||||
|
||||
// Read in the machine array
|
||||
jtr.Read();
|
||||
var js = new JsonSerializer();
|
||||
JArray machineArray = js.Deserialize<JArray>(jtr) ?? [];
|
||||
|
||||
// Loop through each machine object and process
|
||||
foreach (JObject machineObj in machineArray.Cast<JObject>())
|
||||
{
|
||||
ReadMachine(machineObj, statsOnly, source, sourceIndex, filterRunner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read machine object information
|
||||
/// </summary>
|
||||
/// <param name="machineObj">JObject representing a single machine</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="source">Source representing the DAT</param>
|
||||
/// <param name="sourceIndex">Index of the Source representing the DAT</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ReadMachine(JObject machineObj, bool statsOnly, Source source, long sourceIndex, FilterRunner? filterRunner)
|
||||
{
|
||||
// If object is invalid, skip it
|
||||
if (machineObj is null)
|
||||
return;
|
||||
|
||||
// Prepare internal variables
|
||||
Machine? machine = null;
|
||||
|
||||
// Read the machine info, if possible
|
||||
if (machineObj.ContainsKey("machine"))
|
||||
machine = machineObj["machine"]?.ToObject<Machine>();
|
||||
|
||||
// If the machine doesn't pass the filter
|
||||
if (machine is not null && filterRunner is not null && !machine.PassesFilter(filterRunner))
|
||||
return;
|
||||
|
||||
// Add the machine to the dictionary
|
||||
// long machineIndex = -1;
|
||||
// if (machine is not null)
|
||||
// machineIndex = AddMachineDB(machine);
|
||||
|
||||
// Read items, if possible
|
||||
if (machineObj.ContainsKey("items"))
|
||||
{
|
||||
ReadItems(machineObj["items"] as JArray,
|
||||
statsOnly,
|
||||
source,
|
||||
sourceIndex,
|
||||
machine,
|
||||
machineIndex: 0,
|
||||
filterRunner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read item array information
|
||||
/// </summary>
|
||||
/// <param name="itemsArr">JArray representing the items list</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="source">Source representing the DAT</param>
|
||||
/// <param name="sourceIndex">Index of the Source representing the DAT</param>
|
||||
/// <param name="machine">Machine information to add to the parsed items</param>
|
||||
/// <param name="machineIndex">Index of the Machine to add to the parsed items</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ReadItems(
|
||||
JArray? itemsArr,
|
||||
bool statsOnly,
|
||||
|
||||
// Standard Dat parsing
|
||||
Source source,
|
||||
long sourceIndex,
|
||||
|
||||
// Miscellaneous
|
||||
Machine? machine,
|
||||
long machineIndex,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If the array is invalid, skip
|
||||
if (itemsArr is null)
|
||||
return;
|
||||
|
||||
// Loop through each datitem object and process
|
||||
foreach (JObject itemObj in itemsArr.Cast<JObject>())
|
||||
{
|
||||
ReadItem(itemObj, statsOnly, source, sourceIndex, machine, machineIndex, filterRunner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read item information
|
||||
/// </summary>
|
||||
/// <param name="itemObj">JObject representing a single datitem</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="source">Source representing the DAT</param>
|
||||
/// <param name="sourceIndex">Index of the Source representing the DAT</param>
|
||||
/// <param name="machine">Machine information to add to the parsed items</param>
|
||||
/// <param name="machineIndex">Index of the Machine to add to the parsed items</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ReadItem(
|
||||
JObject itemObj,
|
||||
bool statsOnly,
|
||||
|
||||
// Standard Dat parsing
|
||||
Source source,
|
||||
long sourceIndex,
|
||||
|
||||
// Miscellaneous
|
||||
Machine? machine,
|
||||
long machineIndex,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If we have an empty item, skip it
|
||||
if (itemObj is null)
|
||||
return;
|
||||
|
||||
// Prepare internal variables
|
||||
DatItem? datItem = null;
|
||||
|
||||
// Read the datitem info, if possible
|
||||
if (itemObj.ContainsKey("datitem"))
|
||||
{
|
||||
JToken? datItemObj = itemObj["datitem"];
|
||||
if (datItemObj is null)
|
||||
return;
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItemObj.Value<string>("type").AsItemType())
|
||||
{
|
||||
case ItemType.Adjuster:
|
||||
datItem = datItemObj.ToObject<Adjuster>();
|
||||
break;
|
||||
case ItemType.Analog:
|
||||
datItem = datItemObj.ToObject<Analog>();
|
||||
break;
|
||||
case ItemType.Archive:
|
||||
datItem = datItemObj.ToObject<Archive>();
|
||||
break;
|
||||
case ItemType.BiosSet:
|
||||
datItem = datItemObj.ToObject<BiosSet>();
|
||||
break;
|
||||
case ItemType.Blank:
|
||||
datItem = datItemObj.ToObject<Blank>();
|
||||
break;
|
||||
case ItemType.Chip:
|
||||
datItem = datItemObj.ToObject<Chip>();
|
||||
break;
|
||||
case ItemType.Condition:
|
||||
datItem = datItemObj.ToObject<Condition>();
|
||||
break;
|
||||
case ItemType.Configuration:
|
||||
datItem = datItemObj.ToObject<Configuration>();
|
||||
break;
|
||||
case ItemType.ConfLocation:
|
||||
datItem = datItemObj.ToObject<ConfLocation>();
|
||||
break;
|
||||
case ItemType.ConfSetting:
|
||||
datItem = datItemObj.ToObject<ConfSetting>();
|
||||
break;
|
||||
case ItemType.Control:
|
||||
datItem = datItemObj.ToObject<Control>();
|
||||
break;
|
||||
case ItemType.DataArea:
|
||||
datItem = datItemObj.ToObject<DataArea>();
|
||||
break;
|
||||
case ItemType.Device:
|
||||
datItem = datItemObj.ToObject<Device>();
|
||||
break;
|
||||
case ItemType.DeviceRef:
|
||||
datItem = datItemObj.ToObject<DeviceRef>();
|
||||
break;
|
||||
case ItemType.DipLocation:
|
||||
datItem = datItemObj.ToObject<DipLocation>();
|
||||
break;
|
||||
case ItemType.DipValue:
|
||||
datItem = datItemObj.ToObject<DipValue>();
|
||||
break;
|
||||
case ItemType.DipSwitch:
|
||||
datItem = datItemObj.ToObject<DipSwitch>();
|
||||
break;
|
||||
case ItemType.Disk:
|
||||
datItem = datItemObj.ToObject<Disk>();
|
||||
break;
|
||||
case ItemType.DiskArea:
|
||||
datItem = datItemObj.ToObject<DiskArea>();
|
||||
break;
|
||||
case ItemType.Display:
|
||||
datItem = datItemObj.ToObject<Display>();
|
||||
break;
|
||||
case ItemType.Driver:
|
||||
datItem = datItemObj.ToObject<Driver>();
|
||||
break;
|
||||
case ItemType.Extension:
|
||||
datItem = datItemObj.ToObject<Extension>();
|
||||
break;
|
||||
case ItemType.Feature:
|
||||
datItem = datItemObj.ToObject<Feature>();
|
||||
break;
|
||||
case ItemType.Info:
|
||||
datItem = datItemObj.ToObject<Info>();
|
||||
break;
|
||||
case ItemType.Input:
|
||||
datItem = datItemObj.ToObject<Input>();
|
||||
break;
|
||||
case ItemType.Instance:
|
||||
datItem = datItemObj.ToObject<Instance>();
|
||||
break;
|
||||
case ItemType.Media:
|
||||
datItem = datItemObj.ToObject<Media>();
|
||||
break;
|
||||
case ItemType.Part:
|
||||
datItem = datItemObj.ToObject<Part>();
|
||||
break;
|
||||
case ItemType.PartFeature:
|
||||
datItem = datItemObj.ToObject<PartFeature>();
|
||||
break;
|
||||
case ItemType.Port:
|
||||
datItem = datItemObj.ToObject<Port>();
|
||||
break;
|
||||
case ItemType.RamOption:
|
||||
datItem = datItemObj.ToObject<RamOption>();
|
||||
break;
|
||||
case ItemType.Release:
|
||||
datItem = datItemObj.ToObject<Release>();
|
||||
break;
|
||||
case ItemType.ReleaseDetails:
|
||||
datItem = datItemObj.ToObject<ReleaseDetails>();
|
||||
break;
|
||||
case ItemType.Rom:
|
||||
datItem = datItemObj.ToObject<Rom>();
|
||||
break;
|
||||
case ItemType.Sample:
|
||||
datItem = datItemObj.ToObject<Sample>();
|
||||
break;
|
||||
case ItemType.Serials:
|
||||
datItem = datItemObj.ToObject<Serials>();
|
||||
break;
|
||||
case ItemType.SharedFeat:
|
||||
datItem = datItemObj.ToObject<SharedFeat>();
|
||||
break;
|
||||
case ItemType.Slot:
|
||||
datItem = datItemObj.ToObject<Slot>();
|
||||
break;
|
||||
case ItemType.SlotOption:
|
||||
datItem = datItemObj.ToObject<SlotOption>();
|
||||
break;
|
||||
case ItemType.SoftwareList:
|
||||
datItem = datItemObj.ToObject<DatItems.Formats.SoftwareList>();
|
||||
break;
|
||||
case ItemType.Sound:
|
||||
datItem = datItemObj.ToObject<Sound>();
|
||||
break;
|
||||
case ItemType.SourceDetails:
|
||||
datItem = datItemObj.ToObject<SourceDetails>();
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
// If we got a valid datitem, copy machine info and add
|
||||
if (datItem is not null)
|
||||
{
|
||||
// If the item doesn't pass the filter
|
||||
if (filterRunner is not null && !datItem.PassesFilter(filterRunner))
|
||||
return;
|
||||
|
||||
datItem.CopyMachineInformation(machine);
|
||||
datItem.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
AddItem(datItem, statsOnly);
|
||||
AddItemDB(datItem, machineIndex, sourceIndex, statsOnly);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
FileStream fs = System.IO.File.Create(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs is null)
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamWriter sw = new(fs, new UTF8Encoding(false));
|
||||
JsonTextWriter jtw = new(sw)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(jtw);
|
||||
|
||||
// Write out each of the machines and roms
|
||||
string? lastgame = null;
|
||||
|
||||
// Use a sorted list of games to output
|
||||
foreach (string key in Items.SortedKeys)
|
||||
{
|
||||
List<DatItem> datItems = GetItemsForBucket(key, filter: true);
|
||||
|
||||
// If this machine doesn't contain any writable items, skip
|
||||
if (!ContainsWritable(datItems))
|
||||
continue;
|
||||
|
||||
// Resolve the names in the block
|
||||
datItems = ResolveNames(datItems);
|
||||
|
||||
for (int index = 0; index < datItems.Count; index++)
|
||||
{
|
||||
DatItem datItem = datItems[index];
|
||||
|
||||
// If we have a different game and we're not at the start of the list, output the end of last item
|
||||
if (lastgame is not null && !string.Equals(lastgame, datItem.GetMachine()!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteEndGame(jtw);
|
||||
|
||||
// If we have a new game, output the beginning of the new item
|
||||
if (lastgame is null || !string.Equals(lastgame, datItem.GetMachine()!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteStartGame(jtw, datItem);
|
||||
|
||||
// Check for a "null" item
|
||||
datItem = ProcessNullifiedItem(datItem);
|
||||
|
||||
// Write out the item if we're not ignoring
|
||||
if (!ShouldIgnore(datItem, ignoreblanks))
|
||||
WriteDatItem(jtw, datItem);
|
||||
|
||||
// Set the new data to compare against
|
||||
lastgame = datItem.GetMachine()!.GetName();
|
||||
}
|
||||
}
|
||||
|
||||
// Write the file footer out
|
||||
WriteFooter(jtw);
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
jtw.Close();
|
||||
fs.Dispose();
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFileDB(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
FileStream fs = System.IO.File.Create(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs is null)
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamWriter sw = new(fs, new UTF8Encoding(false));
|
||||
JsonTextWriter jtw = new(sw)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(jtw);
|
||||
|
||||
// Write out each of the machines and roms
|
||||
string? lastgame = null;
|
||||
|
||||
// Use a sorted list of games to output
|
||||
foreach (string key in ItemsDB.SortedKeys)
|
||||
{
|
||||
// If this machine doesn't contain any writable items, skip
|
||||
var itemsDict = GetItemsForBucketDB(key, filter: true);
|
||||
if (itemsDict is null || !ContainsWritable([.. itemsDict.Values]))
|
||||
continue;
|
||||
|
||||
// Resolve the names in the block
|
||||
var items = ResolveNamesDB([.. itemsDict]);
|
||||
|
||||
foreach (var kvp in items)
|
||||
{
|
||||
// Get the machine for the item
|
||||
var machine = GetMachineForItemDB(kvp.Key);
|
||||
|
||||
// If we have a different game and we're not at the start of the list, output the end of last item
|
||||
if (lastgame is not null && !string.Equals(lastgame, machine.Value!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteEndGame(jtw);
|
||||
|
||||
// If we have a new game, output the beginning of the new item
|
||||
if (lastgame is null || !string.Equals(lastgame, machine.Value!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteStartGame(jtw, kvp.Value);
|
||||
|
||||
// Check for a "null" item
|
||||
var datItem = new KeyValuePair<long, DatItem>(kvp.Key, ProcessNullifiedItem(kvp.Value));
|
||||
|
||||
// Write out the item if we're not ignoring
|
||||
if (!ShouldIgnore(datItem.Value, ignoreblanks))
|
||||
WriteDatItemDB(jtw, datItem);
|
||||
|
||||
// Set the new data to compare against
|
||||
lastgame = machine.Value!.GetName();
|
||||
}
|
||||
}
|
||||
|
||||
// Write the file footer out
|
||||
WriteFooter(jtw);
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
jtw.Close();
|
||||
fs.Dispose();
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DAT header using the supplied JsonTextWriter
|
||||
/// </summary>
|
||||
/// <param name="jtw">JsonTextWriter to output to</param>
|
||||
private void WriteHeader(JsonTextWriter jtw)
|
||||
{
|
||||
jtw.WriteStartObject();
|
||||
|
||||
// Write the DatHeader
|
||||
jtw.WritePropertyName("header");
|
||||
JsonSerializer js = new() { Formatting = Formatting.Indented };
|
||||
js.Serialize(jtw, Header);
|
||||
|
||||
jtw.WritePropertyName("machines");
|
||||
jtw.WriteStartArray();
|
||||
|
||||
jtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out Game start using the supplied JsonTextWriter
|
||||
/// </summary>
|
||||
/// <param name="jtw">JsonTextWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
private static void WriteStartGame(JsonTextWriter jtw, DatItem datItem)
|
||||
{
|
||||
// No game should start with a path separator
|
||||
if (!string.IsNullOrEmpty(datItem.GetMachine()!.GetName()))
|
||||
datItem.GetMachine()!.SetName(datItem.GetMachine()!.GetName()!.TrimStart(Path.DirectorySeparatorChar));
|
||||
|
||||
// Build the state
|
||||
jtw.WriteStartObject();
|
||||
|
||||
// Write the Machine
|
||||
jtw.WritePropertyName("machine");
|
||||
JsonSerializer js = new() { Formatting = Formatting.Indented };
|
||||
js.Serialize(jtw, datItem.GetMachine()!);
|
||||
|
||||
jtw.WritePropertyName("items");
|
||||
jtw.WriteStartArray();
|
||||
|
||||
jtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out Game end using the supplied JsonTextWriter
|
||||
/// </summary>
|
||||
/// <param name="jtw">JsonTextWriter to output to</param>
|
||||
private static void WriteEndGame(JsonTextWriter jtw)
|
||||
{
|
||||
// End items
|
||||
jtw.WriteEndArray();
|
||||
|
||||
// End machine
|
||||
jtw.WriteEndObject();
|
||||
|
||||
jtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DatItem using the supplied JsonTextWriter
|
||||
/// </summary>
|
||||
/// <param name="jtw">JsonTextWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
private void WriteDatItem(JsonTextWriter jtw, DatItem datItem)
|
||||
{
|
||||
// Get the machine for the item
|
||||
var machine = datItem.GetMachine();
|
||||
|
||||
// Pre-process the item name
|
||||
ProcessItemName(datItem, machine, forceRemoveQuotes: true, forceRomName: false);
|
||||
|
||||
// Build the state
|
||||
jtw.WriteStartObject();
|
||||
|
||||
// Write the DatItem
|
||||
jtw.WritePropertyName("datitem");
|
||||
JsonSerializer js = new() { ContractResolver = new BaseFirstContractResolver(), Formatting = Formatting.Indented };
|
||||
js.Serialize(jtw, datItem);
|
||||
|
||||
// End item
|
||||
jtw.WriteEndObject();
|
||||
|
||||
jtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DatItem using the supplied JsonTextWriter
|
||||
/// </summary>
|
||||
/// <param name="jtw">JsonTextWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
private void WriteDatItemDB(JsonTextWriter jtw, KeyValuePair<long, DatItem> datItem)
|
||||
{
|
||||
// Get the machine for the item
|
||||
var machine = GetMachineForItemDB(datItem.Key);
|
||||
|
||||
// Pre-process the item name
|
||||
ProcessItemName(datItem.Value, machine.Value, forceRemoveQuotes: true, forceRomName: false);
|
||||
|
||||
// Build the state
|
||||
jtw.WriteStartObject();
|
||||
|
||||
// Write the DatItem
|
||||
jtw.WritePropertyName("datitem");
|
||||
JsonSerializer js = new() { ContractResolver = new BaseFirstContractResolver(), Formatting = Formatting.Indented };
|
||||
js.Serialize(jtw, datItem);
|
||||
|
||||
// End item
|
||||
jtw.WriteEndObject();
|
||||
|
||||
jtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DAT footer using the supplied JsonTextWriter
|
||||
/// </summary>
|
||||
/// <param name="jtw">JsonTextWriter to output to</param>
|
||||
private static void WriteFooter(JsonTextWriter jtw)
|
||||
{
|
||||
// End items
|
||||
jtw.WriteEndArray();
|
||||
|
||||
// End machine
|
||||
jtw.WriteEndObject();
|
||||
|
||||
// End machines
|
||||
jtw.WriteEndArray();
|
||||
|
||||
// End file
|
||||
jtw.WriteEndObject();
|
||||
|
||||
jtw.Flush();
|
||||
}
|
||||
|
||||
// https://github.com/dotnet/runtime/issues/728
|
||||
private class BaseFirstContractResolver : DefaultContractResolver
|
||||
{
|
||||
public BaseFirstContractResolver()
|
||||
{
|
||||
NamingStrategy = new CamelCaseNamingStrategy();
|
||||
}
|
||||
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
return [.. base.CreateProperties(type, memberSerialization)
|
||||
.Where(p => p is not null)
|
||||
.OrderBy(p => BaseTypesAndSelf(p.DeclaringType).Count())];
|
||||
|
||||
static IEnumerable<Type?> BaseTypesAndSelf(Type? t)
|
||||
{
|
||||
while (t is not null)
|
||||
{
|
||||
yield return t;
|
||||
t = t.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
522
SabreTools.Metadata.DatFiles/Formats/SabreXML.cs
Normal file
522
SabreTools.Metadata.DatFiles/Formats/SabreXML.cs
Normal file
@@ -0,0 +1,522 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
|
||||
#pragma warning disable IDE0060 // Remove unused parameter
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of a SabreDAT XML
|
||||
/// </summary>
|
||||
/// TODO: Transform this into direct serialization and deserialization of the Metadata type
|
||||
public sealed class SabreXML : DatFile
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> Enum.GetValues(typeof(ItemType)) as ItemType[] ?? [];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SabreXML(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.SabreXML);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
// Prepare all internal variables
|
||||
XmlReader? xtr = XmlReader.Create(filename, new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
#if NET40_OR_GREATER
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
#endif
|
||||
IgnoreComments = true,
|
||||
IgnoreWhitespace = true,
|
||||
ValidationFlags = XmlSchemaValidationFlags.None,
|
||||
ValidationType = ValidationType.None,
|
||||
});
|
||||
var source = new Source(indexId, filename);
|
||||
long sourceIndex = AddSourceDB(source);
|
||||
|
||||
// If we got a null reader, just return
|
||||
if (xtr is null)
|
||||
return;
|
||||
|
||||
// Otherwise, read the file to the end
|
||||
try
|
||||
{
|
||||
xtr.MoveToContent();
|
||||
while (!xtr.EOF)
|
||||
{
|
||||
// We only want elements
|
||||
if (xtr.NodeType != XmlNodeType.Element)
|
||||
{
|
||||
xtr.Read();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (xtr.Name)
|
||||
{
|
||||
case "header":
|
||||
XmlSerializer xs = new(typeof(DatHeader));
|
||||
DatHeader? header = xs.Deserialize(xtr.ReadSubtree()) as DatHeader;
|
||||
SetHeader(header);
|
||||
xtr.Skip();
|
||||
break;
|
||||
|
||||
case "directory":
|
||||
ReadDirectory(xtr.ReadSubtree(), statsOnly, source, sourceIndex, filterRunner);
|
||||
|
||||
// Skip the directory node now that we've processed it
|
||||
xtr.Read();
|
||||
break;
|
||||
default:
|
||||
xtr.Read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Warning(ex, $"Exception found while parsing '{filename}'");
|
||||
|
||||
// For XML errors, just skip the affected node
|
||||
xtr?.Read();
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER
|
||||
xtr?.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read directory information
|
||||
/// </summary>
|
||||
/// <param name="xtr">XmlReader to use to parse the header</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="source">Source representing the DAT</param>
|
||||
/// <param name="sourceIndex">Index of the Source representing the DAT</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ReadDirectory(XmlReader xtr,
|
||||
bool statsOnly,
|
||||
Source source,
|
||||
long sourceIndex,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If the reader is invalid, skip
|
||||
if (xtr is null)
|
||||
return;
|
||||
|
||||
// Prepare internal variables
|
||||
Machine? machine = null;
|
||||
long machineIndex = -1;
|
||||
|
||||
// Otherwise, read the directory
|
||||
xtr.MoveToContent();
|
||||
while (!xtr.EOF)
|
||||
{
|
||||
// We only want elements
|
||||
if (xtr.NodeType != XmlNodeType.Element)
|
||||
{
|
||||
xtr.Read();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (xtr.Name)
|
||||
{
|
||||
case "machine":
|
||||
XmlSerializer xs = new(typeof(Machine));
|
||||
machine = xs?.Deserialize(xtr.ReadSubtree()) as Machine;
|
||||
|
||||
// If the machine doesn't pass the filter
|
||||
if (machine is not null && filterRunner is not null && !machine.PassesFilter(filterRunner))
|
||||
machine = null;
|
||||
|
||||
if (machine is not null)
|
||||
machineIndex = AddMachineDB(machine);
|
||||
|
||||
xtr.Skip();
|
||||
break;
|
||||
|
||||
case "files":
|
||||
ReadFiles(xtr.ReadSubtree(),
|
||||
machine,
|
||||
machineIndex,
|
||||
statsOnly,
|
||||
source,
|
||||
sourceIndex,
|
||||
filterRunner);
|
||||
|
||||
// Skip the directory node now that we've processed it
|
||||
xtr.Read();
|
||||
break;
|
||||
default:
|
||||
xtr.Read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read Files information
|
||||
/// </summary>
|
||||
/// <param name="xtr">XmlReader to use to parse the header</param>
|
||||
/// <param name="machine">Machine to copy information from</param>
|
||||
/// <param name="machineIndex">Index of the Machine to add to the parsed items</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <param name="source">Source representing the DAT</param>
|
||||
/// <param name="sourceIndex">Index of the Source representing the DAT</param>
|
||||
/// <param name="filterRunner">Optional FilterRunner to filter items on parse</param>
|
||||
private void ReadFiles(XmlReader xtr,
|
||||
Machine? machine,
|
||||
long machineIndex,
|
||||
bool statsOnly,
|
||||
Source source,
|
||||
long sourceIndex,
|
||||
FilterRunner? filterRunner)
|
||||
{
|
||||
// If the reader is invalid, skip
|
||||
if (xtr is null)
|
||||
return;
|
||||
|
||||
// Otherwise, read the items
|
||||
xtr.MoveToContent();
|
||||
while (!xtr.EOF)
|
||||
{
|
||||
// We only want elements
|
||||
if (xtr.NodeType != XmlNodeType.Element)
|
||||
{
|
||||
xtr.Read();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (xtr.Name)
|
||||
{
|
||||
case "datitem":
|
||||
XmlSerializer xs = new(typeof(DatItem));
|
||||
if (xs.Deserialize(xtr.ReadSubtree()) is DatItem item)
|
||||
{
|
||||
// If the item doesn't pass the filter
|
||||
if (filterRunner is not null && !item.PassesFilter(filterRunner))
|
||||
{
|
||||
xtr.Skip();
|
||||
break;
|
||||
}
|
||||
|
||||
item.CopyMachineInformation(machine);
|
||||
item.SetFieldValue<Source?>(DatItem.SourceKey, source);
|
||||
AddItem(item, statsOnly);
|
||||
// AddItemDB(item, machineIndex, sourceIndex, statsOnly);
|
||||
}
|
||||
|
||||
xtr.Skip();
|
||||
break;
|
||||
default:
|
||||
xtr.Read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
FileStream fs = File.Create(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs is null)
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1,
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
|
||||
// Write out each of the machines and roms
|
||||
string? lastgame = null;
|
||||
|
||||
// Use a sorted list of games to output
|
||||
foreach (string key in Items.SortedKeys)
|
||||
{
|
||||
List<DatItem> datItems = GetItemsForBucket(key, filter: true);
|
||||
|
||||
// If this machine doesn't contain any writable items, skip
|
||||
if (!ContainsWritable(datItems))
|
||||
continue;
|
||||
|
||||
// Resolve the names in the block
|
||||
datItems = ResolveNames(datItems);
|
||||
|
||||
for (int index = 0; index < datItems.Count; index++)
|
||||
{
|
||||
DatItem datItem = datItems[index];
|
||||
|
||||
// If we have a different game and we're not at the start of the list, output the end of last item
|
||||
if (lastgame is not null && !string.Equals(lastgame, datItem.GetMachine()!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteEndGame(xtw);
|
||||
|
||||
// If we have a new game, output the beginning of the new item
|
||||
if (lastgame is null || !string.Equals(lastgame, datItem.GetMachine()!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteStartGame(xtw, datItem);
|
||||
|
||||
// Check for a "null" item
|
||||
datItem = ProcessNullifiedItem(datItem);
|
||||
|
||||
// Write out the item if we're not ignoring
|
||||
if (!ShouldIgnore(datItem, ignoreblanks))
|
||||
WriteDatItem(xtw, datItem);
|
||||
|
||||
// Set the new data to compare against
|
||||
lastgame = datItem.GetMachine()!.GetName();
|
||||
}
|
||||
}
|
||||
|
||||
// Write the file footer out
|
||||
WriteFooter(xtw);
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
#if NET452_OR_GREATER
|
||||
xtw.Dispose();
|
||||
#endif
|
||||
fs.Dispose();
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFileDB(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
FileStream fs = File.Create(outfile);
|
||||
|
||||
// If we get back null for some reason, just log and return
|
||||
if (fs is null)
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlTextWriter xtw = new(fs, new UTF8Encoding(false))
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
IndentChar = '\t',
|
||||
Indentation = 1,
|
||||
};
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(xtw);
|
||||
|
||||
// Write out each of the machines and roms
|
||||
string? lastgame = null;
|
||||
|
||||
// Use a sorted list of games to output
|
||||
foreach (string key in ItemsDB.SortedKeys)
|
||||
{
|
||||
// If this machine doesn't contain any writable items, skip
|
||||
var itemsDict = GetItemsForBucketDB(key, filter: true);
|
||||
if (itemsDict is null || !ContainsWritable([.. itemsDict.Values]))
|
||||
continue;
|
||||
|
||||
// Resolve the names in the block
|
||||
var items = ResolveNamesDB([.. itemsDict]);
|
||||
|
||||
foreach (var kvp in items)
|
||||
{
|
||||
// Get the machine for the item
|
||||
var machine = GetMachineForItemDB(kvp.Key);
|
||||
|
||||
// If we have a different game and we're not at the start of the list, output the end of last item
|
||||
if (lastgame is not null && !string.Equals(lastgame, machine.Value!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteEndGame(xtw);
|
||||
|
||||
// If we have a new game, output the beginning of the new item
|
||||
if (lastgame is null || !string.Equals(lastgame, machine.Value!.GetName(), StringComparison.OrdinalIgnoreCase))
|
||||
WriteStartGame(xtw, kvp.Value);
|
||||
|
||||
// Check for a "null" item
|
||||
var datItem = new KeyValuePair<long, DatItem>(kvp.Key, ProcessNullifiedItem(kvp.Value));
|
||||
|
||||
// Write out the item if we're not ignoring
|
||||
if (!ShouldIgnore(datItem.Value, ignoreblanks))
|
||||
WriteDatItemDB(xtw, datItem);
|
||||
|
||||
// Set the new data to compare against
|
||||
lastgame = machine.Value!.GetName();
|
||||
}
|
||||
}
|
||||
|
||||
// Write the file footer out
|
||||
WriteFooter(xtw);
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
#if NET452_OR_GREATER
|
||||
xtw.Dispose();
|
||||
#endif
|
||||
fs.Dispose();
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DAT header using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="xtw">XmlTextWriter to output to</param>
|
||||
private void WriteHeader(XmlTextWriter xtw)
|
||||
{
|
||||
xtw.WriteStartDocument();
|
||||
|
||||
xtw.WriteStartElement("datafile");
|
||||
|
||||
XmlSerializer xs = new(typeof(DatHeader));
|
||||
XmlSerializerNamespaces ns = new();
|
||||
ns.Add("", "");
|
||||
xs.Serialize(xtw, Header, ns);
|
||||
|
||||
xtw.WriteStartElement("data");
|
||||
|
||||
xtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out Game start using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="xtw">XmlTextWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
private static void WriteStartGame(XmlTextWriter xtw, DatItem datItem)
|
||||
{
|
||||
// No game should start with a path separator
|
||||
datItem.GetMachine()!.SetName(datItem.GetMachine()!.GetName()?.TrimStart(Path.DirectorySeparatorChar) ?? string.Empty);
|
||||
|
||||
// Write the machine
|
||||
xtw.WriteStartElement("directory");
|
||||
XmlSerializer xs = new(typeof(Machine));
|
||||
XmlSerializerNamespaces ns = new();
|
||||
ns.Add("", "");
|
||||
xs.Serialize(xtw, datItem.GetMachine(), ns);
|
||||
|
||||
xtw.WriteStartElement("files");
|
||||
|
||||
xtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out Game start using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="xtw">XmlTextWriter to output to</param>
|
||||
private static void WriteEndGame(XmlTextWriter xtw)
|
||||
{
|
||||
// End files
|
||||
xtw.WriteEndElement();
|
||||
|
||||
// End directory
|
||||
xtw.WriteEndElement();
|
||||
|
||||
xtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DatItem using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="xtw">XmlTextWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
private void WriteDatItem(XmlTextWriter xtw, DatItem datItem)
|
||||
{
|
||||
// Get the machine for the item
|
||||
var machine = datItem.GetMachine();
|
||||
|
||||
// Pre-process the item name
|
||||
ProcessItemName(datItem, machine, forceRemoveQuotes: true, forceRomName: false);
|
||||
|
||||
// Write the DatItem
|
||||
XmlSerializer xs = new(typeof(DatItem));
|
||||
XmlSerializerNamespaces ns = new();
|
||||
ns.Add("", "");
|
||||
xs.Serialize(xtw, datItem, ns);
|
||||
|
||||
xtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DatItem using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="xtw">XmlTextWriter to output to</param>
|
||||
/// <param name="datItem">DatItem object to be output</param>
|
||||
private void WriteDatItemDB(XmlTextWriter xtw, KeyValuePair<long, DatItem> datItem)
|
||||
{
|
||||
// Get the machine for the item
|
||||
var machine = GetMachineForItemDB(datItem.Key);
|
||||
|
||||
// Pre-process the item name
|
||||
ProcessItemName(datItem.Value, machine.Value, forceRemoveQuotes: true, forceRomName: false);
|
||||
|
||||
// Write the DatItem
|
||||
XmlSerializer xs = new(typeof(DatItem));
|
||||
XmlSerializerNamespaces ns = new();
|
||||
ns.Add("", "");
|
||||
xs.Serialize(xtw, datItem, ns);
|
||||
|
||||
xtw.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DAT footer using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="xtw">XmlTextWriter to output to</param>
|
||||
private static void WriteFooter(XmlTextWriter xtw)
|
||||
{
|
||||
// End files
|
||||
xtw.WriteEndElement();
|
||||
|
||||
// End directory
|
||||
xtw.WriteEndElement();
|
||||
|
||||
// End data
|
||||
xtw.WriteEndElement();
|
||||
|
||||
// End datafile
|
||||
xtw.WriteEndElement();
|
||||
|
||||
xtw.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
191
SabreTools.Metadata.DatFiles/Formats/SeparatedValue.cs
Normal file
191
SabreTools.Metadata.DatFiles/Formats/SeparatedValue.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
#pragma warning disable IDE0290 // Use primary constructor
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a value-separated DAT
|
||||
/// </summary>
|
||||
public abstract class SeparatedValue : SerializableDatFile<Data.Models.SeparatedValue.MetadataFile, Serialization.Readers.SeparatedValue, Serialization.Writers.SeparatedValue, Serialization.CrossModel.SeparatedValue>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.Disk,
|
||||
ItemType.Media,
|
||||
ItemType.Rom,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Represents the delimiter between fields
|
||||
/// </summary>
|
||||
protected char _delim;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SeparatedValue(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Deserialize the input file
|
||||
var metadataFile = new Serialization.Readers.SeparatedValue().Deserialize(filename, _delim);
|
||||
var metadata = new Serialization.CrossModel.SeparatedValue().Serialize(metadataFile);
|
||||
|
||||
// Convert to the internal format
|
||||
ConvertFromMetadata(metadata, filename, indexId, keep, statsOnly, filterRunner);
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
string message = $"'{filename}' - An error occurred during parsing";
|
||||
_logger.Error(ex, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
// Check item name
|
||||
if (string.IsNullOrEmpty(datItem.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Rom.NameKey);
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case Disk disk:
|
||||
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key))
|
||||
&& string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Disk.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Media media:
|
||||
if (string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Media.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is null || rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) < 0)
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SizeKey);
|
||||
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key))
|
||||
&& string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey)))
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Rom.SHA1Key);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
|
||||
// Serialize the input file
|
||||
var metadata = ConvertToMetadata(ignoreblanks);
|
||||
var metadataFile = new Serialization.CrossModel.SeparatedValue().Deserialize(metadata);
|
||||
if (!new Serialization.Writers.SeparatedValue().SerializeFile(metadataFile, outfile, _delim, longHeader: false))
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be written! See the log for more details.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a comma-separated value file
|
||||
/// </summary>
|
||||
public sealed class CommaSeparatedValue : SeparatedValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public CommaSeparatedValue(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_delim = ',';
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.CSV);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a semicolon-separated value file
|
||||
/// </summary>
|
||||
public sealed class SemicolonSeparatedValue : SeparatedValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SemicolonSeparatedValue(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_delim = ';';
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.SSV);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a tab-separated value file
|
||||
/// </summary>
|
||||
public sealed class TabSeparatedValue : SeparatedValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public TabSeparatedValue(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
_delim = '\t';
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.TSV);
|
||||
}
|
||||
}
|
||||
}
|
||||
121
SabreTools.Metadata.DatFiles/Formats/SerializableDatFile.cs
Normal file
121
SabreTools.Metadata.DatFiles/Formats/SerializableDatFile.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using SabreTools.Metadata.Filter;
|
||||
using SabreTools.Data.Models.Metadata;
|
||||
using SabreTools.Serialization.CrossModel;
|
||||
using SabreTools.Serialization.Readers;
|
||||
using SabreTools.Serialization.Writers;
|
||||
|
||||
#pragma warning disable IDE0290 // Use primary constructor
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DAT that can be serialized
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">Base internal model for the DAT type</typeparam>
|
||||
/// <typeparam name="TFileReader">IFileReader type to use for conversion</typeparam>
|
||||
/// <typeparam name="TFileWriter">IFileWriter type to use for conversion</typeparam>
|
||||
/// <typeparam name="TCrossModel">ICrossModel for cross-model serialization</typeparam>
|
||||
public abstract class SerializableDatFile<TModel, TFileReader, TFileWriter, TCrossModel> : DatFile
|
||||
where TFileReader : IFileReader<TModel>
|
||||
where TFileWriter : IFileWriter<TModel>
|
||||
where TCrossModel : ICrossModel<TModel, MetadataFile>
|
||||
{
|
||||
#region Static Serialization Instances
|
||||
|
||||
/// <summary>
|
||||
/// File deserializer instance
|
||||
/// </summary>
|
||||
private static readonly TFileReader FileDeserializer = Activator.CreateInstance<TFileReader>();
|
||||
|
||||
/// <summary>
|
||||
/// File serializer instance
|
||||
/// </summary>
|
||||
private static readonly TFileWriter FileSerializer = Activator.CreateInstance<TFileWriter>();
|
||||
|
||||
/// <summary>
|
||||
/// Cross-model serializer instance
|
||||
/// </summary>
|
||||
private static readonly TCrossModel CrossModelSerializer = Activator.CreateInstance<TCrossModel>();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected SerializableDatFile(DatFile? datFile) : base(datFile) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ParseFile(string filename,
|
||||
int indexId,
|
||||
bool keep,
|
||||
bool statsOnly = false,
|
||||
FilterRunner? filterRunner = null,
|
||||
bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Deserialize the input file in two steps
|
||||
var specificFormat = FileDeserializer.Deserialize(filename);
|
||||
var internalFormat = CrossModelSerializer.Serialize(specificFormat);
|
||||
|
||||
// Convert to the internal format
|
||||
ConvertFromMetadata(internalFormat, filename, indexId, keep, statsOnly, filterRunner);
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
string message = $"'{filename}' - An error occurred during parsing";
|
||||
_logger.Error(ex, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
|
||||
// Serialize the input file in two steps
|
||||
var internalFormat = ConvertToMetadata(ignoreblanks);
|
||||
var specificFormat = CrossModelSerializer.Deserialize(internalFormat);
|
||||
if (!FileSerializer.SerializeFile(specificFormat, outfile))
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be written! See the log for more details.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool WriteToFileDB(string outfile, bool ignoreblanks = false, bool throwOnError = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.User($"Writing to '{outfile}'...");
|
||||
|
||||
// Serialize the input file in two steps
|
||||
var internalFormat = ConvertToMetadataDB(ignoreblanks);
|
||||
var specificFormat = CrossModelSerializer.Deserialize(internalFormat);
|
||||
if (!FileSerializer.SerializeFile(specificFormat, outfile))
|
||||
{
|
||||
_logger.Warning($"File '{outfile}' could not be written! See the log for more details.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!throwOnError)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.User($"'{outfile}' written!{Environment.NewLine}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
213
SabreTools.Metadata.DatFiles/Formats/SoftwareList.cs
Normal file
213
SabreTools.Metadata.DatFiles/Formats/SoftwareList.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents parsing and writing of a SoftwareList
|
||||
/// </summary>
|
||||
public sealed class SoftwareList : SerializableDatFile<Data.Models.SoftwareList.SoftwareList, Serialization.Readers.SoftwareList, Serialization.Writers.SoftwareList, Serialization.CrossModel.SoftwareList>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// DTD for original MAME Software List DATs
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: See if there's an updated DTD and then check for required fields
|
||||
/// </remarks>
|
||||
internal const string SoftwareListDTD = @"<!ELEMENT softwarelist (notes?, software+)>
|
||||
<!ATTLIST softwarelist name CDATA #REQUIRED>
|
||||
<!ATTLIST softwarelist description CDATA #IMPLIED>
|
||||
<!ELEMENT notes (#PCDATA)>
|
||||
<!ELEMENT software (description, year, publisher, notes?, info*, sharedfeat*, part*)>
|
||||
<!ATTLIST software name CDATA #REQUIRED>
|
||||
<!ATTLIST software cloneof CDATA #IMPLIED>
|
||||
<!ATTLIST software supported (yes|partial|no) ""yes"">
|
||||
<!ELEMENT description (#PCDATA)>
|
||||
<!ELEMENT year (#PCDATA)>
|
||||
<!ELEMENT publisher (#PCDATA)>
|
||||
<!ELEMENT info EMPTY>
|
||||
<!ATTLIST info name CDATA #REQUIRED>
|
||||
<!ATTLIST info value CDATA #IMPLIED>
|
||||
<!ELEMENT sharedfeat EMPTY>
|
||||
<!ATTLIST sharedfeat name CDATA #REQUIRED>
|
||||
<!ATTLIST sharedfeat value CDATA #IMPLIED>
|
||||
<!ELEMENT part (feature*, dataarea*, diskarea*, dipswitch*)>
|
||||
<!ATTLIST part name CDATA #REQUIRED>
|
||||
<!ATTLIST part interface CDATA #REQUIRED>
|
||||
<!-- feature is used to store things like pcb-type, mapper type, etc. Specific values depend on the system. -->
|
||||
<!ELEMENT feature EMPTY>
|
||||
<!ATTLIST feature name CDATA #REQUIRED>
|
||||
<!ATTLIST feature value CDATA #IMPLIED>
|
||||
<!ELEMENT dataarea (rom*)>
|
||||
<!ATTLIST dataarea name CDATA #REQUIRED>
|
||||
<!ATTLIST dataarea size CDATA #REQUIRED>
|
||||
<!ATTLIST dataarea width (8|16|32|64) ""8"">
|
||||
<!ATTLIST dataarea endianness (big|little) ""little"">
|
||||
<!ELEMENT rom EMPTY>
|
||||
<!ATTLIST rom name CDATA #IMPLIED>
|
||||
<!ATTLIST rom size CDATA #IMPLIED>
|
||||
<!ATTLIST rom crc CDATA #IMPLIED>
|
||||
<!ATTLIST rom sha1 CDATA #IMPLIED>
|
||||
<!ATTLIST rom offset CDATA #IMPLIED>
|
||||
<!ATTLIST rom value CDATA #IMPLIED>
|
||||
<!ATTLIST rom status (baddump|nodump|good) ""good"">
|
||||
<!ATTLIST rom loadflag (load16_byte|load16_word|load16_word_swap|load32_byte|load32_word|load32_word_swap|load32_dword|load64_word|load64_word_swap|reload|fill|continue|reload_plain|ignore) #IMPLIED>
|
||||
<!ELEMENT diskarea (disk*)>
|
||||
<!ATTLIST diskarea name CDATA #REQUIRED>
|
||||
<!ELEMENT disk EMPTY>
|
||||
<!ATTLIST disk name CDATA #REQUIRED>
|
||||
<!ATTLIST disk sha1 CDATA #IMPLIED>
|
||||
<!ATTLIST disk status (baddump|nodump|good) ""good"">
|
||||
<!ATTLIST disk writeable (yes|no) ""no"">
|
||||
<!ELEMENT dipswitch (dipvalue*)>
|
||||
<!ATTLIST dipswitch name CDATA #REQUIRED>
|
||||
<!ATTLIST dipswitch tag CDATA #REQUIRED>
|
||||
<!ATTLIST dipswitch mask CDATA #REQUIRED>
|
||||
<!ELEMENT dipvalue EMPTY>
|
||||
<!ATTLIST dipvalue name CDATA #REQUIRED>
|
||||
<!ATTLIST dipvalue value CDATA #REQUIRED>
|
||||
<!ATTLIST dipvalue default (yes|no) ""no"">
|
||||
";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ItemType[] SupportedTypes
|
||||
=> [
|
||||
ItemType.DipSwitch,
|
||||
ItemType.Disk,
|
||||
ItemType.Info,
|
||||
ItemType.PartFeature,
|
||||
ItemType.Rom,
|
||||
ItemType.SharedFeat,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor designed for casting a base DatFile
|
||||
/// </summary>
|
||||
/// <param name="datFile">Parent DatFile to copy from</param>
|
||||
public SoftwareList(DatFile? datFile) : base(datFile)
|
||||
{
|
||||
Header.SetFieldValue(DatHeader.DatFormatKey, DatFormat.SoftwareList);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override List<string>? GetMissingRequiredFields(DatItem datItem)
|
||||
{
|
||||
List<string> missingFields = [];
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (datItem)
|
||||
{
|
||||
case DipSwitch dipSwitch:
|
||||
if (!dipSwitch.PartSpecified)
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Part.NameKey);
|
||||
missingFields.Add(Data.Models.Metadata.Part.InterfaceKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(dipSwitch.GetFieldValue<Part?>(DipSwitch.PartKey)!.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Part.NameKey);
|
||||
if (string.IsNullOrEmpty(dipSwitch.GetFieldValue<Part?>(DipSwitch.PartKey)!.GetStringFieldValue(Data.Models.Metadata.Part.InterfaceKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Part.InterfaceKey);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(dipSwitch.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.DipSwitch.NameKey);
|
||||
if (string.IsNullOrEmpty(dipSwitch.GetStringFieldValue(Data.Models.Metadata.DipSwitch.TagKey)))
|
||||
missingFields.Add(Data.Models.Metadata.DipSwitch.TagKey);
|
||||
if (string.IsNullOrEmpty(dipSwitch.GetStringFieldValue(Data.Models.Metadata.DipSwitch.MaskKey)))
|
||||
missingFields.Add(Data.Models.Metadata.DipSwitch.MaskKey);
|
||||
if (dipSwitch.ValuesSpecified)
|
||||
{
|
||||
var dipValues = dipSwitch.GetFieldValue<DipValue[]?>(Data.Models.Metadata.DipSwitch.DipValueKey);
|
||||
if (Array.Find(dipValues!, dv => string.IsNullOrEmpty(dv.GetName())) is not null)
|
||||
missingFields.Add(Data.Models.Metadata.DipValue.NameKey);
|
||||
if (Array.Find(dipValues!, dv => string.IsNullOrEmpty(dv.GetStringFieldValue(Data.Models.Metadata.DipValue.ValueKey))) is not null)
|
||||
missingFields.Add(Data.Models.Metadata.DipValue.ValueKey);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Disk disk:
|
||||
if (!disk.PartSpecified)
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Part.NameKey);
|
||||
missingFields.Add(Data.Models.Metadata.Part.InterfaceKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(disk.GetFieldValue<Part?>(Disk.PartKey)!.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Part.NameKey);
|
||||
if (string.IsNullOrEmpty(disk.GetFieldValue<Part?>(Disk.PartKey)!.GetStringFieldValue(Data.Models.Metadata.Part.InterfaceKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Part.InterfaceKey);
|
||||
}
|
||||
|
||||
if (!disk.DiskAreaSpecified)
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.DiskArea.NameKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(disk.GetFieldValue<DiskArea?>(Disk.DiskAreaKey)!.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.DiskArea.NameKey);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(disk.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Disk.NameKey);
|
||||
break;
|
||||
|
||||
case Info info:
|
||||
if (string.IsNullOrEmpty(info.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Info.NameKey);
|
||||
break;
|
||||
|
||||
case Rom rom:
|
||||
if (!rom.PartSpecified)
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.Part.NameKey);
|
||||
missingFields.Add(Data.Models.Metadata.Part.InterfaceKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(rom.GetFieldValue<Part?>(Rom.PartKey)!.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.Part.NameKey);
|
||||
if (string.IsNullOrEmpty(rom.GetFieldValue<Part?>(Rom.PartKey)!.GetStringFieldValue(Data.Models.Metadata.Part.InterfaceKey)))
|
||||
missingFields.Add(Data.Models.Metadata.Part.InterfaceKey);
|
||||
}
|
||||
|
||||
if (!rom.DataAreaSpecified)
|
||||
{
|
||||
missingFields.Add(Data.Models.Metadata.DataArea.NameKey);
|
||||
missingFields.Add(Data.Models.Metadata.DataArea.SizeKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(rom.GetFieldValue<DataArea?>(Rom.DataAreaKey)!.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.DataArea.NameKey);
|
||||
if (rom.GetFieldValue<DataArea?>(Rom.DataAreaKey)!.GetInt64FieldValue(Data.Models.Metadata.DataArea.SizeKey) is null)
|
||||
missingFields.Add(Data.Models.Metadata.DataArea.SizeKey);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SharedFeat sharedFeat:
|
||||
if (string.IsNullOrEmpty(sharedFeat.GetName()))
|
||||
missingFields.Add(Data.Models.Metadata.SharedFeat.NameKey);
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
return missingFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
931
SabreTools.Metadata.DatFiles/ItemDictionary.cs
Normal file
931
SabreTools.Metadata.DatFiles/ItemDictionary.cs
Normal file
@@ -0,0 +1,931 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
using SabreTools.Metadata.DatItems;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Logging;
|
||||
using SabreTools.Text.Compare;
|
||||
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Item dictionary with statistics, bucketing, and sorting
|
||||
/// </summary>
|
||||
[JsonObject("items"), XmlRoot("items")]
|
||||
public class ItemDictionary
|
||||
{
|
||||
#region Private instance variables
|
||||
|
||||
/// <summary>
|
||||
/// Determine the bucketing key for all items
|
||||
/// </summary>
|
||||
private ItemKey _bucketedBy = ItemKey.NULL;
|
||||
|
||||
/// <summary>
|
||||
/// Internal dictionary for the class
|
||||
/// </summary>
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
private readonly ConcurrentDictionary<string, List<DatItem>?> _items = [];
|
||||
#else
|
||||
private readonly Dictionary<string, List<DatItem>?> _items = [];
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Logging object
|
||||
/// </summary>
|
||||
private readonly Logger _logger;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Get the keys in sorted order from the file dictionary
|
||||
/// </summary>
|
||||
/// <returns>List of the keys in sorted order</returns>
|
||||
[JsonIgnore, XmlIgnore]
|
||||
public string[] SortedKeys
|
||||
{
|
||||
get
|
||||
{
|
||||
List<string> keys = [.. _items.Keys];
|
||||
keys.Sort(new NaturalComparer());
|
||||
return [.. keys];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DAT statistics
|
||||
/// </summary>
|
||||
[JsonIgnore, XmlIgnore]
|
||||
public DatStatistics DatStatistics { get; } = new DatStatistics();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Generic constructor
|
||||
/// </summary>
|
||||
public ItemDictionary()
|
||||
{
|
||||
_logger = new Logger(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accessors
|
||||
|
||||
/// <summary>
|
||||
/// Add a DatItem to the dictionary after checking
|
||||
/// </summary>
|
||||
/// <param name="item">Item data to check against</param>
|
||||
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
|
||||
/// <returns>The key for the item</returns>
|
||||
public string AddItem(DatItem item, bool statsOnly)
|
||||
{
|
||||
// If we have a Disk, File, Media, or Rom, clean the hash data
|
||||
if (item is Disk disk)
|
||||
{
|
||||
// If the file has aboslutely no hashes, skip and log
|
||||
if (disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() != ItemStatus.Nodump
|
||||
&& string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key))
|
||||
&& string.IsNullOrEmpty(disk.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)))
|
||||
{
|
||||
_logger.Verbose($"Incomplete entry for '{disk.GetName()}' will be output as nodump");
|
||||
disk.SetFieldValue<string?>(Data.Models.Metadata.Disk.StatusKey, ItemStatus.Nodump.AsStringValue());
|
||||
}
|
||||
|
||||
item = disk;
|
||||
}
|
||||
else if (item is DatItems.Formats.File file)
|
||||
{
|
||||
// If the file has aboslutely no hashes, skip and log
|
||||
if (string.IsNullOrEmpty(file.CRC)
|
||||
&& string.IsNullOrEmpty(file.MD5)
|
||||
&& string.IsNullOrEmpty(file.SHA1)
|
||||
&& string.IsNullOrEmpty(file.SHA256))
|
||||
{
|
||||
_logger.Verbose($"Incomplete entry for '{file.GetName()}' will be output as nodump");
|
||||
}
|
||||
|
||||
item = file;
|
||||
}
|
||||
else if (item is Media media)
|
||||
{
|
||||
// If the file has aboslutely no hashes, skip and log
|
||||
if (string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key))
|
||||
&& string.IsNullOrEmpty(media.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey)))
|
||||
{
|
||||
_logger.Verbose($"Incomplete entry for '{media.GetName()}' will be output as nodump");
|
||||
}
|
||||
|
||||
item = media;
|
||||
}
|
||||
else if (item is Rom rom)
|
||||
{
|
||||
long? size = rom.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey);
|
||||
|
||||
// If we have the case where there is SHA-1 and nothing else, we don't fill in any other part of the data
|
||||
if (size is null && !string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)))
|
||||
{
|
||||
// No-op, just catch it so it doesn't go further
|
||||
//logger.Verbose($"{Header.GetStringFieldValue(DatHeader.FileNameKey)}: Entry with only SHA-1 found - '{rom.GetName()}'");
|
||||
}
|
||||
|
||||
// If we have a rom and it's missing size AND the hashes match a 0-byte file, fill in the rest of the info
|
||||
else if ((size == 0 || size is null)
|
||||
&& (string.IsNullOrEmpty(rom.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)) || rom.HasZeroHash()))
|
||||
{
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, "0");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, HashType.CRC32.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD2Key, null); // HashType.MD2.ZeroString
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD4Key, null); // HashType.MD4.ZeroString
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD5Key, HashType.MD5.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD128Key, null); // HashType.RIPEMD128.ZeroString
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD160Key, null); // HashType.RIPEMD160.ZeroString
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, HashType.SHA1.ZeroString);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA256Key, null); // HashType.SHA256.ZeroString;
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA384Key, null); // HashType.SHA384.ZeroString;
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA512Key, null); // HashType.SHA512.ZeroString;
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SpamSumKey, null); // HashType.SpamSum.ZeroString;
|
||||
}
|
||||
|
||||
// If the file has no size and it's not the above case, skip and log
|
||||
else if (rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() != ItemStatus.Nodump && (size == 0 || size is null))
|
||||
{
|
||||
//logger.Verbose($"{Header.GetStringFieldValue(DatHeader.FileNameKey)}: Incomplete entry for '{rom.GetName()}' will be output as nodump");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.StatusKey, ItemStatus.Nodump.AsStringValue());
|
||||
}
|
||||
|
||||
// If the file has a size but aboslutely no hashes, skip and log
|
||||
else if (rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() != ItemStatus.Nodump
|
||||
&& size is not null && size > 0
|
||||
&& !rom.HasHashes())
|
||||
{
|
||||
//logger.Verbose($"{Header.GetStringFieldValue(DatHeader.FileNameKey)}: Incomplete entry for '{rom.GetName()}' will be output as nodump");
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.StatusKey, ItemStatus.Nodump.AsStringValue());
|
||||
}
|
||||
|
||||
item = rom;
|
||||
}
|
||||
|
||||
// Get the key and add the file
|
||||
string key = GetBucketKey(item, _bucketedBy, lower: true, norename: true);
|
||||
|
||||
// If only adding statistics, we add an empty key for games and then just item stats
|
||||
if (statsOnly)
|
||||
{
|
||||
EnsureBucketingKey(key);
|
||||
DatStatistics.AddItemStatistics(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddItem(key, item);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all items marked for removal
|
||||
/// </summary>
|
||||
public void ClearMarked()
|
||||
{
|
||||
string[] keys = [.. SortedKeys];
|
||||
#if NET452_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(keys, Core.Globals.ParallelOptions, key =>
|
||||
#elif NET40_OR_GREATER
|
||||
Parallel.ForEach(keys, key =>
|
||||
#else
|
||||
foreach (var key in keys)
|
||||
#endif
|
||||
{
|
||||
var list = GetItemsForBucket(key, filter: true);
|
||||
RemoveBucket(key);
|
||||
list.ForEach(item => AddItem(key, item));
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the items associated with a bucket name
|
||||
/// </summary>
|
||||
/// <param name="bucketName">Name of the bucket to retrive items for</param>
|
||||
/// <param name="filter">Indicates if RemoveKey filtering is performed</param>
|
||||
/// <returns>List representing the bucket items, empty on missing</returns>
|
||||
public List<DatItem> GetItemsForBucket(string? bucketName, bool filter = false)
|
||||
{
|
||||
if (bucketName is null)
|
||||
return [];
|
||||
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
if (!_items.TryGetValue(bucketName, out var items))
|
||||
return [];
|
||||
#else
|
||||
if (!_items.ContainsKey(bucketName))
|
||||
return [];
|
||||
|
||||
var items = _items[bucketName];
|
||||
#endif
|
||||
|
||||
if (items is null || !filter)
|
||||
return [.. items ?? []];
|
||||
|
||||
var datItems = new List<DatItem>();
|
||||
foreach (DatItem item in items)
|
||||
{
|
||||
if (item.GetBoolFieldValue(DatItem.RemoveKey) != true)
|
||||
datItems.Add(item);
|
||||
}
|
||||
|
||||
return datItems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a key from the file dictionary if it exists
|
||||
/// </summary>
|
||||
/// <param name="key">Key in the dictionary to remove</param>
|
||||
public bool RemoveBucket(string key)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
bool removed = _items.TryRemove(key, out var list);
|
||||
#else
|
||||
if (!_items.ContainsKey(key))
|
||||
return false;
|
||||
|
||||
bool removed = true;
|
||||
var list = _items[key];
|
||||
_items.Remove(key);
|
||||
#endif
|
||||
if (list is null)
|
||||
return removed;
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
DatStatistics.RemoveItemStatistics(item);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the indexed instance of a value from the file dictionary if it exists
|
||||
/// </summary>
|
||||
/// <param name="key">Key in the dictionary to remove from</param>
|
||||
/// <param name="value">Value to remove from the dictionary</param>
|
||||
/// <param name="index">Index of the item to be removed</param>
|
||||
public bool RemoveItem(string key, DatItem value, int index)
|
||||
{
|
||||
// Explicit lock for some weird corner cases
|
||||
lock (key)
|
||||
{
|
||||
// If the key doesn't exist, return
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
if (!_items.TryGetValue(key, out var list) || list is null)
|
||||
return false;
|
||||
#else
|
||||
if (!_items.ContainsKey(key))
|
||||
return false;
|
||||
|
||||
var list = _items[key];
|
||||
if (list is null)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// If the value doesn't exist in the key, assume it has been removed
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
// Remove the statistics first
|
||||
DatStatistics.RemoveItemStatistics(value);
|
||||
|
||||
list.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override the internal ItemKey value
|
||||
/// </summary>
|
||||
/// <param name="newBucket"></param>
|
||||
public void SetBucketedBy(ItemKey newBucket)
|
||||
{
|
||||
_bucketedBy = newBucket;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a value to the file dictionary
|
||||
/// </summary>
|
||||
/// <param name="key">Key in the dictionary to add to</param>
|
||||
/// <param name="value">Value to add to the dictionary</param>
|
||||
internal void AddItem(string key, DatItem value)
|
||||
{
|
||||
// Explicit lock for some weird corner cases
|
||||
lock (key)
|
||||
{
|
||||
// Ensure the key exists
|
||||
EnsureBucketingKey(key);
|
||||
|
||||
// If item is null, don't add it
|
||||
if (value is null)
|
||||
return;
|
||||
|
||||
// Now add the value
|
||||
_items[key]!.Add(value);
|
||||
|
||||
// Now update the statistics
|
||||
DatStatistics.AddItemStatistics(value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bucketing
|
||||
|
||||
/// <summary>
|
||||
/// Take the arbitrarily bucketed Files Dictionary and convert to one bucketed by a user-defined method
|
||||
/// </summary>
|
||||
/// <param name="bucketBy">ItemKey enum representing how to bucket the individual items</param>
|
||||
/// <param name="lower">True if the key should be lowercased (default), false otherwise</param>
|
||||
/// <param name="norename">True if games should only be compared on game and file name, false if system and source are counted</param>
|
||||
public void BucketBy(ItemKey bucketBy, bool lower = true, bool norename = true)
|
||||
{
|
||||
// If we have a situation where there's no dictionary or no keys at all, we skip
|
||||
if (_items is null || _items.Count == 0)
|
||||
return;
|
||||
|
||||
// If the sorted type isn't the same, we want to sort the dictionary accordingly
|
||||
if (_bucketedBy != bucketBy && bucketBy != ItemKey.NULL)
|
||||
{
|
||||
_logger.User($"Organizing roms by {bucketBy}");
|
||||
PerformBucketing(bucketBy, lower, norename);
|
||||
}
|
||||
|
||||
// Sort the dictionary to be consistent
|
||||
_logger.User($"Sorting roms by {bucketBy}");
|
||||
PerformSorting(norename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform deduplication on the current sorted dictionary
|
||||
/// </summary>
|
||||
public void Deduplicate()
|
||||
{
|
||||
#if NET452_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(SortedKeys, Core.Globals.ParallelOptions, key =>
|
||||
#elif NET40_OR_GREATER
|
||||
Parallel.ForEach(SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in SortedKeys)
|
||||
#endif
|
||||
{
|
||||
// Get the possibly unsorted list
|
||||
List<DatItem> sortedList = GetItemsForBucket(key);
|
||||
|
||||
// Sort and merge the list
|
||||
Sort(ref sortedList, norename: false);
|
||||
sortedList = Merge(sortedList);
|
||||
|
||||
// Add the list back to the dictionary
|
||||
RemoveBucket(key);
|
||||
sortedList.ForEach(item => AddItem(key, item));
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the duplicate status of two items
|
||||
/// </summary>
|
||||
/// <param name="self">Current DatItem</param>
|
||||
/// <param name="last">DatItem to check against</param>
|
||||
/// <returns>The DupeType corresponding to the relationship between the two</returns>
|
||||
public DupeType GetDuplicateStatus(DatItem? self, DatItem? last)
|
||||
{
|
||||
DupeType output = 0x00;
|
||||
|
||||
// If either item is null
|
||||
if (self is null || last is null)
|
||||
return output;
|
||||
|
||||
// If we don't have a duplicate at all, return none
|
||||
if (!self.Equals(last))
|
||||
return output;
|
||||
|
||||
// Get the sources for comparison
|
||||
var selfSource = self.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
var lastSource = last.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
|
||||
// Get the machines for comparison
|
||||
var selfMachine = self.GetMachine();
|
||||
string? selfMachineName = selfMachine?.GetName();
|
||||
var lastMachine = last.GetMachine();
|
||||
string? lastMachineName = lastMachine?.GetName();
|
||||
|
||||
// If the duplicate is external already
|
||||
#if NET20 || NET35
|
||||
if ((last.GetFieldValue<DupeType>(DatItem.DupeTypeKey) & DupeType.External) != 0)
|
||||
#else
|
||||
if (last.GetFieldValue<DupeType>(DatItem.DupeTypeKey).HasFlag(DupeType.External))
|
||||
#endif
|
||||
output |= DupeType.External;
|
||||
|
||||
// If the duplicate should be external
|
||||
else if (lastSource?.Index != selfSource?.Index)
|
||||
output |= DupeType.External;
|
||||
|
||||
// Otherwise, it's considered an internal dupe
|
||||
else
|
||||
output |= DupeType.Internal;
|
||||
|
||||
// If the item and machine names match
|
||||
if (lastMachineName == selfMachineName && last.GetName() == self.GetName())
|
||||
output |= DupeType.All;
|
||||
|
||||
// Otherwise, hash match is assumed
|
||||
else
|
||||
output |= DupeType.Hash;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge an arbitrary set of DatItems based on the supplied information
|
||||
/// </summary>
|
||||
/// <param name="items">List of DatItem objects representing the items to be merged</param>
|
||||
/// <returns>A List of DatItem objects representing the merged items</returns>
|
||||
/// TODO: Make this internal like the DB counterpart
|
||||
public static List<DatItem> Merge(List<DatItem>? items)
|
||||
{
|
||||
// Check for null or blank inputs first
|
||||
if (items is null || items.Count == 0)
|
||||
return [];
|
||||
|
||||
// Create placeholder object for checking duplicates
|
||||
var dupDict = new ItemDictionary();
|
||||
|
||||
// Create output list
|
||||
List<DatItem> output = [];
|
||||
|
||||
// Then deduplicate them by checking to see if data matches previous saved roms
|
||||
int nodumpCount = 0;
|
||||
foreach (DatItem datItem in items)
|
||||
{
|
||||
// If we don't have a Disk, File, Media, or Rom, we skip checking for duplicates
|
||||
if (datItem is not Disk && datItem is not DatItems.Formats.File && datItem is not Media && datItem is not Rom)
|
||||
continue;
|
||||
|
||||
// If it's a nodump, add and skip
|
||||
if (datItem is Rom rom && rom.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus() == ItemStatus.Nodump)
|
||||
{
|
||||
output.Add(datItem);
|
||||
nodumpCount++;
|
||||
continue;
|
||||
}
|
||||
else if (datItem is Disk disk && disk.GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus() == ItemStatus.Nodump)
|
||||
{
|
||||
output.Add(datItem);
|
||||
nodumpCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's the first non-nodump item in the list, don't touch it
|
||||
if (output.Count == nodumpCount)
|
||||
{
|
||||
output.Add(datItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the index of the first duplicate, if one exists
|
||||
int pos = output.FindIndex(lastItem => dupDict.GetDuplicateStatus(datItem, lastItem) != 0x00);
|
||||
if (pos < 0)
|
||||
{
|
||||
output.Add(datItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the duplicate item
|
||||
DatItem savedItem = output[pos];
|
||||
DupeType dupetype = dupDict.GetDuplicateStatus(datItem, savedItem);
|
||||
|
||||
// Disks, File, Media, and Roms have more information to fill
|
||||
if (datItem is Disk diskItem && savedItem is Disk savedDisk)
|
||||
savedDisk.FillMissingInformation(diskItem);
|
||||
else if (datItem is DatItems.Formats.File fileItem && savedItem is DatItems.Formats.File savedFile)
|
||||
savedFile.FillMissingInformation(fileItem);
|
||||
else if (datItem is Media mediaItem && savedItem is Media savedMedia)
|
||||
savedMedia.FillMissingInformation(mediaItem);
|
||||
else if (datItem is Rom romItem && savedItem is Rom savedRom)
|
||||
savedRom.FillMissingInformation(romItem);
|
||||
|
||||
// Set the duplicate type on the saved item
|
||||
savedItem.SetFieldValue(DatItem.DupeTypeKey, dupetype);
|
||||
|
||||
// Get the sources associated with the items
|
||||
var savedSource = savedItem.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
var itemSource = datItem.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
|
||||
// Get the machines associated with the items
|
||||
var savedMachine = savedItem.GetMachine();
|
||||
var itemMachine = datItem.GetMachine();
|
||||
|
||||
// If the current source has a lower ID than the saved, use the saved source
|
||||
if (itemSource?.Index < savedSource?.Index)
|
||||
{
|
||||
datItem.SetFieldValue<Source?>(DatItem.SourceKey, savedSource.Clone() as Source);
|
||||
savedItem.CopyMachineInformation(datItem);
|
||||
savedItem.SetName(datItem.GetName());
|
||||
}
|
||||
|
||||
// If the saved machine is a child of the current machine, use the current machine instead
|
||||
if (savedMachine?.GetStringFieldValue(Data.Models.Metadata.Machine.CloneOfKey) == itemMachine?.GetName()
|
||||
|| savedMachine?.GetStringFieldValue(Data.Models.Metadata.Machine.RomOfKey) == itemMachine?.GetName())
|
||||
{
|
||||
savedItem.CopyMachineInformation(datItem);
|
||||
savedItem.SetName(datItem.GetName());
|
||||
}
|
||||
|
||||
// Replace the original item in the list
|
||||
output.RemoveAt(pos);
|
||||
output.Insert(pos, savedItem);
|
||||
}
|
||||
|
||||
// Then return the result
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all duplicates found in a DAT based on a DatItem
|
||||
/// </summary>
|
||||
/// <param name="datItem">Item to try to match</param>
|
||||
/// <param name="sorted">True if the DAT is already sorted accordingly, false otherwise (default)</param>
|
||||
/// <returns>List of matched DatItem objects</returns>
|
||||
/// <remarks>This also sets the remove flag on any duplicates found</remarks>
|
||||
/// TODO: Figure out if removal should be a flag or just removed entirely
|
||||
internal List<DatItem> GetDuplicates(DatItem datItem, bool sorted = false)
|
||||
{
|
||||
// Check for an empty rom list first
|
||||
if (DatStatistics.TotalCount == 0)
|
||||
return [];
|
||||
|
||||
// We want to get the proper key for the DatItem
|
||||
string key = SortAndGetKey(datItem, sorted);
|
||||
|
||||
// Get the items for the current key, if possible
|
||||
List<DatItem> items = GetItemsForBucket(key, filter: false);
|
||||
if (items.Count == 0)
|
||||
return [];
|
||||
|
||||
// Try to find duplicates
|
||||
List<DatItem> output = [];
|
||||
foreach (DatItem other in items)
|
||||
{
|
||||
// Skip items marked for removal
|
||||
if (other.GetBoolFieldValue(DatItem.RemoveKey) == true)
|
||||
continue;
|
||||
|
||||
// Mark duplicates for future removal
|
||||
if (datItem.Equals(other))
|
||||
{
|
||||
other.SetFieldValue<bool?>(DatItem.RemoveKey, true);
|
||||
output.Add(other);
|
||||
}
|
||||
}
|
||||
|
||||
// Return any matching items
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a DAT contains the given DatItem
|
||||
/// </summary>
|
||||
/// <param name="datItem">Item to try to match</param>
|
||||
/// <param name="sorted">True if the DAT is already sorted accordingly, false otherwise (default)</param>
|
||||
/// <returns>True if it contains the rom, false otherwise</returns>
|
||||
internal bool HasDuplicates(DatItem datItem, bool sorted = false)
|
||||
{
|
||||
// Check for an empty rom list first
|
||||
if (DatStatistics.TotalCount == 0)
|
||||
return false;
|
||||
|
||||
// We want to get the proper key for the DatItem
|
||||
string key = SortAndGetKey(datItem, sorted);
|
||||
|
||||
// Try to find duplicates
|
||||
List<DatItem> roms = GetItemsForBucket(key);
|
||||
if (roms.Count == 0)
|
||||
return false;
|
||||
|
||||
return roms.FindIndex(datItem.Equals) > -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure the key exists in the items dictionary
|
||||
/// </summary>
|
||||
/// <param name="key">Key to ensure</param>
|
||||
private void EnsureBucketingKey(string key)
|
||||
{
|
||||
// If the key is missing from the dictionary, add it
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
_items.GetOrAdd(key, []);
|
||||
#else
|
||||
if (!_items.ContainsKey(key))
|
||||
_items[key] = [];
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the highest-order Field value that represents the statistics
|
||||
/// </summary>
|
||||
private ItemKey GetBestAvailable()
|
||||
{
|
||||
// Get the required counts
|
||||
long diskCount = DatStatistics.GetItemCount(ItemType.Disk);
|
||||
long mediaCount = DatStatistics.GetItemCount(ItemType.Media);
|
||||
long romCount = DatStatistics.GetItemCount(ItemType.Rom);
|
||||
long nodumpCount = DatStatistics.GetStatusCount(ItemStatus.Nodump);
|
||||
|
||||
// If all items are supposed to have a SHA-512, we bucket by that
|
||||
if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.SHA512))
|
||||
return ItemKey.SHA512;
|
||||
|
||||
// If all items are supposed to have a SHA-384, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.SHA384))
|
||||
return ItemKey.SHA384;
|
||||
|
||||
// If all items are supposed to have a SHA-256, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.SHA256))
|
||||
return ItemKey.SHA256;
|
||||
|
||||
// If all items are supposed to have a SHA-1, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.SHA1))
|
||||
return ItemKey.SHA1;
|
||||
|
||||
// If all items are supposed to have a RIPEMD160, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.RIPEMD160))
|
||||
return ItemKey.RIPEMD160;
|
||||
|
||||
// If all items are supposed to have a RIPEMD128, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.RIPEMD128))
|
||||
return ItemKey.RIPEMD128;
|
||||
|
||||
// If all items are supposed to have a MD5, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.MD5))
|
||||
return ItemKey.MD5;
|
||||
|
||||
// If all items are supposed to have a MD4, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.MD4))
|
||||
return ItemKey.MD4;
|
||||
|
||||
// If all items are supposed to have a MD2, we bucket by that
|
||||
else if (diskCount + mediaCount + romCount - nodumpCount == DatStatistics.GetHashCount(HashType.MD2))
|
||||
return ItemKey.MD2;
|
||||
|
||||
// Otherwise, we bucket by CRC
|
||||
else
|
||||
return ItemKey.CRC;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the bucketing key for a given item
|
||||
/// <param name="datItem">The current item</param>
|
||||
/// <param name="bucketBy">ItemKey value representing what key to get</param>
|
||||
/// <param name="lower">True if the key should be lowercased, false otherwise</param>
|
||||
/// <param name="norename">True if games should only be compared on game and file name, false if system and source are counted</param>
|
||||
/// </summary>
|
||||
private static string GetBucketKey(DatItem datItem, ItemKey bucketBy, bool lower, bool norename)
|
||||
{
|
||||
if (datItem is null)
|
||||
return string.Empty;
|
||||
|
||||
// Treat NULL like machine
|
||||
if (bucketBy == ItemKey.NULL)
|
||||
bucketBy = ItemKey.Machine;
|
||||
|
||||
// Get the machine and source
|
||||
var machine = datItem.GetMachine();
|
||||
var source = datItem.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
|
||||
// Get the bucket key
|
||||
return datItem.GetKey(bucketBy, machine, source, lower, norename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform bucketing based on the item key provided
|
||||
/// </summary>
|
||||
/// <param name="bucketBy">ItemKey enum representing how to bucket the individual items</param>
|
||||
/// <param name="lower">True if the key should be lowercased, false otherwise</param>
|
||||
/// <param name="norename">True if games should only be compared on game and file name, false if system and source are counted</param>
|
||||
private void PerformBucketing(ItemKey bucketBy, bool lower, bool norename)
|
||||
{
|
||||
// Set the sorted type
|
||||
_bucketedBy = bucketBy;
|
||||
|
||||
// First do the initial sort of all of the roms inplace
|
||||
List<string> oldkeys = [.. SortedKeys];
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.For(0, oldkeys.Count, Core.Globals.ParallelOptions, k =>
|
||||
#elif NET40_OR_GREATER
|
||||
Parallel.For(0, oldkeys.Count, k =>
|
||||
#else
|
||||
for (int k = 0; k < oldkeys.Count; k++)
|
||||
#endif
|
||||
{
|
||||
string key = oldkeys[k];
|
||||
if (GetItemsForBucket(key, filter: true).Count == 0)
|
||||
RemoveBucket(key);
|
||||
|
||||
// Now add each of the roms to their respective keys
|
||||
for (int i = 0; i < GetItemsForBucket(key).Count; i++)
|
||||
{
|
||||
DatItem item = GetItemsForBucket(key)[i];
|
||||
if (item is null || item.GetBoolFieldValue(DatItem.RemoveKey) == true)
|
||||
continue;
|
||||
|
||||
// Get the machine and source
|
||||
var machine = item.GetMachine();
|
||||
var source = item.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
|
||||
// We want to get the key most appropriate for the given sorting type
|
||||
string newkey = item.GetKey(bucketBy, machine, source, lower, norename);
|
||||
|
||||
// If the key is different, move the item to the new key
|
||||
if (newkey != key)
|
||||
{
|
||||
AddItem(newkey, item);
|
||||
bool removed = RemoveItem(key, item, i);
|
||||
if (!removed)
|
||||
continue;
|
||||
|
||||
i--; // This make sure that the pointer stays on the correct since one was removed
|
||||
}
|
||||
}
|
||||
|
||||
// If the key is now empty, remove it
|
||||
if (GetItemsForBucket(key, filter: true).Count == 0)
|
||||
RemoveBucket(key);
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform inplace sorting of the dictionary
|
||||
/// </summary>
|
||||
private void PerformSorting(bool norename)
|
||||
{
|
||||
#if NET452_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
Parallel.ForEach(SortedKeys, Core.Globals.ParallelOptions, key =>
|
||||
#elif NET40_OR_GREATER
|
||||
Parallel.ForEach(SortedKeys, key =>
|
||||
#else
|
||||
foreach (var key in SortedKeys)
|
||||
#endif
|
||||
{
|
||||
// Get the possibly unsorted list
|
||||
List<DatItem> sortedList = GetItemsForBucket(key);
|
||||
|
||||
// Sort the list of items to be consistent
|
||||
Sort(ref sortedList, norename);
|
||||
|
||||
// Add the list back to the dictionary
|
||||
RemoveBucket(key);
|
||||
sortedList.ForEach(item => AddItem(key, item));
|
||||
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sort a list of DatItem objects by SourceID, Game, and Name (in order)
|
||||
/// </summary>
|
||||
/// <param name="items">List of DatItem objects representing the items to be sorted</param>
|
||||
/// <param name="norename">True if files are not renamed, false otherwise</param>
|
||||
/// <returns>True if it sorted correctly, false otherwise</returns>
|
||||
private bool Sort(ref List<DatItem> items, bool norename)
|
||||
{
|
||||
// Create the comparer extenal to the delegate
|
||||
var nc = new NaturalComparer();
|
||||
|
||||
// Sort by machine, type, item name, and source
|
||||
items.Sort(delegate (DatItem x, DatItem y)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Compare on source if renaming
|
||||
if (!norename)
|
||||
{
|
||||
int xSourceIndex = x.GetFieldValue<Source?>(DatItem.SourceKey)?.Index ?? 0;
|
||||
int ySourceIndex = y.GetFieldValue<Source?>(DatItem.SourceKey)?.Index ?? 0;
|
||||
if (xSourceIndex != ySourceIndex)
|
||||
return xSourceIndex - ySourceIndex;
|
||||
}
|
||||
|
||||
// Get the machines
|
||||
Machine? xMachine = x.GetMachine();
|
||||
Machine? yMachine = y.GetMachine();
|
||||
|
||||
// If machine names don't match
|
||||
string? xMachineName = xMachine?.GetName();
|
||||
string? yMachineName = yMachine?.GetName();
|
||||
if (xMachineName != yMachineName)
|
||||
return nc.Compare(xMachineName, yMachineName);
|
||||
|
||||
// If types don't match
|
||||
string? xType = x.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey);
|
||||
string? yType = y.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey);
|
||||
if (xType != yType)
|
||||
return xType.AsItemType() - yType.AsItemType();
|
||||
|
||||
// If directory names don't match
|
||||
string? xDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(x.GetName() ?? string.Empty));
|
||||
string? yDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(y.GetName() ?? string.Empty));
|
||||
if (xDirectoryName != yDirectoryName)
|
||||
return nc.Compare(xDirectoryName, yDirectoryName);
|
||||
|
||||
// If item names don't match
|
||||
string? xName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(x.GetName() ?? string.Empty));
|
||||
string? yName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(y.GetName() ?? string.Empty));
|
||||
return nc.Compare(xName, yName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Absorb the error
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sort the input DAT and get the key to be used by the item
|
||||
/// </summary>
|
||||
/// <param name="datItem">Item to try to match</param>
|
||||
/// <param name="sorted">True if the DAT is already sorted accordingly, false otherwise (default)</param>
|
||||
/// <returns>Key to try to use</returns>
|
||||
private string SortAndGetKey(DatItem datItem, bool sorted = false)
|
||||
{
|
||||
// If we're not already sorted, take care of it
|
||||
if (!sorted)
|
||||
BucketBy(GetBestAvailable());
|
||||
|
||||
// Now that we have the sorted type, we get the proper key
|
||||
return GetBucketKey(datItem, _bucketedBy, lower: true, norename: true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Statistics
|
||||
|
||||
/// <summary>
|
||||
/// Recalculate the statistics for the Dat
|
||||
/// </summary>
|
||||
public void RecalculateStats()
|
||||
{
|
||||
// Wipe out any stats already there
|
||||
DatStatistics.ResetStatistics();
|
||||
|
||||
// If we have a blank Dat in any way, return
|
||||
if (_items is null || _items.Count == 0)
|
||||
return;
|
||||
|
||||
// Loop through and add
|
||||
foreach (string key in _items.Keys)
|
||||
{
|
||||
List<DatItem>? datItems = _items[key];
|
||||
if (datItems is null)
|
||||
continue;
|
||||
|
||||
foreach (DatItem item in datItems)
|
||||
{
|
||||
DatStatistics.AddItemStatistics(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1263
SabreTools.Metadata.DatFiles/ItemDictionaryDB.cs
Normal file
1263
SabreTools.Metadata.DatFiles/ItemDictionaryDB.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
SabreTools.Metadata.DatFiles/ItemMappings.cs
Normal file
12
SabreTools.Metadata.DatFiles/ItemMappings.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace SabreTools.Metadata.DatFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Class used during deduplication
|
||||
/// </summary>
|
||||
public struct ItemMappings(DatItems.DatItem item, long machineId, long sourceId)
|
||||
{
|
||||
public DatItems.DatItem Item = item;
|
||||
public long MachineId = machineId;
|
||||
public long SourceId = sourceId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>2.3.0</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>DatFile specific functionality for metadata file processing</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2016-2026</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/SabreTools/SabreTools.Serialization</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>metadata dat datfile</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="SabreTools.Metadata.DatFiles.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Data.Extensions\SabreTools.Data.Extensions.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Metadata\SabreTools.Metadata.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Metadata.DatItems\SabreTools.Metadata.DatItems.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Metadata.Filter\SabreTools.Metadata.Filter.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Serialization.CrossModel\SabreTools.Serialization.CrossModel.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Serialization.Readers\SabreTools.Serialization.Readers.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Serialization.Writers\SabreTools.Serialization.Writers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="[2.0.0]" />
|
||||
<PackageReference Include="SabreTools.IO" Version="[2.0.0]" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
37
SabreTools.Metadata.DatItems.Test/ConvertersTests.cs
Normal file
37
SabreTools.Metadata.DatItems.Test/ConvertersTests.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using SabreTools.Metadata.Tools;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Test
|
||||
{
|
||||
public class ConvertersTests
|
||||
{
|
||||
#region Generators
|
||||
|
||||
[Theory]
|
||||
[InlineData(ChipType.NULL, 2)]
|
||||
[InlineData(ControlType.NULL, 15)]
|
||||
[InlineData(DeviceType.NULL, 21)]
|
||||
[InlineData(DisplayType.NULL, 5)]
|
||||
[InlineData(Endianness.NULL, 2)]
|
||||
[InlineData(FeatureStatus.NULL, 2)]
|
||||
[InlineData(FeatureType.NULL, 14)]
|
||||
[InlineData(ItemStatus.NULL, 7)]
|
||||
[InlineData(ItemType.NULL, 54)]
|
||||
[InlineData(LoadFlag.NULL, 14)]
|
||||
[InlineData(MachineType.None, 6)]
|
||||
[InlineData(OpenMSXSubType.NULL, 3)]
|
||||
[InlineData(Relation.NULL, 6)]
|
||||
[InlineData(Runnable.NULL, 3)]
|
||||
[InlineData(SoftwareListStatus.None, 3)]
|
||||
[InlineData(Supported.NULL, 5)]
|
||||
[InlineData(SupportStatus.NULL, 3)]
|
||||
public void GenerateToEnumTest<T>(T value, int expected)
|
||||
{
|
||||
var actual = Converters.GenerateToEnum<T>();
|
||||
Assert.Equal(default, value);
|
||||
Assert.Equal(expected, actual.Keys.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
539
SabreTools.Metadata.DatItems.Test/DatItemTests.cs
Normal file
539
SabreTools.Metadata.DatItems.Test/DatItemTests.cs
Normal file
@@ -0,0 +1,539 @@
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Test
|
||||
{
|
||||
public class DatItemTests
|
||||
{
|
||||
#region Private Testing Classes
|
||||
|
||||
/// <summary>
|
||||
/// Testing implementation of Data.Models.Metadata.DatItem
|
||||
/// </summary>
|
||||
private class TestDatItemModel : Data.Models.Metadata.DatItem
|
||||
{
|
||||
public const string NameKey = "__NAME__";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Testing implementation of DatItem
|
||||
/// </summary>
|
||||
private class TestDatItem : DatItem<TestDatItemModel>
|
||||
{
|
||||
private readonly string? _nameKey;
|
||||
|
||||
protected override ItemType ItemType => ItemType.Blank;
|
||||
|
||||
public TestDatItem() => _nameKey = TestDatItemModel.NameKey;
|
||||
|
||||
public TestDatItem(string? nameKey) => _nameKey = nameKey;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? GetName() => _nameKey is not null ? _internal.ReadString(_nameKey) : null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetName(string? name)
|
||||
{
|
||||
if (_nameKey is not null)
|
||||
_internal[_nameKey] = name;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CopyMachineInformation
|
||||
|
||||
[Fact]
|
||||
public void CopyMachineInformation_NewItem_Overwrite()
|
||||
{
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("machineA");
|
||||
|
||||
var romA = new Rom();
|
||||
|
||||
var romB = new Rom();
|
||||
romB.RemoveField(DatItem.MachineKey);
|
||||
|
||||
romA.CopyMachineInformation(romB);
|
||||
var actualMachineA = romA.GetMachine();
|
||||
Assert.NotNull(actualMachineA);
|
||||
Assert.Null(actualMachineA.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyMachineInformation_EmptyItem_NoChange()
|
||||
{
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("machineA");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetFieldValue(DatItem.MachineKey, machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.RemoveField(DatItem.MachineKey);
|
||||
|
||||
romA.CopyMachineInformation(romB);
|
||||
var actualMachineA = romA.GetMachine();
|
||||
Assert.NotNull(actualMachineA);
|
||||
Assert.Equal("machineA", actualMachineA.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyMachineInformation_NullMachine_NoChange()
|
||||
{
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("machineA");
|
||||
|
||||
Machine? machineB = null;
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetFieldValue(DatItem.MachineKey, machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetFieldValue(DatItem.MachineKey, machineB);
|
||||
|
||||
romA.CopyMachineInformation(romB);
|
||||
var actualMachineA = romA.GetMachine();
|
||||
Assert.NotNull(actualMachineA);
|
||||
Assert.Equal("machineA", actualMachineA.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyMachineInformation_EmptyMachine_Overwrite()
|
||||
{
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("machineA");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetFieldValue(DatItem.MachineKey, machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetFieldValue(DatItem.MachineKey, machineB);
|
||||
|
||||
romA.CopyMachineInformation(romB);
|
||||
var actualMachineA = romA.GetMachine();
|
||||
Assert.NotNull(actualMachineA);
|
||||
Assert.Null(actualMachineA.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyMachineInformation_FilledMachine_Overwrite()
|
||||
{
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("machineA");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
machineB.SetName("machineB");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetFieldValue(DatItem.MachineKey, machineA);
|
||||
|
||||
var romB = new Rom();
|
||||
romB.SetFieldValue(DatItem.MachineKey, machineB);
|
||||
|
||||
romA.CopyMachineInformation(romB);
|
||||
var actualMachineA = romA.GetMachine();
|
||||
Assert.NotNull(actualMachineA);
|
||||
Assert.Equal("machineB", actualMachineA.GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyMachineInformation_MismatchedType_Overwrite()
|
||||
{
|
||||
Machine? machineA = new Machine();
|
||||
machineA.SetName("machineA");
|
||||
|
||||
Machine? machineB = new Machine();
|
||||
machineB.SetName("machineB");
|
||||
|
||||
var romA = new Rom();
|
||||
romA.SetFieldValue(DatItem.MachineKey, machineA);
|
||||
|
||||
var diskB = new Disk();
|
||||
diskB.SetFieldValue(DatItem.MachineKey, machineB);
|
||||
|
||||
romA.CopyMachineInformation(diskB);
|
||||
var actualMachineA = romA.GetMachine();
|
||||
Assert.NotNull(actualMachineA);
|
||||
Assert.Equal("machineB", actualMachineA.GetName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CompareTo
|
||||
|
||||
[Fact]
|
||||
public void CompareTo_NullOther_Returns1()
|
||||
{
|
||||
DatItem self = new Rom();
|
||||
DatItem? other = null;
|
||||
|
||||
int actual = self.CompareTo(other);
|
||||
Assert.Equal(1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareTo_DifferentOther_Returns1()
|
||||
{
|
||||
DatItem self = new Rom();
|
||||
self.SetName("name");
|
||||
|
||||
DatItem? other = new Disk();
|
||||
other.SetName("name");
|
||||
|
||||
int actual = self.CompareTo(other);
|
||||
Assert.Equal(1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareTo_Empty_Returns1()
|
||||
{
|
||||
DatItem self = new Rom();
|
||||
DatItem? other = new Rom();
|
||||
|
||||
int actual = self.CompareTo(other);
|
||||
Assert.Equal(1, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, 0)]
|
||||
[InlineData("name", null, 1)]
|
||||
[InlineData("name", "other", -1)]
|
||||
[InlineData(null, "name", -1)]
|
||||
[InlineData("other", "name", 1)]
|
||||
[InlineData("name", "name", 0)]
|
||||
public void CompareTo_NamesOnly(string? selfName, string? otherName, int expected)
|
||||
{
|
||||
DatItem self = new Rom();
|
||||
self.SetName(selfName);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
|
||||
DatItem? other = new Rom();
|
||||
other.SetName(otherName);
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
|
||||
int actual = self.CompareTo(other);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equals
|
||||
|
||||
[Fact]
|
||||
public void Equals_Null_False()
|
||||
{
|
||||
DatItem self = new TestDatItem();
|
||||
DatItem? other = null;
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_MismatchedType_False()
|
||||
{
|
||||
DatItem self = new TestDatItem();
|
||||
DatItem? other = new Rom();
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_DefaultInternal_True()
|
||||
{
|
||||
DatItem self = new TestDatItem();
|
||||
DatItem? other = new TestDatItem();
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_MismatchedInternal_False()
|
||||
{
|
||||
DatItem self = new TestDatItem();
|
||||
self.SetName("self");
|
||||
|
||||
DatItem? other = new TestDatItem();
|
||||
other.SetName("other");
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_EqualInternal_True()
|
||||
{
|
||||
DatItem self = new TestDatItem();
|
||||
self.SetName("name");
|
||||
|
||||
DatItem? other = new TestDatItem();
|
||||
other.SetName("name");
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Change when Machine retrieval gets fixed
|
||||
#region GetKey
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemKey.NULL, false, false, "")]
|
||||
[InlineData(ItemKey.NULL, false, true, "")]
|
||||
[InlineData(ItemKey.NULL, true, false, "")]
|
||||
[InlineData(ItemKey.NULL, true, true, "")]
|
||||
[InlineData(ItemKey.Machine, false, false, "0000000000-Machine")]
|
||||
[InlineData(ItemKey.Machine, false, true, "Machine")]
|
||||
[InlineData(ItemKey.Machine, true, false, "0000000000-machine")]
|
||||
[InlineData(ItemKey.Machine, true, true, "machine")]
|
||||
[InlineData(ItemKey.CRC, false, false, "00000000")]
|
||||
[InlineData(ItemKey.CRC, false, true, "00000000")]
|
||||
[InlineData(ItemKey.CRC, true, false, "00000000")]
|
||||
[InlineData(ItemKey.CRC, true, true, "00000000")]
|
||||
[InlineData(ItemKey.MD2, false, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, false, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD4, false, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, false, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD5, false, false, "d41d8cd98f00b204e9800998ecf8427e")]
|
||||
[InlineData(ItemKey.MD5, false, true, "d41d8cd98f00b204e9800998ecf8427e")]
|
||||
[InlineData(ItemKey.MD5, true, false, "d41d8cd98f00b204e9800998ecf8427e")]
|
||||
[InlineData(ItemKey.MD5, true, true, "d41d8cd98f00b204e9800998ecf8427e")]
|
||||
[InlineData(ItemKey.RIPEMD128, false, false, "cdf26213a150dc3ecb610f18f6b38b46")]
|
||||
[InlineData(ItemKey.RIPEMD128, false, true, "cdf26213a150dc3ecb610f18f6b38b46")]
|
||||
[InlineData(ItemKey.RIPEMD128, true, false, "cdf26213a150dc3ecb610f18f6b38b46")]
|
||||
[InlineData(ItemKey.RIPEMD128, true, true, "cdf26213a150dc3ecb610f18f6b38b46")]
|
||||
[InlineData(ItemKey.RIPEMD160, false, false, "9c1185a5c5e9fc54612808977ee8f548b2258d31")]
|
||||
[InlineData(ItemKey.RIPEMD160, false, true, "9c1185a5c5e9fc54612808977ee8f548b2258d31")]
|
||||
[InlineData(ItemKey.RIPEMD160, true, false, "9c1185a5c5e9fc54612808977ee8f548b2258d31")]
|
||||
[InlineData(ItemKey.RIPEMD160, true, true, "9c1185a5c5e9fc54612808977ee8f548b2258d31")]
|
||||
[InlineData(ItemKey.SHA1, false, false, "da39a3ee5e6b4b0d3255bfef95601890afd80709")]
|
||||
[InlineData(ItemKey.SHA1, false, true, "da39a3ee5e6b4b0d3255bfef95601890afd80709")]
|
||||
[InlineData(ItemKey.SHA1, true, false, "da39a3ee5e6b4b0d3255bfef95601890afd80709")]
|
||||
[InlineData(ItemKey.SHA1, true, true, "da39a3ee5e6b4b0d3255bfef95601890afd80709")]
|
||||
[InlineData(ItemKey.SHA256, false, false, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA256, false, true, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA256, true, false, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA256, true, true, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA384, false, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, false, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA512, false, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, false, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SpamSum, false, false, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, false, true, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, true, false, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, true, true, "3::")]
|
||||
public void GetKeyDB_DefaultImplementation(ItemKey bucketedBy, bool lower, bool norename, string expected)
|
||||
{
|
||||
Source source = new Source(0);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "Machine");
|
||||
|
||||
DatItem datItem = new Blank();
|
||||
|
||||
string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemKey.NULL, false, false, "")]
|
||||
[InlineData(ItemKey.NULL, false, true, "")]
|
||||
[InlineData(ItemKey.NULL, true, false, "")]
|
||||
[InlineData(ItemKey.NULL, true, true, "")]
|
||||
[InlineData(ItemKey.Machine, false, false, "0000000000-Machine")]
|
||||
[InlineData(ItemKey.Machine, false, true, "Machine")]
|
||||
[InlineData(ItemKey.Machine, true, false, "0000000000-machine")]
|
||||
[InlineData(ItemKey.Machine, true, true, "machine")]
|
||||
[InlineData(ItemKey.CRC, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.CRC, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.CRC, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.CRC, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.MD2, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD2, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD2, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD2, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.MD4, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD4, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD4, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD4, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.MD5, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD5, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD128, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD128, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD128, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD128, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD160, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD160, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD160, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD160, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA256, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA256, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA384, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA384, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA384, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA384, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA512, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA512, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA512, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA512, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SpamSum, false, false, "BASE64")]
|
||||
[InlineData(ItemKey.SpamSum, false, true, "BASE64")]
|
||||
[InlineData(ItemKey.SpamSum, true, false, "base64")]
|
||||
[InlineData(ItemKey.SpamSum, true, true, "base64")]
|
||||
public void GetKeyDB_CustomImplementation(ItemKey bucketedBy, bool lower, bool norename, string expected)
|
||||
{
|
||||
Source source = new Source(0);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "Machine");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, "BASE64");
|
||||
|
||||
string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetName
|
||||
|
||||
[Fact]
|
||||
public void GetName_NoNameKey_Null()
|
||||
{
|
||||
DatItem item = new TestDatItem(nameKey: null);
|
||||
item.SetFieldValue(TestDatItemModel.NameKey, "name");
|
||||
|
||||
string? actual = item.GetName();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetName_EmptyNameKey_Null()
|
||||
{
|
||||
DatItem item = new TestDatItem(nameKey: string.Empty);
|
||||
item.SetFieldValue(TestDatItemModel.NameKey, "name");
|
||||
|
||||
string? actual = item.GetName();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetName_NameKeyNotExists_Null()
|
||||
{
|
||||
DatItem item = new TestDatItem(nameKey: "INVALID");
|
||||
item.SetFieldValue(TestDatItemModel.NameKey, "name");
|
||||
|
||||
string? actual = item.GetName();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetName_NameKeyExists_Filled()
|
||||
{
|
||||
DatItem item = new TestDatItem(nameKey: TestDatItemModel.NameKey);
|
||||
item.SetFieldValue(TestDatItemModel.NameKey, "name");
|
||||
|
||||
string? actual = item.GetName();
|
||||
Assert.Equal("name", actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetName
|
||||
|
||||
[Fact]
|
||||
public void SetName_NoNameKey_Null()
|
||||
{
|
||||
DatItem item = new TestDatItem(nameKey: null);
|
||||
item.SetName("name");
|
||||
|
||||
string? actual = item.GetName();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetName_EmptyNameKey_Null()
|
||||
{
|
||||
DatItem item = new TestDatItem(nameKey: string.Empty);
|
||||
item.SetName("name");
|
||||
|
||||
string? actual = item.GetName();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetName_NameKeyNonEmpty_Filled()
|
||||
{
|
||||
DatItem item = new TestDatItem(nameKey: TestDatItemModel.NameKey);
|
||||
item.SetName("name");
|
||||
|
||||
string? actual = item.GetName();
|
||||
Assert.Equal("name", actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Clone
|
||||
|
||||
[Fact]
|
||||
public void CloneTest()
|
||||
{
|
||||
DatItem item = new Sample();
|
||||
item.SetName("name");
|
||||
|
||||
object clone = item.Clone();
|
||||
Sample? actual = clone as Sample;
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal("name", actual.GetName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetInternalClone
|
||||
|
||||
[Fact]
|
||||
public void GetInternalCloneTest()
|
||||
{
|
||||
DatItem<TestDatItemModel> item = new TestDatItem();
|
||||
item.SetName("name");
|
||||
|
||||
TestDatItemModel actual = item.GetInternalClone();
|
||||
Assert.Equal("name", actual[TestDatItemModel.NameKey]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
590
SabreTools.Metadata.DatItems.Test/ExtensionsTests.cs
Normal file
590
SabreTools.Metadata.DatItems.Test/ExtensionsTests.cs
Normal file
@@ -0,0 +1,590 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Test
|
||||
{
|
||||
public class ExtensionsTests
|
||||
{
|
||||
#region String to Enum
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, ChipType.NULL)]
|
||||
[InlineData("cpu", ChipType.CPU)]
|
||||
[InlineData("audio", ChipType.Audio)]
|
||||
public void AsChipTypeTest(string? field, ChipType expected)
|
||||
{
|
||||
ChipType actual = field.AsChipType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, ControlType.NULL)]
|
||||
[InlineData("joy", ControlType.Joy)]
|
||||
[InlineData("stick", ControlType.Stick)]
|
||||
[InlineData("paddle", ControlType.Paddle)]
|
||||
[InlineData("pedal", ControlType.Pedal)]
|
||||
[InlineData("lightgun", ControlType.Lightgun)]
|
||||
[InlineData("positional", ControlType.Positional)]
|
||||
[InlineData("dial", ControlType.Dial)]
|
||||
[InlineData("trackball", ControlType.Trackball)]
|
||||
[InlineData("mouse", ControlType.Mouse)]
|
||||
[InlineData("only_buttons", ControlType.OnlyButtons)]
|
||||
[InlineData("keypad", ControlType.Keypad)]
|
||||
[InlineData("keyboard", ControlType.Keyboard)]
|
||||
[InlineData("mahjong", ControlType.Mahjong)]
|
||||
[InlineData("hanafuda", ControlType.Hanafuda)]
|
||||
[InlineData("gambling", ControlType.Gambling)]
|
||||
public void AsControlTypeTest(string? field, ControlType expected)
|
||||
{
|
||||
ControlType actual = field.AsControlType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, DeviceType.NULL)]
|
||||
[InlineData("unknown", DeviceType.Unknown)]
|
||||
[InlineData("cartridge", DeviceType.Cartridge)]
|
||||
[InlineData("floppydisk", DeviceType.FloppyDisk)]
|
||||
[InlineData("harddisk", DeviceType.HardDisk)]
|
||||
[InlineData("cylinder", DeviceType.Cylinder)]
|
||||
[InlineData("cassette", DeviceType.Cassette)]
|
||||
[InlineData("punchcard", DeviceType.PunchCard)]
|
||||
[InlineData("punchtape", DeviceType.PunchTape)]
|
||||
[InlineData("printout", DeviceType.Printout)]
|
||||
[InlineData("serial", DeviceType.Serial)]
|
||||
[InlineData("parallel", DeviceType.Parallel)]
|
||||
[InlineData("snapshot", DeviceType.Snapshot)]
|
||||
[InlineData("quickload", DeviceType.QuickLoad)]
|
||||
[InlineData("memcard", DeviceType.MemCard)]
|
||||
[InlineData("cdrom", DeviceType.CDROM)]
|
||||
[InlineData("magtape", DeviceType.MagTape)]
|
||||
[InlineData("romimage", DeviceType.ROMImage)]
|
||||
[InlineData("midiin", DeviceType.MIDIIn)]
|
||||
[InlineData("midiout", DeviceType.MIDIOut)]
|
||||
[InlineData("picture", DeviceType.Picture)]
|
||||
[InlineData("vidfile", DeviceType.VidFile)]
|
||||
public void AsDeviceTypeTest(string? field, DeviceType expected)
|
||||
{
|
||||
DeviceType actual = field.AsDeviceType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, DisplayType.NULL)]
|
||||
[InlineData("raster", DisplayType.Raster)]
|
||||
[InlineData("vector", DisplayType.Vector)]
|
||||
[InlineData("lcd", DisplayType.LCD)]
|
||||
[InlineData("svg", DisplayType.SVG)]
|
||||
[InlineData("unknown", DisplayType.Unknown)]
|
||||
public void AsDisplayTypeTest(string? field, DisplayType expected)
|
||||
{
|
||||
DisplayType actual = field.AsDisplayType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, Endianness.NULL)]
|
||||
[InlineData("big", Endianness.Big)]
|
||||
[InlineData("little", Endianness.Little)]
|
||||
public void AsEndiannessTest(string? field, Endianness expected)
|
||||
{
|
||||
Endianness actual = field.AsEndianness();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, FeatureStatus.NULL)]
|
||||
[InlineData("unemulated", FeatureStatus.Unemulated)]
|
||||
[InlineData("imperfect", FeatureStatus.Imperfect)]
|
||||
public void AsFeatureStatusTest(string? field, FeatureStatus expected)
|
||||
{
|
||||
FeatureStatus actual = field.AsFeatureStatus();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, FeatureType.NULL)]
|
||||
[InlineData("protection", FeatureType.Protection)]
|
||||
[InlineData("palette", FeatureType.Palette)]
|
||||
[InlineData("graphics", FeatureType.Graphics)]
|
||||
[InlineData("sound", FeatureType.Sound)]
|
||||
[InlineData("controls", FeatureType.Controls)]
|
||||
[InlineData("keyboard", FeatureType.Keyboard)]
|
||||
[InlineData("mouse", FeatureType.Mouse)]
|
||||
[InlineData("microphone", FeatureType.Microphone)]
|
||||
[InlineData("camera", FeatureType.Camera)]
|
||||
[InlineData("disk", FeatureType.Disk)]
|
||||
[InlineData("printer", FeatureType.Printer)]
|
||||
[InlineData("lan", FeatureType.Lan)]
|
||||
[InlineData("wan", FeatureType.Wan)]
|
||||
[InlineData("timing", FeatureType.Timing)]
|
||||
public void AsFeatureTypeTest(string? field, FeatureType expected)
|
||||
{
|
||||
FeatureType actual = field.AsFeatureType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, ItemStatus.NULL)]
|
||||
[InlineData("none", ItemStatus.None)]
|
||||
[InlineData("no", ItemStatus.None)]
|
||||
[InlineData("good", ItemStatus.Good)]
|
||||
[InlineData("baddump", ItemStatus.BadDump)]
|
||||
[InlineData("nodump", ItemStatus.Nodump)]
|
||||
[InlineData("yes", ItemStatus.Nodump)]
|
||||
[InlineData("verified", ItemStatus.Verified)]
|
||||
public void AsItemStatusTest(string? field, ItemStatus expected)
|
||||
{
|
||||
ItemStatus actual = field.AsItemStatus();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, ItemType.NULL)]
|
||||
[InlineData("adjuster", ItemType.Adjuster)]
|
||||
[InlineData("analog", ItemType.Analog)]
|
||||
[InlineData("archive", ItemType.Archive)]
|
||||
[InlineData("biosset", ItemType.BiosSet)]
|
||||
[InlineData("blank", ItemType.Blank)]
|
||||
[InlineData("chip", ItemType.Chip)]
|
||||
[InlineData("condition", ItemType.Condition)]
|
||||
[InlineData("configuration", ItemType.Configuration)]
|
||||
[InlineData("conflocation", ItemType.ConfLocation)]
|
||||
[InlineData("confsetting", ItemType.ConfSetting)]
|
||||
[InlineData("control", ItemType.Control)]
|
||||
[InlineData("dataarea", ItemType.DataArea)]
|
||||
[InlineData("device", ItemType.Device)]
|
||||
[InlineData("deviceref", ItemType.DeviceRef)]
|
||||
[InlineData("device_ref", ItemType.DeviceRef)]
|
||||
[InlineData("diplocation", ItemType.DipLocation)]
|
||||
[InlineData("dipswitch", ItemType.DipSwitch)]
|
||||
[InlineData("dipvalue", ItemType.DipValue)]
|
||||
[InlineData("disk", ItemType.Disk)]
|
||||
[InlineData("diskarea", ItemType.DiskArea)]
|
||||
[InlineData("display", ItemType.Display)]
|
||||
[InlineData("driver", ItemType.Driver)]
|
||||
[InlineData("extension", ItemType.Extension)]
|
||||
[InlineData("feature", ItemType.Feature)]
|
||||
[InlineData("file", ItemType.File)]
|
||||
[InlineData("info", ItemType.Info)]
|
||||
[InlineData("input", ItemType.Input)]
|
||||
[InlineData("instance", ItemType.Instance)]
|
||||
[InlineData("media", ItemType.Media)]
|
||||
[InlineData("part", ItemType.Part)]
|
||||
[InlineData("partfeature", ItemType.PartFeature)]
|
||||
[InlineData("part_feature", ItemType.PartFeature)]
|
||||
[InlineData("port", ItemType.Port)]
|
||||
[InlineData("ramoption", ItemType.RamOption)]
|
||||
[InlineData("ram_option", ItemType.RamOption)]
|
||||
[InlineData("release", ItemType.Release)]
|
||||
[InlineData("releasedetails", ItemType.ReleaseDetails)]
|
||||
[InlineData("release_details", ItemType.ReleaseDetails)]
|
||||
[InlineData("rom", ItemType.Rom)]
|
||||
[InlineData("sample", ItemType.Sample)]
|
||||
[InlineData("serials", ItemType.Serials)]
|
||||
[InlineData("sharedfeat", ItemType.SharedFeat)]
|
||||
[InlineData("shared_feat", ItemType.SharedFeat)]
|
||||
[InlineData("sharedfeature", ItemType.SharedFeat)]
|
||||
[InlineData("shared_feature", ItemType.SharedFeat)]
|
||||
[InlineData("slot", ItemType.Slot)]
|
||||
[InlineData("slotoption", ItemType.SlotOption)]
|
||||
[InlineData("slot_option", ItemType.SlotOption)]
|
||||
[InlineData("softwarelist", ItemType.SoftwareList)]
|
||||
[InlineData("software_list", ItemType.SoftwareList)]
|
||||
[InlineData("sound", ItemType.Sound)]
|
||||
[InlineData("sourcedetails", ItemType.SourceDetails)]
|
||||
[InlineData("source_details", ItemType.SourceDetails)]
|
||||
public void AsItemTypeTest(string? field, ItemType expected)
|
||||
{
|
||||
ItemType actual = field.AsItemType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, LoadFlag.NULL)]
|
||||
[InlineData("load16_byte", LoadFlag.Load16Byte)]
|
||||
[InlineData("load16_word", LoadFlag.Load16Word)]
|
||||
[InlineData("load16_word_swap", LoadFlag.Load16WordSwap)]
|
||||
[InlineData("load32_byte", LoadFlag.Load32Byte)]
|
||||
[InlineData("load32_word", LoadFlag.Load32Word)]
|
||||
[InlineData("load32_word_swap", LoadFlag.Load32WordSwap)]
|
||||
[InlineData("load32_dword", LoadFlag.Load32DWord)]
|
||||
[InlineData("load64_word", LoadFlag.Load64Word)]
|
||||
[InlineData("load64_word_swap", LoadFlag.Load64WordSwap)]
|
||||
[InlineData("reload", LoadFlag.Reload)]
|
||||
[InlineData("fill", LoadFlag.Fill)]
|
||||
[InlineData("continue", LoadFlag.Continue)]
|
||||
[InlineData("reload_plain", LoadFlag.ReloadPlain)]
|
||||
[InlineData("ignore", LoadFlag.Ignore)]
|
||||
public void AsLoadFlagTest(string? field, LoadFlag expected)
|
||||
{
|
||||
LoadFlag actual = field.AsLoadFlag();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, MachineType.None)]
|
||||
[InlineData("none", MachineType.None)]
|
||||
[InlineData("bios", MachineType.Bios)]
|
||||
[InlineData("dev", MachineType.Device)]
|
||||
[InlineData("device", MachineType.Device)]
|
||||
[InlineData("mech", MachineType.Mechanical)]
|
||||
[InlineData("mechanical", MachineType.Mechanical)]
|
||||
public void AsMachineTypeTest(string? field, MachineType expected)
|
||||
{
|
||||
MachineType actual = field.AsMachineType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, OpenMSXSubType.NULL)]
|
||||
[InlineData("rom", OpenMSXSubType.Rom)]
|
||||
[InlineData("megarom", OpenMSXSubType.MegaRom)]
|
||||
[InlineData("sccpluscart", OpenMSXSubType.SCCPlusCart)]
|
||||
public void AsOpenMSXSubTypeTest(string? field, OpenMSXSubType expected)
|
||||
{
|
||||
OpenMSXSubType actual = field.AsOpenMSXSubType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, Relation.NULL)]
|
||||
[InlineData("eq", Relation.Equal)]
|
||||
[InlineData("ne", Relation.NotEqual)]
|
||||
[InlineData("gt", Relation.GreaterThan)]
|
||||
[InlineData("le", Relation.LessThanOrEqual)]
|
||||
[InlineData("lt", Relation.LessThan)]
|
||||
[InlineData("ge", Relation.GreaterThanOrEqual)]
|
||||
public void AsRelationTest(string? field, Relation expected)
|
||||
{
|
||||
Relation actual = field.AsRelation();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, Runnable.NULL)]
|
||||
[InlineData("no", Runnable.No)]
|
||||
[InlineData("partial", Runnable.Partial)]
|
||||
[InlineData("yes", Runnable.Yes)]
|
||||
public void AsRunnableTest(string? field, Runnable expected)
|
||||
{
|
||||
Runnable actual = field.AsRunnable();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, SoftwareListStatus.None)]
|
||||
[InlineData("none", SoftwareListStatus.None)]
|
||||
[InlineData("original", SoftwareListStatus.Original)]
|
||||
[InlineData("compatible", SoftwareListStatus.Compatible)]
|
||||
public void AsSoftwareListStatusTest(string? field, SoftwareListStatus expected)
|
||||
{
|
||||
SoftwareListStatus actual = field.AsSoftwareListStatus();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, Supported.NULL)]
|
||||
[InlineData("no", Supported.No)]
|
||||
[InlineData("unsupported", Supported.No)]
|
||||
[InlineData("partial", Supported.Partial)]
|
||||
[InlineData("yes", Supported.Yes)]
|
||||
[InlineData("supported", Supported.Yes)]
|
||||
public void AsSupportedTest(string? field, Supported expected)
|
||||
{
|
||||
Supported actual = field.AsSupported();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, SupportStatus.NULL)]
|
||||
[InlineData("good", SupportStatus.Good)]
|
||||
[InlineData("imperfect", SupportStatus.Imperfect)]
|
||||
[InlineData("preliminary", SupportStatus.Preliminary)]
|
||||
public void AsSupportStatusTest(string? field, SupportStatus expected)
|
||||
{
|
||||
SupportStatus actual = field.AsSupportStatus();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enum to String
|
||||
|
||||
[Theory]
|
||||
[InlineData(ChipType.NULL, null)]
|
||||
[InlineData(ChipType.CPU, "cpu")]
|
||||
[InlineData(ChipType.Audio, "audio")]
|
||||
public void FromChipTypeTest(ChipType field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ControlType.NULL, null)]
|
||||
[InlineData(ControlType.Joy, "joy")]
|
||||
[InlineData(ControlType.Stick, "stick")]
|
||||
[InlineData(ControlType.Paddle, "paddle")]
|
||||
[InlineData(ControlType.Pedal, "pedal")]
|
||||
[InlineData(ControlType.Lightgun, "lightgun")]
|
||||
[InlineData(ControlType.Positional, "positional")]
|
||||
[InlineData(ControlType.Dial, "dial")]
|
||||
[InlineData(ControlType.Trackball, "trackball")]
|
||||
[InlineData(ControlType.Mouse, "mouse")]
|
||||
[InlineData(ControlType.OnlyButtons, "only_buttons")]
|
||||
[InlineData(ControlType.Keypad, "keypad")]
|
||||
[InlineData(ControlType.Keyboard, "keyboard")]
|
||||
[InlineData(ControlType.Mahjong, "mahjong")]
|
||||
[InlineData(ControlType.Hanafuda, "hanafuda")]
|
||||
[InlineData(ControlType.Gambling, "gambling")]
|
||||
public void FromControlTypeTest(ControlType field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(DeviceType.NULL, null)]
|
||||
[InlineData(DeviceType.Unknown, "unknown")]
|
||||
[InlineData(DeviceType.Cartridge, "cartridge")]
|
||||
[InlineData(DeviceType.FloppyDisk, "floppydisk")]
|
||||
[InlineData(DeviceType.HardDisk, "harddisk")]
|
||||
[InlineData(DeviceType.Cylinder, "cylinder")]
|
||||
[InlineData(DeviceType.Cassette, "cassette")]
|
||||
[InlineData(DeviceType.PunchCard, "punchcard")]
|
||||
[InlineData(DeviceType.PunchTape, "punchtape")]
|
||||
[InlineData(DeviceType.Printout, "printout")]
|
||||
[InlineData(DeviceType.Serial, "serial")]
|
||||
[InlineData(DeviceType.Parallel, "parallel")]
|
||||
[InlineData(DeviceType.Snapshot, "snapshot")]
|
||||
[InlineData(DeviceType.QuickLoad, "quickload")]
|
||||
[InlineData(DeviceType.MemCard, "memcard")]
|
||||
[InlineData(DeviceType.CDROM, "cdrom")]
|
||||
[InlineData(DeviceType.MagTape, "magtape")]
|
||||
[InlineData(DeviceType.ROMImage, "romimage")]
|
||||
[InlineData(DeviceType.MIDIIn, "midiin")]
|
||||
[InlineData(DeviceType.MIDIOut, "midiout")]
|
||||
[InlineData(DeviceType.Picture, "picture")]
|
||||
[InlineData(DeviceType.VidFile, "vidfile")]
|
||||
public void FromDeviceTypeTest(DeviceType field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(DisplayType.NULL, null)]
|
||||
[InlineData(DisplayType.Raster, "raster")]
|
||||
[InlineData(DisplayType.Vector, "vector")]
|
||||
[InlineData(DisplayType.LCD, "lcd")]
|
||||
[InlineData(DisplayType.SVG, "svg")]
|
||||
[InlineData(DisplayType.Unknown, "unknown")]
|
||||
public void FromDisplayTypeTest(DisplayType field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Endianness.NULL, null)]
|
||||
[InlineData(Endianness.Big, "big")]
|
||||
[InlineData(Endianness.Little, "little")]
|
||||
public void FromEndiannessTest(Endianness field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(FeatureStatus.NULL, null)]
|
||||
[InlineData(FeatureStatus.Unemulated, "unemulated")]
|
||||
[InlineData(FeatureStatus.Imperfect, "imperfect")]
|
||||
public void FromFeatureStatusTest(FeatureStatus field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(FeatureType.NULL, null)]
|
||||
[InlineData(FeatureType.Protection, "protection")]
|
||||
[InlineData(FeatureType.Palette, "palette")]
|
||||
[InlineData(FeatureType.Graphics, "graphics")]
|
||||
[InlineData(FeatureType.Sound, "sound")]
|
||||
[InlineData(FeatureType.Controls, "controls")]
|
||||
[InlineData(FeatureType.Keyboard, "keyboard")]
|
||||
[InlineData(FeatureType.Mouse, "mouse")]
|
||||
[InlineData(FeatureType.Microphone, "microphone")]
|
||||
[InlineData(FeatureType.Camera, "camera")]
|
||||
[InlineData(FeatureType.Disk, "disk")]
|
||||
[InlineData(FeatureType.Printer, "printer")]
|
||||
[InlineData(FeatureType.Lan, "lan")]
|
||||
[InlineData(FeatureType.Wan, "wan")]
|
||||
[InlineData(FeatureType.Timing, "timing")]
|
||||
public void FromFeatureTypeTest(FeatureType field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemStatus.NULL, null)]
|
||||
[InlineData(ItemStatus.None, "none")]
|
||||
[InlineData(ItemStatus.Good, "good")]
|
||||
[InlineData(ItemStatus.BadDump, "baddump")]
|
||||
[InlineData(ItemStatus.Nodump, "nodump")]
|
||||
[InlineData(ItemStatus.Verified, "verified")]
|
||||
public void FromItemStatusTest(ItemStatus field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemType.NULL, null)]
|
||||
[InlineData(ItemType.Adjuster, "adjuster")]
|
||||
[InlineData(ItemType.Analog, "analog")]
|
||||
[InlineData(ItemType.Archive, "archive")]
|
||||
[InlineData(ItemType.BiosSet, "biosset")]
|
||||
[InlineData(ItemType.Blank, "blank")]
|
||||
[InlineData(ItemType.Chip, "chip")]
|
||||
[InlineData(ItemType.Condition, "condition")]
|
||||
[InlineData(ItemType.Configuration, "configuration")]
|
||||
[InlineData(ItemType.ConfLocation, "conflocation")]
|
||||
[InlineData(ItemType.ConfSetting, "confsetting")]
|
||||
[InlineData(ItemType.Control, "control")]
|
||||
[InlineData(ItemType.DataArea, "dataarea")]
|
||||
[InlineData(ItemType.Device, "device")]
|
||||
[InlineData(ItemType.DeviceRef, "device_ref")]
|
||||
[InlineData(ItemType.DipLocation, "diplocation")]
|
||||
[InlineData(ItemType.DipSwitch, "dipswitch")]
|
||||
[InlineData(ItemType.DipValue, "dipvalue")]
|
||||
[InlineData(ItemType.Disk, "disk")]
|
||||
[InlineData(ItemType.DiskArea, "diskarea")]
|
||||
[InlineData(ItemType.Display, "display")]
|
||||
[InlineData(ItemType.Driver, "driver")]
|
||||
[InlineData(ItemType.Extension, "extension")]
|
||||
[InlineData(ItemType.Feature, "feature")]
|
||||
[InlineData(ItemType.File, "file")]
|
||||
[InlineData(ItemType.Info, "info")]
|
||||
[InlineData(ItemType.Input, "input")]
|
||||
[InlineData(ItemType.Instance, "instance")]
|
||||
[InlineData(ItemType.Media, "media")]
|
||||
[InlineData(ItemType.Part, "part")]
|
||||
[InlineData(ItemType.PartFeature, "part_feature")]
|
||||
[InlineData(ItemType.Port, "port")]
|
||||
[InlineData(ItemType.RamOption, "ramoption")]
|
||||
[InlineData(ItemType.Release, "release")]
|
||||
[InlineData(ItemType.ReleaseDetails, "release_details")]
|
||||
[InlineData(ItemType.Rom, "rom")]
|
||||
[InlineData(ItemType.Sample, "sample")]
|
||||
[InlineData(ItemType.Serials, "serials")]
|
||||
[InlineData(ItemType.SharedFeat, "sharedfeat")]
|
||||
[InlineData(ItemType.Slot, "slot")]
|
||||
[InlineData(ItemType.SlotOption, "slotoption")]
|
||||
[InlineData(ItemType.SoftwareList, "softwarelist")]
|
||||
[InlineData(ItemType.Sound, "sound")]
|
||||
[InlineData(ItemType.SourceDetails, "source_details")]
|
||||
public void FromItemTypeTest(ItemType field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(LoadFlag.NULL, null)]
|
||||
[InlineData(LoadFlag.Load16Byte, "load16_byte")]
|
||||
[InlineData(LoadFlag.Load16Word, "load16_word")]
|
||||
[InlineData(LoadFlag.Load16WordSwap, "load16_word_swap")]
|
||||
[InlineData(LoadFlag.Load32Byte, "load32_byte")]
|
||||
[InlineData(LoadFlag.Load32Word, "load32_word")]
|
||||
[InlineData(LoadFlag.Load32WordSwap, "load32_word_swap")]
|
||||
[InlineData(LoadFlag.Load32DWord, "load32_dword")]
|
||||
[InlineData(LoadFlag.Load64Word, "load64_word")]
|
||||
[InlineData(LoadFlag.Load64WordSwap, "load64_word_swap")]
|
||||
[InlineData(LoadFlag.Reload, "reload")]
|
||||
[InlineData(LoadFlag.Fill, "fill")]
|
||||
[InlineData(LoadFlag.Continue, "continue")]
|
||||
[InlineData(LoadFlag.ReloadPlain, "reload_plain")]
|
||||
[InlineData(LoadFlag.Ignore, "ignore")]
|
||||
public void FromLoadFlagTest(LoadFlag field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(OpenMSXSubType.NULL, null)]
|
||||
[InlineData(OpenMSXSubType.Rom, "rom")]
|
||||
[InlineData(OpenMSXSubType.MegaRom, "megarom")]
|
||||
[InlineData(OpenMSXSubType.SCCPlusCart, "sccpluscart")]
|
||||
public void FromOpenMSXSubTypeTest(OpenMSXSubType field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Relation.NULL, null)]
|
||||
[InlineData(Relation.Equal, "eq")]
|
||||
[InlineData(Relation.NotEqual, "ne")]
|
||||
[InlineData(Relation.GreaterThan, "gt")]
|
||||
[InlineData(Relation.LessThanOrEqual, "le")]
|
||||
[InlineData(Relation.LessThan, "lt")]
|
||||
[InlineData(Relation.GreaterThanOrEqual, "ge")]
|
||||
public void FromRelationTest(Relation field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Runnable.NULL, null)]
|
||||
[InlineData(Runnable.No, "no")]
|
||||
[InlineData(Runnable.Partial, "partial")]
|
||||
[InlineData(Runnable.Yes, "yes")]
|
||||
public void FromRunnableTest(Runnable field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SoftwareListStatus.None, "none")]
|
||||
[InlineData(SoftwareListStatus.Original, "original")]
|
||||
[InlineData(SoftwareListStatus.Compatible, "compatible")]
|
||||
public void FromSoftwareListStatusTest(SoftwareListStatus field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Supported.NULL, true, null)]
|
||||
[InlineData(Supported.NULL, false, null)]
|
||||
[InlineData(Supported.No, true, "unsupported")]
|
||||
[InlineData(Supported.No, false, "no")]
|
||||
[InlineData(Supported.Partial, true, "partial")]
|
||||
[InlineData(Supported.Partial, false, "partial")]
|
||||
[InlineData(Supported.Yes, true, "supported")]
|
||||
[InlineData(Supported.Yes, false, "yes")]
|
||||
public void FromSupportedTest(Supported field, bool useSecond, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue(useSecond);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SupportStatus.NULL, null)]
|
||||
[InlineData(SupportStatus.Good, "good")]
|
||||
[InlineData(SupportStatus.Imperfect, "imperfect")]
|
||||
[InlineData(SupportStatus.Preliminary, "preliminary")]
|
||||
public void FromSupportStatusTest(SupportStatus field, string? expected)
|
||||
{
|
||||
string? actual = field.AsStringValue();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
269
SabreTools.Metadata.DatItems.Test/Formats/DiskTests.cs
Normal file
269
SabreTools.Metadata.DatItems.Test/Formats/DiskTests.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using SabreTools.Hashing;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats.Test
|
||||
{
|
||||
public class DiskTests
|
||||
{
|
||||
#region ConvertToRom
|
||||
|
||||
[Fact]
|
||||
public void ConvertToRomTest()
|
||||
{
|
||||
DiskArea diskArea = new DiskArea();
|
||||
diskArea.SetName("XXXXXX");
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "XXXXXX");
|
||||
|
||||
Part part = new Part();
|
||||
part.SetName("XXXXXX");
|
||||
|
||||
Source source = new Source(0, "XXXXXX");
|
||||
|
||||
Disk disk = new Disk();
|
||||
disk.SetName("XXXXXX");
|
||||
disk.SetFieldValue(Disk.DiskAreaKey, diskArea);
|
||||
disk.SetFieldValue(Data.Models.Metadata.Disk.MergeKey, "XXXXXX");
|
||||
disk.SetFieldValue(Data.Models.Metadata.Disk.RegionKey, "XXXXXX");
|
||||
disk.SetFieldValue(Data.Models.Metadata.Disk.StatusKey, "good");
|
||||
disk.SetFieldValue(Data.Models.Metadata.Disk.OptionalKey, "XXXXXX");
|
||||
disk.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, HashType.MD5.ZeroString);
|
||||
disk.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, HashType.SHA1.ZeroString);
|
||||
disk.SetFieldValue(DatItem.DupeTypeKey, DupeType.All | DupeType.External);
|
||||
disk.SetFieldValue(DatItem.MachineKey, machine);
|
||||
disk.SetFieldValue(Disk.PartKey, part);
|
||||
disk.SetFieldValue(DatItem.RemoveKey, (bool?)false);
|
||||
disk.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
Rom actual = disk.ConvertToRom();
|
||||
|
||||
Assert.Equal("XXXXXX.chd", actual.GetName());
|
||||
Assert.Equal("XXXXXX", actual.GetStringFieldValue(Data.Models.Metadata.Rom.MergeKey));
|
||||
Assert.Equal("XXXXXX", actual.GetStringFieldValue(Data.Models.Metadata.Rom.RegionKey));
|
||||
Assert.Equal("good", actual.GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey));
|
||||
Assert.Equal("XXXXXX", actual.GetStringFieldValue(Data.Models.Metadata.Rom.OptionalKey));
|
||||
Assert.Equal(HashType.MD5.ZeroString, actual.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key));
|
||||
Assert.Equal(HashType.SHA1.ZeroString, actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal(DupeType.All | DupeType.External, actual.GetFieldValue<DupeType>(DatItem.DupeTypeKey));
|
||||
|
||||
DataArea? actualDataArea = actual.GetFieldValue<DataArea?>(Rom.DataAreaKey);
|
||||
Assert.NotNull(actualDataArea);
|
||||
Assert.Equal("XXXXXX", actualDataArea.GetStringFieldValue(Data.Models.Metadata.DataArea.NameKey));
|
||||
|
||||
Machine? actualMachine = actual.GetMachine();
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("XXXXXX", actualMachine.GetName());
|
||||
|
||||
Assert.Equal(false, actual.GetBoolFieldValue(DatItem.RemoveKey));
|
||||
|
||||
Part? actualPart = actual.GetFieldValue<Part?>(Rom.PartKey);
|
||||
Assert.NotNull(actualPart);
|
||||
Assert.Equal("XXXXXX", actualPart.GetStringFieldValue(Data.Models.Metadata.Part.NameKey));
|
||||
|
||||
Source? actualSource = actual.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
Assert.NotNull(actualSource);
|
||||
Assert.Equal(0, actualSource.Index);
|
||||
Assert.Equal("XXXXXX", actualSource.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FillMissingInformation
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_BothEmpty()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
Disk other = new Disk();
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_AllMissing()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
|
||||
Disk other = new Disk();
|
||||
other.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, "XXXXXX");
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasHashes
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_NoHash_False()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
bool actual = self.HasHashes();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_MD5_True()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA1_True()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, "XXXXXX");
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_All_True()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, "XXXXXX");
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasZeroHash
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NoHash_True()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NonZeroHash_False()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, "DEADBEEF");
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, "DEADBEEF");
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroMD5_True()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, HashType.MD5.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA1_True()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, HashType.SHA1.ZeroString);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroAll_True()
|
||||
{
|
||||
Disk self = new Disk();
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, HashType.MD5.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, HashType.SHA1.ZeroString);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Change when Machine retrieval gets fixed
|
||||
#region GetKey
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemKey.NULL, false, false, "")]
|
||||
[InlineData(ItemKey.NULL, false, true, "")]
|
||||
[InlineData(ItemKey.NULL, true, false, "")]
|
||||
[InlineData(ItemKey.NULL, true, true, "")]
|
||||
[InlineData(ItemKey.Machine, false, false, "0000000000-Machine")]
|
||||
[InlineData(ItemKey.Machine, false, true, "Machine")]
|
||||
[InlineData(ItemKey.Machine, true, false, "0000000000-machine")]
|
||||
[InlineData(ItemKey.Machine, true, true, "machine")]
|
||||
[InlineData(ItemKey.CRC, false, false, "00000000")]
|
||||
[InlineData(ItemKey.CRC, false, true, "00000000")]
|
||||
[InlineData(ItemKey.CRC, true, false, "00000000")]
|
||||
[InlineData(ItemKey.CRC, true, true, "00000000")]
|
||||
[InlineData(ItemKey.MD2, false, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, false, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD4, false, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, false, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD5, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD5, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, false, false, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA256, false, true, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA256, true, false, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA256, true, true, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")]
|
||||
[InlineData(ItemKey.SHA384, false, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, false, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA512, false, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, false, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SpamSum, false, false, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, false, true, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, true, false, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, true, true, "3::")]
|
||||
public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected)
|
||||
{
|
||||
Source source = new Source(0);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "Machine");
|
||||
|
||||
DatItem datItem = new Disk();
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Disk.MD5Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Disk.SHA1Key, "DEADBEEF");
|
||||
|
||||
string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
360
SabreTools.Metadata.DatItems.Test/Formats/FileTests.cs
Normal file
360
SabreTools.Metadata.DatItems.Test/Formats/FileTests.cs
Normal file
@@ -0,0 +1,360 @@
|
||||
using SabreTools.Hashing;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats.Test
|
||||
{
|
||||
public class FileTests
|
||||
{
|
||||
#region ConvertToRom
|
||||
|
||||
[Fact]
|
||||
public void ConvertToRomTest()
|
||||
{
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "XXXXXX");
|
||||
|
||||
Source source = new Source(0, "XXXXXX");
|
||||
|
||||
var file = new File
|
||||
{
|
||||
Id = "XXXXXX",
|
||||
Extension = "XXXXXX",
|
||||
Size = 12345,
|
||||
CRC = "DEADBEEF",
|
||||
MD5 = "DEADBEEF",
|
||||
SHA1 = "DEADBEEF",
|
||||
SHA256 = "DEADBEEF",
|
||||
Format = "XXXXXX"
|
||||
};
|
||||
file.SetFieldValue(DatItem.DupeTypeKey, DupeType.All | DupeType.External);
|
||||
file.SetFieldValue(DatItem.MachineKey, machine);
|
||||
file.SetFieldValue(DatItem.RemoveKey, (bool?)false);
|
||||
file.SetFieldValue(DatItem.SourceKey, source);
|
||||
file.SetFieldValue(DatItem.MachineKey, machine);
|
||||
file.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
Rom actual = file.ConvertToRom();
|
||||
|
||||
Assert.Equal("XXXXXX.XXXXXX", actual.GetName());
|
||||
Assert.Equal(12345, actual.GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey));
|
||||
Assert.Equal("deadbeef", actual.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey));
|
||||
Assert.Equal("000000000000000000000000deadbeef", actual.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key));
|
||||
Assert.Equal("00000000000000000000000000000000deadbeef", actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal("00000000000000000000000000000000000000000000000000000000deadbeef", actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key));
|
||||
Assert.Equal(DupeType.All | DupeType.External, actual.GetFieldValue<DupeType>(DatItem.DupeTypeKey));
|
||||
|
||||
Machine? actualMachine = actual.GetMachine();
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("XXXXXX", actualMachine.GetName());
|
||||
|
||||
Assert.Equal(false, actual.GetBoolFieldValue(DatItem.RemoveKey));
|
||||
|
||||
Source? actualSource = actual.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
Assert.NotNull(actualSource);
|
||||
Assert.Equal(0, actualSource.Index);
|
||||
Assert.Equal("XXXXXX", actualSource.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FillMissingInformation
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_BothEmpty()
|
||||
{
|
||||
File self = new File();
|
||||
File other = new File();
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Null(self.Size);
|
||||
Assert.Null(self.CRC);
|
||||
Assert.Null(self.MD5);
|
||||
Assert.Null(self.SHA1);
|
||||
Assert.Null(self.SHA256);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_AllMissing()
|
||||
{
|
||||
File self = new File();
|
||||
|
||||
File other = new File
|
||||
{
|
||||
Size = 12345,
|
||||
CRC = "DEADBEEF",
|
||||
MD5 = "DEADBEEF",
|
||||
SHA1 = "DEADBEEF",
|
||||
SHA256 = "DEADBEEF",
|
||||
};
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Equal(12345, self.Size);
|
||||
Assert.Equal("deadbeef", self.CRC);
|
||||
Assert.Equal("000000000000000000000000deadbeef", self.MD5);
|
||||
Assert.Equal("00000000000000000000000000000000deadbeef", self.SHA1);
|
||||
Assert.Equal("00000000000000000000000000000000000000000000000000000000deadbeef", self.SHA256);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasHashes
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_NoHash_False()
|
||||
{
|
||||
File self = new File();
|
||||
bool actual = self.HasHashes();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_CRC_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = "deadbeef",
|
||||
MD5 = string.Empty,
|
||||
SHA1 = string.Empty,
|
||||
SHA256 = string.Empty,
|
||||
};
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_MD5_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = string.Empty,
|
||||
MD5 = "deadbeef",
|
||||
SHA1 = string.Empty,
|
||||
SHA256 = string.Empty,
|
||||
};
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA1_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = string.Empty,
|
||||
MD5 = string.Empty,
|
||||
SHA1 = "deadbeef",
|
||||
SHA256 = string.Empty,
|
||||
};
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA256_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = string.Empty,
|
||||
MD5 = string.Empty,
|
||||
SHA1 = string.Empty,
|
||||
SHA256 = "deadbeef",
|
||||
};
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_All_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = "deadbeef",
|
||||
MD5 = "deadbeef",
|
||||
SHA1 = "deadbeef",
|
||||
SHA256 = "deadbeef",
|
||||
};
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasZeroHash
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NoHash_True()
|
||||
{
|
||||
File self = new File();
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NonZeroHash_False()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = "deadbeef",
|
||||
MD5 = "deadbeef",
|
||||
SHA1 = "deadbeef",
|
||||
SHA256 = "deadbeef",
|
||||
};
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroCRC_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = HashType.CRC32.ZeroString,
|
||||
MD5 = string.Empty,
|
||||
SHA1 = string.Empty,
|
||||
SHA256 = string.Empty,
|
||||
};
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroMD5_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = string.Empty,
|
||||
MD5 = HashType.MD5.ZeroString,
|
||||
SHA1 = string.Empty,
|
||||
SHA256 = string.Empty,
|
||||
};
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA1_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = string.Empty,
|
||||
MD5 = string.Empty,
|
||||
SHA1 = HashType.SHA1.ZeroString,
|
||||
SHA256 = string.Empty,
|
||||
};
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA256_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = string.Empty,
|
||||
MD5 = string.Empty,
|
||||
SHA1 = string.Empty,
|
||||
SHA256 = HashType.SHA256.ZeroString,
|
||||
};
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroAll_True()
|
||||
{
|
||||
File self = new File
|
||||
{
|
||||
CRC = HashType.CRC32.ZeroString,
|
||||
MD5 = HashType.MD5.ZeroString,
|
||||
SHA1 = HashType.SHA1.ZeroString,
|
||||
SHA256 = HashType.SHA256.ZeroString,
|
||||
};
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Change when Machine retrieval gets fixed
|
||||
#region GetKey
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemKey.NULL, false, false, "")]
|
||||
[InlineData(ItemKey.NULL, false, true, "")]
|
||||
[InlineData(ItemKey.NULL, true, false, "")]
|
||||
[InlineData(ItemKey.NULL, true, true, "")]
|
||||
[InlineData(ItemKey.Machine, false, false, "0000000000-Machine")]
|
||||
[InlineData(ItemKey.Machine, false, true, "Machine")]
|
||||
[InlineData(ItemKey.Machine, true, false, "0000000000-machine")]
|
||||
[InlineData(ItemKey.Machine, true, true, "machine")]
|
||||
[InlineData(ItemKey.CRC, false, false, "deadbeef")]
|
||||
[InlineData(ItemKey.CRC, false, true, "deadbeef")]
|
||||
[InlineData(ItemKey.CRC, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.CRC, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.MD2, false, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, false, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD4, false, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, false, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD5, false, false, "000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.MD5, false, true, "000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.MD5, true, false, "000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.MD5, true, true, "000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, false, false, "00000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, false, true, "00000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, true, false, "00000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, true, true, "00000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, false, false, "00000000000000000000000000000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, false, true, "00000000000000000000000000000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, true, false, "00000000000000000000000000000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, true, true, "00000000000000000000000000000000000000000000000000000000deadbeef")]
|
||||
[InlineData(ItemKey.SHA384, false, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, false, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA512, false, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, false, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SpamSum, false, false, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, false, true, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, true, false, "3::")]
|
||||
[InlineData(ItemKey.SpamSum, true, true, "3::")]
|
||||
public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected)
|
||||
{
|
||||
Source source = new Source(0);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "Machine");
|
||||
|
||||
DatItem datItem = new File
|
||||
{
|
||||
CRC = "DEADBEEF",
|
||||
MD5 = "DEADBEEF",
|
||||
SHA1 = "DEADBEEF",
|
||||
SHA256 = "DEADBEEF",
|
||||
};
|
||||
|
||||
string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
323
SabreTools.Metadata.DatItems.Test/Formats/MediaTests.cs
Normal file
323
SabreTools.Metadata.DatItems.Test/Formats/MediaTests.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
using SabreTools.Hashing;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats.Test
|
||||
{
|
||||
public class MediaTests
|
||||
{
|
||||
#region ConvertToRom
|
||||
|
||||
[Fact]
|
||||
public void ConvertToRomTest()
|
||||
{
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "XXXXXX");
|
||||
|
||||
Source source = new Source(0, "XXXXXX");
|
||||
|
||||
Media media = new Media();
|
||||
media.SetName("XXXXXX");
|
||||
media.SetFieldValue(Data.Models.Metadata.Media.MD5Key, HashType.MD5.ZeroString);
|
||||
media.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, HashType.SHA1.ZeroString);
|
||||
media.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, HashType.SHA256.ZeroString);
|
||||
media.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, HashType.SpamSum.ZeroString);
|
||||
media.SetFieldValue(DatItem.DupeTypeKey, DupeType.All | DupeType.External);
|
||||
media.SetFieldValue(DatItem.MachineKey, machine);
|
||||
media.SetFieldValue(DatItem.RemoveKey, (bool?)false);
|
||||
media.SetFieldValue(DatItem.SourceKey, source);
|
||||
|
||||
Rom actual = media.ConvertToRom();
|
||||
|
||||
Assert.Equal("XXXXXX.aaruf", actual.GetName());
|
||||
Assert.Equal(HashType.MD5.ZeroString, actual.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key));
|
||||
Assert.Equal(HashType.SHA1.ZeroString, actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal(HashType.SHA256.ZeroString, actual.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key));
|
||||
Assert.Equal(HashType.SpamSum.ZeroString, actual.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey));
|
||||
Assert.Equal(DupeType.All | DupeType.External, actual.GetFieldValue<DupeType>(DatItem.DupeTypeKey));
|
||||
|
||||
Machine? actualMachine = actual.GetMachine();
|
||||
Assert.NotNull(actualMachine);
|
||||
Assert.Equal("XXXXXX", actualMachine.GetName());
|
||||
|
||||
Assert.Equal(false, actual.GetBoolFieldValue(DatItem.RemoveKey));
|
||||
|
||||
Source? actualSource = actual.GetFieldValue<Source?>(DatItem.SourceKey);
|
||||
Assert.NotNull(actualSource);
|
||||
Assert.Equal(0, actualSource.Index);
|
||||
Assert.Equal("XXXXXX", actualSource.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FillMissingInformation
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_BothEmpty()
|
||||
{
|
||||
Media self = new Media();
|
||||
Media other = new Media();
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_AllMissing()
|
||||
{
|
||||
Media self = new Media();
|
||||
|
||||
Media other = new Media();
|
||||
other.SetFieldValue(Data.Models.Metadata.Media.MD5Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, "XXXXXX");
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Media.MD5Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasHashes
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_NoHash_False()
|
||||
{
|
||||
Media self = new Media();
|
||||
bool actual = self.HasHashes();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_MD5_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA1_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA256_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SpamSum_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, "XXXXXX");
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_All_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, "XXXXXX");
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasZeroHash
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NoHash_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NonZeroHash_False()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, "XXXXXX");
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroMD5_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, HashType.MD5.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA1_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, HashType.SHA1.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA256_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, HashType.SHA256.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSpamSum_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, HashType.SpamSum.ZeroString);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroAll_True()
|
||||
{
|
||||
Media self = new Media();
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.MD5Key, HashType.MD5.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, HashType.SHA1.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, HashType.SHA256.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, HashType.SpamSum.ZeroString);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Change when Machine retrieval gets fixed
|
||||
#region GetKey
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemKey.NULL, false, false, "")]
|
||||
[InlineData(ItemKey.NULL, false, true, "")]
|
||||
[InlineData(ItemKey.NULL, true, false, "")]
|
||||
[InlineData(ItemKey.NULL, true, true, "")]
|
||||
[InlineData(ItemKey.Machine, false, false, "0000000000-Machine")]
|
||||
[InlineData(ItemKey.Machine, false, true, "Machine")]
|
||||
[InlineData(ItemKey.Machine, true, false, "0000000000-machine")]
|
||||
[InlineData(ItemKey.Machine, true, true, "machine")]
|
||||
[InlineData(ItemKey.CRC, false, false, "00000000")]
|
||||
[InlineData(ItemKey.CRC, false, true, "00000000")]
|
||||
[InlineData(ItemKey.CRC, true, false, "00000000")]
|
||||
[InlineData(ItemKey.CRC, true, true, "00000000")]
|
||||
[InlineData(ItemKey.MD2, false, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, false, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, false, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD2, true, true, "8350e5a3e24c153df2275c9f80692773")]
|
||||
[InlineData(ItemKey.MD4, false, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, false, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, false, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD4, true, true, "31d6cfe0d16ae931b73c59d7e0c089c0")]
|
||||
[InlineData(ItemKey.MD5, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD5, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA256, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA256, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA384, false, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, false, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, false, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA384, true, true, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")]
|
||||
[InlineData(ItemKey.SHA512, false, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, false, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, false, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SHA512, true, true, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
|
||||
[InlineData(ItemKey.SpamSum, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SpamSum, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SpamSum, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SpamSum, true, true, "deadbeef")]
|
||||
public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected)
|
||||
{
|
||||
Source source = new Source(0);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "Machine");
|
||||
|
||||
DatItem datItem = new Media();
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Media.MD5Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Media.SHA1Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Media.SHA256Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Media.SpamSumKey, "DEADBEEF");
|
||||
|
||||
string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
672
SabreTools.Metadata.DatItems.Test/Formats/RomTests.cs
Normal file
672
SabreTools.Metadata.DatItems.Test/Formats/RomTests.cs
Normal file
@@ -0,0 +1,672 @@
|
||||
using SabreTools.Hashing;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats.Test
|
||||
{
|
||||
public class RomTests
|
||||
{
|
||||
#region FillMissingInformation
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_BothEmpty()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
Rom other = new Rom();
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key));
|
||||
Assert.Null(self.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FillMissingInformation_AllMissing()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
|
||||
Rom other = new Rom();
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, "XXXXXX");
|
||||
other.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, "XXXXXX");
|
||||
|
||||
self.FillMissingInformation(other);
|
||||
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key));
|
||||
Assert.Equal("XXXXXX", self.GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasHashes
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_NoHash_False()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
bool actual = self.HasHashes();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_CRC_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_MD2_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_MD4_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_MD5_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_RIPEMD128_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_RIPEMD160_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA1_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA256_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA384_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SHA512_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_SpamSum_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, "XXXXXX");
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHashes_All_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, "XXXXXX");
|
||||
|
||||
bool actual = self.HasHashes();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasZeroHash
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NoHash_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_NonZeroHash_False()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, "XXXXXX");
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, "XXXXXX");
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroCRC_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, HashType.CRC32.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroMD2_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, HashType.MD2.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroMD4_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, HashType.MD4.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroMD5_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, HashType.MD5.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroRIPEMD128_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, HashType.RIPEMD128.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroRIPEMD160_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, HashType.RIPEMD160.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA1_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, HashType.SHA1.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA256_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, HashType.SHA256.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA384_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, HashType.SHA384.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSHA512_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, HashType.SHA512.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, string.Empty);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroSpamSum_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, string.Empty);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, HashType.SpamSum.ZeroString);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasZeroHash_ZeroAll_True()
|
||||
{
|
||||
Rom self = new Rom();
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, HashType.CRC32.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, HashType.MD2.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, HashType.MD4.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, HashType.MD5.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, HashType.RIPEMD128.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, HashType.RIPEMD160.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, HashType.SHA1.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, HashType.SHA256.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, HashType.SHA384.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, HashType.SHA512.ZeroString);
|
||||
self.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, HashType.SpamSum.ZeroString);
|
||||
|
||||
bool actual = self.HasZeroHash();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Change when Machine retrieval gets fixed
|
||||
#region GetKey
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemKey.NULL, false, false, "")]
|
||||
[InlineData(ItemKey.NULL, false, true, "")]
|
||||
[InlineData(ItemKey.NULL, true, false, "")]
|
||||
[InlineData(ItemKey.NULL, true, true, "")]
|
||||
[InlineData(ItemKey.Machine, false, false, "0000000000-Machine")]
|
||||
[InlineData(ItemKey.Machine, false, true, "Machine")]
|
||||
[InlineData(ItemKey.Machine, true, false, "0000000000-machine")]
|
||||
[InlineData(ItemKey.Machine, true, true, "machine")]
|
||||
[InlineData(ItemKey.CRC, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.CRC, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.CRC, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.CRC, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.MD2, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD2, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD2, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD2, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.MD4, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD4, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD4, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD4, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.MD5, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.MD5, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.MD5, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD128, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD128, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD128, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD128, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD160, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD160, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.RIPEMD160, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.RIPEMD160, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA1, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA1, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA256, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA256, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA256, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA384, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA384, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA384, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA384, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA512, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA512, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SHA512, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SHA512, true, true, "deadbeef")]
|
||||
[InlineData(ItemKey.SpamSum, false, false, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SpamSum, false, true, "DEADBEEF")]
|
||||
[InlineData(ItemKey.SpamSum, true, false, "deadbeef")]
|
||||
[InlineData(ItemKey.SpamSum, true, true, "deadbeef")]
|
||||
public void GetKeyDBTest(ItemKey bucketedBy, bool lower, bool norename, string expected)
|
||||
{
|
||||
Source source = new Source(0);
|
||||
|
||||
Machine machine = new Machine();
|
||||
machine.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "Machine");
|
||||
|
||||
DatItem datItem = new Rom();
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.CRCKey, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.MD2Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.MD4Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.MD5Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA1Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA256Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA384Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SHA512Key, "DEADBEEF");
|
||||
datItem.SetFieldValue(Data.Models.Metadata.Rom.SpamSumKey, "DEADBEEF");
|
||||
|
||||
string actual = datItem.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
87
SabreTools.Metadata.DatItems.Test/MachineTests.cs
Normal file
87
SabreTools.Metadata.DatItems.Test/MachineTests.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Test
|
||||
{
|
||||
public class MachineTests
|
||||
{
|
||||
#region Clone
|
||||
|
||||
[Fact]
|
||||
public void CloneTest()
|
||||
{
|
||||
Machine item = new Machine();
|
||||
item.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "name");
|
||||
|
||||
object clone = item.Clone();
|
||||
Machine? actual = clone as Machine;
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal("name", actual.GetName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetInternalClone
|
||||
|
||||
[Fact]
|
||||
public void GetInternalCloneTest()
|
||||
{
|
||||
Machine item = new Machine();
|
||||
item.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "name");
|
||||
|
||||
Data.Models.Metadata.Machine actual = item.GetInternalClone();
|
||||
Assert.Equal("name", actual[Data.Models.Metadata.Machine.NameKey]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equals
|
||||
|
||||
[Fact]
|
||||
public void Equals_Null_False()
|
||||
{
|
||||
Machine self = new Machine();
|
||||
Machine? other = null;
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_DefaultInternal_True()
|
||||
{
|
||||
Machine self = new Machine();
|
||||
Machine? other = new Machine();
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_MismatchedInternal_False()
|
||||
{
|
||||
Machine self = new Machine();
|
||||
self.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "self");
|
||||
|
||||
Machine? other = new Machine();
|
||||
other.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "other");
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_EqualInternal_True()
|
||||
{
|
||||
Machine self = new Machine();
|
||||
self.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "name");
|
||||
|
||||
Machine? other = new Machine();
|
||||
other.SetFieldValue(Data.Models.Metadata.Machine.NameKey, "name");
|
||||
|
||||
bool actual = self.Equals(other);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="[2.0.0]" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Metadata.DatItems\SabreTools.Metadata.DatItems.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
23
SabreTools.Metadata.DatItems.Test/SourceTests.cs
Normal file
23
SabreTools.Metadata.DatItems.Test/SourceTests.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Test
|
||||
{
|
||||
public class SourceTests
|
||||
{
|
||||
#region Clone
|
||||
|
||||
[Fact]
|
||||
public void CloneTest()
|
||||
{
|
||||
Source item = new Source(1, source: "src");
|
||||
|
||||
object clone = item.Clone();
|
||||
Source? actual = clone as Source;
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(1, actual.Index);
|
||||
Assert.Equal("src", actual.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
346
SabreTools.Metadata.DatItems/DatItem.cs
Normal file
346
SabreTools.Metadata.DatItems/DatItem.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Logging;
|
||||
using SabreTools.Metadata.DatItems.Formats;
|
||||
using SabreTools.Metadata.Filter;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all items included in a set
|
||||
/// </summary>
|
||||
[JsonObject("datitem"), XmlRoot("datitem")]
|
||||
[XmlInclude(typeof(Adjuster))]
|
||||
[XmlInclude(typeof(Analog))]
|
||||
[XmlInclude(typeof(Archive))]
|
||||
[XmlInclude(typeof(BiosSet))]
|
||||
[XmlInclude(typeof(Blank))]
|
||||
[XmlInclude(typeof(Chip))]
|
||||
[XmlInclude(typeof(Condition))]
|
||||
[XmlInclude(typeof(Configuration))]
|
||||
[XmlInclude(typeof(ConfLocation))]
|
||||
[XmlInclude(typeof(ConfSetting))]
|
||||
[XmlInclude(typeof(Control))]
|
||||
[XmlInclude(typeof(DataArea))]
|
||||
[XmlInclude(typeof(Device))]
|
||||
[XmlInclude(typeof(DeviceRef))]
|
||||
[XmlInclude(typeof(DipLocation))]
|
||||
[XmlInclude(typeof(DipSwitch))]
|
||||
[XmlInclude(typeof(DipValue))]
|
||||
[XmlInclude(typeof(Disk))]
|
||||
[XmlInclude(typeof(DiskArea))]
|
||||
[XmlInclude(typeof(Display))]
|
||||
[XmlInclude(typeof(Driver))]
|
||||
[XmlInclude(typeof(Extension))]
|
||||
[XmlInclude(typeof(Feature))]
|
||||
[XmlInclude(typeof(Info))]
|
||||
[XmlInclude(typeof(Input))]
|
||||
[XmlInclude(typeof(Instance))]
|
||||
[XmlInclude(typeof(Media))]
|
||||
[XmlInclude(typeof(Part))]
|
||||
[XmlInclude(typeof(PartFeature))]
|
||||
[XmlInclude(typeof(Port))]
|
||||
[XmlInclude(typeof(RamOption))]
|
||||
[XmlInclude(typeof(Release))]
|
||||
[XmlInclude(typeof(Rom))]
|
||||
[XmlInclude(typeof(Sample))]
|
||||
[XmlInclude(typeof(SharedFeat))]
|
||||
[XmlInclude(typeof(Slot))]
|
||||
[XmlInclude(typeof(SlotOption))]
|
||||
[XmlInclude(typeof(SoftwareList))]
|
||||
[XmlInclude(typeof(Sound))]
|
||||
public abstract class DatItem : ModelBackedItem<Data.Models.Metadata.DatItem>, IEquatable<DatItem>, IComparable<DatItem>, ICloneable
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate type when compared to another item
|
||||
/// </summary>
|
||||
public const string DupeTypeKey = "DUPETYPE";
|
||||
|
||||
/// <summary>
|
||||
/// Machine associated with the item
|
||||
/// </summary>
|
||||
public const string MachineKey = "MACHINE";
|
||||
|
||||
/// <summary>
|
||||
/// Flag if item should be removed
|
||||
/// </summary>
|
||||
public const string RemoveKey = "REMOVE";
|
||||
|
||||
/// <summary>
|
||||
/// Source information
|
||||
/// </summary>
|
||||
public const string SourceKey = "SOURCE";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Item type for the object
|
||||
/// </summary>
|
||||
protected abstract ItemType ItemType { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logging
|
||||
|
||||
/// <summary>
|
||||
/// Static logger for static methods
|
||||
/// </summary>
|
||||
[JsonIgnore, XmlIgnore]
|
||||
protected static readonly Logger _staticLogger = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accessors
|
||||
|
||||
/// <summary>
|
||||
/// Get the machine for a DatItem
|
||||
/// </summary>
|
||||
/// <returns>Machine if available, null otherwise</returns>
|
||||
/// <remarks>Relies on <see cref="MachineKey"/></remarks>
|
||||
public Machine? GetMachine() => _internal.Read<Machine>(MachineKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name to use for a DatItem
|
||||
/// </summary>
|
||||
/// <returns>Name if available, null otherwise</returns>
|
||||
public virtual string? GetName() => _internal.GetName();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name to use for a DatItem
|
||||
/// </summary>
|
||||
/// <param name="name">Name to set for the item</param>
|
||||
public virtual void SetName(string? name) => _internal.SetName(name);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clone the DatItem
|
||||
/// </summary>
|
||||
/// <returns>Clone of the DatItem</returns>
|
||||
public abstract object Clone();
|
||||
|
||||
/// <summary>
|
||||
/// Copy all machine information over in one shot
|
||||
/// </summary>
|
||||
/// <param name="item">Existing item to copy information from</param>
|
||||
public void CopyMachineInformation(DatItem item)
|
||||
{
|
||||
// If there is no machine
|
||||
if (!item._internal.ContainsKey(MachineKey))
|
||||
return;
|
||||
|
||||
var machine = item.GetMachine();
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy all machine information over in one shot
|
||||
/// </summary>
|
||||
/// <param name="machine">Existing machine to copy information from</param>
|
||||
public void CopyMachineInformation(Machine? machine)
|
||||
{
|
||||
if (machine is null)
|
||||
return;
|
||||
|
||||
if (machine.Clone() is Machine cloned)
|
||||
SetFieldValue(MachineKey, cloned);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CompareTo(DatItem? other)
|
||||
{
|
||||
// If the other item doesn't exist
|
||||
if (other is null)
|
||||
return 1;
|
||||
|
||||
// Get the names to avoid changing values
|
||||
string? selfName = GetName();
|
||||
string? otherName = other.GetName();
|
||||
|
||||
// If the names are equal
|
||||
if (selfName == otherName)
|
||||
return Equals(other) ? 0 : 1;
|
||||
|
||||
// If `otherName` is null, Compare will return > 0
|
||||
// If `selfName` is null, Compare will return < 0
|
||||
return string.Compare(selfName, otherName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(ModelBackedItem? other)
|
||||
{
|
||||
// If other is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// If the type is mismatched
|
||||
if (other is not DatItem otherItem)
|
||||
return false;
|
||||
|
||||
// Compare internal models
|
||||
return _internal.Equals(otherItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(ModelBackedItem<Data.Models.Metadata.DatItem>? other)
|
||||
{
|
||||
// If other is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// If the type is mismatched
|
||||
if (other is not DatItem otherItem)
|
||||
return false;
|
||||
|
||||
// Compare internal models
|
||||
return _internal.Equals(otherItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if an item is a duplicate using partial matching logic
|
||||
/// </summary>
|
||||
/// <param name="other">DatItem to use as a baseline</param>
|
||||
/// <returns>True if the items are duplicates, false otherwise</returns>
|
||||
public virtual bool Equals(DatItem? other)
|
||||
{
|
||||
// If the other item is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// Get the types for comparison
|
||||
ItemType selfType = GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType();
|
||||
ItemType otherType = other.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType();
|
||||
|
||||
// If we don't have a matched type, return false
|
||||
if (selfType != otherType)
|
||||
return false;
|
||||
|
||||
// Compare the internal models
|
||||
return _internal.EqualTo(other._internal);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Runs a filter and determines if it passes or not
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Filter runner to use for checking</param>
|
||||
/// <returns>True if the item and its machine passes the filter, false otherwise</returns>
|
||||
public bool PassesFilter(FilterRunner filterRunner)
|
||||
{
|
||||
var machine = GetMachine();
|
||||
if (machine is not null && !machine.PassesFilter(filterRunner))
|
||||
return false;
|
||||
|
||||
return filterRunner.Run(_internal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a filter and determines if it passes or not
|
||||
/// </summary>
|
||||
/// <param name="filterRunner">Filter runner to use for checking</param>
|
||||
/// <returns>True if the item passes the filter, false otherwise</returns>
|
||||
public bool PassesFilterDB(FilterRunner filterRunner)
|
||||
=> filterRunner.Run(_internal);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sorting and Merging
|
||||
|
||||
/// <summary>
|
||||
/// Get the dictionary key that should be used for a given item and bucketing type
|
||||
/// </summary>
|
||||
/// <param name="bucketedBy">ItemKey value representing what key to get</param>
|
||||
/// <param name="machine">Machine associated with the item for renaming</param>
|
||||
/// <param name="source">Source associated with the item for renaming</param>
|
||||
/// <param name="lower">True if the key should be lowercased (default), false otherwise</param>
|
||||
/// <param name="norename">True if games should only be compared on game and file name, false if system and source are counted</param>
|
||||
/// <returns>String representing the key to be used for the DatItem</returns>
|
||||
public virtual string GetKey(ItemKey bucketedBy, Machine? machine, Source? source, bool lower = true, bool norename = true)
|
||||
{
|
||||
// Set the output key as the default blank string
|
||||
string key = string.Empty;
|
||||
|
||||
string sourceKeyPadded = source?.Index.ToString().PadLeft(10, '0') + '-';
|
||||
string machineName = machine?.GetName() ?? "Default";
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Now determine what the key should be based on the bucketedBy value
|
||||
switch (bucketedBy)
|
||||
{
|
||||
case ItemKey.CRC:
|
||||
key = HashType.CRC32.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.Machine:
|
||||
key = (norename ? string.Empty : sourceKeyPadded) + machineName;
|
||||
break;
|
||||
|
||||
case ItemKey.MD2:
|
||||
key = HashType.MD2.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.MD4:
|
||||
key = HashType.MD4.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.MD5:
|
||||
key = HashType.MD5.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.RIPEMD128:
|
||||
key = HashType.RIPEMD128.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.RIPEMD160:
|
||||
key = HashType.RIPEMD160.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.SHA1:
|
||||
key = HashType.SHA1.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.SHA256:
|
||||
key = HashType.SHA256.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.SHA384:
|
||||
key = HashType.SHA384.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.SHA512:
|
||||
key = HashType.SHA512.ZeroString;
|
||||
break;
|
||||
|
||||
case ItemKey.SpamSum:
|
||||
key = HashType.SpamSum.ZeroString;
|
||||
break;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
// Double and triple check the key for corner cases
|
||||
key ??= string.Empty;
|
||||
if (lower)
|
||||
key = key.ToLowerInvariant();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
112
SabreTools.Metadata.DatItems/DatItemT.cs
Normal file
112
SabreTools.Metadata.DatItems/DatItemT.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all items included in a set that are backed by an internal model
|
||||
/// </summary>
|
||||
public abstract class DatItem<T> : DatItem, IEquatable<DatItem<T>>, IComparable<DatItem<T>>, ICloneable where T : Data.Models.Metadata.DatItem
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a default, empty object
|
||||
/// </summary>
|
||||
public DatItem()
|
||||
{
|
||||
_internal = Activator.CreateInstance<T>();
|
||||
|
||||
SetName(string.Empty);
|
||||
SetFieldValue(Data.Models.Metadata.DatItem.TypeKey, ItemType);
|
||||
SetFieldValue(MachineKey, new Machine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an object from the internal model
|
||||
/// </summary>
|
||||
public DatItem(T item)
|
||||
{
|
||||
_internal = item;
|
||||
|
||||
SetFieldValue(Data.Models.Metadata.DatItem.TypeKey, ItemType);
|
||||
SetFieldValue(MachineKey, new Machine());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clone the DatItem
|
||||
/// </summary>
|
||||
/// <returns>Clone of the DatItem</returns>
|
||||
/// <remarks>
|
||||
/// Throws an exception if there is a DatItem implementation
|
||||
/// that is not a part of this library.
|
||||
/// </remarks>
|
||||
public override object Clone()
|
||||
{
|
||||
var concrete = Array.Find(Assembly.GetExecutingAssembly().GetTypes(),
|
||||
t => !t.IsAbstract && t.IsClass && t.BaseType == typeof(DatItem<T>));
|
||||
|
||||
var clone = Activator.CreateInstance(concrete!);
|
||||
(clone as DatItem<T>)!._internal = _internal?.Clone() as T ?? Activator.CreateInstance<T>();
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a clone of the current internal model
|
||||
/// </summary>
|
||||
public virtual T GetInternalClone() => (_internal.Clone() as T)!;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CompareTo(DatItem<T>? other)
|
||||
{
|
||||
// If the other item doesn't exist
|
||||
if (other is null)
|
||||
return 1;
|
||||
|
||||
// Get the names to avoid changing values
|
||||
string? selfName = GetName();
|
||||
string? otherName = other.GetName();
|
||||
|
||||
// If the names are equal
|
||||
if (selfName == otherName)
|
||||
return Equals(other) ? 0 : 1;
|
||||
|
||||
// If `otherName` is null, Compare will return > 0
|
||||
// If `selfName` is null, Compare will return < 0
|
||||
return string.Compare(selfName, otherName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if an item is a duplicate using partial matching logic
|
||||
/// </summary>
|
||||
/// <param name="other">DatItem to use as a baseline</param>
|
||||
/// <returns>True if the items are duplicates, false otherwise</returns>
|
||||
public virtual bool Equals(DatItem<T>? other)
|
||||
{
|
||||
// If the other value is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// Get the types for comparison
|
||||
ItemType selfType = GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType();
|
||||
ItemType otherType = other.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType();
|
||||
|
||||
// If we don't have a matched type, return false
|
||||
if (selfType != otherType)
|
||||
return false;
|
||||
|
||||
// Compare the internal models
|
||||
return _internal.EqualTo(other._internal);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
685
SabreTools.Metadata.DatItems/Enums.cs
Normal file
685
SabreTools.Metadata.DatItems/Enums.cs
Normal file
@@ -0,0 +1,685 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine the chip type
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ChipType
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("cpu")]
|
||||
CPU = 1 << 0,
|
||||
|
||||
[Mapping("audio")]
|
||||
Audio = 1 << 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the control type
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ControlType
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("joy")]
|
||||
Joy = 1 << 0,
|
||||
|
||||
[Mapping("stick")]
|
||||
Stick = 1 << 1,
|
||||
|
||||
[Mapping("paddle")]
|
||||
Paddle = 1 << 2,
|
||||
|
||||
[Mapping("pedal")]
|
||||
Pedal = 1 << 3,
|
||||
|
||||
[Mapping("lightgun")]
|
||||
Lightgun = 1 << 4,
|
||||
|
||||
[Mapping("positional")]
|
||||
Positional = 1 << 5,
|
||||
|
||||
[Mapping("dial")]
|
||||
Dial = 1 << 6,
|
||||
|
||||
[Mapping("trackball")]
|
||||
Trackball = 1 << 7,
|
||||
|
||||
[Mapping("mouse")]
|
||||
Mouse = 1 << 8,
|
||||
|
||||
[Mapping("only_buttons")]
|
||||
OnlyButtons = 1 << 9,
|
||||
|
||||
[Mapping("keypad")]
|
||||
Keypad = 1 << 10,
|
||||
|
||||
[Mapping("keyboard")]
|
||||
Keyboard = 1 << 11,
|
||||
|
||||
[Mapping("mahjong")]
|
||||
Mahjong = 1 << 12,
|
||||
|
||||
[Mapping("hanafuda")]
|
||||
Hanafuda = 1 << 13,
|
||||
|
||||
[Mapping("gambling")]
|
||||
Gambling = 1 << 14,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the device type
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DeviceType
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("unknown")]
|
||||
Unknown = 1 << 0,
|
||||
|
||||
[Mapping("cartridge")]
|
||||
Cartridge = 1 << 1,
|
||||
|
||||
[Mapping("floppydisk")]
|
||||
FloppyDisk = 1 << 2,
|
||||
|
||||
[Mapping("harddisk")]
|
||||
HardDisk = 1 << 3,
|
||||
|
||||
[Mapping("cylinder")]
|
||||
Cylinder = 1 << 4,
|
||||
|
||||
[Mapping("cassette")]
|
||||
Cassette = 1 << 5,
|
||||
|
||||
[Mapping("punchcard")]
|
||||
PunchCard = 1 << 6,
|
||||
|
||||
[Mapping("punchtape")]
|
||||
PunchTape = 1 << 7,
|
||||
|
||||
[Mapping("printout")]
|
||||
Printout = 1 << 8,
|
||||
|
||||
[Mapping("serial")]
|
||||
Serial = 1 << 9,
|
||||
|
||||
[Mapping("parallel")]
|
||||
Parallel = 1 << 10,
|
||||
|
||||
[Mapping("snapshot")]
|
||||
Snapshot = 1 << 11,
|
||||
|
||||
[Mapping("quickload")]
|
||||
QuickLoad = 1 << 12,
|
||||
|
||||
[Mapping("memcard")]
|
||||
MemCard = 1 << 13,
|
||||
|
||||
[Mapping("cdrom")]
|
||||
CDROM = 1 << 14,
|
||||
|
||||
[Mapping("magtape")]
|
||||
MagTape = 1 << 15,
|
||||
|
||||
[Mapping("romimage")]
|
||||
ROMImage = 1 << 16,
|
||||
|
||||
[Mapping("midiin")]
|
||||
MIDIIn = 1 << 17,
|
||||
|
||||
[Mapping("midiout")]
|
||||
MIDIOut = 1 << 18,
|
||||
|
||||
[Mapping("picture")]
|
||||
Picture = 1 << 19,
|
||||
|
||||
[Mapping("vidfile")]
|
||||
VidFile = 1 << 20,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the display type
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DisplayType
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("raster")]
|
||||
Raster = 1 << 0,
|
||||
|
||||
[Mapping("vector")]
|
||||
Vector = 1 << 1,
|
||||
|
||||
[Mapping("lcd")]
|
||||
LCD = 1 << 2,
|
||||
|
||||
[Mapping("svg")]
|
||||
SVG = 1 << 3,
|
||||
|
||||
[Mapping("unknown")]
|
||||
Unknown = 1 << 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines which type of duplicate a file is
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DupeType
|
||||
{
|
||||
// Type of match
|
||||
Hash = 1 << 0,
|
||||
All = 1 << 1,
|
||||
|
||||
// Location of match
|
||||
Internal = 1 << 2,
|
||||
External = 1 << 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the endianness
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Endianness
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("big")]
|
||||
Big = 1 << 0,
|
||||
|
||||
[Mapping("little")]
|
||||
Little = 1 << 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the emulation status
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FeatureStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("unemulated")]
|
||||
Unemulated = 1 << 0,
|
||||
|
||||
[Mapping("imperfect")]
|
||||
Imperfect = 1 << 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the feature type
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FeatureType
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("protection")]
|
||||
Protection = 1 << 0,
|
||||
|
||||
[Mapping("palette")]
|
||||
Palette = 1 << 1,
|
||||
|
||||
[Mapping("graphics")]
|
||||
Graphics = 1 << 2,
|
||||
|
||||
[Mapping("sound")]
|
||||
Sound = 1 << 3,
|
||||
|
||||
[Mapping("controls")]
|
||||
Controls = 1 << 4,
|
||||
|
||||
[Mapping("keyboard")]
|
||||
Keyboard = 1 << 5,
|
||||
|
||||
[Mapping("mouse")]
|
||||
Mouse = 1 << 6,
|
||||
|
||||
[Mapping("microphone")]
|
||||
Microphone = 1 << 7,
|
||||
|
||||
[Mapping("camera")]
|
||||
Camera = 1 << 8,
|
||||
|
||||
[Mapping("disk")]
|
||||
Disk = 1 << 9,
|
||||
|
||||
[Mapping("printer")]
|
||||
Printer = 1 << 10,
|
||||
|
||||
[Mapping("lan")]
|
||||
Lan = 1 << 11,
|
||||
|
||||
[Mapping("wan")]
|
||||
Wan = 1 << 12,
|
||||
|
||||
[Mapping("timing")]
|
||||
Timing = 1 << 13,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A subset of fields that can be used as keys
|
||||
/// </summary>
|
||||
public enum ItemKey
|
||||
{
|
||||
NULL = 0,
|
||||
|
||||
Machine,
|
||||
|
||||
CRC,
|
||||
MD2,
|
||||
MD4,
|
||||
MD5,
|
||||
RIPEMD128,
|
||||
RIPEMD160,
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512,
|
||||
SpamSum,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the status of the item
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ItemStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("none", "no")]
|
||||
None = 1 << 0,
|
||||
|
||||
[Mapping("good")]
|
||||
Good = 1 << 1,
|
||||
|
||||
[Mapping("baddump")]
|
||||
BadDump = 1 << 2,
|
||||
|
||||
[Mapping("nodump", "yes")]
|
||||
Nodump = 1 << 3,
|
||||
|
||||
[Mapping("verified")]
|
||||
Verified = 1 << 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine what type of file an item is
|
||||
/// </summary>
|
||||
public enum ItemType
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
// "Actionable" item types
|
||||
|
||||
[Mapping("rom")]
|
||||
Rom,
|
||||
|
||||
[Mapping("disk")]
|
||||
Disk,
|
||||
|
||||
[Mapping("file")]
|
||||
File,
|
||||
|
||||
[Mapping("media")]
|
||||
Media,
|
||||
|
||||
// "Auxiliary" item types
|
||||
|
||||
[Mapping("adjuster")]
|
||||
Adjuster,
|
||||
|
||||
[Mapping("analog")]
|
||||
Analog,
|
||||
|
||||
[Mapping("archive")]
|
||||
Archive,
|
||||
|
||||
[Mapping("biosset")]
|
||||
BiosSet,
|
||||
|
||||
[Mapping("chip")]
|
||||
Chip,
|
||||
|
||||
[Mapping("condition")]
|
||||
Condition,
|
||||
|
||||
[Mapping("configuration")]
|
||||
Configuration,
|
||||
|
||||
[Mapping("conflocation")]
|
||||
ConfLocation,
|
||||
|
||||
[Mapping("confsetting")]
|
||||
ConfSetting,
|
||||
|
||||
[Mapping("control")]
|
||||
Control,
|
||||
|
||||
[Mapping("dataarea")]
|
||||
DataArea,
|
||||
|
||||
[Mapping("device")]
|
||||
Device,
|
||||
|
||||
[Mapping("device_ref", "deviceref")]
|
||||
DeviceRef,
|
||||
|
||||
[Mapping("diplocation")]
|
||||
DipLocation,
|
||||
|
||||
[Mapping("dipswitch")]
|
||||
DipSwitch,
|
||||
|
||||
[Mapping("dipvalue")]
|
||||
DipValue,
|
||||
|
||||
[Mapping("diskarea")]
|
||||
DiskArea,
|
||||
|
||||
[Mapping("display")]
|
||||
Display,
|
||||
|
||||
[Mapping("driver")]
|
||||
Driver,
|
||||
|
||||
[Mapping("extension")]
|
||||
Extension,
|
||||
|
||||
[Mapping("feature")]
|
||||
Feature,
|
||||
|
||||
[Mapping("info")]
|
||||
Info,
|
||||
|
||||
[Mapping("input")]
|
||||
Input,
|
||||
|
||||
[Mapping("instance")]
|
||||
Instance,
|
||||
|
||||
[Mapping("original")]
|
||||
Original,
|
||||
|
||||
[Mapping("part")]
|
||||
Part,
|
||||
|
||||
[Mapping("part_feature", "partfeature")]
|
||||
PartFeature,
|
||||
|
||||
[Mapping("port")]
|
||||
Port,
|
||||
|
||||
[Mapping("ramoption", "ram_option")]
|
||||
RamOption,
|
||||
|
||||
[Mapping("release")]
|
||||
Release,
|
||||
|
||||
[Mapping("release_details", "releasedetails")]
|
||||
ReleaseDetails,
|
||||
|
||||
[Mapping("sample")]
|
||||
Sample,
|
||||
|
||||
[Mapping("serials")]
|
||||
Serials,
|
||||
|
||||
[Mapping("sharedfeat", "shared_feat", "sharedfeature", "shared_feature")]
|
||||
SharedFeat,
|
||||
|
||||
[Mapping("slot")]
|
||||
Slot,
|
||||
|
||||
[Mapping("slotoption", "slot_option")]
|
||||
SlotOption,
|
||||
|
||||
[Mapping("softwarelist", "software_list")]
|
||||
SoftwareList,
|
||||
|
||||
[Mapping("sound")]
|
||||
Sound,
|
||||
|
||||
[Mapping("source_details", "sourcedetails")]
|
||||
SourceDetails,
|
||||
|
||||
[Mapping("blank")]
|
||||
Blank = 99, // This is not a real type, only used internally
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the loadflag value
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum LoadFlag
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("load16_byte")]
|
||||
Load16Byte = 1 << 0,
|
||||
|
||||
[Mapping("load16_word")]
|
||||
Load16Word = 1 << 1,
|
||||
|
||||
[Mapping("load16_word_swap")]
|
||||
Load16WordSwap = 1 << 2,
|
||||
|
||||
[Mapping("load32_byte")]
|
||||
Load32Byte = 1 << 3,
|
||||
|
||||
[Mapping("load32_word")]
|
||||
Load32Word = 1 << 4,
|
||||
|
||||
[Mapping("load32_word_swap")]
|
||||
Load32WordSwap = 1 << 5,
|
||||
|
||||
[Mapping("load32_dword")]
|
||||
Load32DWord = 1 << 6,
|
||||
|
||||
[Mapping("load64_word")]
|
||||
Load64Word = 1 << 7,
|
||||
|
||||
[Mapping("load64_word_swap")]
|
||||
Load64WordSwap = 1 << 8,
|
||||
|
||||
[Mapping("reload")]
|
||||
Reload = 1 << 9,
|
||||
|
||||
[Mapping("fill")]
|
||||
Fill = 1 << 10,
|
||||
|
||||
[Mapping("continue")]
|
||||
Continue = 1 << 11,
|
||||
|
||||
[Mapping("reload_plain")]
|
||||
ReloadPlain = 1 << 12,
|
||||
|
||||
[Mapping("ignore")]
|
||||
Ignore = 1 << 13,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine what type of machine it is
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum MachineType
|
||||
{
|
||||
[Mapping("none")]
|
||||
None = 0,
|
||||
|
||||
[Mapping("bios")]
|
||||
Bios = 1 << 0,
|
||||
|
||||
[Mapping("device", "dev")]
|
||||
Device = 1 << 1,
|
||||
|
||||
[Mapping("mechanical", "mech")]
|
||||
Mechanical = 1 << 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine which OpenMSX subtype an item is
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum OpenMSXSubType
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("rom")]
|
||||
Rom = 1 << 0,
|
||||
|
||||
[Mapping("megarom")]
|
||||
MegaRom = 1 << 1,
|
||||
|
||||
[Mapping("sccpluscart")]
|
||||
SCCPlusCart = 1 << 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine relation of value to condition
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Relation
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("eq")]
|
||||
Equal = 1 << 0,
|
||||
|
||||
[Mapping("ne")]
|
||||
NotEqual = 1 << 1,
|
||||
|
||||
[Mapping("gt")]
|
||||
GreaterThan = 1 << 2,
|
||||
|
||||
[Mapping("le")]
|
||||
LessThanOrEqual = 1 << 3,
|
||||
|
||||
[Mapping("lt")]
|
||||
LessThan = 1 << 4,
|
||||
|
||||
[Mapping("ge")]
|
||||
GreaterThanOrEqual = 1 << 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine machine runnable status
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Runnable
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("no")]
|
||||
No = 1 << 0,
|
||||
|
||||
[Mapping("partial")]
|
||||
Partial = 1 << 1,
|
||||
|
||||
[Mapping("yes")]
|
||||
Yes = 1 << 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine software list status
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum SoftwareListStatus
|
||||
{
|
||||
[Mapping("none")]
|
||||
None = 0,
|
||||
|
||||
[Mapping("original")]
|
||||
Original = 1 << 0,
|
||||
|
||||
[Mapping("compatible")]
|
||||
Compatible = 1 << 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine machine support status
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Supported
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("no", "unsupported")]
|
||||
No = 1 << 0,
|
||||
|
||||
[Mapping("partial")]
|
||||
Partial = 1 << 1,
|
||||
|
||||
[Mapping("yes", "supported")]
|
||||
Yes = 1 << 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine driver support statuses
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum SupportStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a fake flag that is used for filter only
|
||||
/// </summary>
|
||||
NULL = 0,
|
||||
|
||||
[Mapping("good")]
|
||||
Good = 1 << 0,
|
||||
|
||||
[Mapping("imperfect")]
|
||||
Imperfect = 1 << 1,
|
||||
|
||||
[Mapping("preliminary")]
|
||||
Preliminary = 1 << 2,
|
||||
}
|
||||
}
|
||||
788
SabreTools.Metadata.DatItems/Extensions.cs
Normal file
788
SabreTools.Metadata.DatItems/Extensions.cs
Normal file
@@ -0,0 +1,788 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
#region Private Maps
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for ChipType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, ChipType> _toChipTypeMap = Converters.GenerateToEnum<ChipType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for ChipType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<ChipType, string> _fromChipTypeMap = Converters.GenerateToString<ChipType>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for ControlType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, ControlType> _toControlTypeMap = Converters.GenerateToEnum<ControlType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for ControlType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<ControlType, string> _fromControlTypeMap = Converters.GenerateToString<ControlType>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for DeviceType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, DeviceType> _toDeviceTypeMap = Converters.GenerateToEnum<DeviceType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for DeviceType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<DeviceType, string> _fromDeviceTypeMap = Converters.GenerateToString<DeviceType>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for DisplayType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, DisplayType> _toDisplayTypeMap = Converters.GenerateToEnum<DisplayType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for DisplayType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<DisplayType, string> _fromDisplayTypeMap = Converters.GenerateToString<DisplayType>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for Endianness
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, Endianness> _toEndiannessMap = Converters.GenerateToEnum<Endianness>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for Endianness
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Endianness, string> _fromEndiannessMap = Converters.GenerateToString<Endianness>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for FeatureStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, FeatureStatus> _toFeatureStatusMap = Converters.GenerateToEnum<FeatureStatus>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for FeatureStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<FeatureStatus, string> _fromFeatureStatusMap = Converters.GenerateToString<FeatureStatus>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for FeatureType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, FeatureType> _toFeatureTypeMap = Converters.GenerateToEnum<FeatureType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for FeatureType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<FeatureType, string> _fromFeatureTypeMap = Converters.GenerateToString<FeatureType>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for ItemStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, ItemStatus> _toItemStatusMap = Converters.GenerateToEnum<ItemStatus>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for ItemStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<ItemStatus, string> _fromItemStatusMap = Converters.GenerateToString<ItemStatus>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for ItemType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, ItemType> _toItemTypeMap = Converters.GenerateToEnum<ItemType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for ItemType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<ItemType, string> _fromItemTypeMap = Converters.GenerateToString<ItemType>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for LoadFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, LoadFlag> _toLoadFlagMap = Converters.GenerateToEnum<LoadFlag>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for LoadFlag
|
||||
/// </summary>
|
||||
private static readonly Dictionary<LoadFlag, string> _fromLoadFlagMap = Converters.GenerateToString<LoadFlag>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for MachineType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, MachineType> _toMachineTypeMap = Converters.GenerateToEnum<MachineType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for OpenMSXSubType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, OpenMSXSubType> _toOpenMSXSubTypeMap = Converters.GenerateToEnum<OpenMSXSubType>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for OpenMSXSubType
|
||||
/// </summary>
|
||||
private static readonly Dictionary<OpenMSXSubType, string> _fromOpenMSXSubTypeMap = Converters.GenerateToString<OpenMSXSubType>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for Relation
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, Relation> _toRelationMap = Converters.GenerateToEnum<Relation>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for Relation
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Relation, string> _fromRelationMap = Converters.GenerateToString<Relation>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for Runnable
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, Runnable> _toRunnableMap = Converters.GenerateToEnum<Runnable>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for Runnable
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Runnable, string> _fromRunnableMap = Converters.GenerateToString<Runnable>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for SoftwareListStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, SoftwareListStatus> _toSoftwareListStatusMap = Converters.GenerateToEnum<SoftwareListStatus>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for SoftwareListStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<SoftwareListStatus, string> _fromSoftwareListStatusMap = Converters.GenerateToString<SoftwareListStatus>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for Supported
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, Supported> _toSupportedMap = Converters.GenerateToEnum<Supported>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for Supported
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Supported, string> _fromSupportedMap = Converters.GenerateToString<Supported>(useSecond: false);
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for Supported (secondary)
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Supported, string> _fromSupportedSecondaryMap = Converters.GenerateToString<Supported>(useSecond: true);
|
||||
|
||||
/// <summary>
|
||||
/// Set of enum to string mappings for SupportStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, SupportStatus> _toSupportStatusMap = Converters.GenerateToEnum<SupportStatus>();
|
||||
|
||||
/// <summary>
|
||||
/// Set of string to enum mappings for SupportStatus
|
||||
/// </summary>
|
||||
private static readonly Dictionary<SupportStatus, string> _fromSupportStatusMap = Converters.GenerateToString<SupportStatus>(useSecond: false);
|
||||
|
||||
#endregion
|
||||
|
||||
#region String to Enum
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static ChipType AsChipType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toChipTypeMap.ContainsKey(value))
|
||||
return _toChipTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static ControlType AsControlType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toControlTypeMap.ContainsKey(value))
|
||||
return _toControlTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static DeviceType AsDeviceType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toDeviceTypeMap.ContainsKey(value))
|
||||
return _toDeviceTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static DisplayType AsDisplayType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toDisplayTypeMap.ContainsKey(value))
|
||||
return _toDisplayTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static Endianness AsEndianness(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toEndiannessMap.ContainsKey(value))
|
||||
return _toEndiannessMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static FeatureStatus AsFeatureStatus(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toFeatureStatusMap.ContainsKey(value))
|
||||
return _toFeatureStatusMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static FeatureType AsFeatureType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toFeatureTypeMap.ContainsKey(value))
|
||||
return _toFeatureTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static ItemStatus AsItemStatus(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toItemStatusMap.ContainsKey(value))
|
||||
return _toItemStatusMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static ItemType AsItemType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toItemTypeMap.ContainsKey(value))
|
||||
return _toItemTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static LoadFlag AsLoadFlag(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toLoadFlagMap.ContainsKey(value))
|
||||
return _toLoadFlagMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static MachineType AsMachineType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toMachineTypeMap.ContainsKey(value))
|
||||
return _toMachineTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static OpenMSXSubType AsOpenMSXSubType(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toOpenMSXSubTypeMap.ContainsKey(value))
|
||||
return _toOpenMSXSubTypeMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static Relation AsRelation(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toRelationMap.ContainsKey(value))
|
||||
return _toRelationMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static Runnable AsRunnable(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toRunnableMap.ContainsKey(value))
|
||||
return _toRunnableMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static SoftwareListStatus AsSoftwareListStatus(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toSoftwareListStatusMap.ContainsKey(value))
|
||||
return _toSoftwareListStatusMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static Supported AsSupported(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toSupportedMap.ContainsKey(value))
|
||||
return _toSupportedMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the enum value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns>Enum value representing the input, default on error</returns>
|
||||
public static SupportStatus AsSupportStatus(this string? value)
|
||||
{
|
||||
// Normalize the input value
|
||||
value = value?.ToLowerInvariant();
|
||||
if (value is null)
|
||||
return default;
|
||||
|
||||
// Try to get the value from the mappings
|
||||
if (_toSupportStatusMap.ContainsKey(value))
|
||||
return _toSupportStatusMap[value];
|
||||
|
||||
// Otherwise, return the default value for the enum
|
||||
return default;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enum to String
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this ChipType value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromChipTypeMap.ContainsKey(value))
|
||||
return _fromChipTypeMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this ControlType value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromControlTypeMap.ContainsKey(value))
|
||||
return _fromControlTypeMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this DeviceType value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromDeviceTypeMap.ContainsKey(value))
|
||||
return _fromDeviceTypeMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this DisplayType value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromDisplayTypeMap.ContainsKey(value))
|
||||
return _fromDisplayTypeMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this Endianness value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromEndiannessMap.ContainsKey(value))
|
||||
return _fromEndiannessMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this FeatureStatus value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromFeatureStatusMap.ContainsKey(value))
|
||||
return _fromFeatureStatusMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this FeatureType value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromFeatureTypeMap.ContainsKey(value))
|
||||
return _fromFeatureTypeMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this ItemStatus value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromItemStatusMap.ContainsKey(value))
|
||||
return _fromItemStatusMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this ItemType value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromItemTypeMap.ContainsKey(value))
|
||||
return _fromItemTypeMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this LoadFlag value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromLoadFlagMap.ContainsKey(value))
|
||||
return _fromLoadFlagMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this OpenMSXSubType value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromOpenMSXSubTypeMap.ContainsKey(value))
|
||||
return _fromOpenMSXSubTypeMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this Relation value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromRelationMap.ContainsKey(value))
|
||||
return _fromRelationMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this Runnable value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromRunnableMap.ContainsKey(value))
|
||||
return _fromRunnableMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this SoftwareListStatus value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromSoftwareListStatusMap.ContainsKey(value))
|
||||
return _fromSoftwareListStatusMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this Supported value, bool useSecond = false)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (!useSecond && _fromSupportedMap.ContainsKey(value))
|
||||
return _fromSupportedMap[value];
|
||||
else if (useSecond && _fromSupportedSecondaryMap.ContainsKey(value))
|
||||
return _fromSupportedSecondaryMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for an input enum, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">Enum value to parse/param>
|
||||
/// <param name="useSecond">True to use the second mapping option, if it exists</param>
|
||||
/// <returns>String value representing the input, default on error</returns>
|
||||
public static string? AsStringValue(this SupportStatus value)
|
||||
{
|
||||
// Try to get the value from the mappings
|
||||
if (_fromSupportStatusMap.ContainsKey(value))
|
||||
return _fromSupportStatusMap[value];
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
70
SabreTools.Metadata.DatItems/Formats/Adjuster.cs
Normal file
70
SabreTools.Metadata.DatItems/Formats/Adjuster.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which Adjuster(s) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("adjuster"), XmlRoot("adjuster")]
|
||||
public sealed class Adjuster : DatItem<Data.Models.Metadata.Adjuster>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Adjuster;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ConditionsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditions = GetFieldValue<Condition[]?>(Data.Models.Metadata.Adjuster.ConditionKey);
|
||||
return conditions is not null && conditions.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Adjuster() : base() { }
|
||||
|
||||
public Adjuster(Data.Models.Metadata.Adjuster item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Adjuster.DefaultKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Adjuster.DefaultKey, GetBoolFieldValue(Data.Models.Metadata.Adjuster.DefaultKey).FromYesNo());
|
||||
|
||||
// Handle subitems
|
||||
var condition = item.Read<Data.Models.Metadata.Condition>(Data.Models.Metadata.Adjuster.ConditionKey);
|
||||
if (condition is not null)
|
||||
SetFieldValue(Data.Models.Metadata.Adjuster.ConditionKey, new Condition(condition));
|
||||
}
|
||||
|
||||
public Adjuster(Data.Models.Metadata.Adjuster item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.Adjuster GetInternalClone()
|
||||
{
|
||||
var adjusterItem = base.GetInternalClone();
|
||||
|
||||
var condition = GetFieldValue<Condition?>(Data.Models.Metadata.Adjuster.ConditionKey);
|
||||
if (condition is not null)
|
||||
adjusterItem[Data.Models.Metadata.Adjuster.ConditionKey] = condition.GetInternalClone();
|
||||
|
||||
return adjusterItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
SabreTools.Metadata.DatItems/Formats/Analog.cs
Normal file
33
SabreTools.Metadata.DatItems/Formats/Analog.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single analog item
|
||||
/// </summary>
|
||||
[JsonObject("analog"), XmlRoot("analog")]
|
||||
public sealed class Analog : DatItem<Data.Models.Metadata.Analog>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Analog;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Analog() : base() { }
|
||||
|
||||
public Analog(Data.Models.Metadata.Analog item) : base(item) { }
|
||||
|
||||
public Analog(Data.Models.Metadata.Analog item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
100
SabreTools.Metadata.DatItems/Formats/Archive.cs
Normal file
100
SabreTools.Metadata.DatItems/Formats/Archive.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents generic archive files to be included in a set
|
||||
/// </summary>
|
||||
[JsonObject("archive"), XmlRoot("archive")]
|
||||
public sealed class Archive : DatItem<Data.Models.Metadata.Archive>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Archive;
|
||||
|
||||
// TODO: None of the following are used or checked
|
||||
|
||||
/// <summary>
|
||||
/// Archive ID number
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
[JsonProperty("number"), XmlElement("number")]
|
||||
public string? Number { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Clone value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
[JsonProperty("clone"), XmlElement("clone")]
|
||||
public string? CloneValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Regional parent value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
[JsonProperty("regparent"), XmlElement("regparent")]
|
||||
public string? RegParent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Region value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
[JsonProperty("region"), XmlElement("region")]
|
||||
public string? Region { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Languages value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
[JsonProperty("languages"), XmlElement("languages")]
|
||||
public string? Languages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Development status value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
[JsonProperty("devstatus"), XmlElement("devstatus")]
|
||||
public string? DevStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Physical value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
/// <remarks>TODO: Is this numeric or a flag?</remarks>
|
||||
[JsonProperty("physical"), XmlElement("physical")]
|
||||
public string? Physical { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Complete value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
/// <remarks>TODO: Is this numeric or a flag?</remarks>
|
||||
[JsonProperty("complete"), XmlElement("complete")]
|
||||
public string? Complete { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Categories value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: No-Intro database export only</remarks>
|
||||
[JsonProperty("categories"), XmlElement("categories")]
|
||||
public string? Categories { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Archive() : base() { }
|
||||
|
||||
public Archive(Data.Models.Metadata.Archive item) : base(item) { }
|
||||
|
||||
public Archive(Data.Models.Metadata.Archive item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
SabreTools.Metadata.DatItems/Formats/BiosSet.cs
Normal file
39
SabreTools.Metadata.DatItems/Formats/BiosSet.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which BIOS(es) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("biosset"), XmlRoot("biosset")]
|
||||
public sealed class BiosSet : DatItem<Data.Models.Metadata.BiosSet>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.BiosSet;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public BiosSet() : base() { }
|
||||
|
||||
public BiosSet(Data.Models.Metadata.BiosSet item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.BiosSet.DefaultKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.BiosSet.DefaultKey, GetBoolFieldValue(Data.Models.Metadata.BiosSet.DefaultKey).FromYesNo());
|
||||
}
|
||||
|
||||
public BiosSet(Data.Models.Metadata.BiosSet item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
95
SabreTools.Metadata.DatItems/Formats/Blank.cs
Normal file
95
SabreTools.Metadata.DatItems/Formats/Blank.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a blank set from an input DAT
|
||||
/// </summary>
|
||||
[JsonObject("blank"), XmlRoot("blank")]
|
||||
public sealed class Blank : DatItem
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Blank;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a default, empty Blank object
|
||||
/// </summary>
|
||||
public Blank()
|
||||
{
|
||||
SetFieldValue(Data.Models.Metadata.DatItem.TypeKey, ItemType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override object Clone()
|
||||
{
|
||||
var blank = new Blank();
|
||||
blank.SetFieldValue(MachineKey, GetMachine());
|
||||
blank.SetFieldValue(RemoveKey, GetBoolFieldValue(RemoveKey));
|
||||
blank.SetFieldValue<Source?>(SourceKey, GetFieldValue<Source?>(SourceKey));
|
||||
blank.SetFieldValue<string?>(Data.Models.Metadata.DatItem.TypeKey, GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType().AsStringValue());
|
||||
|
||||
return blank;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(ModelBackedItem? other)
|
||||
{
|
||||
// If other is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// If the type is mismatched
|
||||
if (other is not DatItem otherItem)
|
||||
return false;
|
||||
|
||||
// Compare internal models
|
||||
return Equals(otherItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(ModelBackedItem<Data.Models.Metadata.DatItem>? other)
|
||||
{
|
||||
// If other is null
|
||||
if (other is null)
|
||||
return false;
|
||||
|
||||
// If the type is mismatched
|
||||
if (other is not DatItem otherItem)
|
||||
return false;
|
||||
|
||||
// Compare internal models
|
||||
return Equals(otherItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(DatItem? other)
|
||||
{
|
||||
// If we don't have a blank, return false
|
||||
if (GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey) != other?.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey))
|
||||
return false;
|
||||
|
||||
// Otherwise, treat it as a Blank
|
||||
Blank? newOther = other as Blank;
|
||||
|
||||
// If the machine information matches
|
||||
return GetMachine() == newOther!.GetMachine();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
41
SabreTools.Metadata.DatItems/Formats/Chip.cs
Normal file
41
SabreTools.Metadata.DatItems/Formats/Chip.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which Chip(s) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("chip"), XmlRoot("chip")]
|
||||
public sealed class Chip : DatItem<Data.Models.Metadata.Chip>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Chip;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Chip() : base() { }
|
||||
|
||||
public Chip(Data.Models.Metadata.Chip item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Chip.SoundOnlyKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Chip.SoundOnlyKey, GetBoolFieldValue(Data.Models.Metadata.Chip.SoundOnlyKey).FromYesNo());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Chip.ChipTypeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Chip.ChipTypeKey, GetStringFieldValue(Data.Models.Metadata.Chip.ChipTypeKey).AsChipType().AsStringValue());
|
||||
}
|
||||
|
||||
public Chip(Data.Models.Metadata.Chip item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
38
SabreTools.Metadata.DatItems/Formats/Condition.cs
Normal file
38
SabreTools.Metadata.DatItems/Formats/Condition.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a condition on a machine or other item
|
||||
/// </summary>
|
||||
[JsonObject("condition"), XmlRoot("condition")]
|
||||
public sealed class Condition : DatItem<Data.Models.Metadata.Condition>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Condition;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Condition() : base() { }
|
||||
|
||||
public Condition(Data.Models.Metadata.Condition item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Condition.RelationKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Condition.RelationKey, GetStringFieldValue(Data.Models.Metadata.Condition.RelationKey).AsRelation().AsStringValue());
|
||||
}
|
||||
|
||||
public Condition(Data.Models.Metadata.Condition item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
SabreTools.Metadata.DatItems/Formats/ConfLocation.cs
Normal file
39
SabreTools.Metadata.DatItems/Formats/ConfLocation.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one conflocation
|
||||
/// </summary>
|
||||
[JsonObject("conflocation"), XmlRoot("conflocation")]
|
||||
public sealed class ConfLocation : DatItem<Data.Models.Metadata.ConfLocation>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.ConfLocation;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ConfLocation() : base() { }
|
||||
|
||||
public ConfLocation(Data.Models.Metadata.ConfLocation item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.ConfLocation.InvertedKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.ConfLocation.InvertedKey, GetBoolFieldValue(Data.Models.Metadata.ConfLocation.InvertedKey).FromYesNo());
|
||||
}
|
||||
|
||||
public ConfLocation(Data.Models.Metadata.ConfLocation item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
71
SabreTools.Metadata.DatItems/Formats/ConfSetting.cs
Normal file
71
SabreTools.Metadata.DatItems/Formats/ConfSetting.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one ListXML confsetting
|
||||
/// </summary>
|
||||
[JsonObject("confsetting"), XmlRoot("confsetting")]
|
||||
public sealed class ConfSetting : DatItem<Data.Models.Metadata.ConfSetting>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.ConfSetting;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ConditionsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditions = GetFieldValue<Condition[]?>(Data.Models.Metadata.ConfSetting.ConditionKey);
|
||||
return conditions is not null && conditions.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ConfSetting() : base() { }
|
||||
|
||||
public ConfSetting(Data.Models.Metadata.ConfSetting item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.ConfSetting.DefaultKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.ConfSetting.DefaultKey, GetBoolFieldValue(Data.Models.Metadata.ConfSetting.DefaultKey).FromYesNo());
|
||||
|
||||
// Handle subitems
|
||||
var condition = GetFieldValue<Data.Models.Metadata.Condition>(Data.Models.Metadata.ConfSetting.ConditionKey);
|
||||
if (condition is not null)
|
||||
SetFieldValue<Condition?>(Data.Models.Metadata.ConfSetting.ConditionKey, new Condition(condition));
|
||||
}
|
||||
|
||||
public ConfSetting(Data.Models.Metadata.ConfSetting item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.ConfSetting GetInternalClone()
|
||||
{
|
||||
var confSettingItem = base.GetInternalClone();
|
||||
|
||||
// Handle subitems
|
||||
var condition = GetFieldValue<Condition>(Data.Models.Metadata.ConfSetting.ConditionKey);
|
||||
if (condition is not null)
|
||||
confSettingItem[Data.Models.Metadata.ConfSetting.ConditionKey] = condition.GetInternalClone();
|
||||
|
||||
return confSettingItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
115
SabreTools.Metadata.DatItems/Formats/Configuration.cs
Normal file
115
SabreTools.Metadata.DatItems/Formats/Configuration.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which Configuration(s) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("configuration"), XmlRoot("configuration")]
|
||||
public sealed class Configuration : DatItem<Data.Models.Metadata.Configuration>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Configuration;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ConditionsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditions = GetFieldValue<Condition[]?>(Data.Models.Metadata.Configuration.ConditionKey);
|
||||
return conditions is not null && conditions.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool LocationsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var locations = GetFieldValue<ConfLocation[]?>(Data.Models.Metadata.Configuration.ConfLocationKey);
|
||||
return locations is not null && locations.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool SettingsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var settings = GetFieldValue<ConfSetting[]?>(Data.Models.Metadata.Configuration.ConfSettingKey);
|
||||
return settings is not null && settings.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Configuration() : base() { }
|
||||
|
||||
public Configuration(Data.Models.Metadata.Configuration item) : base(item)
|
||||
{
|
||||
// Handle subitems
|
||||
var condition = item.Read<Data.Models.Metadata.Condition>(Data.Models.Metadata.Configuration.ConditionKey);
|
||||
if (condition is not null)
|
||||
SetFieldValue<Condition?>(Data.Models.Metadata.Configuration.ConditionKey, new Condition(condition));
|
||||
|
||||
var confLocations = item.ReadItemArray<Data.Models.Metadata.ConfLocation>(Data.Models.Metadata.Configuration.ConfLocationKey);
|
||||
if (confLocations is not null)
|
||||
{
|
||||
ConfLocation[] confLocationItems = Array.ConvertAll(confLocations, confLocation => new ConfLocation(confLocation));
|
||||
SetFieldValue<ConfLocation[]?>(Data.Models.Metadata.Configuration.ConfLocationKey, confLocationItems);
|
||||
}
|
||||
|
||||
var confSettings = item.ReadItemArray<Data.Models.Metadata.ConfSetting>(Data.Models.Metadata.Configuration.ConfSettingKey);
|
||||
if (confSettings is not null)
|
||||
{
|
||||
ConfSetting[] confSettingItems = Array.ConvertAll(confSettings, confSetting => new ConfSetting(confSetting));
|
||||
SetFieldValue<ConfSetting[]?>(Data.Models.Metadata.Configuration.ConfSettingKey, confSettingItems);
|
||||
}
|
||||
}
|
||||
|
||||
public Configuration(Data.Models.Metadata.Configuration item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.Configuration GetInternalClone()
|
||||
{
|
||||
var configurationItem = base.GetInternalClone();
|
||||
|
||||
var condition = GetFieldValue<Condition?>(Data.Models.Metadata.Configuration.ConditionKey);
|
||||
if (condition is not null)
|
||||
configurationItem[Data.Models.Metadata.Configuration.ConditionKey] = condition.GetInternalClone();
|
||||
|
||||
var confLocations = GetFieldValue<ConfLocation[]?>(Data.Models.Metadata.Configuration.ConfLocationKey);
|
||||
if (confLocations is not null)
|
||||
{
|
||||
Data.Models.Metadata.ConfLocation[] confLocationItems = Array.ConvertAll(confLocations, confLocation => confLocation.GetInternalClone());
|
||||
configurationItem[Data.Models.Metadata.Configuration.ConfLocationKey] = confLocationItems;
|
||||
}
|
||||
|
||||
var confSettings = GetFieldValue<ConfSetting[]?>(Data.Models.Metadata.Configuration.ConfSettingKey);
|
||||
if (confSettings is not null)
|
||||
{
|
||||
Data.Models.Metadata.ConfSetting[] confSettingItems = Array.ConvertAll(confSettings, confSetting => confSetting.GetInternalClone());
|
||||
configurationItem[Data.Models.Metadata.Configuration.ConfSettingKey] = confSettingItems;
|
||||
}
|
||||
|
||||
return configurationItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
55
SabreTools.Metadata.DatItems/Formats/Control.cs
Normal file
55
SabreTools.Metadata.DatItems/Formats/Control.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents control for an input
|
||||
/// </summary>
|
||||
[JsonObject("control"), XmlRoot("control")]
|
||||
public sealed class Control : DatItem<Data.Models.Metadata.Control>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Control;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Control() : base() { }
|
||||
|
||||
public Control(Data.Models.Metadata.Control item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Control.ButtonsKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.ButtonsKey, GetInt64FieldValue(Data.Models.Metadata.Control.ButtonsKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Control.KeyDeltaKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.KeyDeltaKey, GetInt64FieldValue(Data.Models.Metadata.Control.KeyDeltaKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Control.MaximumKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.MaximumKey, GetInt64FieldValue(Data.Models.Metadata.Control.MaximumKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Control.MinimumKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.MinimumKey, GetInt64FieldValue(Data.Models.Metadata.Control.MinimumKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Control.PlayerKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.PlayerKey, GetInt64FieldValue(Data.Models.Metadata.Control.PlayerKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Control.ReqButtonsKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.ReqButtonsKey, GetInt64FieldValue(Data.Models.Metadata.Control.ReqButtonsKey).ToString());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Control.ReverseKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.ReverseKey, GetBoolFieldValue(Data.Models.Metadata.Control.ReverseKey).FromYesNo());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Control.SensitivityKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.SensitivityKey, GetInt64FieldValue(Data.Models.Metadata.Control.SensitivityKey).ToString());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Control.ControlTypeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Control.ControlTypeKey, GetStringFieldValue(Data.Models.Metadata.Control.ControlTypeKey).AsControlType().AsStringValue());
|
||||
}
|
||||
|
||||
public Control(Data.Models.Metadata.Control item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
43
SabreTools.Metadata.DatItems/Formats/DataArea.cs
Normal file
43
SabreTools.Metadata.DatItems/Formats/DataArea.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// SoftwareList dataarea information
|
||||
/// </summary>
|
||||
/// <remarks>One DataArea can contain multiple Rom items</remarks>
|
||||
[JsonObject("dataarea"), XmlRoot("dataarea")]
|
||||
public sealed class DataArea : DatItem<Data.Models.Metadata.DataArea>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.DataArea;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DataArea() : base() { }
|
||||
|
||||
public DataArea(Data.Models.Metadata.DataArea item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetStringFieldValue(Data.Models.Metadata.DataArea.EndiannessKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.DataArea.EndiannessKey, GetStringFieldValue(Data.Models.Metadata.DataArea.EndiannessKey).AsEndianness().AsStringValue());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.DataArea.SizeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.DataArea.SizeKey, GetInt64FieldValue(Data.Models.Metadata.DataArea.SizeKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.DataArea.WidthKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.DataArea.WidthKey, GetInt64FieldValue(Data.Models.Metadata.DataArea.WidthKey).ToString());
|
||||
}
|
||||
|
||||
public DataArea(Data.Models.Metadata.DataArea item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
98
SabreTools.Metadata.DatItems/Formats/Device.cs
Normal file
98
SabreTools.Metadata.DatItems/Formats/Device.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single device on the machine
|
||||
/// </summary>
|
||||
[JsonObject("device"), XmlRoot("device")]
|
||||
public sealed class Device : DatItem<Data.Models.Metadata.Device>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Device;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool InstancesSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var instances = GetFieldValue<Instance[]?>(Data.Models.Metadata.Device.InstanceKey);
|
||||
return instances is not null && instances.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ExtensionsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var extensions = GetFieldValue<Extension[]?>(Data.Models.Metadata.Device.ExtensionKey);
|
||||
return extensions is not null && extensions.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Device() : base() { }
|
||||
|
||||
public Device(Data.Models.Metadata.Device item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Device.MandatoryKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Device.MandatoryKey, GetBoolFieldValue(Data.Models.Metadata.Device.MandatoryKey).FromYesNo());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Device.DeviceTypeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Device.DeviceTypeKey, GetStringFieldValue(Data.Models.Metadata.Device.DeviceTypeKey).AsDeviceType().AsStringValue());
|
||||
|
||||
// Handle subitems
|
||||
var instance = item.Read<Data.Models.Metadata.Instance>(Data.Models.Metadata.Device.InstanceKey);
|
||||
if (instance is not null)
|
||||
SetFieldValue<Instance?>(Data.Models.Metadata.Device.InstanceKey, new Instance(instance));
|
||||
|
||||
var extensions = item.ReadItemArray<Data.Models.Metadata.Extension>(Data.Models.Metadata.Device.ExtensionKey);
|
||||
if (extensions is not null)
|
||||
{
|
||||
Extension[] extensionItems = Array.ConvertAll(extensions, extension => new Extension(extension));
|
||||
SetFieldValue<Extension[]?>(Data.Models.Metadata.Device.ExtensionKey, extensionItems);
|
||||
}
|
||||
}
|
||||
|
||||
public Device(Data.Models.Metadata.Device item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.Device GetInternalClone()
|
||||
{
|
||||
var deviceItem = base.GetInternalClone();
|
||||
|
||||
var instance = GetFieldValue<Instance?>(Data.Models.Metadata.Device.InstanceKey);
|
||||
if (instance is not null)
|
||||
deviceItem[Data.Models.Metadata.Device.InstanceKey] = instance.GetInternalClone();
|
||||
|
||||
var extensions = GetFieldValue<Extension[]?>(Data.Models.Metadata.Device.ExtensionKey);
|
||||
if (extensions is not null)
|
||||
{
|
||||
Data.Models.Metadata.Extension[] extensionItems = Array.ConvertAll(extensions, extension => extension.GetInternalClone());
|
||||
deviceItem[Data.Models.Metadata.Device.ExtensionKey] = extensionItems;
|
||||
}
|
||||
|
||||
return deviceItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
SabreTools.Metadata.DatItems/Formats/DeviceRef.cs
Normal file
33
SabreTools.Metadata.DatItems/Formats/DeviceRef.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which Device Reference(s) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("device_ref"), XmlRoot("device_ref")]
|
||||
public sealed class DeviceRef : DatItem<Data.Models.Metadata.DeviceRef>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.DeviceRef;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DeviceRef() : base() { }
|
||||
|
||||
public DeviceRef(Data.Models.Metadata.DeviceRef item) : base(item) { }
|
||||
|
||||
public DeviceRef(Data.Models.Metadata.DeviceRef item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
SabreTools.Metadata.DatItems/Formats/DipLocation.cs
Normal file
39
SabreTools.Metadata.DatItems/Formats/DipLocation.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one diplocation
|
||||
/// </summary>
|
||||
[JsonObject("diplocation"), XmlRoot("diplocation")]
|
||||
public sealed class DipLocation : DatItem<Data.Models.Metadata.DipLocation>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.DipLocation;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DipLocation() : base() { }
|
||||
|
||||
public DipLocation(Data.Models.Metadata.DipLocation item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.DipLocation.InvertedKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.DipLocation.InvertedKey, GetBoolFieldValue(Data.Models.Metadata.DipLocation.InvertedKey).FromYesNo());
|
||||
}
|
||||
|
||||
public DipLocation(Data.Models.Metadata.DipLocation item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
141
SabreTools.Metadata.DatItems/Formats/DipSwitch.cs
Normal file
141
SabreTools.Metadata.DatItems/Formats/DipSwitch.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which DIP Switch(es) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("dipswitch"), XmlRoot("dipswitch")]
|
||||
public sealed class DipSwitch : DatItem<Data.Models.Metadata.DipSwitch>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Non-standard key for inverted logic
|
||||
/// </summary>
|
||||
public const string PartKey = "PART";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.DipSwitch;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ConditionsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditions = GetFieldValue<Condition[]?>(Data.Models.Metadata.DipSwitch.ConditionKey);
|
||||
return conditions is not null && conditions.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool LocationsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var locations = GetFieldValue<DipLocation[]?>(Data.Models.Metadata.DipSwitch.DipLocationKey);
|
||||
return locations is not null && locations.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ValuesSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var values = GetFieldValue<DipValue[]?>(Data.Models.Metadata.DipSwitch.DipValueKey);
|
||||
return values is not null && values.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool PartSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var part = GetFieldValue<Part?>(PartKey);
|
||||
return part is not null
|
||||
&& (!string.IsNullOrEmpty(part.GetName())
|
||||
|| !string.IsNullOrEmpty(part.GetStringFieldValue(Data.Models.Metadata.Part.InterfaceKey)));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DipSwitch() : base() { }
|
||||
|
||||
public DipSwitch(Data.Models.Metadata.DipSwitch item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.DipSwitch.DefaultKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.DipSwitch.DefaultKey, GetBoolFieldValue(Data.Models.Metadata.DipSwitch.DefaultKey).FromYesNo());
|
||||
|
||||
// Handle subitems
|
||||
var condition = item.Read<Data.Models.Metadata.Condition>(Data.Models.Metadata.DipSwitch.ConditionKey);
|
||||
if (condition is not null)
|
||||
SetFieldValue<Condition?>(Data.Models.Metadata.DipSwitch.ConditionKey, new Condition(condition));
|
||||
|
||||
var dipLocations = item.ReadItemArray<Data.Models.Metadata.DipLocation>(Data.Models.Metadata.DipSwitch.DipLocationKey);
|
||||
if (dipLocations is not null)
|
||||
{
|
||||
DipLocation[] dipLocationItems = Array.ConvertAll(dipLocations, dipLocation => new DipLocation(dipLocation));
|
||||
SetFieldValue<DipLocation[]?>(Data.Models.Metadata.DipSwitch.DipLocationKey, dipLocationItems);
|
||||
}
|
||||
|
||||
var dipValues = item.ReadItemArray<Data.Models.Metadata.DipValue>(Data.Models.Metadata.DipSwitch.DipValueKey);
|
||||
if (dipValues is not null)
|
||||
{
|
||||
DipValue[] dipValueItems = Array.ConvertAll(dipValues, dipValue => new DipValue(dipValue));
|
||||
SetFieldValue<DipValue[]?>(Data.Models.Metadata.DipSwitch.DipValueKey, dipValueItems);
|
||||
}
|
||||
}
|
||||
|
||||
public DipSwitch(Data.Models.Metadata.DipSwitch item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.DipSwitch GetInternalClone()
|
||||
{
|
||||
var dipSwitchItem = base.GetInternalClone();
|
||||
|
||||
var condition = GetFieldValue<Condition?>(Data.Models.Metadata.DipSwitch.ConditionKey);
|
||||
if (condition is not null)
|
||||
dipSwitchItem[Data.Models.Metadata.DipSwitch.ConditionKey] = condition.GetInternalClone();
|
||||
|
||||
var dipLocations = GetFieldValue<DipLocation[]?>(Data.Models.Metadata.DipSwitch.DipLocationKey);
|
||||
if (dipLocations is not null)
|
||||
{
|
||||
Data.Models.Metadata.DipLocation[] dipLocationItems = Array.ConvertAll(dipLocations, dipLocation => dipLocation.GetInternalClone());
|
||||
dipSwitchItem[Data.Models.Metadata.DipSwitch.DipLocationKey] = dipLocationItems;
|
||||
}
|
||||
|
||||
var dipValues = GetFieldValue<DipValue[]?>(Data.Models.Metadata.DipSwitch.DipValueKey);
|
||||
if (dipValues is not null)
|
||||
{
|
||||
Data.Models.Metadata.DipValue[] dipValueItems = Array.ConvertAll(dipValues, dipValue => dipValue.GetInternalClone());
|
||||
dipSwitchItem[Data.Models.Metadata.DipSwitch.DipValueKey] = dipValueItems;
|
||||
}
|
||||
|
||||
return dipSwitchItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
71
SabreTools.Metadata.DatItems/Formats/DipValue.cs
Normal file
71
SabreTools.Metadata.DatItems/Formats/DipValue.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one ListXML dipvalue
|
||||
/// </summary>
|
||||
[JsonObject("dipvalue"), XmlRoot("dipvalue")]
|
||||
public sealed class DipValue : DatItem<Data.Models.Metadata.DipValue>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.DipValue;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ConditionsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditions = GetFieldValue<Condition[]?>(Data.Models.Metadata.DipValue.ConditionKey);
|
||||
return conditions is not null && conditions.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DipValue() : base() { }
|
||||
|
||||
public DipValue(Data.Models.Metadata.DipValue item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.DipValue.DefaultKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.DipValue.DefaultKey, GetBoolFieldValue(Data.Models.Metadata.DipValue.DefaultKey).FromYesNo());
|
||||
|
||||
// Handle subitems
|
||||
var condition = GetFieldValue<Data.Models.Metadata.Condition>(Data.Models.Metadata.DipValue.ConditionKey);
|
||||
if (condition is not null)
|
||||
SetFieldValue<Condition?>(Data.Models.Metadata.DipValue.ConditionKey, new Condition(condition));
|
||||
}
|
||||
|
||||
public DipValue(Data.Models.Metadata.DipValue item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.DipValue GetInternalClone()
|
||||
{
|
||||
var dipValueItem = base.GetInternalClone();
|
||||
|
||||
// Handle subitems
|
||||
var subCondition = GetFieldValue<Condition>(Data.Models.Metadata.DipValue.ConditionKey);
|
||||
if (subCondition is not null)
|
||||
dipValueItem[Data.Models.Metadata.DipValue.ConditionKey] = subCondition.GetInternalClone();
|
||||
|
||||
return dipValueItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
184
SabreTools.Metadata.DatItems/Formats/Disk.cs
Normal file
184
SabreTools.Metadata.DatItems/Formats/Disk.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents Compressed Hunks of Data (CHD) formatted disks which use internal hashes
|
||||
/// </summary>
|
||||
[JsonObject("disk"), XmlRoot("disk")]
|
||||
public sealed class Disk : DatItem<Data.Models.Metadata.Disk>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Non-standard key for inverted logic
|
||||
/// </summary>
|
||||
public const string DiskAreaKey = "DISKAREA";
|
||||
|
||||
/// <summary>
|
||||
/// Non-standard key for inverted logic
|
||||
/// </summary>
|
||||
public const string PartKey = "PART";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Disk;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool DiskAreaSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var diskArea = GetFieldValue<DiskArea?>(DiskAreaKey);
|
||||
return diskArea is not null && !string.IsNullOrEmpty(diskArea.GetName());
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool PartSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var part = GetFieldValue<Part?>(PartKey);
|
||||
return part is not null
|
||||
&& (!string.IsNullOrEmpty(part.GetName())
|
||||
|| !string.IsNullOrEmpty(part.GetStringFieldValue(Data.Models.Metadata.Part.InterfaceKey)));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Disk() : base()
|
||||
{
|
||||
SetFieldValue<DupeType>(DupeTypeKey, 0x00);
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Disk.StatusKey, ItemStatus.None.AsStringValue());
|
||||
}
|
||||
|
||||
public Disk(Data.Models.Metadata.Disk item) : base(item)
|
||||
{
|
||||
SetFieldValue<DupeType>(DupeTypeKey, 0x00);
|
||||
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Disk.OptionalKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Disk.OptionalKey, GetBoolFieldValue(Data.Models.Metadata.Disk.OptionalKey).FromYesNo());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Disk.StatusKey, GetStringFieldValue(Data.Models.Metadata.Disk.StatusKey).AsItemStatus().AsStringValue());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Disk.WritableKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Disk.WritableKey, GetBoolFieldValue(Data.Models.Metadata.Disk.WritableKey).FromYesNo());
|
||||
|
||||
// Process hash values
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Disk.MD5Key, TextHelper.NormalizeMD5(GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Disk.SHA1Key, TextHelper.NormalizeSHA1(GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key)));
|
||||
}
|
||||
|
||||
public Disk(Data.Models.Metadata.Disk item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <summary>
|
||||
/// Convert a disk to the closest Rom approximation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Rom ConvertToRom()
|
||||
{
|
||||
var rom = new Rom(_internal.ConvertToRom()!);
|
||||
|
||||
// Create a DataArea if there was an existing DiskArea
|
||||
var diskArea = GetFieldValue<DiskArea?>(DiskAreaKey);
|
||||
if (diskArea is not null)
|
||||
{
|
||||
var dataArea = new DataArea();
|
||||
|
||||
string? diskAreaName = diskArea.GetStringFieldValue(Data.Models.Metadata.DiskArea.NameKey);
|
||||
dataArea.SetFieldValue(Data.Models.Metadata.DataArea.NameKey, diskAreaName);
|
||||
|
||||
rom.SetFieldValue<DataArea?>(Rom.DataAreaKey, dataArea);
|
||||
}
|
||||
|
||||
rom.SetFieldValue(DupeTypeKey, GetFieldValue<DupeType>(DupeTypeKey));
|
||||
rom.SetFieldValue(MachineKey, GetMachine()?.Clone() as Machine);
|
||||
rom.SetFieldValue(Rom.PartKey, GetFieldValue<Part>(PartKey)?.Clone() as Part);
|
||||
rom.SetFieldValue(RemoveKey, GetBoolFieldValue(RemoveKey));
|
||||
rom.SetFieldValue<Source?>(SourceKey, GetFieldValue<Source?>(SourceKey)?.Clone() as Source);
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <summary>
|
||||
/// Fill any missing size and hash information from another Disk
|
||||
/// </summary>
|
||||
/// <param name="other">Disk to fill information from</param>
|
||||
public void FillMissingInformation(Disk other)
|
||||
=> _internal.FillMissingHashes(other._internal);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the Rom contains any hashes
|
||||
/// </summary>
|
||||
/// <returns>True if any hash exists, false otherwise</returns>
|
||||
public bool HasHashes() => _internal.HasHashes();
|
||||
|
||||
/// <summary>
|
||||
/// Returns if all of the hashes are set to their 0-byte values
|
||||
/// </summary>
|
||||
/// <returns>True if any hash matches the 0-byte value, false otherwise</returns>
|
||||
public bool HasZeroHash() => _internal.HasZeroHash();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sorting and Merging
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetKey(ItemKey bucketedBy, Machine? machine, Source? source, bool lower = true, bool norename = true)
|
||||
{
|
||||
// Set the output key as the default blank string
|
||||
string? key;
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Now determine what the key should be based on the bucketedBy value
|
||||
switch (bucketedBy)
|
||||
{
|
||||
case ItemKey.MD5:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Disk.MD5Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SHA1:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Disk.SHA1Key);
|
||||
break;
|
||||
|
||||
// Let the base handle generic stuff
|
||||
default:
|
||||
return base.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
// Double and triple check the key for corner cases
|
||||
key ??= string.Empty;
|
||||
if (lower)
|
||||
key = key.ToLowerInvariant();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
34
SabreTools.Metadata.DatItems/Formats/DiskArea.cs
Normal file
34
SabreTools.Metadata.DatItems/Formats/DiskArea.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// SoftwareList diskarea information
|
||||
/// </summary>
|
||||
/// <remarks>One DiskArea can contain multiple Disk items</remarks>
|
||||
[JsonObject("diskarea"), XmlRoot("diskarea")]
|
||||
public sealed class DiskArea : DatItem<Data.Models.Metadata.DiskArea>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.DiskArea;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DiskArea() : base() { }
|
||||
|
||||
public DiskArea(Data.Models.Metadata.DiskArea item) : base(item) { }
|
||||
|
||||
public DiskArea(Data.Models.Metadata.DiskArea item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
106
SabreTools.Metadata.DatItems/Formats/Display.cs
Normal file
106
SabreTools.Metadata.DatItems/Formats/Display.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one machine display
|
||||
/// </summary>
|
||||
[JsonObject("display"), XmlRoot("display")]
|
||||
public sealed class Display : DatItem<Data.Models.Metadata.Display>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Display;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Display() : base() { }
|
||||
|
||||
public Display(Data.Models.Metadata.Display item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Display.FlipXKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.FlipXKey, GetBoolFieldValue(Data.Models.Metadata.Display.FlipXKey).FromYesNo());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.HBEndKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.HBEndKey, GetInt64FieldValue(Data.Models.Metadata.Display.HBEndKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.HBStartKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.HBStartKey, GetInt64FieldValue(Data.Models.Metadata.Display.HBStartKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.HeightKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.HeightKey, GetInt64FieldValue(Data.Models.Metadata.Display.HeightKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.HTotalKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.HTotalKey, GetInt64FieldValue(Data.Models.Metadata.Display.HTotalKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.PixClockKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.PixClockKey, GetInt64FieldValue(Data.Models.Metadata.Display.PixClockKey).ToString());
|
||||
if (GetDoubleFieldValue(Data.Models.Metadata.Display.RefreshKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.RefreshKey, GetDoubleFieldValue(Data.Models.Metadata.Display.RefreshKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.RotateKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.RotateKey, GetInt64FieldValue(Data.Models.Metadata.Display.RotateKey).ToString());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Display.DisplayTypeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.DisplayTypeKey, GetStringFieldValue(Data.Models.Metadata.Display.DisplayTypeKey).AsDisplayType().AsStringValue());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.VBEndKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.VBEndKey, GetInt64FieldValue(Data.Models.Metadata.Display.VBEndKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.VBStartKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.VBStartKey, GetInt64FieldValue(Data.Models.Metadata.Display.VBStartKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.VTotalKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.VTotalKey, GetInt64FieldValue(Data.Models.Metadata.Display.VTotalKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Display.WidthKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.WidthKey, GetInt64FieldValue(Data.Models.Metadata.Display.WidthKey).ToString());
|
||||
}
|
||||
|
||||
public Display(Data.Models.Metadata.Display item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
public Display(Data.Models.Metadata.Video item) : base()
|
||||
{
|
||||
SetFieldValue(Data.Models.Metadata.Video.AspectXKey, NumberHelper.ConvertToInt64(item.ReadString(Data.Models.Metadata.Video.AspectXKey)));
|
||||
SetFieldValue(Data.Models.Metadata.Video.AspectYKey, NumberHelper.ConvertToInt64(item.ReadString(Data.Models.Metadata.Video.AspectYKey)));
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.DisplayTypeKey, item.ReadString(Data.Models.Metadata.Video.ScreenKey).AsDisplayType().AsStringValue());
|
||||
SetFieldValue(Data.Models.Metadata.Display.HeightKey, NumberHelper.ConvertToInt64(item.ReadString(Data.Models.Metadata.Video.HeightKey)));
|
||||
SetFieldValue(Data.Models.Metadata.Display.RefreshKey, NumberHelper.ConvertToDouble(item.ReadString(Data.Models.Metadata.Video.RefreshKey)));
|
||||
SetFieldValue(Data.Models.Metadata.Display.WidthKey, NumberHelper.ConvertToInt64(item.ReadString(Data.Models.Metadata.Video.WidthKey)));
|
||||
|
||||
switch (item.ReadString(Data.Models.Metadata.Video.OrientationKey))
|
||||
{
|
||||
case "horizontal":
|
||||
SetFieldValue<long?>(Data.Models.Metadata.Display.RotateKey, 0);
|
||||
break;
|
||||
case "vertical":
|
||||
SetFieldValue<long?>(Data.Models.Metadata.Display.RotateKey, 90);
|
||||
break;
|
||||
default:
|
||||
// TODO: Log invalid values
|
||||
break;
|
||||
}
|
||||
|
||||
// Process flag values
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Video.AspectXKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Video.AspectXKey, GetInt64FieldValue(Data.Models.Metadata.Video.AspectXKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Video.AspectYKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Video.AspectYKey, GetInt64FieldValue(Data.Models.Metadata.Video.AspectYKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Video.HeightKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.HeightKey, GetInt64FieldValue(Data.Models.Metadata.Video.HeightKey).ToString());
|
||||
if (GetDoubleFieldValue(Data.Models.Metadata.Video.RefreshKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.RefreshKey, GetDoubleFieldValue(Data.Models.Metadata.Video.RefreshKey).ToString());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Video.ScreenKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.DisplayTypeKey, GetStringFieldValue(Data.Models.Metadata.Video.ScreenKey).AsDisplayType().AsStringValue());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Video.WidthKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Display.WidthKey, GetInt64FieldValue(Data.Models.Metadata.Video.WidthKey).ToString());
|
||||
}
|
||||
|
||||
public Display(Data.Models.Metadata.Video item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
59
SabreTools.Metadata.DatItems/Formats/Driver.cs
Normal file
59
SabreTools.Metadata.DatItems/Formats/Driver.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the a driver of the machine
|
||||
/// </summary>
|
||||
[JsonObject("driver"), XmlRoot("driver")]
|
||||
public sealed class Driver : DatItem<Data.Models.Metadata.Driver>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Driver;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Driver() : base() { }
|
||||
|
||||
public Driver(Data.Models.Metadata.Driver item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Driver.CocktailKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.CocktailKey, GetStringFieldValue(Data.Models.Metadata.Driver.CocktailKey).AsSupportStatus().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Driver.ColorKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.ColorKey, GetStringFieldValue(Data.Models.Metadata.Driver.ColorKey).AsSupportStatus().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Driver.EmulationKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.EmulationKey, GetStringFieldValue(Data.Models.Metadata.Driver.EmulationKey).AsSupportStatus().AsStringValue());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Driver.IncompleteKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.IncompleteKey, GetBoolFieldValue(Data.Models.Metadata.Driver.IncompleteKey).FromYesNo());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Driver.NoSoundHardwareKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.NoSoundHardwareKey, GetBoolFieldValue(Data.Models.Metadata.Driver.NoSoundHardwareKey).FromYesNo());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Driver.PaletteSizeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.PaletteSizeKey, GetInt64FieldValue(Data.Models.Metadata.Driver.PaletteSizeKey).ToString());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Driver.RequiresArtworkKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.RequiresArtworkKey, GetBoolFieldValue(Data.Models.Metadata.Driver.RequiresArtworkKey).FromYesNo());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Driver.SaveStateKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.SaveStateKey, GetStringFieldValue(Data.Models.Metadata.Driver.SaveStateKey).AsSupported().AsStringValue(useSecond: true));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Driver.SoundKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.SoundKey, GetStringFieldValue(Data.Models.Metadata.Driver.SoundKey).AsSupportStatus().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Driver.StatusKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.StatusKey, GetStringFieldValue(Data.Models.Metadata.Driver.StatusKey).AsSupportStatus().AsStringValue());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Driver.UnofficialKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Driver.UnofficialKey, GetBoolFieldValue(Data.Models.Metadata.Driver.UnofficialKey).FromYesNo());
|
||||
}
|
||||
|
||||
public Driver(Data.Models.Metadata.Driver item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
SabreTools.Metadata.DatItems/Formats/Extension.cs
Normal file
33
SabreTools.Metadata.DatItems/Formats/Extension.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a matchable extension
|
||||
/// </summary>
|
||||
[JsonObject("extension"), XmlRoot("extension")]
|
||||
public sealed class Extension : DatItem<Data.Models.Metadata.Extension>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Extension;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Extension() : base() { }
|
||||
|
||||
public Extension(Data.Models.Metadata.Extension item) : base(item) { }
|
||||
|
||||
public Extension(Data.Models.Metadata.Extension item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
42
SabreTools.Metadata.DatItems/Formats/Feature.cs
Normal file
42
SabreTools.Metadata.DatItems/Formats/Feature.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the a feature of the machine
|
||||
/// </summary>
|
||||
[JsonObject("feature"), XmlRoot("feature")]
|
||||
public sealed class Feature : DatItem<Data.Models.Metadata.Feature>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Feature;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Feature() : base() { }
|
||||
|
||||
public Feature(Data.Models.Metadata.Feature item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Feature.OverallKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Feature.OverallKey, GetStringFieldValue(Data.Models.Metadata.Feature.OverallKey).AsFeatureStatus().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Feature.StatusKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Feature.StatusKey, GetStringFieldValue(Data.Models.Metadata.Feature.StatusKey).AsFeatureStatus().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Feature.FeatureTypeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Feature.FeatureTypeKey, GetStringFieldValue(Data.Models.Metadata.Feature.FeatureTypeKey).AsFeatureType().AsStringValue());
|
||||
}
|
||||
|
||||
public Feature(Data.Models.Metadata.Feature item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
323
SabreTools.Metadata.DatItems/Formats/File.cs
Normal file
323
SabreTools.Metadata.DatItems/Formats/File.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
// TODO: Add item mappings for all fields
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single file item
|
||||
/// </summary>
|
||||
[JsonObject("file"), XmlRoot("file")]
|
||||
public sealed class File : DatItem
|
||||
{
|
||||
#region Private instance variables
|
||||
|
||||
private byte[]? _crc; // 8 bytes
|
||||
private byte[]? _md5; // 16 bytes
|
||||
private byte[]? _sha1; // 20 bytes
|
||||
private byte[]? _sha256; // 32 bytes
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.File;
|
||||
|
||||
/// <summary>
|
||||
/// ID value
|
||||
/// </summary>
|
||||
[JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extension value
|
||||
/// </summary>
|
||||
[JsonProperty("extension", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("extension")]
|
||||
public string? Extension { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Byte size of the rom
|
||||
/// </summary>
|
||||
[JsonProperty("size", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("size")]
|
||||
public long? Size { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// File CRC32 hash
|
||||
/// </summary>
|
||||
[JsonProperty("crc", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("crc")]
|
||||
public string? CRC
|
||||
{
|
||||
get { return _crc.ToHexString(); }
|
||||
set { _crc = value == "null" ? HashType.CRC32.ZeroBytes : TextHelper.NormalizeCRC32(value).FromHexString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File MD5 hash
|
||||
/// </summary>
|
||||
[JsonProperty("md5", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("md5")]
|
||||
public string? MD5
|
||||
{
|
||||
get { return _md5.ToHexString(); }
|
||||
set { _md5 = TextHelper.NormalizeMD5(value).FromHexString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File SHA-1 hash
|
||||
/// </summary>
|
||||
[JsonProperty("sha1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha1")]
|
||||
public string? SHA1
|
||||
{
|
||||
get { return _sha1.ToHexString(); }
|
||||
set { _sha1 = TextHelper.NormalizeSHA1(value).FromHexString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File SHA-256 hash
|
||||
/// </summary>
|
||||
[JsonProperty("sha256", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha256")]
|
||||
public string? SHA256
|
||||
{
|
||||
get { return _sha256.ToHexString(); }
|
||||
set { _sha256 = TextHelper.NormalizeSHA256(value).FromHexString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format value
|
||||
/// </summary>
|
||||
[JsonProperty("format", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("format")]
|
||||
public string? Format { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a default, empty File object
|
||||
/// </summary>
|
||||
public File()
|
||||
{
|
||||
SetFieldValue(Data.Models.Metadata.DatItem.TypeKey, ItemType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override object Clone()
|
||||
{
|
||||
var file = new File()
|
||||
{
|
||||
Id = this.Id,
|
||||
Extension = this.Extension,
|
||||
Size = this.Size,
|
||||
_crc = this._crc,
|
||||
_md5 = this._md5,
|
||||
_sha1 = this._sha1,
|
||||
_sha256 = this._sha256,
|
||||
Format = this.Format,
|
||||
};
|
||||
file.SetFieldValue(DupeTypeKey, GetFieldValue<DupeType>(DupeTypeKey));
|
||||
file.SetFieldValue(MachineKey, GetMachine()!.Clone() as Machine ?? new Machine());
|
||||
file.SetFieldValue(RemoveKey, GetBoolFieldValue(RemoveKey));
|
||||
file.SetFieldValue<Source?>(SourceKey, GetFieldValue<Source?>(SourceKey));
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a disk to the closest Rom approximation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Rom ConvertToRom()
|
||||
{
|
||||
var rom = new Rom();
|
||||
|
||||
rom.SetName($"{Id}.{Extension}");
|
||||
rom.SetFieldValue(Data.Models.Metadata.Rom.SizeKey, Size);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, CRC);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.MD5Key, MD5);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, SHA1);
|
||||
rom.SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA256Key, SHA256);
|
||||
|
||||
rom.SetFieldValue(DupeTypeKey, GetFieldValue<DupeType>(DupeTypeKey));
|
||||
rom.SetFieldValue(MachineKey, GetMachine()?.Clone() as Machine);
|
||||
rom.SetFieldValue(RemoveKey, GetBoolFieldValue(RemoveKey));
|
||||
rom.SetFieldValue<Source?>(SourceKey, GetFieldValue<Source?>(SourceKey));
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(DatItem? other)
|
||||
{
|
||||
bool dupefound = false;
|
||||
|
||||
// If we don't have a file, return false
|
||||
if (GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey) != other?.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey))
|
||||
return dupefound;
|
||||
|
||||
// Otherwise, treat it as a File
|
||||
File? newOther = other as File;
|
||||
|
||||
// If all hashes are empty, then they're dupes
|
||||
if (!HasHashes() && !newOther!.HasHashes())
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
// If we have a file that has no known size, rely on the hashes only
|
||||
else if (Size is null && HashMatch(newOther!))
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
// Otherwise if we get a partial match
|
||||
else if (Size == newOther!.Size && HashMatch(newOther))
|
||||
{
|
||||
dupefound = true;
|
||||
}
|
||||
|
||||
return dupefound;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill any missing size and hash information from another Rom
|
||||
/// </summary>
|
||||
/// <param name="other">File to fill information from</param>
|
||||
public void FillMissingInformation(File other)
|
||||
{
|
||||
if (Size is null && other.Size is not null)
|
||||
Size = other.Size;
|
||||
|
||||
if (_crc.IsNullOrEmpty() && !other._crc.IsNullOrEmpty())
|
||||
_crc = other._crc;
|
||||
|
||||
if (_md5.IsNullOrEmpty() && !other._md5.IsNullOrEmpty())
|
||||
_md5 = other._md5;
|
||||
|
||||
if (_sha1.IsNullOrEmpty() && !other._sha1.IsNullOrEmpty())
|
||||
_sha1 = other._sha1;
|
||||
|
||||
if (_sha256.IsNullOrEmpty() && !other._sha256.IsNullOrEmpty())
|
||||
_sha256 = other._sha256;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the File contains any hashes
|
||||
/// </summary>
|
||||
/// <returns>True if any hash exists, false otherwise</returns>
|
||||
public bool HasHashes()
|
||||
{
|
||||
return !_crc.IsNullOrEmpty()
|
||||
|| !_md5.IsNullOrEmpty()
|
||||
|| !_sha1.IsNullOrEmpty()
|
||||
|| !_sha256.IsNullOrEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if all of the hashes are set to their 0-byte values
|
||||
/// </summary>
|
||||
/// <returns>True if any hash matches the 0-byte value, false otherwise</returns>
|
||||
public bool HasZeroHash()
|
||||
{
|
||||
bool crcNull = string.IsNullOrEmpty(CRC) || string.Equals(CRC, HashType.CRC32.ZeroString, StringComparison.OrdinalIgnoreCase);
|
||||
bool md5Null = string.IsNullOrEmpty(MD5) || string.Equals(MD5, HashType.MD5.ZeroString, StringComparison.OrdinalIgnoreCase);
|
||||
bool sha1Null = string.IsNullOrEmpty(SHA1) || string.Equals(SHA1, HashType.SHA1.ZeroString, StringComparison.OrdinalIgnoreCase);
|
||||
bool sha256Null = string.IsNullOrEmpty(SHA256) || string.Equals(SHA256, HashType.SHA256.ZeroString, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return crcNull && md5Null && sha1Null && sha256Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if there are no, non-empty hashes in common with another File
|
||||
/// </summary>
|
||||
/// <param name="other">File to compare against</param>
|
||||
/// <returns>True if at least one hash is not mutually exclusive, false otherwise</returns>
|
||||
private bool HasCommonHash(File other)
|
||||
{
|
||||
return !(_crc.IsNullOrEmpty() ^ other._crc.IsNullOrEmpty())
|
||||
|| !(_md5.IsNullOrEmpty() ^ other._md5.IsNullOrEmpty())
|
||||
|| !(_sha1.IsNullOrEmpty() ^ other._sha1.IsNullOrEmpty())
|
||||
|| !(_sha256.IsNullOrEmpty() ^ other._sha256.IsNullOrEmpty());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if any hashes are common with another File
|
||||
/// </summary>
|
||||
/// <param name="other">File to compare against</param>
|
||||
/// <returns>True if any hashes are in common, false otherwise</returns>
|
||||
private bool HashMatch(File other)
|
||||
{
|
||||
// If either have no hashes, we return false, otherwise this would be a false positive
|
||||
if (!HasHashes() || !other.HasHashes())
|
||||
return false;
|
||||
|
||||
// If neither have hashes in common, we return false, otherwise this would be a false positive
|
||||
if (!HasCommonHash(other))
|
||||
return false;
|
||||
|
||||
// Return if all hashes match according to merge rules
|
||||
return MetadataExtensions.ConditionalHashEquals(_crc, other._crc)
|
||||
&& MetadataExtensions.ConditionalHashEquals(_md5, other._md5)
|
||||
&& MetadataExtensions.ConditionalHashEquals(_sha1, other._sha1)
|
||||
&& MetadataExtensions.ConditionalHashEquals(_sha256, other._sha256);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sorting and Merging
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetKey(ItemKey bucketedBy, Machine? machine, Source? source, bool lower = true, bool norename = true)
|
||||
{
|
||||
// Set the output key as the default blank string
|
||||
string? key;
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Now determine what the key should be based on the bucketedBy value
|
||||
switch (bucketedBy)
|
||||
{
|
||||
case ItemKey.CRC:
|
||||
key = CRC;
|
||||
break;
|
||||
|
||||
case ItemKey.MD5:
|
||||
key = MD5;
|
||||
break;
|
||||
|
||||
case ItemKey.SHA1:
|
||||
key = SHA1;
|
||||
break;
|
||||
|
||||
case ItemKey.SHA256:
|
||||
key = SHA256;
|
||||
break;
|
||||
|
||||
// Let the base handle generic stuff
|
||||
default:
|
||||
return base.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
// Double and triple check the key for corner cases
|
||||
key ??= string.Empty;
|
||||
if (lower)
|
||||
key = key.ToLowerInvariant();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
SabreTools.Metadata.DatItems/Formats/Info.cs
Normal file
33
SabreTools.Metadata.DatItems/Formats/Info.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents special information about a machine
|
||||
/// </summary>
|
||||
[JsonObject("info"), XmlRoot("info")]
|
||||
public sealed class Info : DatItem<Data.Models.Metadata.Info>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Info;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Info() : base() { }
|
||||
|
||||
public Info(Data.Models.Metadata.Info item) : base(item) { }
|
||||
|
||||
public Info(Data.Models.Metadata.Info item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
86
SabreTools.Metadata.DatItems/Formats/Input.cs
Normal file
86
SabreTools.Metadata.DatItems/Formats/Input.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one ListXML input
|
||||
/// </summary>
|
||||
[JsonObject("input"), XmlRoot("input")]
|
||||
public sealed class Input : DatItem<Data.Models.Metadata.Input>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Input;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ControlsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var controls = GetFieldValue<Control[]?>(Data.Models.Metadata.Input.ControlKey);
|
||||
return controls is not null && controls.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Input() : base() { }
|
||||
|
||||
public Input(Data.Models.Metadata.Input item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Input.ButtonsKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Input.ButtonsKey, GetInt64FieldValue(Data.Models.Metadata.Input.ButtonsKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Input.CoinsKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Input.CoinsKey, GetInt64FieldValue(Data.Models.Metadata.Input.CoinsKey).ToString());
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Input.PlayersKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Input.PlayersKey, GetInt64FieldValue(Data.Models.Metadata.Input.PlayersKey).ToString());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Input.ServiceKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Input.ServiceKey, GetBoolFieldValue(Data.Models.Metadata.Input.ServiceKey).FromYesNo());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Input.TiltKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Input.TiltKey, GetBoolFieldValue(Data.Models.Metadata.Input.TiltKey).FromYesNo());
|
||||
|
||||
// Handle subitems
|
||||
var controls = item.ReadItemArray<Data.Models.Metadata.Control>(Data.Models.Metadata.Input.ControlKey);
|
||||
if (controls is not null)
|
||||
{
|
||||
Control[] controlItems = Array.ConvertAll(controls, control => new Control(control));
|
||||
SetFieldValue<Control[]?>(Data.Models.Metadata.Input.ControlKey, controlItems);
|
||||
}
|
||||
}
|
||||
|
||||
public Input(Data.Models.Metadata.Input item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.Input GetInternalClone()
|
||||
{
|
||||
var inputItem = base.GetInternalClone();
|
||||
|
||||
var controls = GetFieldValue<Control[]?>(Data.Models.Metadata.Input.ControlKey);
|
||||
if (controls is not null)
|
||||
{
|
||||
Data.Models.Metadata.Control[] controlItems = Array.ConvertAll(controls, control => control.GetInternalClone());
|
||||
inputItem[Data.Models.Metadata.Input.ControlKey] = controlItems;
|
||||
}
|
||||
|
||||
return inputItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
SabreTools.Metadata.DatItems/Formats/Instance.cs
Normal file
33
SabreTools.Metadata.DatItems/Formats/Instance.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single instance of another item
|
||||
/// </summary>
|
||||
[JsonObject("instance"), XmlRoot("instance")]
|
||||
public sealed class Instance : DatItem<Data.Models.Metadata.Instance>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Instance;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Instance() : base() { }
|
||||
|
||||
public Instance(Data.Models.Metadata.Instance item) : base(item) { }
|
||||
|
||||
public Instance(Data.Models.Metadata.Instance item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
136
SabreTools.Metadata.DatItems/Formats/Media.cs
Normal file
136
SabreTools.Metadata.DatItems/Formats/Media.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents Aaruformat images which use internal hashes
|
||||
/// </summary>
|
||||
[JsonObject("media"), XmlRoot("media")]
|
||||
public sealed class Media : DatItem<Data.Models.Metadata.Media>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Media;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Media() : base()
|
||||
{
|
||||
SetFieldValue<DupeType>(DupeTypeKey, 0x00);
|
||||
}
|
||||
|
||||
public Media(Data.Models.Metadata.Media item) : base(item)
|
||||
{
|
||||
SetFieldValue<DupeType>(DupeTypeKey, 0x00);
|
||||
|
||||
// Process hash values
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Media.MD5Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Media.MD5Key, TextHelper.NormalizeMD5(GetStringFieldValue(Data.Models.Metadata.Media.MD5Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Media.SHA1Key, TextHelper.NormalizeSHA1(GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Media.SHA256Key, TextHelper.NormalizeSHA256(GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key)));
|
||||
}
|
||||
|
||||
public Media(Data.Models.Metadata.Media item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <summary>
|
||||
/// Convert a media to the closest Rom approximation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Rom ConvertToRom()
|
||||
{
|
||||
var rom = new Rom(_internal.ConvertToRom()!);
|
||||
|
||||
rom.SetFieldValue(DupeTypeKey, GetFieldValue<DupeType>(DupeTypeKey));
|
||||
rom.SetFieldValue(MachineKey, GetMachine());
|
||||
rom.SetFieldValue(RemoveKey, GetBoolFieldValue(RemoveKey));
|
||||
rom.SetFieldValue<Source?>(SourceKey, GetFieldValue<Source?>(SourceKey));
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <summary>
|
||||
/// Fill any missing size and hash information from another Media
|
||||
/// </summary>
|
||||
/// <param name="other">Media to fill information from</param>
|
||||
public void FillMissingInformation(Media other)
|
||||
=> _internal.FillMissingHashes(other._internal);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the Rom contains any hashes
|
||||
/// </summary>
|
||||
/// <returns>True if any hash exists, false otherwise</returns>
|
||||
public bool HasHashes() => _internal.HasHashes();
|
||||
|
||||
/// <summary>
|
||||
/// Returns if all of the hashes are set to their 0-byte values
|
||||
/// </summary>
|
||||
/// <returns>True if any hash matches the 0-byte value, false otherwise</returns>
|
||||
public bool HasZeroHash() => _internal.HasZeroHash();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sorting and Merging
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetKey(ItemKey bucketedBy, Machine? machine, Source? source, bool lower = true, bool norename = true)
|
||||
{
|
||||
// Set the output key as the default blank string
|
||||
string? key;
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Now determine what the key should be based on the bucketedBy value
|
||||
switch (bucketedBy)
|
||||
{
|
||||
case ItemKey.MD5:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Media.MD5Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SHA1:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Media.SHA1Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SHA256:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Media.SHA256Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SpamSum:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Media.SpamSumKey);
|
||||
break;
|
||||
|
||||
// Let the base handle generic stuff
|
||||
default:
|
||||
return base.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
// Double and triple check the key for corner cases
|
||||
key ??= string.Empty;
|
||||
if (lower)
|
||||
key = key.ToLowerInvariant();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
32
SabreTools.Metadata.DatItems/Formats/Original.cs
Normal file
32
SabreTools.Metadata.DatItems/Formats/Original.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the OpenMSX original value
|
||||
/// </summary>
|
||||
[JsonObject("original"), XmlRoot("original")]
|
||||
public sealed class Original
|
||||
{
|
||||
[JsonProperty("value"), XmlElement("value")]
|
||||
public bool? Value
|
||||
{
|
||||
get => _internal.ReadBool(Data.Models.Metadata.Original.ValueKey);
|
||||
set => _internal[Data.Models.Metadata.Original.ValueKey] = value;
|
||||
}
|
||||
|
||||
[JsonProperty("content"), XmlElement("content")]
|
||||
public string? Content
|
||||
{
|
||||
get => _internal.ReadString(Data.Models.Metadata.Original.ContentKey);
|
||||
set => _internal[Data.Models.Metadata.Original.ContentKey] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal Original model
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
private readonly Data.Models.Metadata.Original _internal = [];
|
||||
}
|
||||
}
|
||||
44
SabreTools.Metadata.DatItems/Formats/Part.cs
Normal file
44
SabreTools.Metadata.DatItems/Formats/Part.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// SoftwareList part information
|
||||
/// </summary>
|
||||
/// <remarks>One Part can contain multiple PartFeature, DataArea, DiskArea, and DipSwitch items</remarks>
|
||||
[JsonObject("part"), XmlRoot("part")]
|
||||
public sealed class Part : DatItem<Data.Models.Metadata.Part>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Part;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool FeaturesSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var features = GetFieldValue<PartFeature[]?>(Data.Models.Metadata.Part.FeatureKey);
|
||||
return features is not null && features.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Part() : base() { }
|
||||
|
||||
public Part(Data.Models.Metadata.Part item) : base(item) { }
|
||||
|
||||
public Part(Data.Models.Metadata.Part item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
51
SabreTools.Metadata.DatItems/Formats/PartFeature.cs
Normal file
51
SabreTools.Metadata.DatItems/Formats/PartFeature.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one part feature object
|
||||
/// </summary>
|
||||
[JsonObject("part_feature"), XmlRoot("part_feature")]
|
||||
public sealed class PartFeature : DatItem<Data.Models.Metadata.Feature>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Non-standard key for inverted logic
|
||||
/// </summary>
|
||||
public const string PartKey = "PART";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.PartFeature;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public PartFeature() : base() { }
|
||||
|
||||
public PartFeature(Data.Models.Metadata.Feature item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Feature.OverallKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Feature.OverallKey, GetStringFieldValue(Data.Models.Metadata.Feature.OverallKey).AsFeatureStatus().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Feature.StatusKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Feature.StatusKey, GetStringFieldValue(Data.Models.Metadata.Feature.StatusKey).AsFeatureStatus().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Feature.FeatureTypeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Feature.FeatureTypeKey, GetStringFieldValue(Data.Models.Metadata.Feature.FeatureTypeKey).AsFeatureType().AsStringValue());
|
||||
}
|
||||
|
||||
public PartFeature(Data.Models.Metadata.Feature item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
73
SabreTools.Metadata.DatItems/Formats/Port.cs
Normal file
73
SabreTools.Metadata.DatItems/Formats/Port.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single port on a machine
|
||||
/// </summary>
|
||||
[JsonObject("port"), XmlRoot("port")]
|
||||
public sealed class Port : DatItem<Data.Models.Metadata.Port>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Port;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool AnalogsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var analogs = GetFieldValue<Analog[]?>(Data.Models.Metadata.Port.AnalogKey);
|
||||
return analogs is not null && analogs.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Port() : base() { }
|
||||
|
||||
public Port(Data.Models.Metadata.Port item) : base(item)
|
||||
{
|
||||
// Handle subitems
|
||||
var analogs = item.ReadItemArray<Data.Models.Metadata.Analog>(Data.Models.Metadata.Port.AnalogKey);
|
||||
if (analogs is not null)
|
||||
{
|
||||
Analog[] analogItems = Array.ConvertAll(analogs, analog => new Analog(analog));
|
||||
SetFieldValue<Analog[]?>(Data.Models.Metadata.Port.AnalogKey, analogItems);
|
||||
}
|
||||
}
|
||||
|
||||
public Port(Data.Models.Metadata.Port item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.Port GetInternalClone()
|
||||
{
|
||||
var portItem = base.GetInternalClone();
|
||||
|
||||
var analogs = GetFieldValue<Analog[]?>(Data.Models.Metadata.Port.AnalogKey);
|
||||
if (analogs is not null)
|
||||
{
|
||||
Data.Models.Metadata.Analog[] analogItems = Array.ConvertAll(analogs, analog => analog.GetInternalClone());
|
||||
portItem[Data.Models.Metadata.Port.AnalogKey] = analogItems;
|
||||
}
|
||||
|
||||
return portItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
SabreTools.Metadata.DatItems/Formats/RamOption.cs
Normal file
39
SabreTools.Metadata.DatItems/Formats/RamOption.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which RAM option(s) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("ramoption"), XmlRoot("ramoption")]
|
||||
public sealed class RamOption : DatItem<Data.Models.Metadata.RamOption>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.RamOption;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public RamOption() : base() { }
|
||||
|
||||
public RamOption(Data.Models.Metadata.RamOption item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.RamOption.DefaultKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.RamOption.DefaultKey, GetBoolFieldValue(Data.Models.Metadata.RamOption.DefaultKey).FromYesNo());
|
||||
}
|
||||
|
||||
public RamOption(Data.Models.Metadata.RamOption item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
SabreTools.Metadata.DatItems/Formats/Release.cs
Normal file
39
SabreTools.Metadata.DatItems/Formats/Release.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents release information about a set
|
||||
/// </summary>
|
||||
[JsonObject("release"), XmlRoot("release")]
|
||||
public sealed class Release : DatItem<Data.Models.Metadata.Release>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Release;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Release() : base() { }
|
||||
|
||||
public Release(Data.Models.Metadata.Release item) : base(item)
|
||||
{
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Release.DefaultKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Release.DefaultKey, GetBoolFieldValue(Data.Models.Metadata.Release.DefaultKey).FromYesNo());
|
||||
}
|
||||
|
||||
public Release(Data.Models.Metadata.Release item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
189
SabreTools.Metadata.DatItems/Formats/ReleaseDetails.cs
Normal file
189
SabreTools.Metadata.DatItems/Formats/ReleaseDetails.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
// TODO: Add item mappings for all fields
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single release details item
|
||||
/// </summary>
|
||||
[JsonObject("release_details"), XmlRoot("release_details")]
|
||||
public sealed class ReleaseDetails : DatItem
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.ReleaseDetails;
|
||||
|
||||
/// <summary>
|
||||
/// Id value
|
||||
/// </summary>
|
||||
/// <remarks>TODO: Is this required?</remarks>
|
||||
[JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Directory name value
|
||||
/// </summary>
|
||||
[JsonProperty("dirname", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("dirname")]
|
||||
public string? DirName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rom info value
|
||||
/// </summary>
|
||||
[JsonProperty("rominfo", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("rominfo")]
|
||||
public string? RomInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Category value
|
||||
/// </summary>
|
||||
[JsonProperty("category", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("category")]
|
||||
public string? Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// NFO name value
|
||||
/// </summary>
|
||||
[JsonProperty("nfoname", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("nfoname")]
|
||||
public string? NfoName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// NFO size value
|
||||
/// </summary>
|
||||
[JsonProperty("nfosize", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("nfosize")]
|
||||
public long? NfoSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// NFO CRC value
|
||||
/// </summary>
|
||||
[JsonProperty("nfocrc", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("nfocrc")]
|
||||
public string? NfoCrc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Archive name value
|
||||
/// </summary>
|
||||
[JsonProperty("archivename", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("archivename")]
|
||||
public string? ArchiveName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Original format value
|
||||
/// </summary>
|
||||
[JsonProperty("originalformat", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("originalformat")]
|
||||
public string? OriginalFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Date value
|
||||
/// </summary>
|
||||
[JsonProperty("date", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("date")]
|
||||
public string? Date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Grpup value
|
||||
/// </summary>
|
||||
[JsonProperty("group", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("group")]
|
||||
public string? Group { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Comment value
|
||||
/// </summary>
|
||||
[JsonProperty("comment", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tool value
|
||||
/// </summary>
|
||||
[JsonProperty("tool", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tool")]
|
||||
public string? Tool { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Region value
|
||||
/// </summary>
|
||||
[JsonProperty("region", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("region")]
|
||||
public string? Region { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Origin value
|
||||
/// </summary>
|
||||
[JsonProperty("origin", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("origin")]
|
||||
public string? Origin { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a default, empty ReleaseDetails object
|
||||
/// </summary>
|
||||
public ReleaseDetails()
|
||||
{
|
||||
SetFieldValue(Data.Models.Metadata.DatItem.TypeKey, ItemType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override object Clone()
|
||||
{
|
||||
var releaseDetails = new ReleaseDetails()
|
||||
{
|
||||
Id = this.Id,
|
||||
DirName = this.DirName,
|
||||
RomInfo = this.RomInfo,
|
||||
Category = this.Category,
|
||||
NfoName = this.NfoName,
|
||||
NfoSize = this.NfoSize,
|
||||
NfoCrc = this.NfoCrc,
|
||||
ArchiveName = this.ArchiveName,
|
||||
OriginalFormat = this.OriginalFormat,
|
||||
Date = this.Date,
|
||||
Group = this.Group,
|
||||
Comment = this.Comment,
|
||||
Tool = this.Tool,
|
||||
Region = this.Region,
|
||||
Origin = this.Origin,
|
||||
};
|
||||
releaseDetails.SetFieldValue(DupeTypeKey, GetFieldValue<DupeType>(DupeTypeKey));
|
||||
releaseDetails.SetFieldValue(MachineKey, GetMachine());
|
||||
releaseDetails.SetFieldValue(RemoveKey, GetBoolFieldValue(RemoveKey));
|
||||
releaseDetails.SetFieldValue<Source?>(SourceKey, GetFieldValue<Source?>(SourceKey));
|
||||
releaseDetails.SetFieldValue<string?>(Data.Models.Metadata.DatItem.TypeKey, GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType().AsStringValue());
|
||||
|
||||
return releaseDetails;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(DatItem? other)
|
||||
{
|
||||
// If we don't have a Details, return false
|
||||
if (GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey) != other?.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey))
|
||||
return false;
|
||||
|
||||
// Otherwise, treat it as a Details
|
||||
ReleaseDetails? newOther = other as ReleaseDetails;
|
||||
|
||||
// If the Details information matches
|
||||
return Id == newOther!.Id
|
||||
&& DirName == newOther.DirName
|
||||
&& RomInfo == newOther.RomInfo
|
||||
&& Category == newOther.Category
|
||||
&& NfoName == newOther.NfoName
|
||||
&& NfoSize == newOther.NfoSize
|
||||
&& NfoCrc == newOther.NfoCrc
|
||||
&& ArchiveName == newOther.ArchiveName
|
||||
&& OriginalFormat == newOther.OriginalFormat
|
||||
&& Date == newOther.Date
|
||||
&& Group == newOther.Group
|
||||
&& Comment == newOther.Comment
|
||||
&& Tool == newOther.Tool
|
||||
&& Region == newOther.Region
|
||||
&& Origin == newOther.Origin;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
312
SabreTools.Metadata.DatItems/Formats/Rom.cs
Normal file
312
SabreTools.Metadata.DatItems/Formats/Rom.cs
Normal file
@@ -0,0 +1,312 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Metadata.Tools;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic file within a set
|
||||
/// </summary>
|
||||
[JsonObject("rom"), XmlRoot("rom")]
|
||||
public sealed class Rom : DatItem<Data.Models.Metadata.Rom>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Non-standard key for inverted logic
|
||||
/// </summary>
|
||||
public const string DataAreaKey = "DATAAREA";
|
||||
|
||||
/// <summary>
|
||||
/// Non-standard key for inverted logic
|
||||
/// </summary>
|
||||
public const string PartKey = "PART";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Rom;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ItemStatusSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var status = GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus();
|
||||
return status != ItemStatus.NULL && status != ItemStatus.None;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool OriginalSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var original = GetFieldValue<Original?>("ORIGINAL");
|
||||
return original is not null && original != default;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool DataAreaSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var dataArea = GetFieldValue<DataArea?>(DataAreaKey);
|
||||
return dataArea is not null
|
||||
&& (!string.IsNullOrEmpty(dataArea.GetName())
|
||||
|| dataArea.GetInt64FieldValue(Data.Models.Metadata.DataArea.SizeKey) is not null
|
||||
|| dataArea.GetInt64FieldValue(Data.Models.Metadata.DataArea.WidthKey) is not null
|
||||
|| dataArea.GetStringFieldValue(Data.Models.Metadata.DataArea.EndiannessKey).AsEndianness() != Endianness.NULL);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool PartSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var part = GetFieldValue<Part?>(PartKey);
|
||||
return part is not null
|
||||
&& (!string.IsNullOrEmpty(part.GetName())
|
||||
|| !string.IsNullOrEmpty(part.GetStringFieldValue(Data.Models.Metadata.Part.InterfaceKey)));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Rom() : base()
|
||||
{
|
||||
SetFieldValue<DupeType>(DupeTypeKey, 0x00);
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.StatusKey, ItemStatus.None.AsStringValue());
|
||||
}
|
||||
|
||||
public Rom(Data.Models.Metadata.Dump item, Machine machine, Source source, int index)
|
||||
{
|
||||
// If we don't have rom data, we can't do anything
|
||||
Data.Models.Metadata.Rom? rom = null;
|
||||
OpenMSXSubType subType = OpenMSXSubType.NULL;
|
||||
if (item.Read<Data.Models.Metadata.Rom>(Data.Models.Metadata.Dump.RomKey) is not null)
|
||||
{
|
||||
rom = item.Read<Data.Models.Metadata.Rom>(Data.Models.Metadata.Dump.RomKey);
|
||||
subType = OpenMSXSubType.Rom;
|
||||
}
|
||||
else if (item.Read<Data.Models.Metadata.Rom>(Data.Models.Metadata.Dump.MegaRomKey) is not null)
|
||||
{
|
||||
rom = item.Read<Data.Models.Metadata.Rom>(Data.Models.Metadata.Dump.MegaRomKey);
|
||||
subType = OpenMSXSubType.MegaRom;
|
||||
}
|
||||
else if (item.Read<Data.Models.Metadata.Rom>(Data.Models.Metadata.Dump.SCCPlusCartKey) is not null)
|
||||
{
|
||||
rom = item.Read<Data.Models.Metadata.Rom>(Data.Models.Metadata.Dump.SCCPlusCartKey);
|
||||
subType = OpenMSXSubType.SCCPlusCart;
|
||||
}
|
||||
|
||||
// Just return if nothing valid was found
|
||||
if (rom is null)
|
||||
return;
|
||||
|
||||
string name = $"{machine.GetName()}_{index++}{(!string.IsNullOrEmpty(rom!.ReadString(Data.Models.Metadata.Rom.RemarkKey)) ? $" {rom.ReadString(Data.Models.Metadata.Rom.RemarkKey)}" : string.Empty)}";
|
||||
|
||||
SetName(name);
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.OffsetKey, rom.ReadString(Data.Models.Metadata.Rom.StartKey));
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.OpenMSXMediaType, subType.AsStringValue());
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.OpenMSXType, rom.ReadString(Data.Models.Metadata.Rom.OpenMSXType) ?? rom.ReadString(Data.Models.Metadata.DatItem.TypeKey));
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.RemarkKey, rom.ReadString(Data.Models.Metadata.Rom.RemarkKey));
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, rom.ReadString(Data.Models.Metadata.Rom.SHA1Key));
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.StartKey, rom.ReadString(Data.Models.Metadata.Rom.StartKey));
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
|
||||
if (item.Read<Data.Models.Metadata.Original>(Data.Models.Metadata.Dump.OriginalKey) is not null)
|
||||
{
|
||||
var original = item.Read<Data.Models.Metadata.Original>(Data.Models.Metadata.Dump.OriginalKey)!;
|
||||
SetFieldValue<Original?>("ORIGINAL", new Original
|
||||
{
|
||||
Value = original.ReadBool(Data.Models.Metadata.Original.ValueKey),
|
||||
Content = original.ReadString(Data.Models.Metadata.Original.ContentKey),
|
||||
});
|
||||
}
|
||||
|
||||
CopyMachineInformation(machine);
|
||||
|
||||
// Process hash values
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey).ToString());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, TextHelper.NormalizeCRC32(GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.MD2Key, TextHelper.NormalizeMD2(GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.MD4Key, TextHelper.NormalizeMD5(GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.MD5Key, TextHelper.NormalizeMD5(GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD128Key, TextHelper.NormalizeRIPEMD128(GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD160Key, TextHelper.NormalizeRIPEMD160(GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, TextHelper.NormalizeSHA1(GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA256Key, TextHelper.NormalizeSHA256(GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA384Key, TextHelper.NormalizeSHA384(GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA512Key, TextHelper.NormalizeSHA512(GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key)));
|
||||
}
|
||||
|
||||
public Rom(Data.Models.Metadata.Rom item) : base(item)
|
||||
{
|
||||
SetFieldValue<DupeType>(DupeTypeKey, 0x00);
|
||||
|
||||
// Process flag values
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Rom.DisposeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.DisposeKey, GetBoolFieldValue(Data.Models.Metadata.Rom.DisposeKey).FromYesNo());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Rom.InvertedKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.InvertedKey, GetBoolFieldValue(Data.Models.Metadata.Rom.InvertedKey).FromYesNo());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.LoadFlagKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.LoadFlagKey, GetStringFieldValue(Data.Models.Metadata.Rom.LoadFlagKey).AsLoadFlag().AsStringValue());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.OpenMSXMediaType) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.OpenMSXMediaType, GetStringFieldValue(Data.Models.Metadata.Rom.OpenMSXMediaType).AsOpenMSXSubType().AsStringValue());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Rom.MIAKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.MIAKey, GetBoolFieldValue(Data.Models.Metadata.Rom.MIAKey).FromYesNo());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Rom.OptionalKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.OptionalKey, GetBoolFieldValue(Data.Models.Metadata.Rom.OptionalKey).FromYesNo());
|
||||
if (GetBoolFieldValue(Data.Models.Metadata.Rom.SoundOnlyKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SoundOnlyKey, GetBoolFieldValue(Data.Models.Metadata.Rom.SoundOnlyKey).FromYesNo());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.StatusKey, GetStringFieldValue(Data.Models.Metadata.Rom.StatusKey).AsItemStatus().AsStringValue());
|
||||
|
||||
// Process hash values
|
||||
if (GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SizeKey, GetInt64FieldValue(Data.Models.Metadata.Rom.SizeKey).ToString());
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.CRCKey, TextHelper.NormalizeCRC32(GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.MD2Key, TextHelper.NormalizeMD2(GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.MD4Key, TextHelper.NormalizeMD4(GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.MD5Key, TextHelper.NormalizeMD5(GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD128Key, TextHelper.NormalizeRIPEMD128(GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.RIPEMD160Key, TextHelper.NormalizeRIPEMD160(GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA1Key, TextHelper.NormalizeSHA1(GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA256Key, TextHelper.NormalizeSHA256(GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA384Key, TextHelper.NormalizeSHA384(GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key)));
|
||||
if (GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key) is not null)
|
||||
SetFieldValue<string?>(Data.Models.Metadata.Rom.SHA512Key, TextHelper.NormalizeSHA512(GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key)));
|
||||
}
|
||||
|
||||
public Rom(Data.Models.Metadata.Rom item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <summary>
|
||||
/// Fill any missing size and hash information from another Rom
|
||||
/// </summary>
|
||||
/// <param name="other">Rom to fill information from</param>
|
||||
public void FillMissingInformation(Rom other)
|
||||
=> _internal.FillMissingHashes(other._internal);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the Rom contains any hashes
|
||||
/// </summary>
|
||||
/// <returns>True if any hash exists, false otherwise</returns>
|
||||
public bool HasHashes() => _internal.HasHashes();
|
||||
|
||||
/// <summary>
|
||||
/// Returns if all of the hashes are set to their 0-byte values
|
||||
/// </summary>
|
||||
/// <returns>True if any hash matches the 0-byte value, false otherwise</returns>
|
||||
public bool HasZeroHash() => _internal.HasZeroHash();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sorting and Merging
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetKey(ItemKey bucketedBy, Machine? machine, Source? source, bool lower = true, bool norename = true)
|
||||
{
|
||||
// Set the output key as the default blank string
|
||||
string? key;
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
// Now determine what the key should be based on the bucketedBy value
|
||||
switch (bucketedBy)
|
||||
{
|
||||
case ItemKey.CRC:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.CRCKey);
|
||||
break;
|
||||
|
||||
case ItemKey.MD2:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.MD2Key);
|
||||
break;
|
||||
|
||||
case ItemKey.MD4:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.MD4Key);
|
||||
break;
|
||||
|
||||
case ItemKey.MD5:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.MD5Key);
|
||||
break;
|
||||
|
||||
case ItemKey.RIPEMD128:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD128Key);
|
||||
break;
|
||||
|
||||
case ItemKey.RIPEMD160:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.RIPEMD160Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SHA1:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.SHA1Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SHA256:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.SHA256Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SHA384:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.SHA384Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SHA512:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.SHA512Key);
|
||||
break;
|
||||
|
||||
case ItemKey.SpamSum:
|
||||
key = GetStringFieldValue(Data.Models.Metadata.Rom.SpamSumKey);
|
||||
break;
|
||||
|
||||
// Let the base handle generic stuff
|
||||
default:
|
||||
return base.GetKey(bucketedBy, machine, source, lower, norename);
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
|
||||
// Double and triple check the key for corner cases
|
||||
key ??= string.Empty;
|
||||
if (lower)
|
||||
key = key.ToLowerInvariant();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
SabreTools.Metadata.DatItems/Formats/Sample.cs
Normal file
33
SabreTools.Metadata.DatItems/Formats/Sample.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a (usually WAV-formatted) sample to be included for use in the set
|
||||
/// </summary>
|
||||
[JsonObject("sample"), XmlRoot("sample")]
|
||||
public class Sample : DatItem<Data.Models.Metadata.Sample>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Sample;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Sample() : base() { }
|
||||
|
||||
public Sample(Data.Models.Metadata.Sample item) : base(item) { }
|
||||
|
||||
public Sample(Data.Models.Metadata.Sample item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
180
SabreTools.Metadata.DatItems/Formats/Serials.cs
Normal file
180
SabreTools.Metadata.DatItems/Formats/Serials.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
// TODO: Add item mappings for all fields
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single serials item
|
||||
/// </summary>
|
||||
[JsonObject("serials"), XmlRoot("serials")]
|
||||
public sealed class Serials : DatItem
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Serials;
|
||||
|
||||
/// <summary>
|
||||
/// Digital serial 1 value
|
||||
/// </summary>
|
||||
[JsonProperty("digital_serial1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("digital_serial1")]
|
||||
public string? DigitalSerial1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digital serial 2 value
|
||||
/// </summary>
|
||||
[JsonProperty("digital_serial2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("digital_serial2")]
|
||||
public string? DigitalSerial2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media serial 1 value
|
||||
/// </summary>
|
||||
[JsonProperty("media_serial1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("media_serial1")]
|
||||
public string? MediaSerial1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media serial 2 value
|
||||
/// </summary>
|
||||
[JsonProperty("media_serial2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("media_serial2")]
|
||||
public string? MediaSerial2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media serial 3 value
|
||||
/// </summary>
|
||||
[JsonProperty("media_serial3", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("media_serial3")]
|
||||
public string? MediaSerial3 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PCB serial value
|
||||
/// </summary>
|
||||
[JsonProperty("pcb_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("pcb_serial")]
|
||||
public string? PcbSerial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rom chip serial 1 value
|
||||
/// </summary>
|
||||
[JsonProperty("romchip_serial1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("romchip_serial1")]
|
||||
public string? RomChipSerial1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rom chip serial 2 value
|
||||
/// </summary>
|
||||
[JsonProperty("romchip_serial2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("romchip_serial2")]
|
||||
public string? RomChipSerial2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lockout serial value
|
||||
/// </summary>
|
||||
[JsonProperty("lockout_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("lockout_serial")]
|
||||
public string? LockoutSerial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save chip serial value
|
||||
/// </summary>
|
||||
[JsonProperty("savechip_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("savechip_serial")]
|
||||
public string? SaveChipSerial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chip serial value
|
||||
/// </summary>
|
||||
[JsonProperty("chip_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("chip_serial")]
|
||||
public string? ChipSerial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Box serial value
|
||||
/// </summary>
|
||||
[JsonProperty("box_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("box_serial")]
|
||||
public string? BoxSerial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media stamp value
|
||||
/// </summary>
|
||||
[JsonProperty("mediastamp", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mediastamp")]
|
||||
public string? MediaStamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Box barcode value
|
||||
/// </summary>
|
||||
[JsonProperty("box_barcode", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("box_barcode")]
|
||||
public string? BoxBarcode { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a default, empty Serials object
|
||||
/// </summary>
|
||||
public Serials()
|
||||
{
|
||||
SetFieldValue(Data.Models.Metadata.DatItem.TypeKey, ItemType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override object Clone()
|
||||
{
|
||||
var serials = new Serials()
|
||||
{
|
||||
DigitalSerial1 = this.DigitalSerial1,
|
||||
DigitalSerial2 = this.DigitalSerial2,
|
||||
MediaSerial1 = this.MediaSerial1,
|
||||
MediaSerial2 = this.MediaSerial2,
|
||||
MediaSerial3 = this.MediaSerial3,
|
||||
PcbSerial = this.PcbSerial,
|
||||
RomChipSerial1 = this.RomChipSerial1,
|
||||
RomChipSerial2 = this.RomChipSerial2,
|
||||
LockoutSerial = this.LockoutSerial,
|
||||
SaveChipSerial = this.SaveChipSerial,
|
||||
ChipSerial = this.ChipSerial,
|
||||
BoxSerial = this.BoxSerial,
|
||||
MediaStamp = this.MediaStamp,
|
||||
BoxBarcode = this.BoxBarcode,
|
||||
};
|
||||
serials.SetFieldValue(DupeTypeKey, GetFieldValue<DupeType>(DupeTypeKey));
|
||||
serials.SetFieldValue(MachineKey, GetMachine());
|
||||
serials.SetFieldValue(RemoveKey, GetBoolFieldValue(RemoveKey));
|
||||
serials.SetFieldValue<Source?>(SourceKey, GetFieldValue<Source?>(SourceKey));
|
||||
serials.SetFieldValue<string?>(Data.Models.Metadata.DatItem.TypeKey, GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey).AsItemType().AsStringValue());
|
||||
|
||||
return serials;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparision Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(DatItem? other)
|
||||
{
|
||||
// If we don't have a Serials, return false
|
||||
if (GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey) != other?.GetStringFieldValue(Data.Models.Metadata.DatItem.TypeKey))
|
||||
return false;
|
||||
|
||||
// Otherwise, treat it as a Serials
|
||||
Serials? newOther = other as Serials;
|
||||
|
||||
// If the Serials information matches
|
||||
return DigitalSerial1 == newOther!.DigitalSerial1
|
||||
&& DigitalSerial2 == newOther.DigitalSerial2
|
||||
&& MediaSerial1 == newOther.MediaSerial1
|
||||
&& MediaSerial2 == newOther.MediaSerial2
|
||||
&& MediaSerial3 == newOther.MediaSerial3
|
||||
&& PcbSerial == newOther.PcbSerial
|
||||
&& RomChipSerial1 == newOther.RomChipSerial1
|
||||
&& RomChipSerial2 == newOther.RomChipSerial2
|
||||
&& LockoutSerial == newOther.LockoutSerial
|
||||
&& SaveChipSerial == newOther.SaveChipSerial
|
||||
&& ChipSerial == newOther.ChipSerial
|
||||
&& BoxSerial == newOther.BoxSerial
|
||||
&& MediaStamp == newOther.MediaStamp
|
||||
&& BoxBarcode == newOther.BoxBarcode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
SabreTools.Metadata.DatItems/Formats/SharedFeat.cs
Normal file
33
SabreTools.Metadata.DatItems/Formats/SharedFeat.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one shared feature object
|
||||
/// </summary>
|
||||
[JsonObject("sharedfeat"), XmlRoot("sharedfeat")]
|
||||
public sealed class SharedFeat : DatItem<Data.Models.Metadata.SharedFeat>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.SharedFeat;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public SharedFeat() : base() { }
|
||||
|
||||
public SharedFeat(Data.Models.Metadata.SharedFeat item) : base(item) { }
|
||||
|
||||
public SharedFeat(Data.Models.Metadata.SharedFeat item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
73
SabreTools.Metadata.DatItems/Formats/Slot.cs
Normal file
73
SabreTools.Metadata.DatItems/Formats/Slot.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.Data.Extensions;
|
||||
|
||||
namespace SabreTools.Metadata.DatItems.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents which Slot(s) is associated with a set
|
||||
/// </summary>
|
||||
[JsonObject("slot"), XmlRoot("slot")]
|
||||
public sealed class Slot : DatItem<Data.Models.Metadata.Slot>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <inheritdoc>/>
|
||||
protected override ItemType ItemType => ItemType.Slot;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool SlotOptionsSpecified
|
||||
{
|
||||
get
|
||||
{
|
||||
var slotOptions = GetFieldValue<SlotOption[]?>(Data.Models.Metadata.Slot.SlotOptionKey);
|
||||
return slotOptions is not null && slotOptions.Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Slot() : base() { }
|
||||
|
||||
public Slot(Data.Models.Metadata.Slot item) : base(item)
|
||||
{
|
||||
// Handle subitems
|
||||
var slotOptions = item.ReadItemArray<Data.Models.Metadata.SlotOption>(Data.Models.Metadata.Slot.SlotOptionKey);
|
||||
if (slotOptions is not null)
|
||||
{
|
||||
SlotOption[] slotOptionItems = Array.ConvertAll(slotOptions, slotOption => new SlotOption(slotOption));
|
||||
SetFieldValue<SlotOption[]?>(Data.Models.Metadata.Slot.SlotOptionKey, slotOptionItems);
|
||||
}
|
||||
}
|
||||
|
||||
public Slot(Data.Models.Metadata.Slot item, Machine machine, Source source) : this(item)
|
||||
{
|
||||
SetFieldValue<Source?>(SourceKey, source);
|
||||
CopyMachineInformation(machine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cloning Methods
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Data.Models.Metadata.Slot GetInternalClone()
|
||||
{
|
||||
var slotItem = base.GetInternalClone();
|
||||
|
||||
var slotOptions = GetFieldValue<SlotOption[]?>(Data.Models.Metadata.Slot.SlotOptionKey);
|
||||
if (slotOptions is not null)
|
||||
{
|
||||
Data.Models.Metadata.SlotOption[] slotOptionItems = Array.ConvertAll(slotOptions, slotOption => slotOption.GetInternalClone());
|
||||
slotItem[Data.Models.Metadata.Slot.SlotOptionKey] = slotOptionItems;
|
||||
}
|
||||
|
||||
return slotItem;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user