• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

xGal's slightly better C# Tutorial (Make your own hacking tools)

xGal

Mhm
241
Posts
12
Years
  • OK. If you're not familiar with C#, you should study it a bit before trying to understand this. Obviously, you will understand what I say because my teaching level is over 9000 but it'll be quite hard for you to make a good program with it, so yeh. Google is your friend here.

    I made some functions for you people. The basic functions, writeHex and readHex were made to be in a school project I did but I said meh let's do some more functions and release it here, and also making a better tutorial than my old one.

    The functions can be found in the attached files.

    Without further ado, let's go!

    Requirements:

    • Basic C# Knoledge
    • A C# Compiler (I use Visual Studio 2013)
    • My C# Functions (They're in the attachments)

    Before I start explaining how to make the code, I will first explain about every function I did:

    WriteHex -> Writes hexadecimal data into a location at the ROM. Can write only one or two bytes.

    ReadHex -> Reads hexadecimal data from the ROM, returns a string with the bytes read.

    GetGameCode -> Reads the two bytes at 0xAC (default game code location for .GBA Pokémon games), converts it to UTF8 and returns a string with the game code.

    IpsPatch -> Patches an .IPS file into a ROM.

    WriteHexArray -> Writes hexadecimal data into a location at the ROM. Can write more than one or two bytes; depending on the array length.


    Here's the usage manual and examples:
    Spoiler:

    Starting the program!
    OK. I am going to show you how to make a program that enables/disable the flashback that happens when you load a save file in FireRed.

    That is for Visual C#. Console-ish C# is ugly imo, therefore I won't cover it in this tutorial.

    Start by making 3 buttons. Name one button "btnOpen", name another "btnEnable" and name another "btnDisable". Now, make a OpenFileDialog and name it openFileDialog1 (the default name).

    Go to the code area. Where all the "using [X]" are, paste:
    Code:
     using Gal;
    Now, you are officially using me (lol jk it's just a reference to the namespace of my class).

    Look for:
    Code:
            public [yourprojectname]()
            {
                InitializeComponent();
            }

    After that code, paste:

    Code:
            GBAFunctions gf = new GBAFunctions(); //References to the class I made.
            byte[] EnableFlashback = { 0x00, 0x28, 0x0F, 0xD0 }; //Creates a new byte array, that's holding the data to enable the flashback.
            byte[] DisableFlashback = { 0x00, 0x1C, 0x0F, 0xE0 }; //Creates a new byte array, that's holding the data to disable the flackback.

    Go back on the desinger and double click on btnOpen and paste in the code zone:

    Code:
    if (openFileDialog1.ShowDialog() == DialogResult.OK) //Opens a file choosing dialog and checks if a file's been chosen.
                {
    
                    string gameCode = gf.GetGameCode(openFileDialog1.FileName); //Creates a new string that'll hold the game code.
    
                    if (gameCode != "BPRE") //If the game code isn't "BPRE", English FireRed's game code.
                    {
                        MessageBox.Show("Sorry. Only FireRed Support.", "Sorry");
                        openFileDialog1.FileName = ""; //Resets the file the open file dialog's holding.
                    }
                    else {
                        MessageBox.Show("FireRed Opened!", "Success!");
                    
                    }
    
                }
    
                else
                {
                     //Whatever you want it to do if the user didn't choose a file.
                }

    You made the open file button! Feel free to test it and change the text of the button.

    Go back to the designer and double click btnEnable and paste in the code zone:
    Code:
           gf.WriteHexArray(EnableFlashback, "110F54", openFileDialog1.FileName); //Writes the EnableFlashback array we created before into the offset 0x110F54, where the bytes determine if the flashback system is enabled or not are located.

    You made the enable flashback button! Again, feel free to test it and change the text of the button.

    Again, go back to the designer and double click btnDiasble and paste in the code zone:

    Code:
           gf.WriteHexArray(DisableFlashBack, "110F54", openFileDialog1.FileName); //Writes the DisableFlashBack array we created before into the offset 0x110F54, where the bytes determine if the flashback system is enabled or not are located.
    Nice nice. You finished coding your program haha. That was quick... Now design it... Make your own unique design that'll attract users. If you don't do it... why would they choose to use your program and not another one's program?

    Optional: If you want to avoid error that nobody really understands, you may make a 'try' thingy before any function call. For example:

    Code:
    try{
    gf.ReadHex(2,"800000",openFileDialog1.FileName,false);
    }
    catch (Exception ex){
    MessageBox.Show("Something went wrong with the ReadHex Command","damn"); //Alternatively, you could use MessageBox.Show(ex.ToString(),"damn");
    
    }

    Credits (no particular order)
    • Hopeless Masquerade - Helping me a lot with C# and with the ReadHex function!
    • SMWiki - IPS patching code. Wasn't written by me!
    • Microsoft - The awesome Visual Studio program and C#!
    If I forgot someone, please let me know!

    The functions should work fine. If you find an error or more, please contact me so I could fix it ASAP. Also, if you find a mistake in the tutorial itself, contact me so I could fix it ASAP. Sorry for not providing pictures in the tutorial. I am a really lazy person. If people find this tutorial REALLY hard to understand, I will provide pictures. I am writing this when I am really tired, so you will probably find something wrong with this tutorial.
     

    Attachments

    • Gal'sFuncs.zip
      2.1 KB · Views: 38
    Last edited:

    Danny0317

    Fluorite's back, brah
    1,067
    Posts
    10
    Years
    • Age 24
    • Seen Nov 19, 2023
    I've made a pretty cool one (on phone, will exain tomorrow), can I go ahead and post it here?
     
    91
    Posts
    14
    Years
    • Seen Feb 22, 2023
    I don't want to offend anyone but it does not seem like a well thought-through implementation to use strings in order to specify addresses instead of, well, unsigned integers. You are using objects to define methods that could be static all-through since they are not connected to the instance as far as I can see. As you are not saving the rom in the memory, if you want to execute more than one write operation you would have constant file access which is pretty slow.

    Most of what your library offers can also be accomplished using simple streams and a binary reader / writer, I don't see the necesity to wrap those. (Except maybe the IPS functionality which is a bit more complicated)

    As said, no offence to anyone, but I think using this library does not enhance your project. The data conversion and file access slows down the execution time.

    ~SBird
     
    3,830
    Posts
    14
    Years
    • Age 27
    • OH
    • Seen Feb 26, 2024
    Frankly the functions you've provided are pointless when the default IO library for .Net does exactly this and more.

    What you've written can be done much more easily with existing classes called BinaryReader and BinaryWriter.

    Code:
    // First, be sure to include "System.IO" at the to with the other included namespaces.
    
    // This code goes within the function called by the button or whatever
    using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(openFileDialog1.FileName)))
    {
        bw.BaseStream.Seek(0x110F54, SeekOrigin.Begin);
        bw.Write(EnableFlashback);
    }

    Code:
    // To get the game code
    // Encodings may need a reference to System.Text (iirc)
    using (BinaryReader br = new BinaryReader(File.OpenRead(openFileDialog1.FileName)))
    {
        br.BaseStream.Seek(0xAC, SeekOrigin.Begin);
        string gameCode = Encoding.UTF8.GetString(br.ReadBytes(4));
    }

    Much better looking, is it not?

    EDIT:

    While I'm thinking about it, here's some basic code to apply an .ips patch.

    Code:
    static void ApplyIPSPatch(string patchFile, string romFile)
    {
        // First, read the file data for the patch and the ROM
        byte[] patchData = File.ReadAllBytes(patchFile);
        byte[] romData = File.ReadAllBytes(romFile);
        
        // Now check the patch for the Header and EOF
        if (Encoding.UTF8.GetString(patchData, 0, 5) != "PATCH") throw new Exception("Invalid patch header!");
        if (Encoding.UTF8.GetString(patchData, patchData.Length - 3, 3) != "EOF") throw new Exception("Invalid patch footer!");
        
        // Since the header/footer checked out, apply the patch data
        int i = 5;
        while (i < patchData.Length - 3)
        {
            // So IPS patches store changes in "packets"
            // Each packet starts with a 24 bit offset (max value of 0xFFFFFF which is why IPS doesn't work with expanded ROMs)
            // By default, BinaryReaders and BinaryWriters cannot work with 24 bit integers which is why arrays are being used here.
            int offset = GetInt24(patchData, i);
            i += 3;
            
            // Next in the packet is the size of the data to copy from the patch to the ROM
            // This is a 16 bit integer
            int size = GetInt16(patchData, i);
            i += 2;
            
            // However, if the size is zero, we have an "[URL="https://en.wikipedia.org/wiki/Run-length_encoding"]RLE[/URL]" packet
            // Otherwise "size" data is copied from the patch to the ROM
            if (size == 0)
            {
                // A second size is provided for an RLE packet
                int rleSize = GetInt16(patchData, i);
                i += 2;
                
                byte repeat = patchData[i];
                i++;
                
                for (int j = 0; j < rleSize; j++)
                {
                    romData[j + offset] = repeat;
                }
            }
            else
            {
                for (int j = 0; j < size; j++)
                {
                    romData[j + offset] = patchData[i];
                    i++;
                }
            }
        }
    
        // Finally, write the changes made to the ROM file
        File.WriteAllBytes(romFile, romData);
    }
    
    // IPS patch data is provided in big endian (which BinaryReader/Writers also cannot handle by default)
    // So these functions help convert arrays of bytes into integers
    static int GetInt24(byte[] buffer, int pos)
    {
        return buffer[pos] | (buffer[pos + 1] << 8) | (buffer[pos + 2] << 16);
    }
    
    static int GetInt16(byte[] buffer, int pos)
    {
        return buffer[pos] | (buffer[pos + 1] << 8);
    }

    That should about cover it, although I didn't actually test it or anything. I figured it would be nice to share the general idea though.
     
    Last edited:

    xGal

    Mhm
    241
    Posts
    12
    Years
  • I don't want to offend anyone but it does not seem like a well thought-through implementation to use strings in order to specify addresses instead of, well, unsigned integers. You are using objects to define methods that could be static all-through since they are not connected to the instance as far as I can see. As you are not saving the rom in the memory, if you want to execute more than one write operation you would have constant file access which is pretty slow.

    Most of what your library offers can also be accomplished using simple streams and a binary reader / writer, I don't see the necesity to wrap those. (Except maybe the IPS functionality which is a bit more complicated)

    As said, no offence to anyone, but I think using this library does not enhance your project. The data conversion and file access slows down the execution time.

    ~SBird

    Frankly the functions you've provided are pointless when the default IO library for .Net does exactly this and more.

    What you've written can be done much more easily with existing classes called BinaryReader and BinaryWriter.

    Code:
    // First, be sure to include "System.IO" at the to with the other included namespaces.
    
    // This code goes within the function called by the button or whatever
    using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(openFileDialog1.FileName)))
    {
        bw.BaseStream.Seek(0x110F54, SeekOrigin.Begin);
        bw.Write(EnableFlashback);
    }

    Code:
    // To get the game code
    // Encodings may need a reference to System.Text (iirc)
    using (BinaryReader br = new BinaryReader(File.OpenRead(openFileDialog1.FileName)))
    {
        br.BaseStream.Seek(0xAC, SeekOrigin.Begin);
        string gameCode = Encoding.UTF8.GetString(br.ReadBytes(4));
    }

    Much better looking, is it not?

    EDIT:

    While I'm thinking about it, here's some basic code to apply an .ips patch.

    Code:
    static void ApplyIPSPatch(string patchFile, string romFile)
    {
        // First, read the file data for the patch and the ROM
        byte[] patchData = File.ReadAllBytes(patchFile);
        byte[] romData = File.ReadAllBytes(romFile);
        
        // Now check the patch for the Header and EOF
        if (Encoding.UTF8.GetString(patchData, 0, 5) != "PATCH") throw new Exception("Invalid patch header!");
        if (Encoding.UTF8.GetString(patchData, patchData.Length - 3, 3) != "EOF") throw new Exception("Invalid patch footer!");
        
        // Since the header/footer checked out, apply the patch data
        int i = 5;
        while (i < patchData.Length - 3)
        {
            // So IPS patches store changes in "packets"
            // Each packet starts with a 24 bit offset (max value of 0xFFFFFF which is why IPS doesn't work with expanded ROMs)
            // By default, BinaryReaders and BinaryWriters cannot work with 24 bit integers which is why arrays are being used here.
            int offset = GetInt24(patchData, i);
            i += 3;
            
            // Next in the packet is the size of the data to copy from the patch to the ROM
            // This is a 16 bit integer
            int size = GetInt16(patchData, i);
            i += 2;
            
            // However, if the size is zero, we have an "[URL="https://en.wikipedia.org/wiki/Run-length_encoding"]RLE[/URL]" packet
            // Otherwise "size" data is copied from the patch to the ROM
            if (size == 0)
            {
                // A second size is provided for an RLE packet
                int rleSize = GetInt16(patchData, i);
                i += 2;
                
                byte repeat = patchData[i];
                i++;
                
                for (int j = 0; j < rleSize; j++)
                {
                    romData[j + offset] = repeat;
                }
            }
            else
            {
                for (int j = 0; j < size; j++)
                {
                    romData[j + offset] = patchData[i];
                    i++;
                }
            }
        }
    
        // Finally, write the changes made to the ROM file
        File.WriteAllBytes(romFile, romData);
    }
    
    // IPS patch data is provided in big endian (which BinaryReader/Writers also cannot handle by default)
    // So these functions help convert arrays of bytes into integers
    static int GetInt24(byte[] buffer, int pos)
    {
        return buffer[pos] | (buffer[pos + 1] << 8) | (buffer[pos + 2] << 16);
    }
    
    static int GetInt16(byte[] buffer, int pos)
    {
        return buffer[pos] | (buffer[pos + 1] << 8);
    }

    That should about cover it, although I didn't actually test it or anything. I figured it would be nice to share the general idea though.

    Thank y'all for the comments. Yes, using the normal .NET functions is fine but I made this thread so people that aren't so-good in C# could make tools too. As a beginner, I had a really hard time understanding the BinaryWriter and the BinaryReader commands, however, Hopelees Masquerade was there to help me. I am pretty sure that you don't have the time and the energy to help everyone as much as you helped me (you helped me a lot, didn't you? haha) so these functions can be used by beginners. Obviously, the beginners using these functions could always hop to the source code after they have more hacking experience and after a while, even making a tool that we all could use for hacking (if you are still hacking, ofcourse). Also, why should you write 4~5 lines when you can write 1 line? C:
     

    Danny0317

    Fluorite's back, brah
    1,067
    Posts
    10
    Years
    • Age 24
    • Seen Nov 19, 2023
    Here's a toPointer method. I made this to erase a **** load of stuff from the XML. What this does is create a pointer from a given offset. For example, if you repoint the, let's say, names list offset, you wouldn't have to change anything in the XML or ini because this checks the pointer to the pointer. Feel free to tell me if there's bugs.

    Code:
     private long toPointer(long pointer)
            {
                byte[] temp = new byte[4];
                long longPointer = 0;
                br = new BinaryReader(File.OpenRead(ofd.FileName));
                br.BaseStream.Seek(pointer, SeekOrigin.Begin);
                temp = br.ReadBytes(4);
                String[] temporary = new String[4];
                for(int i = 0; i < 4; i++)
                {
                    temporary[i] = temp[i] + "";
                    temporary[i] = Convert.ToInt32(temporary[i]).ToString("X");
                    if (temporary[i].Length == 1)
                    {
                        temporary[i] = "0" + temporary[i];
                    }
                    else if (temporary[i].Length == 0)
                    {
                        temporary[i] = "00";
                    }
                }
                String tempy = temporary[2] + "" + temporary[1] + "" + temporary[0];
                longPointer = Convert.ToInt64(tempy, 16);
                if(temp[3] > 8)
                {
                    longPointer += 0x1000000;
                }
                br.Close();
                return longPointer;
            }
     
    64
    Posts
    10
    Years
    • Seen Mar 30, 2016
    A conversion like this eats up performance because you are using many string operations. This is much easier:

    public void WritePointer(uint offset)
    {
    // bw is your binarywriter in a global scope
    bw.Write(BitConverter.GetBytes(offset + 0x08000000));
    }

    This code basically adds 0x8000000 to the offset => 0x 08 AB CD EF.
    BitConverter.GetBytes(); reverses this 32-bit value to EF CD AB 08 and stores it as a representation of bytes (Byte[] { EF, CD, AB, 08 }) which you can write to ROM via bw.Write(Byte[] bytes) overload.
     

    Danny0317

    Fluorite's back, brah
    1,067
    Posts
    10
    Years
    • Age 24
    • Seen Nov 19, 2023
    A conversion like this eats up performance because you are using many string operations. This is much easier:

    public void WritePointer(uint offset)
    {
    // bw is your binarywriter in a global scope
    bw.Write(BitConverter.GetBytes(offset + 0x08000000));
    }

    This code basically adds 0x8000000 to the offset => 0x 08 AB CD EF.
    BitConverter.GetBytes(); reverses this 32-bit value to EF CD AB 08 and stores it as a representation of bytes (Byte[] { EF, CD, AB, 08 }) which you can write to ROM via bw.Write(Byte[] bytes) overload.

    Hmm, interesting. Pretty cool, and definitely much more efficient. Thank you.
     
    3,830
    Posts
    14
    Years
    • Age 27
    • OH
    • Seen Feb 26, 2024
    Why even include the BitConverter stuff? You can just write it as an integer for the same effect, as BinaryWriters and Readers already work in little endian.
    Code:
    // ...
    bw.Write(offset + 0x08000000);
    // ...
     
    64
    Posts
    10
    Years
    • Seen Mar 30, 2016
    I would not rely on the conversion performance-wise. Streaming is terribly slow on MONO, as cross-platform ability has to be assured. But thanks, I didn't know you could just pass any integral type to this function. :-)
     
    3,830
    Posts
    14
    Years
    • Age 27
    • OH
    • Seen Feb 26, 2024
    I would not rely on the conversion performance-wise. Streaming is terribly slow on MONO, as cross-platform ability has to be assured. But thanks, I didn't know you could just pass any integral type to this function. :-)

    You can do more than that! It even accepts a number of other types, including Booleans and Strings. Any Read method that the BinaryReader implements is provided as an overloaded Write function in a BinaryWriter.

    https://msdn.microsoft.com/en-us/library/system.io.binaryreader_methods(v=vs.110).aspx
    https://msdn.microsoft.com/en-us/library/system.io.binarywriter_methods(v=vs.110).aspx
     

    Joexv

    ManMadeOfGouda joexv.github.io
    1,037
    Posts
    11
    Years
  • Here are a few basic functions I wrote a while back. They might be messy and easily simplified but oh well:

    Reads bytes at desired location(meant for use with offsets) and converts them to a string.
    Displays as such: 0x123456
    Code:
            public string DisplayOffset1(long Offset1, string FileLocation, string result)
            {
                BinaryReader br = new BinaryReader(File.OpenRead(FileLocation));
                br.BaseStream.Seek(Offset1, SeekOrigin.Begin);
                byte[] vIn = br.ReadBytes(8);
                int vOut = BitConverter.ToInt32(vIn, 0);
                int vOut2 = vOut - 0x8000000;
                result = vOut2.ToString("x7");
                return result;
            }

    Displays the ending header(used by G3HS)
    Code:
            public string DisplayEndingHeader(string FileLocation, string result)
            {
                BinaryReader br = new BinaryReader(File.OpenRead(FileLocation));
                br.BaseStream.Seek(0xfffffe, SeekOrigin.Begin);
                result = Encoding.UTF8.GetString(br.ReadBytes(4));
                return result;
            }
     
    Back
    Top