public BootGodDb(string basePath) { // notes: there can be multiple each of prg,chr,wram,vram // we aren't tracking the individual hashes yet. string xmlPath = Path.Combine(basePath, "NesCarts.xml"); string x7zPath = Path.Combine(basePath, "NesCarts.7z"); bool loadXml = File.Exists(xmlPath); using var nesCartFile = new HawkFile(loadXml ? xmlPath : x7zPath); if (!loadXml) { nesCartFile.BindFirst(); } var stream = nesCartFile.GetStream(); // in anticipation of any slowness annoying people, and just for shits and giggles, i made a super fast parser int state = 0; var xmlReader = XmlReader.Create(stream); CartInfo currCart = null; string currName = null; while (xmlReader.Read()) { switch (state) { case 0: if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "game") { currName = xmlReader.GetAttribute("name"); state = 1; } break; case 2: if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "board") { currCart.BoardType = xmlReader.GetAttribute("type"); currCart.Pcb = xmlReader.GetAttribute("pcb"); int mapper = int.Parse(xmlReader.GetAttribute("mapper")); if (validate && mapper > 255) { throw new Exception("didnt expect mapper>255!"); } // we don't actually use this value at all; only the board name state = 3; } break; case 3: if (xmlReader.NodeType == XmlNodeType.Element) { switch (xmlReader.Name) { case "prg": currCart.PrgSize += (short)ParseSize(xmlReader.GetAttribute("size")); break; case "chr": currCart.ChrSize += (short)ParseSize(xmlReader.GetAttribute("size")); break; case "vram": currCart.VramSize += (short)ParseSize(xmlReader.GetAttribute("size")); break; case "wram": currCart.WramSize += (short)ParseSize(xmlReader.GetAttribute("size")); if (xmlReader.GetAttribute("battery") != null) { currCart.WramBattery = true; } break; case "pad": currCart.PadH = byte.Parse(xmlReader.GetAttribute("h")); currCart.PadV = byte.Parse(xmlReader.GetAttribute("v")); break; case "chip": currCart.Chips.Add(xmlReader.GetAttribute("type")); if (xmlReader.GetAttribute("battery") != null) { currCart.WramBattery = true; } break; } } else if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "board") { state = 4; } break; case 4: if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "cartridge") { _sha1Table[currCart.Sha1].Add(currCart); currCart = null; state = 5; } break; case 5: case 1: if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "cartridge") { currCart = new CartInfo(); currCart.System = xmlReader.GetAttribute("system"); currCart.Sha1 = "sha1:" + xmlReader.GetAttribute("sha1"); currCart.Name = currName; state = 2; } if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "game") { currName = null; state = 0; } break; } } //end xmlreader loop }