Created
December 28, 2020 07:00
-
-
Save SixBeeps/c1745fb7a2c4f5169db7bf2424a9214a to your computer and use it in GitHub Desktop.
NES ROM Graphics Injector
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.IO; | |
| using System.Linq; | |
| namespace NESMixer { | |
| class Program { | |
| public static Random rng = new Random(); | |
| static void Main(string[] args) { | |
| if (args.Length == 2) { | |
| ROM rom = new ROM(args[0]); | |
| rom.chrRom = rom.chrRom.OrderByDescending(b => (int)b).ToArray(); | |
| rom.SaveToFile(args[1]); | |
| } else if (args.Length == 3) { | |
| ROM r1 = new ROM(args[0]); | |
| ROM r2 = new ROM(args[1]); | |
| r1.chrRom = r2.chrRom; | |
| r1.chrRomLength = r2.chrRomLength; | |
| r1.SaveToFile(args[2]); | |
| } else { | |
| Console.WriteLine("Please use 2 or 3 arguments as the paths to NES Roms"); | |
| return; | |
| } | |
| Console.WriteLine("Done!"); | |
| } | |
| } | |
| public class ROM { | |
| public long prgRomLength, chrRomLength; | |
| public byte[] prgRom, chrRom, flags, trainer, extraData; | |
| public const int PRG_BLOCK_SIZE = 16384; // PRG block size in bytes | |
| public const int CHR_BLOCK_SIZE = 8192; // Same thing but for CHR | |
| private static byte[] validSignature = { 0x4e, 0x45, 0x53, 0x1a }; | |
| public ROM(string filePath) : this(File.Open(filePath, FileMode.Open)) { } | |
| public ROM(FileStream fs) { | |
| if (!fs.CanRead) throw new IOException("Given FileStream cannot be read"); | |
| // Check if file has magic bytes | |
| byte[] signature = new byte[4]; | |
| fs.Read(signature, 0, 4); | |
| if (!signature.SequenceEqual(validSignature)) throw new FormatException($"Given file does not appear to be in iNES format. Magic bytes are {BitConverter.ToString(validSignature)}, got {BitConverter.ToString(signature)}"); | |
| // Calculate how much memory to allocate for PRG ROM and CHR ROM | |
| prgRomLength = fs.ReadByte() * PRG_BLOCK_SIZE; | |
| chrRomLength = fs.ReadByte() * CHR_BLOCK_SIZE; | |
| prgRom = new byte[prgRomLength]; | |
| chrRom = new byte[chrRomLength]; | |
| // Flags from ROM | |
| flags = new byte[5]; | |
| fs.Read(flags, 0, 5); | |
| fs.Seek(5, SeekOrigin.Current); // Should be zero-padded so we can ignore | |
| // Write to trainer if it exists | |
| if (GetBit(flags[0], 2)) { | |
| trainer = new byte[512]; | |
| fs.Read(trainer, 0, 512); | |
| } | |
| // Write the PRG and CHR data | |
| fs.Read(prgRom, 0, (int)prgRomLength); | |
| fs.Read(chrRom, 0, (int)chrRomLength); | |
| // Get any remaining data | |
| int exLength = (int)(fs.Length - fs.Position); | |
| if (exLength > 0) { | |
| extraData = new byte[exLength]; | |
| fs.Read(extraData, 0, exLength); | |
| Console.WriteLine($"There were {exLength} bytes of information left"); | |
| } else { | |
| Console.WriteLine($"No extra info to parse"); | |
| } | |
| } | |
| public void SaveToFile(string filePath) { | |
| using (FileStream fs = File.Create(filePath)) { | |
| fs.Write(validSignature); | |
| fs.WriteByte((byte)(prgRomLength / PRG_BLOCK_SIZE)); | |
| fs.WriteByte((byte)(chrRomLength / CHR_BLOCK_SIZE)); | |
| fs.Write(flags); | |
| for (int i = 0; i < 5; i++) | |
| fs.WriteByte(0x00); | |
| if (trainer != null) | |
| fs.Write(trainer); | |
| fs.Write(prgRom); | |
| fs.Write(chrRom); | |
| } | |
| } | |
| public long TotalSize() { | |
| return prgRomLength + chrRomLength; | |
| } | |
| private static bool GetBit(byte b, int k) { | |
| return (b & (1 << (k - 1))) > 0; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment