diff --git a/SabreTools.Library/DatFiles/DatFile.cs b/SabreTools.Library/DatFiles/DatFile.cs index 93564921..f0c1fab1 100644 --- a/SabreTools.Library/DatFiles/DatFile.cs +++ b/SabreTools.Library/DatFiles/DatFile.cs @@ -5658,6 +5658,12 @@ namespace SabreTools.Library.DatFiles outfileNames.Add(DatFormat.DOSCenter, CreateOutfileNamesHelper(outDir, ".dc.dat", overwrite)); } + // JSON + if((DatFormat & DatFormat.Json) != 0) + { + outfileNames.Add(DatFormat.Json, CreateOutfileNamesHelper(outDir, ".json", overwrite)); + } + // Logiqx XML if ((DatFormat & DatFormat.Logiqx) != 0) { diff --git a/SabreTools.Library/DatFiles/DatHeader.cs b/SabreTools.Library/DatFiles/DatHeader.cs index 5486d67c..62bcfae8 100644 --- a/SabreTools.Library/DatFiles/DatHeader.cs +++ b/SabreTools.Library/DatFiles/DatHeader.cs @@ -1,6 +1,7 @@ using System; using SabreTools.Library.Data; +using Newtonsoft.Json; namespace SabreTools.Library.DatFiles { @@ -16,121 +17,145 @@ namespace SabreTools.Library.DatFiles /// /// External name of the DAT /// + [JsonProperty("filename")] public string FileName { get; set; } /// /// Internal name of the DAT /// + [JsonProperty("name")] public string Name { get; set; } /// /// DAT description /// + [JsonProperty("description")] public string Description { get; set; } /// /// Root directory for the files; currently TruRip/EmuARC-exclusive /// + [JsonProperty("rootdir")] public string RootDir { get; set; } /// /// General category of items found in the DAT /// + [JsonProperty("category")] public string Category { get; set; } /// /// Version of the DAT /// + [JsonProperty("version")] public string Version { get; set; } /// /// Creation or modification date /// + [JsonProperty("date")] public string Date { get; set; } /// /// List of authors who contributed to the DAT /// + [JsonProperty("author")] public string Author { get; set; } /// /// Email address for DAT author(s) /// + [JsonProperty("email")] public string Email { get; set; } /// /// Author or distribution homepage name /// + [JsonProperty("homepage")] public string Homepage { get; set; } /// /// Author or distribution URL /// + [JsonProperty("url")] public string Url { get; set; } /// /// Any comment that does not already fit an existing field /// + [JsonProperty("comment")] public string Comment { get; set; } /// /// Header skipper to be used when loading the DAT /// + [JsonProperty("header")] public string Header { get; set; } /// /// Classification of the DAT. Generally only used for SuperDAT /// + [JsonProperty("type")] public string Type { get; set; } /// /// Force a merging style when loaded /// + [JsonProperty("forcemerging")] public ForceMerging ForceMerging { get; set; } /// /// Force nodump handling when loaded /// + [JsonProperty("forcenodump")] public ForceNodump ForceNodump { get; set; } /// /// Force output packing when loaded /// + [JsonProperty("forcepacking")] public ForcePacking ForcePacking { get; set; } /// /// Read or write format /// + [JsonIgnore] public DatFormat DatFormat { get; set; } /// /// List of fields in machine and items to exclude from writing /// + [JsonIgnore] public bool[] ExcludeFields { get; set; } = new bool[Enum.GetNames(typeof(Field)).Length]; /// /// Enable "One Rom, One Region (1G1R)" mode /// + [JsonIgnore] public bool OneRom { get; set; } /// /// Keep machines that don't contain any items /// + [JsonIgnore] public bool KeepEmptyGames { get; set; } /// /// Remove scene dates from the beginning of machine names /// + [JsonIgnore] public bool SceneDateStrip { get; set; } /// /// Deduplicate items using the given method /// + [JsonIgnore] public DedupeType DedupeRoms { get; set; } /// /// Strip hash types from items /// + [JsonIgnore] public Hash StripHash { get; private set; } #endregion @@ -140,41 +165,49 @@ namespace SabreTools.Library.DatFiles /// /// Text to prepend to all outputted lines /// + [JsonIgnore] public string Prefix { get; set; } /// /// Text to append to all outputted lines /// + [JsonIgnore] public string Postfix { get; set; } /// /// Add a new extension to all items /// + [JsonIgnore] public string AddExtension { get; set; } /// /// Replace all item extensions /// + [JsonIgnore] public string ReplaceExtension { get; set; } /// /// Remove all item extensions /// + [JsonIgnore] public bool RemoveExtension { get; set; } /// /// Romba output mode /// + [JsonIgnore] public bool Romba { get; set; } /// /// Output the machine name /// + [JsonIgnore] public bool GameName { get; set; } /// /// Wrap quotes around the entire line, sans prefix and postfix /// + [JsonIgnore] public bool Quotes { get; set; } #endregion @@ -184,6 +217,7 @@ namespace SabreTools.Library.DatFiles /// /// Output the item name /// + [JsonIgnore] public bool UseRomName { get; set; } #endregion diff --git a/SabreTools.Library/DatFiles/Json.cs b/SabreTools.Library/DatFiles/Json.cs new file mode 100644 index 00000000..899e79cb --- /dev/null +++ b/SabreTools.Library/DatFiles/Json.cs @@ -0,0 +1,775 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using SabreTools.Library.Data; +using SabreTools.Library.DatItems; +using SabreTools.Library.Tools; +using NaturalSort; +using Newtonsoft.Json; + +namespace SabreTools.Library.DatFiles +{ + /// + /// Represents parsing and writing of a JSON DAT + /// + internal class Json : DatFile + { + /// + /// Constructor designed for casting a base DatFile + /// + /// Parent DatFile to copy from + public Json(DatFile datFile) + : base(datFile, cloneHeader: false) + { + } + + /// + /// Create and open an output file for writing direct from a dictionary + /// + /// Name of the file to write to + /// True if blank roms should be skipped on output, false otherwise (default) + /// True if the DAT was written correctly, false otherwise + public override bool WriteToFile(string outfile, bool ignoreblanks = false) + { + try + { + Globals.Logger.User($"Opening file for writing: {outfile}"); + FileStream fs = Utilities.TryCreate(outfile); + + // If we get back null for some reason, just log and return + if (fs == null) + { + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); + return false; + } + + StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + JsonTextWriter jtw = new JsonTextWriter(sw); + jtw.Formatting = Formatting.Indented; + jtw.IndentChar = '\t'; + jtw.Indentation = 1; + + // Write out the header + WriteHeader(jtw); + + // Write out each of the machines and roms + string lastgame = null; + + // Get a properly sorted set of keys + List keys = Keys; + keys.Sort(new NaturalComparer()); + + foreach (string key in keys) + { + List roms = this[key]; + + // Resolve the names in the block + roms = DatItem.ResolveNames(roms); + + for (int index = 0; index < roms.Count; index++) + { + DatItem rom = roms[index]; + + // There are apparently times when a null rom can skip by, skip them + if (rom.Name == null || rom.MachineName == null) + { + Globals.Logger.Warning("Null rom found!"); + continue; + } + + // If we have a different game and we're not at the start of the list, output the end of last item + if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) + WriteEndGame(jtw); + + // If we have a new game, output the beginning of the new item + if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) + WriteStartGame(jtw, rom); + + // If we have a "null" game (created by DATFromDir or something similar), log it to file + if (rom.ItemType == ItemType.Rom + && ((Rom)rom).Size == -1 + && ((Rom)rom).CRC == "null") + { + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); + + rom.Name = (rom.Name == "null" ? "-" : rom.Name); + ((Rom)rom).Size = Constants.SizeZero; + ((Rom)rom).CRC = ((Rom)rom).CRC == "null" ? Constants.CRCZero : null; + ((Rom)rom).MD5 = ((Rom)rom).MD5 == "null" ? Constants.MD5Zero : null; + ((Rom)rom).RIPEMD160 = ((Rom)rom).RIPEMD160 == "null" ? Constants.RIPEMD160Zero : null; + ((Rom)rom).SHA1 = ((Rom)rom).SHA1 == "null" ? Constants.SHA1Zero : null; + ((Rom)rom).SHA256 = ((Rom)rom).SHA256 == "null" ? Constants.SHA256Zero : null; + ((Rom)rom).SHA384 = ((Rom)rom).SHA384 == "null" ? Constants.SHA384Zero : null; + ((Rom)rom).SHA512 = ((Rom)rom).SHA512 == "null" ? Constants.SHA512Zero : null; + } + + // Now, output the rom data + WriteDatItem(jtw, rom, ignoreblanks); + + // Set the new data to compare against + lastgame = rom.MachineName; + } + } + + // Write the file footer out + WriteFooter(jtw); + + Globals.Logger.Verbose("File written!" + Environment.NewLine); + jtw.Close(); + fs.Dispose(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + + /// + /// Write out DAT header using the supplied StreamWriter + /// + /// JsonTextWriter to output to + /// True if the data was written, false on error + private bool WriteHeader(JsonTextWriter jtw) + { + try + { + jtw.WriteStartObject(); + + jtw.WritePropertyName("header"); + jtw.WriteStartObject(); + + jtw.WritePropertyName("name"); + jtw.WriteValue(Name); + jtw.WritePropertyName("description"); + jtw.WriteValue(Description); + if (!string.IsNullOrWhiteSpace(RootDir)) + { + jtw.WritePropertyName("rootdir"); + jtw.WriteValue(RootDir); + } + if (!string.IsNullOrWhiteSpace(Category)) + { + jtw.WritePropertyName("category"); + jtw.WriteValue(Category); + } + jtw.WritePropertyName("version"); + jtw.WriteValue(Version); + if (!string.IsNullOrWhiteSpace(Date)) + { + jtw.WritePropertyName("date"); + jtw.WriteValue(Date); + } + jtw.WritePropertyName("author"); + jtw.WriteValue(Author); + if (!string.IsNullOrWhiteSpace(Email)) + { + jtw.WritePropertyName("email"); + jtw.WriteValue(Email); + } + if (!string.IsNullOrWhiteSpace(Homepage)) + { + jtw.WritePropertyName("homepage"); + jtw.WriteValue(Homepage); + } + if (!string.IsNullOrWhiteSpace(Url)) + { + jtw.WritePropertyName("date"); + jtw.WriteValue(Url); + } + if (!string.IsNullOrWhiteSpace(Comment)) + { + jtw.WritePropertyName("comment"); + jtw.WriteValue(Comment); + } + if (!string.IsNullOrWhiteSpace(Type)) + { + jtw.WritePropertyName("type"); + jtw.WriteValue(Type); + } + if (ForceMerging != ForceMerging.None) + { + jtw.WritePropertyName("forcemerging"); + switch (ForceMerging) + { + case ForceMerging.Full: + jtw.WriteValue("full"); + break; + case ForceMerging.Split: + jtw.WriteValue("split"); + break; + case ForceMerging.Merged: + jtw.WriteValue("merged"); + break; + case ForceMerging.NonMerged: + jtw.WriteValue("nonmerged"); + break; + } + } + if (ForcePacking != ForcePacking.None) + { + jtw.WritePropertyName("forcepacking"); + switch (ForcePacking) + { + case ForcePacking.Unzip: + jtw.WriteValue("unzip"); + break; + case ForcePacking.Zip: + jtw.WriteValue("zip"); + break; + } + } + if (ForceNodump != ForceNodump.None) + { + jtw.WritePropertyName("forcenodump"); + switch (ForceNodump) + { + case ForceNodump.Ignore: + jtw.WriteValue("ignore"); + break; + case ForceNodump.Obsolete: + jtw.WriteValue("obsolete"); + break; + case ForceNodump.Required: + jtw.WriteValue("required"); + break; + } + } + if (!string.IsNullOrWhiteSpace(Header)) + { + jtw.WritePropertyName("header"); + jtw.WriteValue(Header); + } + + // End header + jtw.WriteEndObject(); + + jtw.WritePropertyName("machines"); + jtw.WriteStartArray(); + + jtw.Flush(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + + /// + /// Write out Game start using the supplied StreamWriter + /// + /// JsonTextWriter to output to + /// DatItem object to be output + /// True if the data was written, false on error + private bool WriteStartGame(JsonTextWriter jtw, DatItem datItem) + { + try + { + // No game should start with a path separator + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); + + // Build the state based on excluded fields + jtw.WriteStartObject(); + + jtw.WritePropertyName("name"); + jtw.WriteValue(datItem.GetField(Field.MachineName, ExcludeFields)); + + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Comment, ExcludeFields))) + { + jtw.WritePropertyName("comment"); + jtw.WriteValue(datItem.Comment); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields))) + { + jtw.WritePropertyName("description"); + jtw.WriteValue(datItem.MachineDescription); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields))) + { + jtw.WritePropertyName("year"); + jtw.WriteValue(datItem.Year); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, ExcludeFields))) + { + jtw.WritePropertyName("manufacturer"); + jtw.WriteValue(datItem.Manufacturer); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields))) + { + jtw.WritePropertyName("publisher"); + jtw.WriteValue(datItem.Publisher); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase)) + { + jtw.WritePropertyName("romof"); + jtw.WriteValue(datItem.RomOf); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase)) + { + jtw.WritePropertyName("cloneof"); + jtw.WriteValue(datItem.CloneOf); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase)) + { + jtw.WritePropertyName("sampleof"); + jtw.WriteValue(datItem.SampleOf); + } + if (!ExcludeFields[(int)Field.Supported] && datItem.Supported != null) + { + if (datItem.Supported == true) + { + jtw.WritePropertyName("supported"); + jtw.WriteValue("yes"); + } + else if (datItem.Supported == false) + { + jtw.WritePropertyName("supported"); + jtw.WriteValue("no"); + } + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SourceFile, ExcludeFields))) + { + jtw.WritePropertyName("sourcefile"); + jtw.WriteValue(datItem.SourceFile); + } + if (!ExcludeFields[(int)Field.Runnable] && datItem.Runnable != null) + { + if (datItem.Runnable == true) + { + jtw.WritePropertyName("runnable"); + jtw.WriteValue("yes"); + } + else if (datItem.Runnable == false) + { + jtw.WritePropertyName("runnable"); + jtw.WriteValue("no"); + } + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Board, ExcludeFields))) + { + jtw.WritePropertyName("board"); + jtw.WriteValue(datItem.Board); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RebuildTo, ExcludeFields))) + { + jtw.WritePropertyName("rebuildto"); + jtw.WriteValue(datItem.RebuildTo); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Devices, ExcludeFields))) + { + jtw.WritePropertyName("devices"); + jtw.WriteStartArray(); + foreach (string device in datItem.Devices) + { + jtw.WriteValue(device); + } + + jtw.WriteEndArray(); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SlotOptions, ExcludeFields))) + { + jtw.WritePropertyName("slotoptions"); + jtw.WriteStartArray(); + foreach (string slotoption in datItem.SlotOptions) + { + jtw.WriteValue(slotoption); + } + + jtw.WriteEndArray(); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Infos, ExcludeFields))) + { + jtw.WritePropertyName("infos"); + jtw.WriteStartArray(); + foreach (var info in datItem.Infos) + { + jtw.WriteStartObject(); + jtw.WritePropertyName(info.Key); + jtw.WriteValue(info.Value); + jtw.WriteEndObject(); + } + + jtw.WriteEndArray(); + } + if (!ExcludeFields[(int)Field.MachineType]) + { + if ((datItem.MachineType & MachineType.Bios) != 0) + { + jtw.WritePropertyName("isbios"); + jtw.WriteValue("yes"); + } + if ((datItem.MachineType & MachineType.Device) != 0) + { + jtw.WritePropertyName("isdevice"); + jtw.WriteValue("yes"); + } + if ((datItem.MachineType & MachineType.Mechanical) != 0) + { + jtw.WritePropertyName("ismechanical"); + jtw.WriteValue("yes"); + } + } + + jtw.WritePropertyName("items"); + jtw.WriteStartArray(); + + jtw.Flush(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + + /// + /// Write out Game end using the supplied StreamWriter + /// + /// JsonTextWriter to output to + /// True if the data was written, false on error + private bool WriteEndGame(JsonTextWriter jtw) + { + try + { + // End items + jtw.WriteEndArray(); + + // End machine + jtw.WriteEndObject(); + + jtw.Flush(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + + /// + /// Write out DatItem using the supplied StreamWriter + /// + /// JsonTextWriter to output to + /// DatItem object to be output + /// True if blank roms should be skipped on output, false otherwise (default) + /// True if the data was written, false on error + private bool WriteDatItem(JsonTextWriter jtw, DatItem datItem, bool ignoreblanks = false) + { + // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) + return true; + + // If we have the blank item type somehow, skip + if (datItem.ItemType == ItemType.Blank) + return true; + + try + { + // Pre-process the item name + ProcessItemName(datItem, true); + + // Build the state based on excluded fields + jtw.WriteStartObject(); + jtw.WritePropertyName("type"); + + switch (datItem.ItemType) + { + case ItemType.Archive: + jtw.WriteValue("archive"); + jtw.WritePropertyName("name"); + jtw.WriteValue(datItem.GetField(Field.Name, ExcludeFields)); + break; + + case ItemType.BiosSet: + var biosSet = datItem as BiosSet; + jtw.WriteValue("biosset"); + jtw.WritePropertyName("name"); + jtw.WriteValue(biosSet.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields))) + { + jtw.WritePropertyName("description"); + jtw.WriteValue(biosSet.Description); + } + if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null) + { + jtw.WritePropertyName("default"); + jtw.WriteValue(biosSet.Default); + } + break; + + case ItemType.Disk: + var disk = datItem as Disk; + jtw.WriteValue("disk"); + jtw.WritePropertyName("name"); + jtw.WriteValue(disk.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.MD5, ExcludeFields))) + { + jtw.WritePropertyName("md5"); + jtw.WriteValue(disk.MD5.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.MD5, ExcludeFields))) + { + jtw.WritePropertyName("ripemd160"); + jtw.WriteValue(disk.RIPEMD160.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA1, ExcludeFields))) + { + jtw.WritePropertyName("sha1"); + jtw.WriteValue(disk.SHA1.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA256, ExcludeFields))) + { + jtw.WritePropertyName("sha256"); + jtw.WriteValue(disk.SHA256.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA384, ExcludeFields))) + { + jtw.WritePropertyName("sha384"); + jtw.WriteValue(disk.SHA384.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.SHA512, ExcludeFields))) + { + jtw.WritePropertyName("sha512"); + jtw.WriteValue(disk.SHA512.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Merge, ExcludeFields))) + { + jtw.WritePropertyName("merge"); + jtw.WriteValue(disk.MergeTag); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Region, ExcludeFields))) + { + jtw.WritePropertyName("region"); + jtw.WriteValue(disk.Region); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Index, ExcludeFields))) + { + jtw.WritePropertyName("index"); + jtw.WriteValue(disk.Index); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Writable, ExcludeFields))) + { + jtw.WritePropertyName("writable"); + jtw.WriteValue(disk.Writable); + } + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None) + { + jtw.WritePropertyName("status"); + jtw.WriteValue(disk.ItemStatus.ToString().ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(disk.GetField(Field.Optional, ExcludeFields))) + { + jtw.WritePropertyName("optional"); + jtw.WriteValue(disk.Optional); + } + break; + + case ItemType.Release: + var release = datItem as Release; + jtw.WriteValue("release"); + jtw.WritePropertyName("name"); + jtw.WriteValue(release.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(release.GetField(Field.Region, ExcludeFields))) + { + jtw.WritePropertyName("region"); + jtw.WriteValue(release.Region); + } + if (!string.IsNullOrWhiteSpace(release.GetField(Field.Language, ExcludeFields))) + { + jtw.WritePropertyName("language"); + jtw.WriteValue(release.Language); + } + if (!string.IsNullOrWhiteSpace(release.GetField(Field.Date, ExcludeFields))) + { + jtw.WritePropertyName("date"); + jtw.WriteValue(release.Date); + } + if (!ExcludeFields[(int)Field.Default] && release.Default != null) + { + jtw.WritePropertyName("default"); + jtw.WriteValue(release.Default); + } + break; + + case ItemType.Rom: + var rom = datItem as Rom; + jtw.WriteValue("rom"); + jtw.WritePropertyName("name"); + jtw.WriteValue(rom.GetField(Field.Name, ExcludeFields)); + if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) + { + jtw.WritePropertyName("size"); + jtw.WriteValue(rom.Size); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Offset, ExcludeFields))) + { + jtw.WritePropertyName("offset"); + jtw.WriteValue(rom.Offset); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.CRC, ExcludeFields))) + { + jtw.WritePropertyName("crc"); + jtw.WriteValue(rom.CRC.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.MD5, ExcludeFields))) + { + jtw.WritePropertyName("md5"); + jtw.WriteValue(rom.MD5.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.MD5, ExcludeFields))) + { + jtw.WritePropertyName("ripemd160"); + jtw.WriteValue(rom.RIPEMD160.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA1, ExcludeFields))) + { + jtw.WritePropertyName("sha1"); + jtw.WriteValue(rom.SHA1.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA256, ExcludeFields))) + { + jtw.WritePropertyName("sha256"); + jtw.WriteValue(rom.SHA256.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA384, ExcludeFields))) + { + jtw.WritePropertyName("sha384"); + jtw.WriteValue(rom.SHA384.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.SHA512, ExcludeFields))) + { + jtw.WritePropertyName("sha512"); + jtw.WriteValue(rom.SHA512.ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Bios, ExcludeFields))) + { + jtw.WritePropertyName("bios"); + jtw.WriteValue(rom.Bios); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Merge, ExcludeFields))) + { + jtw.WritePropertyName("merge"); + jtw.WriteValue(rom.MergeTag); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Region, ExcludeFields))) + { + jtw.WritePropertyName("region"); + jtw.WriteValue(rom.Region); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Date, ExcludeFields))) + { + jtw.WritePropertyName("date"); + jtw.WriteValue(rom.Date); + } + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None) + { + jtw.WritePropertyName("status"); + jtw.WriteValue(rom.ItemStatus.ToString().ToLowerInvariant()); + } + if (!string.IsNullOrWhiteSpace(rom.GetField(Field.Optional, ExcludeFields))) + { + jtw.WritePropertyName("optional"); + jtw.WriteValue(rom.Optional); + } + break; + + case ItemType.Sample: + jtw.WriteValue("sample"); + jtw.WritePropertyName("name"); + jtw.WriteValue(datItem.GetField(Field.Name, ExcludeFields)); + break; + } + + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.PartName, ExcludeFields))) + { + jtw.WritePropertyName("partname"); + jtw.WriteValue(datItem.PartName); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.PartInterface, ExcludeFields))) + { + jtw.WritePropertyName("partinterface"); + jtw.WriteValue(datItem.PartInterface); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Features, ExcludeFields))) + { + jtw.WritePropertyName("features"); + jtw.WriteStartArray(); + foreach (var feature in datItem.Features) + { + jtw.WriteStartObject(); + jtw.WritePropertyName(feature.Key); + jtw.WriteValue(feature.Value); + jtw.WriteEndObject(); + } + + jtw.WriteEndArray(); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.AreaName, ExcludeFields))) + { + jtw.WritePropertyName("areaname"); + jtw.WriteValue(datItem.AreaName); + } + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.AreaSize, ExcludeFields))) + { + jtw.WritePropertyName("areasize"); + jtw.WriteValue(datItem.AreaSize); + } + + // End item + jtw.WriteEndObject(); + + jtw.Flush(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + + /// + /// Write out DAT footer using the supplied StreamWriter + /// + /// JsonTextWriter to output to + /// True if the data was written, false on error + private bool WriteFooter(JsonTextWriter jtw) + { + try + { + // End items + jtw.WriteEndArray(); + + // End machine + jtw.WriteEndObject(); + + // End machines + jtw.WriteEndArray(); + + // End file + jtw.WriteEndObject(); + + jtw.Flush(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + } +} diff --git a/SabreTools.Library/Data/Flags.cs b/SabreTools.Library/Data/Flags.cs index af24b63c..394284f7 100644 --- a/SabreTools.Library/Data/Flags.cs +++ b/SabreTools.Library/Data/Flags.cs @@ -336,6 +336,11 @@ namespace SabreTools.Library.Data /// EverdriveSMDB = 1 << 16, + /// + /// JSON + /// + Json = 1 << 17, + #endregion #region SFV-similar Formats @@ -343,42 +348,40 @@ namespace SabreTools.Library.Data /// /// CRC32 hash list /// - RedumpSFV = 1 << 17, + RedumpSFV = 1 << 18, /// /// MD5 hash list /// - RedumpMD5 = 1 << 18, + RedumpMD5 = 1 << 19, /// /// RIPEMD160 hash list /// - RedumpRIPEMD160 = 1 << 19, + RedumpRIPEMD160 = 1 << 20, /// /// SHA-1 hash list /// - RedumpSHA1 = 1 << 20, + RedumpSHA1 = 1 << 21, /// /// SHA-256 hash list /// - RedumpSHA256 = 1 << 21, + RedumpSHA256 = 1 << 22, /// /// SHA-384 hash list /// - RedumpSHA384 = 1 << 22, + RedumpSHA384 = 1 << 23, /// /// SHA-512 hash list /// - RedumpSHA512 = 1 << 23, + RedumpSHA512 = 1 << 24, #endregion - // TODO: Add JSON output. Just for kicks - // Specialty combinations ALL = Int32.MaxValue, } diff --git a/SabreTools.Library/README.1ST b/SabreTools.Library/README.1ST index edfbc936..23762409 100644 --- a/SabreTools.Library/README.1ST +++ b/SabreTools.Library/README.1ST @@ -219,6 +219,7 @@ Options: cmp, clrmamepro - ClrMamePro csv - Standardized Comma-Separated Value dc, doscenter - DOSCenter + json - JSON lr, listrom - MAME Listrom lx, listxml - MAME Listxml miss, missfile - GoodTools Missfile @@ -757,6 +758,7 @@ Options: cmp, clrmamepro - ClrMamePro csv - Standardized Comma-Separated Value dc, doscenter - DOSCenter + json - JSON lr, listrom - MAME Listrom lx, listxml - MAME Listxml miss, missfile - GoodTools Missfile @@ -931,6 +933,7 @@ Options: cmp, clrmamepro - ClrMamePro csv - Standardized Comma-Separated Value dc, doscenter - DOSCenter + json - JSON lr, listrom - MAME Listrom lx, listxml - MAME Listxml miss, missfile - GoodTools Missfile diff --git a/SabreTools.Library/Tools/Utilities.cs b/SabreTools.Library/Tools/Utilities.cs index 71d105cc..a0c961a0 100644 --- a/SabreTools.Library/Tools/Utilities.cs +++ b/SabreTools.Library/Tools/Utilities.cs @@ -619,6 +619,9 @@ namespace SabreTools.Library.Tools case DatFormat.EverdriveSMDB: return new EverdriveSMDB(baseDat); + case DatFormat.Json: + return new Json(baseDat); + case DatFormat.Listrom: return new Listrom(baseDat); @@ -763,6 +766,8 @@ namespace SabreTools.Library.Tools case "dc": case "doscenter": return DatFormat.DOSCenter; + case "json": + return DatFormat.Json; case "lr": case "listrom": return DatFormat.Listrom; @@ -1271,6 +1276,8 @@ namespace SabreTools.Library.Tools { case "csv": return DatFormat.CSV; + case "json": + return DatFormat.Json; case "md5": return DatFormat.RedumpMD5; case "ripemd160": diff --git a/SabreTools/SabreTools.Help.cs b/SabreTools/SabreTools.Help.cs index 007c3fe1..b33e3e33 100644 --- a/SabreTools/SabreTools.Help.cs +++ b/SabreTools/SabreTools.Help.cs @@ -1710,6 +1710,7 @@ Possible values are: cmp, clrmamepro - ClrMamePro csv - Standardized Comma-Separated Value dc, doscenter - DOSCenter + json - JSON lr, listrom - MAME Listrom lx, listxml - MAME Listxml miss, missfile - GoodTools Missfile