diff --git a/CHANGELIST.md b/CHANGELIST.md index 946ba4b1..8058f520 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -1,5 +1,6 @@ ### WIP (xxxx-xx-xx) - Start overhauling Redump information pulling, again +- Add internal structure for special site codes ### 2.2 (2021-12-30) - Fix Saturn header finding diff --git a/MPF.Library/DumpEnvironment.cs b/MPF.Library/DumpEnvironment.cs index 87d31a87..cd852858 100644 --- a/MPF.Library/DumpEnvironment.cs +++ b/MPF.Library/DumpEnvironment.cs @@ -330,6 +330,11 @@ namespace MPF.Library resultProgress?.Report(Result.Success("Disc information skipped!")); } + // Process special fields for site codes + resultProgress?.Report(Result.Success("Processing site codes...")); + InfoTool.ProcessSpecialFields(submissionInfo); + resultProgress?.Report(Result.Success("Processing complete!")); + // Format the information for the text output resultProgress?.Report(Result.Success("Formatting information...")); List formattedValues = InfoTool.FormatOutputData(submissionInfo); diff --git a/MPF.Library/InfoTool.cs b/MPF.Library/InfoTool.cs index a509fcd7..e4b13cff 100644 --- a/MPF.Library/InfoTool.cs +++ b/MPF.Library/InfoTool.cs @@ -80,7 +80,9 @@ namespace MPF.Library Serial = (options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty), Barcode = (options.AddPlaceholders ? Template.OptionalValue : string.Empty), Contents = (options.AddPlaceholders ? Template.OptionalValue : string.Empty), + ContentsSpecialFields = new Dictionary(), Comments = string.Empty, + CommentsSpecialFields = new Dictionary(), }, VersionAndEditions = new VersionAndEditionsSection() { @@ -101,12 +103,9 @@ namespace MPF.Library if (!string.IsNullOrWhiteSpace(info.SizeAndChecksums.CRC32)) info.TracksAndWriteOffsets.ClrMameProData = null; - // Shortcut for adding volume label for now - string volumeLabel = (SiteCode.VolumeLabel as SiteCode?).LongName(); - // Add the volume label to comments, if possible - if (!string.IsNullOrWhiteSpace(drive?.VolumeLabel) && !info.CommonDiscInfo.Comments.Contains(volumeLabel)) - info.CommonDiscInfo.Comments += $"{volumeLabel} {drive.VolumeLabel}\n"; + if (!string.IsNullOrWhiteSpace(drive?.VolumeLabel) && !info.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VolumeLabel)) + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel] = drive.VolumeLabel; // Extract info based generically on MediaType switch (mediaType) @@ -221,9 +220,6 @@ namespace MPF.Library break; } - // Shortcut for adding ISBN for now - string isbn = (SiteCode.ISBN as SiteCode?).LongName(); - // Extract info based specifically on RedumpSystem switch (system) { @@ -237,8 +233,8 @@ namespace MPF.Library case RedumpSystem.PalmOS: case RedumpSystem.PocketPC: case RedumpSystem.RainbowDisc: - if (string.IsNullOrWhiteSpace(info.CommonDiscInfo.Comments)) - info.CommonDiscInfo.Comments += $"{isbn} {(options.AddPlaceholders ? Template.OptionalValue : "")}"; + // Remove any ISBN in the comments + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN] = (options.AddPlaceholders ? Template.OptionalValue : string.Empty); resultProgress?.Report(Result.Success("Running copy protection scan... this might take a while!")); info.CopyProtection.Protection = await GetCopyProtection(drive, options, protectionProgress); @@ -413,7 +409,6 @@ namespace MPF.Library return info; } - /// /// Ensures that all required output files have been created /// @@ -865,6 +860,51 @@ namespace MPF.Library } } + /// + /// Process any fields that have to be combined + /// + /// Information object to normalize + public static void ProcessSpecialFields(SubmissionInfo info) + { + // Process the comments field + if (info.CommonDiscInfo?.CommentsSpecialFields != null && info.CommonDiscInfo.CommentsSpecialFields?.Any() == true) + { + // If the field is missing, add an empty one to fill in + if (info.CommonDiscInfo.Comments == null) + info.CommonDiscInfo.Comments = string.Empty; + + // Add all special fields before any comments + info.CommonDiscInfo.Comments = string.Join( + "\n", info.CommonDiscInfo.CommentsSpecialFields.Select(kvp => $"{kvp.Key.ShortName()} {kvp.Value}") + ) + "\n" + info.CommonDiscInfo.Comments; + + // Trim the comments field + info.CommonDiscInfo.Comments = info.CommonDiscInfo.Comments.Trim(); + + // Wipe out the special fields dictionary + info.CommonDiscInfo.CommentsSpecialFields = null; + } + + // Process the contents field + if (info.CommonDiscInfo?.ContentsSpecialFields != null && info.CommonDiscInfo.ContentsSpecialFields?.Any() == true) + { + // If the field is missing, add an empty one to fill in + if (info.CommonDiscInfo.Contents == null) + info.CommonDiscInfo.Contents = string.Empty; + + // Add all special fields before any contents + info.CommonDiscInfo.Contents = string.Join( + "\n", info.CommonDiscInfo.ContentsSpecialFields.Select(kvp => $"{kvp.Key.ShortName()} {kvp.Value}") + ) + "\n" + info.CommonDiscInfo.Contents; + + // Trim the contents field + info.CommonDiscInfo.Contents = info.CommonDiscInfo.Contents.Trim(); + + // Wipe out the special fields dictionary + info.CommonDiscInfo.ContentsSpecialFields = null; + } + } + /// /// Write the data to the output folder /// @@ -1058,8 +1098,9 @@ namespace MPF.Library /// Create a new SubmissionInfo object from a disc page /// /// String containing the HTML disc data + /// Filled SubmissionInfo object on success, null on error /// Not currently working - private static void CreateFromID(string discData) + private static SubmissionInfo CreateFromID(string discData) { SubmissionInfo info = new SubmissionInfo() { @@ -1069,22 +1110,22 @@ namespace MPF.Library // No disc data means we can't parse it if (string.IsNullOrWhiteSpace(discData)) - return; + return null; try { // Load the current disc page into an XML document - XmlDocument redumpPage = new XmlDocument() { PreserveWhitespace = true }; + XmlDocument redumpPage = new XmlDocument() {PreserveWhitespace = true}; redumpPage.LoadXml(discData); // If the current page isn't valid, we can't parse it if (!redumpPage.HasChildNodes) - return; + return null; // Get the body node, if possible XmlNode bodyNode = redumpPage["html"]?["body"]; if (bodyNode == null || !bodyNode.HasChildNodes) - return; + return null; // Loop through and get the main node, if possible XmlNode mainNode = null; @@ -1108,7 +1149,7 @@ namespace MPF.Library // If the main node is invalid, we can't do anything if (mainNode == null || !mainNode.HasChildNodes) - return; + return null; // Try to find elements as we're going foreach (XmlNode childNode in mainNode.ChildNodes) @@ -1122,7 +1163,8 @@ namespace MPF.Library continue; // Only 2 of the internal divs have classes attached and one is not used here - if (childNode.Attributes != null && string.Equals(childNode.Attributes["class"]?.Value, "game", StringComparison.OrdinalIgnoreCase)) + if (childNode.Attributes != null && string.Equals(childNode.Attributes["class"]?.Value, "game", + StringComparison.OrdinalIgnoreCase)) { // If we don't have children nodes, skip this one over if (!childNode.HasChildNodes) @@ -1139,7 +1181,8 @@ namespace MPF.Library continue; // The gameinfo node contains most of the major information - if (string.Equals(gameNode.Attributes["class"]?.Value, "gameinfo", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(gameNode.Attributes["class"]?.Value, "gameinfo", + StringComparison.OrdinalIgnoreCase)) { // If we don't have children nodes, skip this one over if (!gameNode.HasChildNodes) @@ -1161,15 +1204,15 @@ namespace MPF.Library if (string.Equals(gameInfoNodeHeader.InnerText, "System", StringComparison.OrdinalIgnoreCase)) { - info.CommonDiscInfo.System = RedumpLib.Data.Extensions.ToRedumpSystem(gameInfoNodeData["a"]?.InnerText); + info.CommonDiscInfo.System = Extensions.ToRedumpSystem(gameInfoNodeData["a"]?.InnerText); } else if (string.Equals(gameInfoNodeHeader.InnerText, "Media", StringComparison.OrdinalIgnoreCase)) { - info.CommonDiscInfo.Media = RedumpLib.Data.Extensions.ToDiscType(gameInfoNodeData.InnerText); + info.CommonDiscInfo.Media = Extensions.ToDiscType(gameInfoNodeData.InnerText); } else if (string.Equals(gameInfoNodeHeader.InnerText, "Category", StringComparison.OrdinalIgnoreCase)) { - info.CommonDiscInfo.Category = RedumpLib.Data.Extensions.ToDiscCategory(gameInfoNodeData.InnerText); + info.CommonDiscInfo.Category = Extensions.ToDiscCategory(gameInfoNodeData.InnerText); } else if (string.Equals(gameInfoNodeHeader.InnerText, "Region", StringComparison.OrdinalIgnoreCase)) { @@ -1220,7 +1263,12 @@ namespace MPF.Library // TODO: COMPLETE } } - catch { } + catch + { + return null; + } + + return info; } /// @@ -1335,26 +1383,128 @@ namespace MPF.Library info.DumpersAndStatus.Dumpers = tempDumpers.ToArray(); } + // TODO: Unify handling of fields that can include site codes (Comments/Contents) + // Comments - // TODO: Re-enable when there's a better way of parsing comments - //match = Constants.CommentsRegex.Match(discData); - //if (match.Success) - //{ - // info.CommonDiscInfo.Comments += (string.IsNullOrEmpty(info.CommonDiscInfo.Comments) ? string.Empty : "\n") - // + WebUtility.HtmlDecode(match.Groups[1].Value) - // .Replace("
", "\n") - // .ReplaceHtmlWithSiteCodes() + "\n"; - //} + match = Constants.CommentsRegex.Match(discData); + if (match.Success) + { + // Process the old comments block + string oldComments = info.CommonDiscInfo.Comments + + (string.IsNullOrEmpty(info.CommonDiscInfo.Comments) ? string.Empty : "\n") + + WebUtility.HtmlDecode(match.Groups[1].Value) + .Replace("
", "\n") + .ReplaceHtmlWithSiteCodes(); + + // Setup the new comments block + string newComments = string.Empty; + + // Process the comments block line-by-line + string[] commentsSeparated = oldComments.Split('\n'); + for (int i = 0; i < commentsSeparated.Length; i++) + { + string commentLine = commentsSeparated[i].Trim(); + + // If we have an empty line, we want to treat this as intentional + if (string.IsNullOrWhiteSpace(commentLine)) + { + newComments += $"{commentLine}\n"; + continue; + } + + // If the line doesn't contain a site code tag, just keep it + if (!commentLine.Contains("[T:")) + { + newComments += $"{commentLine}\n"; + continue; + } + + // Otherwise, we need to find what tag is in use + bool foundTag = false; + foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode))) + { + // If the line doesn't contain this tag, just skip + if (!commentLine.Contains(siteCode.ShortName())) + continue; + + // If we don't already have this site code, add it to the dictionary + if (!info.CommonDiscInfo.CommentsSpecialFields.ContainsKey(siteCode)) + info.CommonDiscInfo.CommentsSpecialFields[siteCode] = commentLine.Replace(siteCode.ShortName(), string.Empty).Trim(); + + // Mark as having found a tag + foundTag = true; + break; + } + + // If we didn't find a known tag, just add the line, just in case + if (!foundTag) + newComments += $"{commentLine}\n"; + } + + // Set the new comments field + info.CommonDiscInfo.Comments = newComments; + } // Contents match = Constants.ContentsRegex.Match(discData); if (match.Success) { - info.CommonDiscInfo.Contents = WebUtility.HtmlDecode(match.Groups[1].Value) - .Replace("
", "\n") - .Replace("", "") - .ReplaceHtmlWithSiteCodes(); - info.CommonDiscInfo.Contents = Regex.Replace(info.CommonDiscInfo.Contents, @"
", ""); + // Process the old contents block + string oldContents = info.CommonDiscInfo.Contents + + (string.IsNullOrEmpty(info.CommonDiscInfo.Contents) ? string.Empty : "\n") + + WebUtility.HtmlDecode(match.Groups[1].Value) + .Replace("
", "\n") + .Replace("
", string.Empty) + .ReplaceHtmlWithSiteCodes(); + oldContents = Regex.Replace(oldContents, @"
", string.Empty); + + // Setup the new contents block + string newContents = string.Empty; + + // Process the contents block line-by-line + string[] contentsSeparated = oldContents.Split('\n'); + for (int i = 0; i < contentsSeparated.Length; i++) + { + string contentLine = contentsSeparated[i].Trim(); + + // If we have an empty line, we want to treat this as intentional + if (string.IsNullOrWhiteSpace(contentLine)) + { + newContents += $"{contentLine}\n"; + continue; + } + + // If the line doesn't contain a site code tag, just keep it + if (!contentLine.Contains("[T:")) + { + newContents += $"{contentLine}\n"; + continue; + } + + // Otherwise, we need to find what tag is in use + bool foundTag = false; + foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode))) + { + // If the line doesn't contain this tag, just skip + if (!contentLine.Contains(siteCode.ShortName())) + continue; + + // If we don't already have this site code, add it to the dictionary + if (!info.CommonDiscInfo.ContentsSpecialFields.ContainsKey(siteCode)) + info.CommonDiscInfo.ContentsSpecialFields[siteCode] = contentLine.Replace(siteCode.ShortName(), string.Empty).Trim(); + + // Mark as having found a tag + foundTag = true; + break; + } + + // If we didn't find a known tag, just add the line, just in case + if (!foundTag) + newContents += $"{contentLine}\n"; + } + + // Set the new contents field + info.CommonDiscInfo.Contents = newContents; } // Added diff --git a/MPF.Modules/Aaru/Parameters.cs b/MPF.Modules/Aaru/Parameters.cs index 86ba1f6d..9d285928 100644 --- a/MPF.Modules/Aaru/Parameters.cs +++ b/MPF.Modules/Aaru/Parameters.cs @@ -257,9 +257,6 @@ namespace MPF.Modules.Aaru break; } - // Shortcut for adding ISN for now - string internalSerialName = (SiteCode.InternalSerialName as SiteCode?).LongName(); - switch (this.System) { // TODO: Can we get SecuROM data? @@ -277,7 +274,8 @@ namespace MPF.Modules.Aaru case RedumpSystem.KonamiPython2: if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {pythonTwoSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion; info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate; } @@ -325,7 +323,8 @@ namespace MPF.Modules.Aaru case RedumpSystem.SonyPlayStation: if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {playstationSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion; info.CommonDiscInfo.EXEDateBuildDate = playstationDate; } @@ -335,7 +334,8 @@ namespace MPF.Modules.Aaru case RedumpSystem.SonyPlayStation2: if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {playstationTwoSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion; info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate; } diff --git a/MPF.Modules/DD/Parameters.cs b/MPF.Modules/DD/Parameters.cs index 6916b726..23fc3d60 100644 --- a/MPF.Modules/DD/Parameters.cs +++ b/MPF.Modules/DD/Parameters.cs @@ -85,15 +85,13 @@ namespace MPF.Modules.DD // Determine type-specific differences } - // Shortcut for adding ISN for now - string internalSerialName = (SiteCode.InternalSerialName as SiteCode?).LongName(); - switch (this.System) { case RedumpSystem.KonamiPython2: if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {pythonTwoSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion; info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate; } @@ -104,7 +102,8 @@ namespace MPF.Modules.DD case RedumpSystem.SonyPlayStation: if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {playstationSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion; info.CommonDiscInfo.EXEDateBuildDate = playstationDate; } @@ -114,7 +113,8 @@ namespace MPF.Modules.DD case RedumpSystem.SonyPlayStation2: if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {playstationTwoSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion; info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate; } diff --git a/MPF.Modules/DiscImageCreator/Parameters.cs b/MPF.Modules/DiscImageCreator/Parameters.cs index 7c6398ad..366fae28 100644 --- a/MPF.Modules/DiscImageCreator/Parameters.cs +++ b/MPF.Modules/DiscImageCreator/Parameters.cs @@ -439,9 +439,6 @@ namespace MPF.Modules.DiscImageCreator break; } - // Shortcut for adding ISN for now - string internalSerialName = (SiteCode.InternalSerialName as SiteCode?).LongName(); - // Extract info based specifically on RedumpSystem switch (this.System) { @@ -466,7 +463,8 @@ namespace MPF.Modules.DiscImageCreator case RedumpSystem.KonamiPython2: if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {pythonTwoSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion; info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate; } @@ -513,7 +511,8 @@ namespace MPF.Modules.DiscImageCreator if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {gdSerial ?? ""}"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; info.VersionAndEditions.Version = gdVersion ?? ""; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? ""; } @@ -530,7 +529,8 @@ namespace MPF.Modules.DiscImageCreator if (GetSegaCDBuildInfo(info.Extras.Header, out string scdSerial, out string fixedDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {scdSerial ?? ""}"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = scdSerial ?? string.Empty; info.CommonDiscInfo.EXEDateBuildDate = fixedDate ?? ""; } @@ -547,7 +547,8 @@ namespace MPF.Modules.DiscImageCreator if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {gdSerial ?? ""}"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; info.VersionAndEditions.Version = gdVersion ?? ""; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? ""; } @@ -566,7 +567,8 @@ namespace MPF.Modules.DiscImageCreator if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {gdSerial ?? ""}"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; info.VersionAndEditions.Version = gdVersion ?? ""; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? ""; } @@ -585,7 +587,8 @@ namespace MPF.Modules.DiscImageCreator if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {gdSerial ?? ""}"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; info.VersionAndEditions.Version = gdVersion ?? ""; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? ""; } @@ -604,7 +607,8 @@ namespace MPF.Modules.DiscImageCreator if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {gdSerial ?? ""}"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; info.VersionAndEditions.Version = gdVersion ?? ""; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? ""; } @@ -621,7 +625,8 @@ namespace MPF.Modules.DiscImageCreator if (GetSaturnBuildInfo(info.Extras.Header, out string saturnSerial, out string saturnVersion, out string buildDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {saturnSerial ?? ""}"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = saturnSerial ?? string.Empty; info.VersionAndEditions.Version = saturnVersion ?? ""; info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? ""; } @@ -631,7 +636,8 @@ namespace MPF.Modules.DiscImageCreator case RedumpSystem.SonyPlayStation: if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {playstationSerial ?? ""}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion; info.CommonDiscInfo.EXEDateBuildDate = playstationDate; } @@ -655,7 +661,8 @@ namespace MPF.Modules.DiscImageCreator case RedumpSystem.SonyPlayStation2: if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate)) { - info.CommonDiscInfo.Comments += $"{internalSerialName} {playstationTwoSerial}\n"; + // Ensure internal serial is pulled from local data + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion; info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate; } diff --git a/MPF.Test/Library/InfoToolTests.cs b/MPF.Test/Library/InfoToolTests.cs index 61ad3a9f..32f8c240 100644 --- a/MPF.Test/Library/InfoToolTests.cs +++ b/MPF.Test/Library/InfoToolTests.cs @@ -1,4 +1,5 @@ -using MPF.Library; +using System.Collections.Generic; +using MPF.Library; using RedumpLib.Data; using Xunit; @@ -61,5 +62,135 @@ namespace MPF.Test.Library Assert.Equal(expectedOutputDirectory, actualOutputDirectory); Assert.Equal(expectedOutputFilename, actualOutputFilename); } + + [Fact] + public void ProcessSpecialFieldsCompleteTest() + { + // Create a new SubmissionInfo object + SubmissionInfo info = new SubmissionInfo() + { + CommonDiscInfo = new CommonDiscInfoSection() + { + Comments = "This is a comments line\n[T:ISBN] ISBN Value", + CommentsSpecialFields = new Dictionary() + { + [SiteCode.VolumeLabel] = "VOLUME_LABEL", + }, + + Contents = "This is a contents line\n[T:GF] Game Footage", + ContentsSpecialFields = new Dictionary() + { + [SiteCode.Patches] = "1.04 patch", + }, + } + }; + + // Process the special fields + InfoTool.ProcessSpecialFields(info); + + // Validate the basics + Assert.NotNull(info.CommonDiscInfo.Comments); + Assert.Null(info.CommonDiscInfo.CommentsSpecialFields); + Assert.NotNull(info.CommonDiscInfo.Contents); + Assert.Null(info.CommonDiscInfo.ContentsSpecialFields); + + // Split the values + string[] splitComments = info.CommonDiscInfo.Comments.Split('\n'); + string[] splitContents = info.CommonDiscInfo.Contents.Split('\n'); + + // Validate the lines + Assert.Equal(3, splitComments.Length); + Assert.Equal(3, splitContents.Length); + } + + [Fact] + public void ProcessSpecialFieldsNullObjectTest() + { + // Create a new SubmissionInfo object + SubmissionInfo info = new SubmissionInfo() + { + CommonDiscInfo = null, + }; + + // Process the special fields + InfoTool.ProcessSpecialFields(info); + + // Validate + Assert.Null(info.CommonDiscInfo); + } + + [Fact] + public void ProcessSpecialFieldsNullCommentsContentsTest() + { + // Create a new SubmissionInfo object + SubmissionInfo info = new SubmissionInfo() + { + CommonDiscInfo = new CommonDiscInfoSection() + { + Comments = null, + CommentsSpecialFields = new Dictionary() + { + [SiteCode.VolumeLabel] = "VOLUME_LABEL", + }, + + Contents = null, + ContentsSpecialFields = new Dictionary() + { + [SiteCode.Patches] = "1.04 patch", + }, + } + }; + + // Process the special fields + InfoTool.ProcessSpecialFields(info); + + // Validate the basics + Assert.NotNull(info.CommonDiscInfo.Comments); + Assert.Null(info.CommonDiscInfo.CommentsSpecialFields); + Assert.NotNull(info.CommonDiscInfo.Contents); + Assert.Null(info.CommonDiscInfo.ContentsSpecialFields); + + // Split the values + string[] splitComments = info.CommonDiscInfo.Comments.Split('\n'); + string[] splitContents = info.CommonDiscInfo.Contents.Split('\n'); + + // Validate the lines + Assert.Single(splitComments); + Assert.Single(splitContents); + } + + [Fact] + public void ProcessSpecialFieldsNullDictionariesTest() + { + // Create a new SubmissionInfo object + SubmissionInfo info = new SubmissionInfo() + { + CommonDiscInfo = new CommonDiscInfoSection() + { + Comments = "This is a comments line\n[T:ISBN] ISBN Value", + CommentsSpecialFields = null, + + Contents = "This is a contents line\n[T:GF] Game Footage", + ContentsSpecialFields = null, + } + }; + + // Process the special fields + InfoTool.ProcessSpecialFields(info); + + // Validate the basics + Assert.NotNull(info.CommonDiscInfo.Comments); + Assert.Null(info.CommonDiscInfo.CommentsSpecialFields); + Assert.NotNull(info.CommonDiscInfo.Contents); + Assert.Null(info.CommonDiscInfo.ContentsSpecialFields); + + // Split the values + string[] splitComments = info.CommonDiscInfo.Comments.Split('\n'); + string[] splitContents = info.CommonDiscInfo.Contents.Split('\n'); + + // Validate the lines + Assert.Equal(2, splitComments.Length); + Assert.Equal(2, splitContents.Length); + } } } diff --git a/RedumpLib/Data/SubmissionInfo.cs b/RedumpLib/Data/SubmissionInfo.cs index c56830d2..b68b1d8d 100644 --- a/RedumpLib/Data/SubmissionInfo.cs +++ b/RedumpLib/Data/SubmissionInfo.cs @@ -211,9 +211,15 @@ namespace RedumpLib.Data [JsonProperty(PropertyName = "d_comments", NullValueHandling = NullValueHandling.Ignore)] public string Comments { get; set; } + [JsonIgnore] + public Dictionary CommentsSpecialFields { get; set; } + [JsonProperty(PropertyName = "d_contents", NullValueHandling = NullValueHandling.Ignore)] public string Contents { get; set; } + [JsonIgnore] + public Dictionary ContentsSpecialFields { get; set; } + public object Clone() { return new CommonDiscInfoSection @@ -254,7 +260,9 @@ namespace RedumpLib.Data EXEDateBuildDate = this.EXEDateBuildDate, ErrorsCount = this.ErrorsCount, Comments = this.Comments, + CommentsSpecialFields = this.CommentsSpecialFields.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), Contents = this.Contents, + ContentsSpecialFields = this.ContentsSpecialFields.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), }; } }