From c69fe813667edb0c3161c00ec3adfddaced2e375 Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Tue, 10 Oct 2017 10:17:00 +0300 Subject: Added GameGenie for SNES but it glitchy and commented out --- GameGenie/GameGenieDataBase.cs | 255 ++++++++++++++++++++++++++++++++ GameGenie/GameGenieFormatException.cs | 17 +++ GameGenie/GameGenieNotFoundException.cs | 17 +++ GameGenie/GameGeniePatcherNes.cs | 103 +++++++++++++ GameGenie/GameGeniePatcherSnes.cs | 72 +++++++++ 5 files changed, 464 insertions(+) create mode 100644 GameGenie/GameGenieDataBase.cs create mode 100644 GameGenie/GameGenieFormatException.cs create mode 100644 GameGenie/GameGenieNotFoundException.cs create mode 100644 GameGenie/GameGeniePatcherNes.cs create mode 100644 GameGenie/GameGeniePatcherSnes.cs (limited to 'GameGenie') diff --git a/GameGenie/GameGenieDataBase.cs b/GameGenie/GameGenieDataBase.cs new file mode 100644 index 00000000..7531807b --- /dev/null +++ b/GameGenie/GameGenieDataBase.cs @@ -0,0 +1,255 @@ +using com.clusterrr.Famicom; +using com.clusterrr.hakchi_gui.Properties; +using System.Collections.Generic; +using System.IO; +using System.Windows.Forms; +using System.Xml; + +namespace com.clusterrr.hakchi_gui +{ + class GameGenieCode + { + public delegate void ChangedEvent(GameGenieCode e); + public event ChangedEvent Changed; + + private string FCode = ""; + private string FDescription = ""; + private string FOldCode; + private string FOldDescription; + + public string Code + { + get { return FCode; } + set + { + if (FCode != value) + { + FOldCode = FCode; + FCode = value; + if ((FOldCode != "") && (Changed != null)) + Changed(this); + } + } + } + public string Description + { + get { return FDescription; } + set + { + if (FDescription != value) + { + FOldDescription = FDescription; + FDescription = value; + if ((FOldDescription != "") && (Changed != null)) + Changed(this); + } + } + } + public string OldCode { get { return FOldCode; } } + public string OldDescription { get { return FOldDescription; } } + + public override string ToString() + { + return Description; + } + + public GameGenieCode(string ACode, string ADescription) + { + Code = ACode; + Description = ADescription; + } + } + + class GameGenieDataBase + { + private XmlDocument FXml = new XmlDocument(); + private XmlNode FGameNode = null; + private List FGameCodes = null; + private string originalDatabasePath = Path.Combine(Path.Combine(Program.BaseDirectoryInternal, "data"), "GameGenieDB.xml"); + private string userDatabasePath = Path.Combine(Path.Combine(Program.BaseDirectoryExternal, ConfigIni.ConfigDir), "GameGenieDB.xml"); + private NesMiniApplication FGame = null; + private bool FModified = false; + + private XmlNode GameNode + { + get + { + if (FGameNode == null) + { + FGameNode = FXml.SelectSingleNode(string.Format("/database/game[@code='{0}']", FGame.Code)); + + if (FGameNode == null) + { + string lGamesDir = Path.Combine(Program.BaseDirectoryExternal, "games"); + NesFile lGame = new NesFile(Path.Combine(Path.Combine(lGamesDir, FGame.Code), FGame.Code + ".nes")); + XmlAttribute lXmlAttribute; + + FGameNode = FXml.CreateElement("game"); + FXml.DocumentElement.AppendChild(FGameNode); + + lXmlAttribute = FXml.CreateAttribute("code"); + lXmlAttribute.Value = FGame.Code; + FGameNode.Attributes.Append(lXmlAttribute); + + lXmlAttribute = FXml.CreateAttribute("name"); + lXmlAttribute.Value = FGame.Name; + FGameNode.Attributes.Append(lXmlAttribute); + + lXmlAttribute = FXml.CreateAttribute("crc"); + lXmlAttribute.Value = lGame.CRC32.ToString("X"); + FGameNode.Attributes.Append(lXmlAttribute); + } + } + return FGameNode; + } + } + + public List GameCodes + { + get + { + if (FGameCodes == null) + { + FGameCodes = new List(); + XmlNodeList lCodes = FXml.SelectNodes(string.Format("/database/game[@code='{0}']//gamegenie", FGame.Code)); + foreach (XmlNode lCurNode in lCodes) + { + GameGenieCode lCurCode = new GameGenieCode(lCurNode.Attributes.GetNamedItem("code").Value, lCurNode.Attributes.GetNamedItem("description").Value); + FGameCodes.Add(lCurCode); + } + } + return FGameCodes; + } + } + + public bool Modified + { + get + { + return FModified; + } + + } + + public GameGenieDataBase(NesMiniApplication AGame) + { + //DataBasePath = Path.Combine(Path.Combine(Program.BaseDirectoryInternal, "data"), "GameGenieDB.xml"); + FGame = AGame; + //FDBName = DataBasePath; + if (File.Exists(userDatabasePath)) + FXml.Load(userDatabasePath); + else if (File.Exists(originalDatabasePath)) + FXml.Load(originalDatabasePath); + else + FXml.AppendChild(FXml.CreateElement("database")); + } + + public void AddCode(GameGenieCode ACode) + { + FModified = true; + + XmlNode lCodeNode = FXml.CreateElement("gamegenie"); + GameNode.AppendChild(lCodeNode); + + XmlAttribute lAttribute; + + lAttribute = FXml.CreateAttribute("code"); + lAttribute.Value = ACode.Code.ToUpper().Trim(); + lCodeNode.Attributes.Append(lAttribute); + + lAttribute = FXml.CreateAttribute("description"); + lAttribute.Value = ACode.Description; + lCodeNode.Attributes.Append(lAttribute); + + if (FGameCodes == null) + FGameCodes = new List(); + + FGameCodes.Add(ACode); + } + + public void ModifyCode(GameGenieCode ACode) + { + XmlNode lCurCode = GameNode.SelectSingleNode(string.Format("gamegenie[@code='{0}']", ACode.OldCode.ToUpper().Trim())); + if (lCurCode != null) + { + lCurCode.Attributes.GetNamedItem("code").Value = ACode.Code.ToUpper().Trim(); + lCurCode.Attributes.GetNamedItem("description").Value = ACode.Description; + FModified = true; + } + } + + public void DeleteCode(GameGenieCode ACode) + { + XmlNode lCurCode = GameNode.SelectSingleNode(string.Format("gamegenie[@code='{0}']", ACode.Code.ToUpper().Trim())); + if (lCurCode != null) + lCurCode.ParentNode.RemoveChild(lCurCode); + FGameCodes.Remove(ACode); + FModified = true; + } + + public void ImportCodes(string AFileName, bool AQuiet = false) + { + if (File.Exists(AFileName)) + { + XmlDocument lXml = new XmlDocument(); + XmlNodeList lCodes = null; + XmlNode lCodeNode = null; + XmlAttribute lAttribute = null; + + lXml.Load(AFileName); + lCodes = lXml.SelectNodes("//genie/.."); + + FModified = true; + + XmlNode lDeleteNode = GameNode.FirstChild; + while (lDeleteNode != null) + { + GameNode.RemoveChild(GameNode.FirstChild); + lDeleteNode = GameNode.FirstChild; + } + GameCodes.Clear(); + + string lGameFileName = Path.Combine(Path.Combine(Path.Combine(Program.BaseDirectoryExternal, "games"), FGame.Code), FGame.Code + ".nes"); + foreach (XmlNode lCurCode in lCodes) + { + NesFile lGame = new NesFile(lGameFileName); + try + { + lGame.PRG = GameGeniePatcherNes.Patch(lGame.PRG, lCurCode["genie"].InnerText); + + lCodeNode = FXml.CreateElement("gamegenie"); + GameNode.AppendChild(lCodeNode); + + lAttribute = FXml.CreateAttribute("code"); + lAttribute.Value = lCurCode["genie"].InnerText.ToUpper().Trim(); + lCodeNode.Attributes.Append(lAttribute); + + lAttribute = FXml.CreateAttribute("description"); + lAttribute.Value = lCurCode["description"].InnerText; + lCodeNode.Attributes.Append(lAttribute); + + GameCodes.Add(new GameGenieCode(lCurCode["genie"].InnerText.ToUpper().Trim(), lCurCode["description"].InnerText)); + } + catch (GameGenieFormatException) + { + if (!AQuiet) + MessageBox.Show(string.Format(Resources.GameGenieFormatError, lCurCode["genie"].InnerText, FGame.Name), Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + catch (GameGenieNotFoundException) + { + if (!AQuiet) + MessageBox.Show(string.Format(Resources.GameGenieNotFound, lCurCode["genie"].InnerText, FGame.Name), Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } + } + + public void Save() + { + if (GameCodes.Count == 0) + GameNode.ParentNode.RemoveChild(GameNode); + Directory.CreateDirectory(Path.GetDirectoryName(userDatabasePath)); + FXml.Save(userDatabasePath); + } + } +} diff --git a/GameGenie/GameGenieFormatException.cs b/GameGenie/GameGenieFormatException.cs new file mode 100644 index 00000000..baec65b9 --- /dev/null +++ b/GameGenie/GameGenieFormatException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.hakchi_gui +{ + public class GameGenieFormatException : Exception + { + public readonly string Code; + public GameGenieFormatException(string code) + : base(string.Format("Invalid code \"{0}\"", code)) + { + Code = code; + } + } +} diff --git a/GameGenie/GameGenieNotFoundException.cs b/GameGenie/GameGenieNotFoundException.cs new file mode 100644 index 00000000..8a879c58 --- /dev/null +++ b/GameGenie/GameGenieNotFoundException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.hakchi_gui +{ + public class GameGenieNotFoundException : Exception + { + public readonly string Code; + public GameGenieNotFoundException(string code) + : base(string.Format("Invalid code \"{0}\"", code)) + { + Code = code; + } + } +} diff --git a/GameGenie/GameGeniePatcherNes.cs b/GameGenie/GameGeniePatcherNes.cs new file mode 100644 index 00000000..4fa9dba5 --- /dev/null +++ b/GameGenie/GameGeniePatcherNes.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace com.clusterrr.hakchi_gui +{ + public static class GameGeniePatcherNes + { + public static byte[] Patch(byte[] data, string code) + { + code = code.ToUpper().Trim(); + if (string.IsNullOrEmpty(code)) return data; + var result = (byte[])data.Clone(); + + var binaryCode = new StringBuilder(code); + foreach (var l in letterValues.Keys) + binaryCode.Replace(l.ToString(), letterValues[l]); + + byte value, compare; + UInt16 address; + + if (binaryCode.Length == 24) + { + if (binaryCode[8] != '0') throw new GameGenieFormatException(code); + + try + { + value = Convert.ToByte(new string(new char[] { binaryCode[0], binaryCode[5], binaryCode[6], binaryCode[7], binaryCode[20], binaryCode[1], binaryCode[2], binaryCode[3] }), 2); + address = Convert.ToUInt16(new string(new char[] { binaryCode[13], binaryCode[14], binaryCode[15], binaryCode[16], binaryCode[21], binaryCode[22], binaryCode[23], binaryCode[4], binaryCode[9], binaryCode[10], binaryCode[11], binaryCode[12], binaryCode[17], binaryCode[18], binaryCode[19] }), 2); + } + catch + { + throw new GameGenieFormatException(code); + } + + if (result.Length <= 0x8000) + { + result[address % result.Length] = value; + } + else + { + int pos = address % 0x2000; + while (pos < result.Length) + { + result[pos] = value; + pos += 0x2000; + } + } + } + else if (binaryCode.Length == 32) + { + if (binaryCode[8] != '1') throw new GameGenieFormatException(code); + + try + { + value = Convert.ToByte(new string(new char[] { binaryCode[0], binaryCode[5], binaryCode[6], binaryCode[7], binaryCode[28], binaryCode[1], binaryCode[2], binaryCode[3] }), 2); + address = Convert.ToUInt16(new string(new char[] { binaryCode[13], binaryCode[14], binaryCode[15], binaryCode[16], binaryCode[21], binaryCode[22], binaryCode[23], binaryCode[4], binaryCode[9], binaryCode[10], binaryCode[11], binaryCode[12], binaryCode[17], binaryCode[18], binaryCode[19] }), 2); + compare = Convert.ToByte(new string(new char[] { binaryCode[24], binaryCode[29], binaryCode[30], binaryCode[31], binaryCode[20], binaryCode[25], binaryCode[26], binaryCode[27] }), 2); + } + catch + { + throw new GameGenieFormatException(code); + } + + bool replaced = false; + int pos = address % 0x2000; + while (pos < result.Length) + { + if (result[pos] == compare) + { + result[pos] = value; + replaced = true; + } + pos += 0x2000; + } + if (!replaced) throw new GameGenieNotFoundException(code); + } + else throw new GameGenieFormatException(code); + + return result; + } + + static Dictionary letterValues = new Dictionary() + { + { 'A', "0000" }, + { 'P', "0001" }, + { 'Z', "0010" }, + { 'L', "0011" }, + { 'G', "0100" }, + { 'I', "0101" }, + { 'T', "0110" }, + { 'Y', "0111" }, + { 'E', "1000" }, + { 'O', "1001" }, + { 'X', "1010" }, + { 'U', "1011" }, + { 'K', "1100" }, + { 'S', "1101" }, + { 'V', "1110" }, + { 'N', "1111" } + }; + } +} diff --git a/GameGenie/GameGeniePatcherSnes.cs b/GameGenie/GameGeniePatcherSnes.cs new file mode 100644 index 00000000..85f7c1af --- /dev/null +++ b/GameGenie/GameGeniePatcherSnes.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace com.clusterrr.hakchi_gui +{ + public static class GameGeniePatcherSnes + { + public static byte[] Patch(byte[] data, string code) + { + code = code.ToUpper().Trim(); + if (string.IsNullOrEmpty(code)) return data; + if (code.Length != 9 || code[4] != '-') + throw new GameGenieFormatException(code); + code = Regex.Replace(code, "[^0-9A-F]", ""); + if (code.Length != 8) + throw new GameGenieFormatException(code); + + byte value = Convert.ToByte(code.Substring(0, 2), 16); + code = code.Substring(2); + + var binaryCode = new StringBuilder(); + foreach (char c in code) + binaryCode.Append(letterValues[c]); + var decoded = new StringBuilder(new string(' ', binaryCode.Length)); + for (int i = 0; i < binaryCode.Length; i++) + { + var c = codeSeq[i]; + var pos = clearSeq.IndexOf(c); + decoded[pos] = binaryCode[i]; + } + + SnesGame.SnesRomType romType; + string gameTitle; + SnesGame.GetCorrectHeader(data, out romType, out gameTitle); + + UInt32 address = (Convert.ToUInt32(decoded.ToString(), 2)) & 0x3FFFFF; + if (romType == SnesGame.SnesRomType.LoRom) + address = address & 0x3FFFF; + + var result = (byte[])data.Clone(); + if (address >= result.Length) + throw new GameGenieFormatException(code); + result[address] = value; + return result; + } + + static Dictionary letterValues = new Dictionary() + { + { 'D', "0000" }, + { 'F', "0001" }, + { '4', "0010" }, + { '7', "0011" }, + { '0', "0100" }, + { '9', "0101" }, + { '1', "0110" }, + { '5', "0111" }, + { '6', "1000" }, + { 'B', "1001" }, + { 'C', "1010" }, + { '8', "1011" }, + { 'A', "1100" }, + { '2', "1101" }, + { '3', "1110" }, + { 'E', "1111" } + }; + + const string codeSeq = "ijklqrstopabcduvwxefghmn"; + const string clearSeq = "abcdefghijklmnopqrstuvwx"; + } +} -- cgit v1.2.3