using System; using System.Collections.Generic; using System.IO; using System.Windows.Media.Imaging; using System.Xml; using Ionic.Zip; namespace ScrapModLoader { public class ScrapMod { public String Name { get; private set; } public String Description { get; private set; } public String ModPath { get; private set; } public BitmapImage Icon { get; private set; } public Boolean Checked { get; set; } public String Category { get; private set; } public String Version { get; private set; } public String RequiredLauncher { get; private set; } public String RequiredGame { get; private set; } public List Authors { get; private set; } public Dictionary> Credits { get; private set; } public ScrapMod(String path) { ModPath = path; Name = Path.GetFileNameWithoutExtension(path); Description = String.Empty; Icon = new BitmapImage(); Checked = false; Category = String.Empty; Version = String.Empty; RequiredLauncher = String.Empty; RequiredGame = String.Empty; Authors = new List(); Credits = new Dictionary>(); LoadFromFile(path); } private void LoadFromFile(String path) { using (ZipFile zipFile = ZipFile.Read(path)) { Byte[] iconBuffer = ExtractFromZip(zipFile, "icon.png"); Byte[] confBuffer = ExtractFromZip(zipFile, "config.xml"); LoadIcon(iconBuffer); LoadConfig(confBuffer); } } private Byte[] ExtractFromZip(ZipFile zip, String entry_path) { ZipEntry? entry = zip[entry_path]; if (entry == null) throw new FileFormatException($"No '{entry}' in {Name} found"); Byte[] buffer = new Byte[entry.UncompressedSize]; using (MemoryStream zipStream = new MemoryStream(buffer)) entry.Extract(zipStream); return buffer; } private void LoadIcon(Byte[] buffer) { using (MemoryStream sourceStream = new MemoryStream(buffer)) { Icon.BeginInit(); Icon.CacheOption = BitmapCacheOption.OnLoad; Icon.StreamSource = sourceStream; Icon.EndInit(); } } private void LoadConfig(Byte[] buffer) { using (MemoryStream sourceStream = new MemoryStream(buffer)) { XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load(sourceStream); ParseModInfoFromXml(xmlDocument); ParseAuthorsFromXml(xmlDocument); ParseCreditsFromXml(xmlDocument); } } private void ParseModInfoFromXml(XmlDocument xmlDocument) { Description = xmlDocument.GetElementsByTagName("Description").Item(0)?.InnerText ?? throw new FileFormatException("No 'Description' tag in 'config.xml'"); Category = xmlDocument.GetElementsByTagName("Category").Item(0)?.InnerText ?? throw new FileFormatException("No 'Category' tag in 'config.xml'"); Version = xmlDocument.GetElementsByTagName("Version").Item(0)?.InnerText ?? throw new FileFormatException("No 'Version' tag in 'config.xml'"); RequiredLauncher = xmlDocument.GetElementsByTagName("RequiredLauncher").Item(0)?.InnerText ?? throw new FileFormatException("No 'RequiredLauncher' tag in 'config.xml'"); RequiredGame = xmlDocument.GetElementsByTagName("RequiredGame").Item(0)?.InnerText ?? throw new FileFormatException("No 'RequiredGame' tag in 'config.xml'"); } private void ParseAuthorsFromXml(XmlDocument xmlDocument) { XmlNodeList authors = xmlDocument.GetElementsByTagName("Author"); foreach (XmlNode author in authors) { XmlAttribute? nameAttr = author.Attributes?["name"]; if (nameAttr == null) throw new FileFormatException("No 'name' attribute in 'Author' tag in 'config.xml'"); Authors.Add(nameAttr.InnerText); } } private void ParseCreditsFromXml(XmlDocument xmlDocument) { XmlNodeList credits = xmlDocument.GetElementsByTagName("Credits"); foreach (XmlNode credit in credits) { List entries = new List(); XmlAttribute? groupAttr = credit.Attributes?["group"]; if (groupAttr == null) throw new FileFormatException("No 'group' attribute in 'Credits' tag in 'config.xml'"); foreach (XmlNode entry in credit) { XmlAttribute? nameAttr = entry.Attributes?["name"]; if (nameAttr == null) throw new FileFormatException("No 'name' attribute in 'Author' tag in 'config.xml'"); entries.Add(nameAttr.InnerText); } Credits.Add(groupAttr.InnerText, entries); } } } }