Just a reminder that providing specifics on, sharing links to, or naming websites where ROMs can be accessed is against the rules. If your post has any of this information it will be removed.
Ever thought it'd be cool to have your art, writing, or challenge runs featured on PokéCommunity? Click here for info - we'd love to spotlight your work!
Our weekly protagonist poll is now up! Vote for your favorite Trading Card Game 2 protagonist in the poll by clicking here.
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.
It basically already is. In your project's folder, there should be another folder called "bin". And in there, depending on whether you've set it as a debug or release build, one of the two folders there will contain your executable file.
In Visual Studio 2013 (I have Ultimate) you can change what version you use so I recommend using 2.0 so it works on older operating systems.
Also you should mention somewhere for people to avoid using functions like cint, clang, and so on because they crash programs on Mono. (Your example doesn't seem to use them but it's a good thing to keep in mind.)
Afaik you do not have LINQ(Language integrated query) compatability from .net 2 downwards, so I would suggest compiling with .net 3.5, which at least gives you XP support.
Also, and yeah I know this is just a basic tutorial, I would like to suggest a few things:
Use configuration / "ini" files to maintain the locations, otherwise people with different roms might complain
Use an encapsulated external class to implement rom functionality, even basic things like reading/writing and probably the rom header, so you do not have to deal with static offsets inside your main code.
Label your variables, do not use alphanumerics but actually use names, it will help others understand your basic concept of coding and you will later find your variables again...^^
It's those things that are very important imo for starters to learn, just because it is somehow hard to change your coding style later on.
Rom Hackers use tools. That's a fact. However, it seems that what people do to make these tools is pretty much black magic. All these code things and it seems to take a ton of effort. But in reality, it's actually rather simple. You just have to take the time to do it. This tutorial may be really long, but it's mostly wordy and whatnot. It's not that bad, trust me. Today, I'm here to help you make a simple tool to add and remove the Flashback system from Pokémon Fire Red and Leaf Green.
Here's what you will need to get started:
A Windows computer
Microsoft Visual C# (You can use 2010 or 2013 for this, the code should work the same in both. If it doesn't, let me know.
Your Fire Red or Leaf Green rom, or your hack that's based on these games.
Getting Started - Plotting out your application
Now, you know what you want to do. However, you need to be able to code for all kinds of events. Like, what happens if the person opening the rom uses the wrong rom? There's all sorts of possibilities. So, we're going to describe the program flow so we know what we want the application to do.
So, first of all, we'll want to open the rom.
OPEN ROM
Now, we need it to decide what game is being used. To do this, we'll check the game code. Fire Red's is BPRE, and Leaf Green's is BPGE. We'll use those to determine what game is being used, because the offset for this code slightly varies depending on the game. Setting this up, we can then tell the application what offset to use.
OPEN ROM -> BPRE or BPGE?
Now, we have a problem. What if the game code is wrong? What if they changed it? We can account for this. We'll simply state that if the game code doesn't match BPRE or BPGE, we'll prompt the user as to whether they'd like to continue or not.
OPEN ROM -> BPRE or BPGE? -> I HAVE [GAME]. LET'S ROLL.
This application will be able to not only remove the Flashback system, but we'll allow it to also re-apply it in case they'd like to add it back. So, we'll do just that: Offer two buttons to apply and remove.
OPEN ROM -> BPRE or BPGE? -> I HAVE [GAME]. LET'S ROLL. -> APPLY or REMOVE?
Once they choose, we know what offsets to use because we checked the game code. But wait! We have offered the ability to load a game regardless if the code matched or not! Don't fear, we'll account for this too. And once we've patched the game, we're done. Once we have our program flow, we can then move on to actually getting coding!
OPEN ROM -> BPRE or BPGE? -> I HAVE [GAME]. LET'S ROLL. -> APPLY or REMOVE? -> PATCH GAME -> DONE!
Coding your application - Creating the Main Form
To get started, open Visual Studio, and create a new project in the File menu. It doesn't matter where you save, although keeping it in the default location (My Documents/Visual Studio [year]/Projects) is the best place, as you won't lose it easily. Be sure to name the application accordingly. When done, and you've submitted your data, you'll be presented with the following:
Spoiler:
Here's a quick rundown of your available tools: 1. Toolbox
The toolbox contains controls (Buttons, textboxes, labels, progress bars, etc etc) you can add to your form. A form is the empty window you see in the middle (Note that I've already added controls to my form). Forms carry out most of the controls essential to your application.
2. Error List
This is your best friend. If anything goes wrong with your application, it'll tell you down here. Warnings, that are shown with a yellow triangle containing an exclamation mark, are potential issues with your application (Example: Not assigning a value to a variable.). They won't halt the application dead in its tracks, but it gives you a heads-up of potential things that can happen. An error, however, highlighted with a stop sign, are problems with your application (Such as typing something that it doesn't understand, missing a semicolon, etc) that Visual Studio cannot fix, and require you to intervene. What's cool with Visual Studio is that it can detect these on the fly, and underline them with a red squiggly line to draw your attention to them, so they'll be almost impossible to miss.
3. Properties
This is only visible in the form editor, but this allows you to change various things with each piece on your forms, or the form itself (Such as assigning an icon, renaming the form, or adding text to a label.
4. Solution Explorer
We won't be using this in this tutorial, but this shows you all the files for your application. You can add more things here, such as classes, or DLL files, and allow your program to make use of those classes/DLLs and use what they have to offer (Libraries, etc).
Now that you have some information on what you're looking at, let's get started! For this particular tutorial, we'll need the following:
3 buttons (Open ROM, Apply Patch, Remove Patch)
2 labels (One for the "Loaded Game" text, and one saying "Please load a game...". We'll programmatically change that later to reflect what game is loaded. It's really cool!)
Find those in the Toolbox, and drag them onto your form. Feel free to use the layout I made, or come up with your own! We'll add new names for them by going to the Properties window. Click on one of the objects, I'll use a button as an example. Select a button, then scroll down to the bottom of the properties box and look for the "Text" property. Change that to what you want the button to say, such as "Open ROM". Do this for all three buttons, as well as your labels. If you'd like to change the name of your form from Form1 to something else, simply select the top bar of the form, and go into the properties window and find the "Text" property and change it accordingly. This lets it be yours and gives it some personality to separate it from everyone else's.
Once you've personalised it, that's all for the easy stuff. Now, we get into the nitty gritty with the coding.
Coding the core of your program
This area will be incredibly long and wordy, as I'll be explaining all the code to you. If you want to just skim through it, feel free, but you may be missing out on information you can use. The bits of code in this application can be recycled to do all sorts of other things. Don't let this tutorial limit you! You can get your application to read data from the ROM and interpret it into something that an end-user can understand. Anyways, let's get going.
Please note: This portion of the tutorial may become borked because vBulletin does not display some code correctly and parses it in a way that could result in code missing. To ensure you are not missing anything, VIEW THIS POST BY ITSELF (Click the 1 at the top-right of the post, then scroll back down here).
To get started, we'll need code for all three of our buttons. We'll set this up in advance, so double-click on one of your buttons. You'll then be sent to your code editor, which is open in a separate tab. It'll be called "Form1.cs". To go back to your form editor and click on the other buttons, click on the "Form1.cs [Design]" tab. Once you've double-clicked on your buttons, your code should be similar to this:
Spoiler:
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace FRLG_Flashback_Remover
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) //Open ROM button
{
}
private void button2_Click(object sender, EventArgs e) //Apply button
{
}
private void button3_Click(object sender, EventArgs e) //Remove button
{
}
}
}
In the code above, I've added comments, which start with two slashes ( // ). These are used for marking notes in your application. You can then refer back to your code later on, and use these comments to identify things, or read up on what something is doing. It's a good idea to get in the habit of using comments in programming, as they can save you a lot of time. For now, identify with comments which button is which. You can add names to your buttons, but for the sake of this tutorial, I'll keep it nice and easy.
Now, we'll get started. Before starting, we'll have to add a namespace to the application. Namespaces define certain things that we can add to our application. They can include data input/output operations (Which we'll be using), networking, and tons more. So, under the last "using" statement, add:
Code:
using System.IO;
This (System.IO, or, the Input/Output class) will allow us to load files into memory, read them, modify them, and save them. You can choose to ignore adding this, but it will become a real pain adding in the references for these later on, so save yourself time and do it like this. It makes it much less painful.
On the topic of Input and Output, I'll introduce to you two things: BinaryReader and BinaryWriter. These are what we'll use to actually read and write (I wonder where that came from...) data to and from the ROM. Thing is, when you set them up, you need to define where they're stored on the computer. So, we'll do two things:
1. Create a string variable to store the location of our file in memory, and
2. Create an OpenFileDialog (I'll explain this later) so we can locate our ROM and assign this location to the variable we'll make.
In order to do this, we'll make a string variable. String variables are collections of characters, such as letters, numbers, and even punctuation marks. Such examples are:
string username = "Team Fail";
string site = "The PokéCommunity Forums";
Note that they're wrapped in quotation marks. This is because we're assigning a value to the string directly. The quotation marks are required so that Visual Studio knows what the string actually is, or else it'll try to interpret it as computer code, and will give you errors. However, we will not be doing this below. We'll use a neat little shortcut to assign a string value that another part of the application will generate all by itself. However, before I get ahead of myself, you might be asking, "What is a variable?" If you're not familiar with High School math, read on.
Variables are essentially containers that hold values for us to use. There are special types of variables that do special things. Picture a variable like a box. Now, we give the box a name, so that we can refer to it later on. We also assign a data type to it. Here are a few examples, as well as what they can hold:
bool (Also known as a boolean, this can hold and return two values: TRUE or FALSE.)
string (Explained above)
int (Also known as an integer. Integers can hold whole numbers (Not decimals) from –2147483648 to 2147483647. Any longer than those two values, and Visual Studio will give you an error, or crash if an integer that exceeds that size is entered into an application.)
We can assign a value to a variable, like what we'll do with our string variable, and we can read from them as well. However, if you plan to assign something to one of these variables, note that only strings need quotation marks. ints and bools do not need them, like this:
int num1 = 42;
bool isTeamFailAwesome = true;
Variable names can be almost anything you want, but they cannot have spaces in them. Keep this in mind. Now, let's add a variable to our program.
Just before your first button's code, but outside of the public Form1() block, create a string variable with this:
Code:
string fileLocation;
It should look like so:
Spoiler:
Code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileLocation;
private void button1_Click(object sender, EventArgs e) //Open ROM button
{
}
Now, what you added is referred to as an unassigned variable. In my examples, there are = signs, which means "Take what's after me, and assign it to the name of the variable before me." However, we're not going to assign anything to it because we won't have to - at least yet - the way we make our program will do it for us.
Now, we've tackled #1 on our list, and made our place to store the location. Now, we have to tackle #2 - Opening the file, and assigning the location to our place we made. In our Open Button's code block, we'll add the following code:
Code:
OpenFileDialog ofd = new OpenFileDialog();
This will tell our application to set up a new dialog window to open a file. Now, we'll make sure that you can only open GBA files. When programming, one thing programmers will do is ensure that you can't do something stupid with your program. Basically, assume that the people using your program know nothing. Add this after the line we just added:
Code:
ofd.Filter = "GBA File (*.gba)|*.gba";
This will add a filter so that you can only select a GBA rom. You don't want to be opening a Microsoft Word document or a PNG image. Now, at this point, we have enough set up so that the user is ready to open a file, now, we can actually display the box to pick a file. However, there's a right way and a wrong way to do this. I pointed this out in xGal's tutorial, and I'll only show you the right way to do this. What we'll do is not only display the OpenFileDialog, but we'll also run a check to make sure that we've either opened up a file or cancelled. After the ofd.filter code, add this:
Code:
if (ofd.ShowDialog() == DialogResult.OK)
{
}
What we're adding is an if statement. What these do is check the conditions within its brackets, and evaluates it. We'll do an ofd.ShowDialog and show the box, and we'll check to see if the user clicked the "Open" button (Which will equate to the DialogResult.OK condition for some odd reason...) and compare that click to the DialogResult and make sure it matches (Return TRUE). If it does, then it will continue into this code block and execute whatever is in there. If it doesn't match (Return FALSE), it will skip this and either look for an else block, or simply do nothing. We won't use an else block yet, but I may use one later on. We'll see. Anyways, we'll do only a few things in here to set up the application. Firstly, we'll need to run a check to see if the game we've opened matches BPRE or BPGE. Then, decide what we want to do if it does or does not match, then assign the location on your computer. So, let's first of all, check what game we've loaded.
Go into your if statement, and add the following:
Code:
BinaryReader br = new BinaryReader(File.OpenRead(ofd.FileName));
We'll set up a way to read our file. BinaryReaders require a file stream for its parameters (The stuff in the parentheses) so we solve that by making a new stream using the File.OpenRead class. We get the location of the file from the OpenFileDialog by using ofd.FileName. This returns the absolute location of the file on your computer. It's this that we'll assign to our variable, however, we'll do this later on. And you'll see why.
For now, we'll add this after defining the BinaryWriter:
Code:
br.BaseStream.Seek(0xAC, SeekOrigin.Begin);
This line tells the program where to go in the file. The first parameter, "0xAC", is the how many bytes in you want to go. We add the "0x" in front of it so that Visual Studio will know that we're referring to the offset in hexadecimal format. We'll also use this later. The second parameter, SeekOrigin, tells the program where to go 0xAC bytes from. In this, we want to go in 0xAC bytes from the beginning of the file. At 0xAC in all GBA roms is the 4-byte game ID. We'll use this to determine what game is loaded. Now, we'll create some more variables for later use. Go back up to the string variable we made earlier, and add this:
We'll use these in our if statement to determine what is loaded. Now, let's go back and put these variables to work.
What we'll do to save time in the application, and to make it faster (Speed is key in programming!), we'll use a switch statement to determine what game we're using. Rather than try to explain it long hand, I'll show you what each portion does:
This portion will set up the switch statement. The parameters that we're defining, Encoding.UTF8.GetString(br.ReadBytes(4)), is a bit crazy, so let me break this down first.
Encoding.UTF8.GetString(byte[]) takes a byte array (What we've made using br.ReadBytes) and converts it into UTF-8-formatted text. br.ReadBytes(4) is the command to tell the program to actually read an amount of bytes (In this case, 4). We take those bytes, give them to the Encoding.UTF8.GetString, and take it's result and use it in the switch statement. Next, here's how that is used.
In the switch statement, add the following:
Code:
case "BPRE":
FireRed = true;
LeafGreen = false;
other = false;
break;
case "BPGE":
FireRed = false;
LeafGreen = true;
other = false;
break;
default:
FireRed = false;
LeafGreen = false;
other = true;
break;
This may seem really daunting, but it's not bad. Let's break it down.
Each case section is taking what the switch is comparing (Our 4 converted bytes) and checking it against each case. We then set the appropriate boolean value to our games. We set them for all three because if someone uses multiple roms on it at a time, it might not reset values properly, so do this to be safe. However, the "other" boolean is set to true in the "default" case. A default case is used as such that if none of the other cases can be met, it will "default" to the default case. Within each of the cases, there is a "break;" command. This simply breaks out of a loop (In this case, it's our switch statement that we're "break"ing out of). If we've satisfied the conditions we're out to seek, there's no need to continue checking other conditions. Now, you may notice that we need to set three booleans, yet we're only checking for two games, Fire Red and Leaf Green. The reason I added that was for in case someone loads a game that doesn't have either of those codes. We identified this as a possible issue in our Program Flow back before we started this. Let's add this exceptional case into our program.
First of all, we'll make an if statement to check if the "other" boolean was set.
Code:
if (other == true)
{
}
If it doesn't equal true (Basically, FireRed or LeafGreen is true), this statement will return false and will skip this chunk of code. Inside this block, add this code:
Code:
DialogResult result = MessageBox.Show("This game cannot be identified. If this is a Fire Red or Leaf Green-based hack, you can continue to load this. If it is not, it may cause irreversible damage. Do you wish to continue?", "Warning", MessageBoxButtons.YesNo);
This will show a message box to the user. It's a fun little thing that can cause hours of enjoyment. Do use responsibly. The first parameter is the message that we display, and the second is the messagebox's title. The third will determine what buttons we have. The button we then click on will then be passed along to the DialogResult, which we can then check. In this case, we'll only check if the user clicks no, because the way we'll design this will account for whether they've clicked yes or not.
Code:
if (result == DialogResult.No)
{
other = false;
}
We'll simply set other to false! You'll see why shortly.
But first, let's take a moment to step back and make sure our code is correct. There's a lot we've covered already, and we need to make sure our code is correct. This is what your "Open Rom" button should have:
Spoiler:
Code:
private void button1_Click(object sender, EventArgs e) //Open ROM button
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "GBA File (*.gba)|*.gba";
if (ofd.ShowDialog() == DialogResult.OK)
{
BinaryReader br = new BinaryReader(File.OpenRead(ofd.FileName));
br.BaseStream.Seek(0xAC, SeekOrigin.Begin);
switch (Encoding.UTF8.GetString(br.ReadBytes(4)))
{
case "BPRE":
FireRed = true;
LeafGreen = false;
other = false;
break;
case "BPGE":
FireRed = false;
LeafGreen = true;
other = false;
break;
default:
FireRed = false;
LeafGreen = false;
other = true;
break;
}
if (other == true)
{
DialogResult result = MessageBox.Show("This game cannot be identified. If this is a Fire Red or Leaf Green-based hack, you can continue to load this. If it is not, it may cause irreversible damage. Do you wish to continue?", "Warning", MessageBoxButtons.YesNo);
if (result == DialogResult.No)
{
other = false;
}
}
}
}
And at this point, you shouldn't have any errors or warnings. If you do, there's something wrong! Look over the code carefully, and correct any errors.
Now, let's add the last portion of code to our Open Rom button. This is all that we need to add, then we're done the Open Rom button! Yay!
Code:
if (FireRed == true)
{
label2.Text = "Fire Red BPRE";
fileLocation = ofd.FileName;
}
if (LeafGreen == true)
{
label2.Text = "Leaf Green BPGE";
fileLocation = ofd.FileName;
}
if (other == true)
{
label2.Text = "Unidentified game";
fileLocation = ofd.FileName;
}
br.Close();
Basically, we're checking if any of our games are set (or are still set, in the case of the "other" game), and setting our game label to the game name, and setting where our file is. Now, to set your label, you'll need to get the proper name of it if you've set a name for it already or not. Go back to your form designer, and click on the label that you're using for the game (That says "Please load a game..."). Click on it, then go to the properties in the bottom-right. Scroll up, and look for the "(Name)" property. The value in the box beside it is what label we'll assign our text to. Now, head back into your code editor. The code you'll be using (You may have to modify my above sample to not use label2, but your own) goes like such:
[label's name].Text = "Value";
Essentially, we're using code to set the Text value of the label! Now, you may notice that I also assigned the FileName that we used in the OpenFileDialog to our first string variable. This is because FileName returns a string value containing the location of our file. Therefore, we store it as a string so we can use it later. br.Close(); simply closes the binaryReader that we made, so that we don't get any "File is in use" errors later on. They're rather problematic and cause all sorts of grief when debugging.
Now, your Open Rom button is completely finished! Yay! But what do we do from here? Well, following our program flow, we're half way to where we want to be. But for right now, take a break. You're just learning, and you've done a lot. Take a restroom break, get a drink of water, stretch out, then come back here. I won't disappear. I promise.
Back? Awesome. We've got a bit of a ways to go still. Now, as you know, we have three current cases for games:
Fire Red
Leaf Green
Unidentified Games
We're going to code a routine for Fire Red and Leaf Green, then we'll use the Other routine to hook into these, saving code. First of all, we're going to make the code that opens and writes the code, then close the file. Then, we'll make ourselves a template so that we can just use a few if statements to check what game we're using. It's quite easy, really, if you've followed along so far.
For now, let's add some new variables. You'll see why they're needed shortly, and you'll see how they're used shortly after that.
Code:
byte[] newBytes = { 0x00, 0x1C, 0x0F, 0xE0}; //bytes to remove feature
byte[] oldBytes = { 0x00, 0x28, 0x0F, 0xD0}; //bytes to add feature
long FireRedOffset = 0x110F54; //FR offset in ROM
long LeafGreenOffset = 0x110F2C; //LG off set in ROM
I've gone and commented these, however, there's some new stuff in there. First off is the byte value. Byte values are special in that they can hold a value between 0 and 255 in a decimal format, or 0x00 to 0xFF if storing in a hexadecimal format. However, you may have also noticed that there's multiple values after it was declared, and it's in curly braces ( { } ) like a code block is, as well there's square braces after the data type ( [ ] ). What I've set up is an array. Arrays can carry groups of data. You can make all sorts of arrays!
What's neat is that in an array, items are arranged with positions, starting from the number 0, like so:
Sally and 17 in each of the arrays are in position 0.
Bob and 23 in each of the arrays are in position 1.
Joan and 34 in each of the arrays are in position 2.
Team Fail and 19 in each of the arrays are in position 3.
They contain 4 items, but there's still 4 positions. Computers just start counting from 0, not 1. The following code is not actually going to be done, but is a neat little tidbit:
MessageBox.Show(names[3] + " is " + ages[3].ToString() + " years old.");
When run with these variables, the messagebox will show: "Team Fail is 19 years old."
When wanting to use a specific variable, you can use the variable's name, and put the number of the element you want to refer to inside the square braces. There's all sorts of other ways to use arrays, though, don't let this limit you.
Anyways, there's also a new data type there, a long. It's another type of integer variable, however, it has room for more numbers, -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. That's a lot of numbers.
Anyways, let's get to coding again. That was a lot. Anyways, what we're going to do is code the routine that will insert our code for us. We have offsets for both games, and we have the data we plan to write into the rom, both for adding the routine back if it was removed, and the data to remove it from the game.
You may remember that we made three empty code blocks for each of our buttons, then filled the one earlier. We're manually going to make a fourth. Underneath the closing curly brace for your last button in your code, type the following:
Code:
private void WriteData(byte[] BytesToWrite, long Offset)
{
}
There's a whole ton going on here, so let's break it down a bit.
private is a keyword known as an access modifier. This gives it a bit of a cloak, so to speak, so that other parts of the application simply can't just reach in and do something with the variables in it. It has to access it from the outside, and allow the inside of it to work. How that happens, I'll explain shortly.
void is known as a return type. You can use other data types in here, such as strings, however, what we're making won't be returning anything, so we'll put "void" in there, to tell it that we don't intend on making it return something for the user.
WriteData is simply a name for our code block that we'll use to refer to it. You can change this as necessary. However, it seems that it also has some parameters, since they're in our brackets. Let's take a look in there.
There's two parameters, one is a byte array, and the other is a long. When doing this, you can use any kind of variable, as long as your usage supports it. Anything that's in the parameters there are known as arguments. You can add as many as you like, however, when calling this code block later on, you'll see that you'll have to supply data for each of those arguments. We have two types declared, as mentioned, but they also have names. These variables are accessible only from within this code block (Back on the topic of access modifiers), so we simply pass them along so that the code block can use them.
Inside this newly-created code block, we're going to add a few lines of code:
Code:
BinaryWriter bw = new BinaryWriter(File.OpenWrite(fileLocation));
bw.BaseStream.Seek(Offset, SeekOrigin.Begin);
bw.Write(BytesToWrite);
bw.Close();
This should at least look vaguely familiar to you, as it's similar to the BinaryReader. However, take note of a few things:
fileLocation is being used again
Offset, one of the parameters we made, is being used
BytesToWrite, another parameter we made, is also being used in bw.Write
Since we made those as variables, we can take the data that's assigned to them and use them accordingly. However, we're also using fileLocation, even though we don't have to pass it on. You'll see why this is and why we're doing this in a bit. The only thing that should be remotely new to you is bw.Write. This writes binary data, from a byte array, to your file. We'll make code to pass along an offset, and what data we want to write, and that'll be our patcher. For now, this code block is done.
Take a step back and take a moment to ensure that our code matches:
Spoiler:
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace FRLG_Flashback_Remover
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileLocation;
bool FireRed = false;
bool LeafGreen = false;
bool other = false;
byte[] newBytes = { 0x00, 0x1C, 0x0F, 0xE0}; //bytes to remove feature
byte[] oldBytes = { 0x00, 0x28, 0x0F, 0xD0}; //bytes to add feature
long FireRedOffset = 0x110F54; //FR offset in ROM
long LeafGreenOffset = 0x110F2C; //LG off set in ROM
private void button1_Click(object sender, EventArgs e) //Open ROM button
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "GBA File (*.gba)|*.gba";
if (ofd.ShowDialog() == DialogResult.OK)
{
BinaryReader br = new BinaryReader(File.OpenRead(ofd.FileName));
br.BaseStream.Seek(0xAC, SeekOrigin.Begin);
switch (Encoding.UTF8.GetString(br.ReadBytes(4)))
{
case "BPRE":
FireRed = true;
LeafGreen = false;
other = false;
break;
case "BPGE":
FireRed = false;
LeafGreen = true;
other = false;
break;
default:
FireRed = false;
LeafGreen = false;
other = true;
break;
}
if (other == true)
{
DialogResult result = MessageBox.Show("This game cannot be identified. If this is a Fire Red or Leaf Green-based hack, you can continue to load this. If it is not, it may cause irreversible damage. Do you wish to continue?", "Warning", MessageBoxButtons.YesNo);
if (result == DialogResult.No)
{
other = false;
}
}
if (FireRed == true)
{
label2.Text = "Fire Red BPRE";
fileLocation = ofd.FileName;
}
if (LeafGreen == true)
{
label2.Text = "Leaf Green BPGE";
fileLocation = ofd.FileName;
}
if (other == true)
{
label2.Text = "Unidentified game";
fileLocation = ofd.FileName;
}
br.Close();
}
}
private void button2_Click(object sender, EventArgs e) //Apply button
{
}
private void button3_Click(object sender, EventArgs e) //Remove button
{
}
private void WriteData(byte[] BytesToWrite, long Offset)
{
BinaryWriter bw = new BinaryWriter(File.OpenWrite(fileLocation));
bw.BaseStream.Seek(Offset, SeekOrigin.Begin);
bw.Write(BytesToWrite);
bw.Close();
}
}
}
Now, we have two portions of code left to make, and they're almost identical. There's only a minor difference between the two, so this part will be relatively easy to do.
Within the Apply Patch button, add the following if statements:
Code:
if (FireRed == true)
{
}
if (LeafGreen == true)
{
}
if (other == true)
{
}
We're going to check what game is being done. However, we're going to add a little twist. But first, let's add the ability for it to at least work for Fire Red and Leaf Green. Inside the Fire Red check block, add this one line of code:
Code:
WriteData(newBytes, FireRedOffset);
Whoa whoa whoa. We just referenced the WriteData code block we wrote, and passed parameters to it! This simplifies things because we can reduce the amount of code we need to write, instead of creating code every time we want to do something repetitive.
Now, subsequently, add the following line to the Leaf Green check:
Code:
WriteData(newBytes, LeafGreenOffset);
This will write data for Leaf Green. It works in the same way.
Now, we have a check for "other" games in there. What do we intend to do there? Well, one way tools are locked out of programs is by denying access if the internal game code (That we checked) doesn't match. However, our tools will account for this and will prompt the user as to whether they'd like to continue on and use the application. So, first of all, add the following in the other game check block:
Code:
DialogResult result = MessageBox.Show("This is an unrecognised game. Click Yes if the game is Fire Red-based, No if it is Leaf Green-based, or Cancel if you wish to not continue.", "Notice", MessageBoxButtons.YesNoCancel);
This time, we're using a yes/no/cancel dialog box, because we can't simply define buttons for each game and have its respective text on it. Doing so is far beyond the scope of this tutorial. We'll use each of the buttons to represent a user action, and in doing so, we'll check what button they pressed to apply the patch.
Add your checks like this, underneath the line you just added:
Code:
if (result == System.Windows.Forms.DialogResult.Yes)
{
WriteData(newBytes, FireRedOffset);
}
if (result == DialogResult.No)
{
WriteData(newBytes, LeafGreenOffset);
}
We'll compare the dialogresult for only two of the buttons. Since we don't have anything defined for the Cancel button, it will do exactly as we intend it to - absolutely nothing. However, if the user clicks Yes or No, we can then determine (hopefully) what game they're patching.
It's at this point that we're almost completely finished! We currently have the following abilities implemented:
Opening a ROM
Patching it to remove the Flashback system
Now, we need to patch it to re-add it, just in case. What's cool though, is that we can essentially recycle the code we just wrote. Take everything in your Apply button block, and copy/paste it into your Remove block. Then, since we have byte arrays for both the old and new bits of code, simply change each of the
And that's it! You can say that your application is completely finished. However, if you'd like to release an application, there's one more thing you should add.
Remember earlier on when I said that, when programming, assume your users know nothing? We're going to add two more lines of code to check if the user loaded a file before we go on to patch it. In its current state, clicking on them won't do anything, but it's a good idea so they don't just sit there for hours and hammer at a button that isn't doing anything.
At the beginning of your Apply and Remove code blocks, but before any of the if statements we made, add the following lines:
Code:
if (fileLocation == null)
{
MessageBox.Show("Please load a game.", "Error");
return;
}
What we're doing is checking if the fileLocation string is assigned or not. When we initially made it, we did not assign anything to it. This can check that, and tell the user that they haven't loaded a game. However, return; is something new. What this does, is breaks out of a statement like this without executing the rest of it. Basically, it'll skip over all the checks we made if that string is still null, and won't waste its time with checking so.
Now, this application is finished. It was a long road getting here, but it was worth it, was it not? If you want to, compare your finished code to my finished code. They should be similar.
Spoiler:
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace FRLG_Flashback_Remover
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileLocation;
bool FireRed = false;
bool LeafGreen = false;
bool other = false;
byte[] newBytes = { 0x00, 0x1C, 0x0F, 0xE0}; //bytes to remove feature
byte[] oldBytes = { 0x00, 0x28, 0x0F, 0xD0}; //bytes to add feature
long FireRedOffset = 0x110F54; //FR offset in ROM
long LeafGreenOffset = 0x110F2C; //LG off set in ROM
private void button1_Click(object sender, EventArgs e) //Open ROM button
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "GBA File (*.gba)|*.gba";
if (ofd.ShowDialog() == DialogResult.OK)
{
BinaryReader br = new BinaryReader(File.OpenRead(ofd.FileName));
br.BaseStream.Seek(0xAC, SeekOrigin.Begin);
switch (Encoding.UTF8.GetString(br.ReadBytes(4)))
{
case "BPRE":
FireRed = true;
LeafGreen = false;
other = false;
break;
case "BPGE":
FireRed = false;
LeafGreen = true;
other = false;
break;
default:
FireRed = false;
LeafGreen = false;
other = true;
break;
}
if (other == true)
{
DialogResult result = MessageBox.Show("This game cannot be identified. If this is a Fire Red or Leaf Green-based hack, you can continue to load this. If it is not, it may cause irreversible damage. Do you wish to continue?", "Warning", MessageBoxButtons.YesNo);
if (result == DialogResult.No)
{
other = false;
}
}
if (FireRed == true)
{
label2.Text = "Fire Red BPRE";
fileLocation = ofd.FileName;
}
if (LeafGreen == true)
{
label2.Text = "Leaf Green BPGE";
fileLocation = ofd.FileName;
}
if (other == true)
{
label2.Text = "Unidentified game";
fileLocation = ofd.FileName;
}
br.Close();
}
}
private void button2_Click(object sender, EventArgs e) //Apply button
{
if (fileLocation == null)
{
MessageBox.Show("Please load a game.", "Error");
return;
}
if (FireRed == true)
{
WriteData(newBytes, FireRedOffset);
}
if (LeafGreen == true)
{
WriteData(newBytes, LeafGreenOffset);
}
if (other == true)
{
DialogResult result = MessageBox.Show("This is an unrecognised game. Click Yes if the game is Fire Red-based, No if it is Leaf Green-based, or Cancel if you wish to not continue.", "Notice", MessageBoxButtons.YesNoCancel);
if (result == System.Windows.Forms.DialogResult.Yes)
{
WriteData(newBytes, FireRedOffset);
}
if (result == DialogResult.No)
{
WriteData(newBytes, LeafGreenOffset);
}
}
}
private void button3_Click(object sender, EventArgs e) //Remove button
{
if (fileLocation == null)
{
MessageBox.Show("Please load a game.", "Error");
return;
}
if (FireRed == true)
{
WriteData(oldBytes, FireRedOffset);
}
if (LeafGreen == true)
{
WriteData(oldBytes, LeafGreenOffset);
}
if (other == true)
{
DialogResult result = MessageBox.Show("This is an unrecognised game. Click Yes if the game is Fire Red-based, No if it is Leaf Green-based, or Cancel if you wish to not continue.", "Notice", MessageBoxButtons.YesNoCancel);
if (result == System.Windows.Forms.DialogResult.Yes)
{
WriteData(oldBytes, FireRedOffset);
}
if (result == DialogResult.No)
{
WriteData(oldBytes, LeafGreenOffset);
}
}
}
private void WriteData(byte[] BytesToWrite, long Offset)
{
BinaryWriter bw = new BinaryWriter(File.OpenWrite(fileLocation));
bw.BaseStream.Seek(Offset, SeekOrigin.Begin);
bw.Write(BytesToWrite);
bw.Close();
}
}
}
The End?
I'd like to apologise for the length of the tutorial. I didn't want it to be a mile and a half long, but there's tons to keep in mind when programming, plus, I wanted to mention each and every new bit of code that we added, so that you understand what's going on. If you need clarification on anything in the tutorial here, feel free to reply, and I'll fix it up for you, because if it isn't clear for you, it may not be clear for somebody else.
I'd also like to thank diegoisawesome for the trick as to removing the Flashback system from Fire Red. I was looking in threads around various sites to see if I could find something simple to use for this tutorial, and this was simply perfect. It just needs to write a few bytes, and that's all I need it to ever do. I also took a few minutes to find it in Leaf Green so that this can be compatible with the two games that use this system.
@Gamer2020 You can target previous .NET frameworks in 2010 as well, however, doesn't compatibility with the latest features also break if you use older frameworks?
I haven't had any problems. Depends on features I guess. Earlier frameworks would just be missing some features I suppose.
Someone told me that 2.0 is best for XP compatibility but honestly I think some higher versions work as well. Also the frameworks are backwards compatible so if 4.5 is installed you can run 2.0. Microsoft has been making their newer software not install on XP.
I did some quick research and I think 4.0 works on XP so that should be OK to use.
I can confirm that 4.5 and 4.0 work on linux as long as you avoid the functions I mentioned in my previous post.
Yeah, I had to find an INI class, although it's a tad buggy. It's easy to fix though. I might see if I can tend to the bugs in the class I have and try getting it working 100%.
Well, that'll definitely be something worth keeping in mind for my future development. I could just as easily get VS2013 Ultimate for free (Software from Dreamspark through my school), but 2010 still supports XNA development and deployment to the Xbox 360.
That is a good point. I also have dreamspark so I usually just get the latest software. I have 2010, 2012, and 2013 all installed alongside each other and they all work fine.
As for your INI problem you may be able to convert the code from PGE. The code is tested on Linux and Windows and I've had no problems.
OK I know this thread is a few months old so sry about this. But if I wanted to show a successful message after the bytes have been inserted how would I do that?
OK I know this thread is a few months old so sry about this. But if I wanted to show a successful message after the bytes have been inserted how would I do that?
Well, the application would fail to write if the file is in use. If you aren't sure if it will work properly, use a try/catch statement.
Code:
try
{
//code here that has the possibility to fail, such as reading/writing from files that may be in use.
}
catch
{
//what will happen if the code in the try block fails, such as a messagebox stating the operation failed. Usually, break statements are also put here so the application won't go on with invalid/incorrect data.
}
Then, the program will continue on elegantly. I wrote myself a little program for a challenge, but I decided to make sure that I only put numbers in for what is needed, so this is what I wrote:
Code:
Console.Write("Enter how many values you'd like to calculate: ");//get # of vals from user.
try
{
numofelements = Convert.ToInt32(Console.ReadLine());
}
catch
{
isValid = false;
numofelements = 0;//default in case of error.
}
If what the user enters is not numbers, such as a letter or punctuation mark, the system can't convert that to an int32 (Number) value, so it errors. However, since it's in a try statement, it will catch the error, and do whatever is in the catch statement. It's great for making sure you have some kind of default handler in case something goes wrong. In your case, what I'd do is:
Code:
try
{
//stuff here for file writing
}
catch
{
//Put messages here if you want...
break; //break in case the file write fails, exit this chunk of code silently. Anything after the catch statement [i]will not[/i] be executed. It'll just jump to the end of whatever event this was tied to.
}
MessageBox.Show("Success!");