From 9004d2bc7b22e2fbb177dcfb6ed99da54318a7e5 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 13 Sep 2023 16:16:47 -0400 Subject: [PATCH] Migrate to Nuget package for XMID --- CHANGELIST.md | 1 + MPF.Core/Data/XgdInfo.cs | 329 +++------------------ MPF.Core/MPF.Core.csproj | 2 + MPF.Modules/DiscImageCreator/Parameters.cs | 8 +- MPF.Modules/MPF.Modules.csproj | 1 + MPF.Test/Core/Data/XgdInfoTests.cs | 34 +-- 6 files changed, 68 insertions(+), 307 deletions(-) diff --git a/CHANGELIST.md b/CHANGELIST.md index dff574a0..834e0d18 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -10,6 +10,7 @@ - Retrofit README - Migrate to Nuget package for Redump - Remove dd for Windows +- Migrate to Nuget package for XMID ### 2.6.3 (2023-08-15) diff --git a/MPF.Core/Data/XgdInfo.cs b/MPF.Core/Data/XgdInfo.cs index c08a353a..ecb943fe 100644 --- a/MPF.Core/Data/XgdInfo.cs +++ b/MPF.Core/Data/XgdInfo.cs @@ -5,28 +5,6 @@ namespace MPF.Core.Data /// /// Contains information specific to an XGD disc /// - /// - /// XGD1 XMID Format Information: - /// - /// AABBBCCD - /// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details) - /// - BBB => Game ID - /// - CC => Version number - /// - D => Region identifier (see GetRegion for details) - /// - /// XGD2/3 XeMID Format Information: - /// - /// AABCCCDDEFFGHH(IIIIIIII) - /// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details) - /// - B => Platform identifier; 2 indicates Xbox 360. - /// - CCC => Game ID - /// - DD => SKU number (unique per SKU of a title) - /// - E => Region identifier (see GetRegion for details) - /// - FF => Base version; usually starts at 01 (can be 1 or 2 characters) - /// - G => Media type identifier (see GetMediaSubtype for details) - /// - HH => Disc number stored in [disc number][total discs] format - /// - IIIIIIII => 8-hex-digit certification submission identifier; usually on test discs only - /// public class XgdInfo { #region Fields @@ -39,75 +17,17 @@ namespace MPF.Core.Data /// /// Raw XMID/XeMID string that all other information is derived from /// - public string XMID { get; private set; } + public string RawXMID { get; private set; } /// - /// 2-character publisher identifier + /// XGD1 XMID /// - public string PublisherIdentifier { get; private set; } + public SabreTools.Models.Xbox.XMID XMID { get; private set; } /// - /// Platform disc is made for, 2 indicates Xbox 360 + /// XGD2/3 XeMID /// - public char? PlatformIdentifier { get; private set; } - - /// - /// Game ID - /// - public string GameID { get; private set; } - - /// - /// For XGD1: Internal version number - /// For XGD2/3: Title-specific SKU - /// - public string SKU { get; private set; } - - /// - /// Region identifier character - /// - public char RegionIdentifier { get; private set; } - - /// - /// Base version of executables, usually starts at 01 - /// - /// - /// TODO: Check if this is always 2 characters for XGD2/3 - /// - public string BaseVersion { get; private set; } - - /// - /// Media subtype identifier - /// - public char MediaSubtypeIdentifier { get; private set; } - - /// - /// Disc number stored in [disc number][total discs] format - /// - public string DiscNumberIdentifier { get; private set; } - - /// - /// 8-hex-digit certification submission identifier; usually on test discs only - /// - public string CertificationSubmissionIdentifier { get; private set; } - - #endregion - - #region Auto-Generated Information - - /// - /// Human-readable name derived from the publisher identifier - /// - public string PublisherName => GetPublisher(this.PublisherIdentifier); - - /// - /// Internally represented region - /// - public Region? InternalRegion => GetRegion(this.RegionIdentifier); - - /// - /// Human-readable subtype derived from the media identifier - /// - public string MediaSubtype => GetMediaSubtype(this.MediaSubtypeIdentifier); + public SabreTools.Models.Xbox.XeMID XeMID { get; private set; } #endregion @@ -115,24 +35,23 @@ namespace MPF.Core.Data /// Populate a set of XGD information from a Master ID (XMID/XeMID) string /// /// XMID/XeMID string representing the DMI information - /// True if value validation should be performed, false otherwise - public XgdInfo(string xmid, bool validate = false) + public XgdInfo(string xmid) { this.Initialized = false; if (string.IsNullOrWhiteSpace(xmid)) return; - this.XMID = xmid.TrimEnd('\0'); - if (string.IsNullOrWhiteSpace(this.XMID)) + this.RawXMID = xmid.TrimEnd('\0'); + if (string.IsNullOrWhiteSpace(this.RawXMID)) return; // XGD1 information is 8 characters - if (this.XMID.Length == 8) - this.Initialized = ParseXGD1XMID(this.XMID, validate); + if (this.RawXMID.Length == 8) + this.Initialized = ParseXGD1XMID(this.RawXMID); // XGD2/3 information is semi-variable length - else if (this.XMID.Length == 13 || this.XMID.Length == 14 || this.XMID.Length == 21 || this.XMID.Length == 22) - this.Initialized = ParseXGD23XeMID(this.XMID, validate); + else if (this.RawXMID.Length == 13 || this.RawXMID.Length == 14 || this.RawXMID.Length == 21 || this.RawXMID.Length == 22) + this.Initialized = ParseXGD23XeMID(this.RawXMID); } /// @@ -147,12 +66,12 @@ namespace MPF.Core.Data try { // XGD1 doesn't use PlatformIdentifier - if (this.PlatformIdentifier == null) - return $"{this.PublisherIdentifier}-{this.GameID}"; + if (XMID != null) + return $"{XMID.PublisherIdentifier}-{XMID.GameID}"; // XGD2/3 uses a specific identifier - else if (this.PlatformIdentifier == '2') - return $"{this.PublisherIdentifier}-{this.PlatformIdentifier}{this.GameID}"; + else if (XeMID?.PlatformIdentifier == '2') + return $"{XeMID.PublisherIdentifier}-{XeMID.PlatformIdentifier}{XeMID.GameID}"; return null; } @@ -175,12 +94,12 @@ namespace MPF.Core.Data try { // XGD1 doesn't use PlatformIdentifier - if (this.PlatformIdentifier == null) - return $"1.{this.SKU}"; + if (XMID != null) + return $"1.{XMID.VersionNumber}"; // XGD2/3 uses a specific identifier - else if (this.PlatformIdentifier == '2') - return $"1.{this.SKU}"; + else if (XeMID?.PlatformIdentifier == '2') + return $"1.{XeMID.SKU}"; return null; } @@ -193,217 +112,55 @@ namespace MPF.Core.Data /// /// Parse an XGD1 XMID string /// - /// XMID string to attempt to parse - /// True if value validation should be performed, false otherwise + /// XMID string to attempt to parse /// True if the XMID could be parsed, false otherwise - private bool ParseXGD1XMID(string xmid, bool validate) + private bool ParseXGD1XMID(string rawXmid) { - if (xmid == null || xmid.Length != 8) - return false; + try + { + var xmid = new SabreTools.Serialization.Files.XMID().Deserialize(rawXmid); + if (xmid == null) + return false; - this.PublisherIdentifier = xmid.Substring(0, 2); - if (validate && string.IsNullOrEmpty(this.PublisherName)) + this.XMID = xmid; + return true; + } + catch + { return false; - - this.GameID = xmid.Substring(2, 3); - this.SKU = xmid.Substring(5, 2); - this.RegionIdentifier = xmid[7]; - if (validate && this.InternalRegion == null) - return false; - - return true; + } } /// /// Parse an XGD2/3 XeMID string /// - /// XeMID string to attempt to parse - /// True if value validation should be performed, false otherwise + /// XeMID string to attempt to parse /// True if the XeMID could be parsed, false otherwise - private bool ParseXGD23XeMID(string xemid, bool validate) + private bool ParseXGD23XeMID(string rawXemid) { - if (xemid == null - || (xemid.Length != 13 && xemid.Length != 14 - && xemid.Length != 21 && xemid.Length != 22)) - return false; - - this.PublisherIdentifier = xemid.Substring(0, 2); - if (validate && string.IsNullOrEmpty(this.PublisherName)) - return false; - - this.PlatformIdentifier = xemid[2]; - if (validate && this.PlatformIdentifier != '2') - return false; - - this.GameID = xemid.Substring(3, 3); - this.SKU = xemid.Substring(6, 2); - this.RegionIdentifier = xemid[8]; - if (validate && this.InternalRegion == null) - return false; - - if (xemid.Length == 13 || xemid.Length == 21) + try { - this.BaseVersion = xemid.Substring(9, 1); - this.MediaSubtypeIdentifier = xemid[10]; - if (validate && string.IsNullOrEmpty(this.MediaSubtype)) + var xemid = new SabreTools.Serialization.Files.XeMID().Deserialize(rawXemid); + if (xemid == null) return false; - this.DiscNumberIdentifier = xemid.Substring(11, 2); + this.XeMID = xemid; + return true; } - else if (xemid.Length == 14 || xemid.Length == 22) + catch { - this.BaseVersion = xemid.Substring(9, 2); - this.MediaSubtypeIdentifier = xemid[11]; - if (validate && string.IsNullOrEmpty(this.MediaSubtype)) - return false; - - this.DiscNumberIdentifier = xemid.Substring(12, 2); + return false; } - - if (xemid.Length == 21) - this.CertificationSubmissionIdentifier = xemid.Substring(13); - else if (xemid.Length == 22) - this.CertificationSubmissionIdentifier = xemid.Substring(14); - - return true; } #region Helpers - /// - /// Determine the XGD type based on the XGD2/3 media type identifier character - /// - /// Character denoting the media type - /// Media subtype as a string, if possible - private static string GetMediaSubtype(char mediaTypeIdentifier) - { - switch (mediaTypeIdentifier) - { - case 'F': return "XGD3"; - case 'X': return "XGD2"; - case 'Z': return "Games on Demand / Marketplace Demo"; - default: return null; - } - } - - /// - /// Get the full name of the publisher from the 2-character identifier - /// - /// Case-sensitive 2-character identifier - /// Publisher name, if possible - /// - private static string GetPublisher(string publisherIdentifier) - { - switch (publisherIdentifier) - { - case "AC": return "Acclaim Entertainment"; - case "AH": return "ARUSH Entertainment"; - case "AQ": return "Aqua System"; - case "AS": return "ASK"; - case "AT": return "Atlus"; - case "AV": return "Activision"; - case "AY": return "Aspyr Media"; - case "BA": return "Bandai"; - case "BL": return "Black Box"; - case "BM": return "BAM! Entertainment"; - case "BR": return "Broccoli Co."; - case "BS": return "Bethesda Softworks"; - case "BU": return "Bunkasha Co."; - case "BV": return "Buena Vista Games"; - case "BW": return "BBC Multimedia"; - case "BZ": return "Blizzard"; - case "CC": return "Capcom"; - case "CK": return "Kemco Corporation"; // TODO: Confirm - case "CM": return "Codemasters"; - case "CV": return "Crave Entertainment"; - case "DC": return "DreamCatcher Interactive"; - case "DX": return "Davilex"; - case "EA": return "Electronic Arts (EA)"; - case "EC": return "Encore inc"; - case "EL": return "Enlight Software"; - case "EM": return "Empire Interactive"; - case "ES": return "Eidos Interactive"; - case "FI": return "Fox Interactive"; - case "FS": return "From Software"; - case "GE": return "Genki Co."; - case "GV": return "Groove Games"; - case "HE": return "Tru Blu (Entertainment division of Home Entertainment Suppliers)"; - case "HP": return "Hip games"; - case "HU": return "Hudson Soft"; - case "HW": return "Highwaystar"; - case "IA": return "Mad Catz Interactive"; - case "IF": return "Idea Factory"; - case "IG": return "Infogrames"; - case "IL": return "Interlex Corporation"; - case "IM": return "Imagine Media"; - case "IO": return "Ignition Entertainment"; - case "IP": return "Interplay Entertainment"; - case "IX": return "InXile Entertainment"; // TODO: Confirm - case "JA": return "Jaleco"; - case "JW": return "JoWooD"; - case "KB": return "Kemco"; // TODO: Confirm - case "KI": return "Kids Station Inc."; // TODO: Confirm - case "KN": return "Konami"; - case "KO": return "KOEI"; - case "KU": return "Kobi and / or GAE (formerly Global A Entertainment)"; // TODO: Confirm - case "LA": return "LucasArts"; - case "LS": return "Black Bean Games (publishing arm of Leader S.p.A.)"; - case "MD": return "Metro3D"; - case "ME": return "Medix"; - case "MI": return "Microïds"; - case "MJ": return "Majesco Entertainment"; - case "MM": return "Myelin Media"; - case "MP": return "MediaQuest"; // TODO: Confirm - case "MS": return "Microsoft Game Studios"; - case "MW": return "Midway Games"; - case "MX": return "Empire Interactive"; // TODO: Confirm - case "NK": return "NewKidCo"; - case "NL": return "NovaLogic"; - case "NM": return "Namco"; - case "OX": return "Oxygen Interactive"; - case "PC": return "Playlogic Entertainment"; - case "PL": return "Phantagram Co., Ltd."; - case "RA": return "Rage"; - case "SA": return "Sammy"; - case "SC": return "SCi Games"; - case "SE": return "SEGA"; - case "SN": return "SNK"; - case "SS": return "Simon & Schuster"; - case "SU": return "Success Corporation"; - case "SW": return "Swing! Deutschland"; - case "TA": return "Takara"; - case "TC": return "Tecmo"; - case "TD": return "The 3DO Company (or just 3DO)"; - case "TK": return "Takuyo"; - case "TM": return "TDK Mediactive"; - case "TQ": return "THQ"; - case "TS": return "Titus Interactive"; - case "TT": return "Take-Two Interactive Software"; - case "US": return "Ubisoft"; - case "VC": return "Victor Interactive Software"; - case "VN": return "Vivendi Universal (just took Interplays publishing rights)"; // TODO: Confirm - case "VU": return "Vivendi Universal Games"; - case "VV": return "Vivendi Universal Games"; // TODO: Confirm - case "WE": return "Wanadoo Edition"; - case "WR": return "Warner Bros. Interactive Entertainment"; // TODO: Confirm - case "XI": return "XPEC Entertainment and Idea Factory"; - case "XK": return "Xbox kiosk disk?"; // TODO: Confirm - case "XL": return "Xbox special bundled or live demo disk?"; // TODO: Confirm - case "XM": return "Evolved Games"; // TODO: Confirm - case "XP": return "XPEC Entertainment"; - case "XR": return "Panorama"; - case "YB": return "YBM Sisa (South-Korea)"; - case "ZD": return "Zushi Games (formerly Zoo Digital Publishing)"; - default: return null; - } - } - /// /// Determine the region based on the XGD serial character /// /// Character denoting the region /// Region, if possible - private static Region? GetRegion(char region) + public static Region? GetRegion(char region) { switch (region) { diff --git a/MPF.Core/MPF.Core.csproj b/MPF.Core/MPF.Core.csproj index f670c483..c397c442 100644 --- a/MPF.Core/MPF.Core.csproj +++ b/MPF.Core/MPF.Core.csproj @@ -44,7 +44,9 @@ + + diff --git a/MPF.Modules/DiscImageCreator/Parameters.cs b/MPF.Modules/DiscImageCreator/Parameters.cs index ba197dde..7fc5194a 100644 --- a/MPF.Modules/DiscImageCreator/Parameters.cs +++ b/MPF.Modules/DiscImageCreator/Parameters.cs @@ -552,11 +552,11 @@ namespace MPF.Modules.DiscImageCreator XgdInfo xgd1Info = new XgdInfo(xgd1XMID); if (xgd1Info?.Initialized == true) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xgd1Info.XMID; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xgd1Info.RawXMID; info.CommonDiscInfo.Serial = xgd1Info.GetSerial() ?? string.Empty; if (!options.EnableRedumpCompatibility) info.VersionAndEditions.Version = xgd1Info.GetVersion() ?? string.Empty; - info.CommonDiscInfo.Region = xgd1Info.InternalRegion; + info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd1Info.XMID.RegionIdentifier); } // If we have the new, external DAT @@ -595,11 +595,11 @@ namespace MPF.Modules.DiscImageCreator XgdInfo xgd23Info = new XgdInfo(xgd23XeMID); if (xgd23Info?.Initialized == true) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xgd23Info.XMID; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xgd23Info.RawXMID; info.CommonDiscInfo.Serial = xgd23Info.GetSerial() ?? string.Empty; if (!options.EnableRedumpCompatibility) info.VersionAndEditions.Version = xgd23Info.GetVersion() ?? string.Empty; - info.CommonDiscInfo.Region = xgd23Info.InternalRegion; + info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd23Info.XeMID.RegionIdentifier); } // If we have the new, external DAT diff --git a/MPF.Modules/MPF.Modules.csproj b/MPF.Modules/MPF.Modules.csproj index ca8e6072..5deb9774 100644 --- a/MPF.Modules/MPF.Modules.csproj +++ b/MPF.Modules/MPF.Modules.csproj @@ -26,6 +26,7 @@ + diff --git a/MPF.Test/Core/Data/XgdInfoTests.cs b/MPF.Test/Core/Data/XgdInfoTests.cs index 335407ab..c78ec068 100644 --- a/MPF.Test/Core/Data/XgdInfoTests.cs +++ b/MPF.Test/Core/Data/XgdInfoTests.cs @@ -22,13 +22,13 @@ namespace MPF.Test.Core.Data [InlineData("AV00100W\0", "AV", "001", "00", 'W')] public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier) { - XgdInfo xgdInfo = new XgdInfo(validString, validate: true); + XgdInfo xgdInfo = new XgdInfo(validString); Assert.True(xgdInfo.Initialized); - Assert.Equal(publisher, xgdInfo.PublisherIdentifier); - Assert.Equal(gameId, xgdInfo.GameID); - Assert.Equal(version, xgdInfo.SKU); - Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier); + Assert.Equal(publisher, xgdInfo.XMID.PublisherIdentifier); + Assert.Equal(gameId, xgdInfo.XMID.GameID); + Assert.Equal(version, xgdInfo.XMID.VersionNumber); + Assert.Equal(regionIdentifier, xgdInfo.XMID.RegionIdentifier); } [Theory] @@ -40,7 +40,7 @@ namespace MPF.Test.Core.Data [InlineData("AV00000Z\0")] public void XGD1InvalidTests(string invalidString) { - XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true); + XgdInfo xgdInfo = new XgdInfo(invalidString); Assert.False(xgdInfo.Initialized); } @@ -55,18 +55,18 @@ namespace MPF.Test.Core.Data [InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")] public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert) { - XgdInfo xgdInfo = new XgdInfo(validString, validate: true); + XgdInfo xgdInfo = new XgdInfo(validString); Assert.True(xgdInfo.Initialized); - Assert.Equal(publisher, xgdInfo.PublisherIdentifier); - Assert.Equal('2', xgdInfo.PlatformIdentifier); - Assert.Equal(gameId, xgdInfo.GameID); - Assert.Equal(sku, xgdInfo.SKU); - Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier); - Assert.Equal(baseVersion, xgdInfo.BaseVersion); - Assert.Equal(mediaSubtype, xgdInfo.MediaSubtypeIdentifier); - Assert.Equal(discNumber, xgdInfo.DiscNumberIdentifier); - Assert.Equal(cert, xgdInfo.CertificationSubmissionIdentifier); + Assert.Equal(publisher, xgdInfo.XeMID.PublisherIdentifier); + Assert.Equal('2', xgdInfo.XeMID.PlatformIdentifier); + Assert.Equal(gameId, xgdInfo.XeMID.GameID); + Assert.Equal(sku, xgdInfo.XeMID.SKU); + Assert.Equal(regionIdentifier, xgdInfo.XeMID.RegionIdentifier); + Assert.Equal(baseVersion, xgdInfo.XeMID.BaseVersion); + Assert.Equal(mediaSubtype, xgdInfo.XeMID.MediaSubtypeIdentifier); + Assert.Equal(discNumber, xgdInfo.XeMID.DiscNumberIdentifier); + Assert.Equal(cert, xgdInfo.XeMID.CertificationSubmissionIdentifier); } [Theory] @@ -108,7 +108,7 @@ namespace MPF.Test.Core.Data [InlineData("AV200000W00A0000000000\0")] public void XGD23InvalidTests(string invalidString) { - XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true); + XgdInfo xgdInfo = new XgdInfo(invalidString); Assert.False(xgdInfo.Initialized); } }