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);
}
}