Skip to content

Commit b587c91

Browse files
Refactor parsers to inherit from BaseParser for shared functionality
- Created a new BaseParser class to encapsulate common parsing logic and reduce code duplication across various parser implementations. - Updated all existing parsers (GenericParser, MAMEParser, MAMERedumpParser, NoIntrosParser, PleasuredomeParser, PureDOSDATParser, RedumpParser, RetroAchievementsParser, TosecParser, WHDLoadParser) to inherit from BaseParser. - Moved common methods such as InitializeFromFile, ParseHeader, and ROM attribute parsing into BaseParser. - Simplified individual parser implementations by removing redundant code related to XML file handling and header parsing.
1 parent 419b919 commit b587c91

File tree

12 files changed

+325
-820
lines changed

12 files changed

+325
-820
lines changed
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
using System;
2+
using System.Xml;
3+
using System.IO;
4+
using gaseous_signature_parser.models.RomSignatureObject;
5+
6+
namespace gaseous_signature_parser.classes.parsers
7+
{
8+
/// <summary>
9+
/// Base class for all signature parsers providing common XML parsing and data extraction functionality
10+
/// </summary>
11+
public abstract class BaseParser : IParser
12+
{
13+
/// <summary>
14+
/// Parse the XML file and return a RomSignatureObject
15+
/// </summary>
16+
/// <param name="xmlFile">Path to the XML signature file</param>
17+
/// <param name="options">Optional parameters specific to the parser implementation</param>
18+
/// <returns>Parsed RomSignatureObject</returns>
19+
public abstract RomSignatureObject Parse(string xmlFile, Dictionary<string, object>? options = null);
20+
21+
/// <summary>
22+
/// Determine if the provided XML document matches this parser's signature type
23+
/// </summary>
24+
/// <param name="xml">XML document to check</param>
25+
/// <returns>SignatureParser type if matched, otherwise Unknown</returns>
26+
public abstract parser.SignatureParser GetXmlType(XmlDocument xml);
27+
28+
/// <summary>
29+
/// Initialize from XML file, calculating hashes and loading the document
30+
/// </summary>
31+
/// <param name="xmlFile">Path to the XML file</param>
32+
/// <param name="md5Hash">Output: MD5 hash of the file</param>
33+
/// <param name="sha1Hash">Output: SHA1 hash of the file</param>
34+
/// <returns>Loaded XmlDocument</returns>
35+
protected XmlDocument InitializeFromFile(string xmlFile, out string md5Hash, out string sha1Hash)
36+
{
37+
var xmlStream = File.OpenRead(xmlFile);
38+
var hashes = Hash.GenerateHashes(xmlStream);
39+
md5Hash = hashes.md5;
40+
sha1Hash = hashes.sha1;
41+
42+
XmlDocument xmlDocument = new XmlDocument();
43+
xmlDocument.Load(xmlFile);
44+
return xmlDocument;
45+
}
46+
47+
/// <summary>
48+
/// Parse the header section of the XML file and populate the RomSignatureObject
49+
/// </summary>
50+
/// <param name="signatureObject">The RomSignatureObject to populate</param>
51+
/// <param name="headerNode">The header XML node</param>
52+
/// <param name="sourceType">The type of source (e.g., "Generic", "TOSEC", "Redump")</param>
53+
/// <param name="md5Hash">MD5 hash of the source file</param>
54+
/// <param name="sha1Hash">SHA1 hash of the source file</param>
55+
protected void ParseHeader(RomSignatureObject signatureObject, XmlNode headerNode, string sourceType, string md5Hash, string sha1Hash)
56+
{
57+
signatureObject.SourceType = sourceType;
58+
signatureObject.SourceMd5 = md5Hash;
59+
signatureObject.SourceSHA1 = sha1Hash;
60+
61+
if (headerNode == null)
62+
return;
63+
64+
foreach (XmlNode childNode in headerNode.ChildNodes)
65+
{
66+
switch (childNode.Name.ToLower())
67+
{
68+
case "name":
69+
signatureObject.Name = childNode.InnerText;
70+
break;
71+
72+
case "description":
73+
signatureObject.Description = childNode.InnerText;
74+
break;
75+
76+
case "category":
77+
signatureObject.Category = childNode.InnerText;
78+
break;
79+
80+
case "version":
81+
signatureObject.Version = childNode.InnerText;
82+
break;
83+
84+
case "author":
85+
signatureObject.Author = childNode.InnerText;
86+
break;
87+
88+
case "email":
89+
signatureObject.Email = childNode.InnerText;
90+
break;
91+
92+
case "homepage":
93+
signatureObject.Homepage = childNode.InnerText;
94+
break;
95+
96+
case "url":
97+
signatureObject.Url = ParseUri(childNode.InnerText);
98+
break;
99+
100+
case "id":
101+
signatureObject.Id = childNode.InnerText;
102+
break;
103+
}
104+
}
105+
}
106+
107+
/// <summary>
108+
/// Safely parse a URI string, returning null if invalid
109+
/// </summary>
110+
/// <param name="uriString">The URI string to parse</param>
111+
/// <returns>Parsed Uri or null if invalid</returns>
112+
protected Uri? ParseUri(string uriString)
113+
{
114+
try
115+
{
116+
// Handle cases where http:// or https:// is missing
117+
if (!uriString.StartsWith("http://") && !uriString.StartsWith("https://"))
118+
{
119+
uriString = "http://" + uriString;
120+
}
121+
return new Uri(uriString);
122+
}
123+
catch
124+
{
125+
return null;
126+
}
127+
}
128+
129+
/// <summary>
130+
/// Create and initialize a Game object with default collections
131+
/// </summary>
132+
/// <returns>A new Game object with initialized collections</returns>
133+
protected RomSignatureObject.Game CreateGameObject()
134+
{
135+
return new RomSignatureObject.Game
136+
{
137+
Roms = new List<RomSignatureObject.Game.Rom>(),
138+
flags = new Dictionary<string, object>(),
139+
Language = new Dictionary<string, string>(),
140+
Country = new Dictionary<string, string>()
141+
};
142+
}
143+
144+
/// <summary>
145+
/// Parse ROM attributes from an XML node, extracting standard hash and size values
146+
/// </summary>
147+
/// <param name="romNode">The XML node containing ROM attributes</param>
148+
/// <param name="signatureSource">The source type for this ROM</param>
149+
/// <returns>A populated Rom object</returns>
150+
protected RomSignatureObject.Game.Rom ParseRomAttributes(XmlNode romNode, RomSignatureObject.Game.Rom.SignatureSourceType signatureSource)
151+
{
152+
RomSignatureObject.Game.Rom rom = new RomSignatureObject.Game.Rom
153+
{
154+
Attributes = new Dictionary<string, object>(),
155+
SignatureSource = signatureSource
156+
};
157+
158+
foreach (XmlAttribute romAttribute in romNode.Attributes)
159+
{
160+
switch (romAttribute.Name.ToLower())
161+
{
162+
case "name":
163+
rom.Name = romAttribute.Value;
164+
break;
165+
166+
case "size":
167+
if (UInt64.TryParse(romAttribute.Value, out ulong sizeValue))
168+
{
169+
rom.Size = sizeValue;
170+
}
171+
else
172+
{
173+
rom.Size = 0;
174+
}
175+
break;
176+
177+
case "crc":
178+
rom.Crc = romAttribute.Value;
179+
break;
180+
181+
case "md5":
182+
rom.Md5 = romAttribute.Value;
183+
break;
184+
185+
case "sha1":
186+
rom.Sha1 = romAttribute.Value;
187+
break;
188+
189+
case "sha256":
190+
rom.Sha256 = romAttribute.Value;
191+
break;
192+
193+
case "status":
194+
rom.Status = romAttribute.Value;
195+
break;
196+
197+
default:
198+
if (!rom.Attributes.ContainsKey(romAttribute.Name))
199+
{
200+
rom.Attributes.Add(romAttribute.Name, romAttribute.Value);
201+
}
202+
break;
203+
}
204+
}
205+
206+
return rom;
207+
}
208+
209+
/// <summary>
210+
/// Add a flag to a game object if it doesn't already exist
211+
/// </summary>
212+
/// <param name="gameObject">The game object to update</param>
213+
/// <param name="flagName">The flag name</param>
214+
/// <param name="flagValue">The flag value</param>
215+
protected void AddGameFlag(RomSignatureObject.Game gameObject, string flagName, object flagValue)
216+
{
217+
if (!gameObject.flags.ContainsKey(flagName))
218+
{
219+
gameObject.flags.Add(flagName, flagValue);
220+
}
221+
}
222+
223+
/// <summary>
224+
/// Add a country to a collection if it doesn't already exist
225+
/// </summary>
226+
/// <param name="countryDict">The country dictionary to update</param>
227+
/// <param name="countryCode">The country code</param>
228+
/// <param name="countryName">The country name</param>
229+
protected void AddCountry(Dictionary<string, string> countryDict, string countryCode, string countryName)
230+
{
231+
if (!countryDict.ContainsKey(countryCode))
232+
{
233+
countryDict.Add(countryCode, countryName);
234+
}
235+
}
236+
237+
/// <summary>
238+
/// Add a language to a collection if it doesn't already exist
239+
/// </summary>
240+
/// <param name="languageDict">The language dictionary to update</param>
241+
/// <param name="languageCode">The language code</param>
242+
/// <param name="languageName">The language name</param>
243+
protected void AddLanguage(Dictionary<string, string> languageDict, string languageCode, string languageName)
244+
{
245+
if (!languageDict.ContainsKey(languageCode))
246+
{
247+
languageDict.Add(languageCode, languageName);
248+
}
249+
}
250+
}
251+
}

0 commit comments

Comments
 (0)