PHO - Pokémon Hackers Online
Go Back   PHO - Pokémon Hackers Online > Discussion Board > Archive

Notices

Archive Old threads that serve no purpose are here.

 
 
Thread Tools Display Modes
Old 14th December 2013, 02:56 AM   #1
Shiny Quagsire
Super Awesome Ninja ASM Guy
PHO VIP
 
Shiny Quagsire's Avatar
 
Join Date: Apr 2010
Age: 20
Posts: 62
Shiny Quagsire
Default [FR, EM] Replacing the GameFreak Intro

Introduction
So several months back I was browsing through the ASM in Fire Red in IDA Pro, and I happened to stumble upon a few callback functions that knizz had found. After researching a few of them, I had discovered that some were empty or had very few functions in them. Using those functions, I recreated an empty callback, essentially giving me a way to leech into and replace certain callbacks with my own.

So I devised a way to test this empty callback. After seeing yet another post by knizz on how to skip to the titlescreen after the copyright, I decided to leech into that calling method, use it to call my code, and return to the titlescreen. It worked.

This is the code I used as my blank callback:
Code:
.text
.global callback

.thumb
.thumb_func
.align 2

main:
push {r0-r7, lr}
bl main_func
bl call_back3
bl sub_080704D0
pop {r0-r7, pc}

call_back3:
ldr r4, callback3_address
bx r4

sub_080704D0:
ldr r4, loc_080704D0
bx r4

main_func:
push {lr}
bl increment_timer
bl getcurrentfunc

increment_timer: @This function will be used to keep a per-frame timer
ldr r0, vram          @similar to the one used at the titlescreen. We will use
ldrh r0, [r0]           @it to route our introduction scenes.
ldr r1, vram
add r0, #0x1
strh r0, [r1]
bx lr

returnmain:
pop {pc}

getcurrentfunc:

@This will be our future 'routing' function
@that we use to go to the various states of our intro.

b titlescreen
bx lr

titlescreen:
bl goto_titlescreen
pop {r0-r7,pc}

goto_titlescreen:
push {lr}
ldr r0, sub_0800CC7A
bx r0

.align 2
callback3_address:		.word 0x08077578+1
loc_080704D0:			.word 0x080704D0+1
sub_0800CC7A:			.word 0x0800CC7A+1
So if you wanted to insert this as it was right now, simply compile and insert the ASM and place the pointer (+1) at 0x0EC5D0.

Explanation

While it looks like quite a bit of code, it's actually quite simple. It first calls out main function where we update and draw all of our backgrounds and sprites. Then after that it calls a few functions that help drive the callback system in Fire Red, and returns. Currently it is set up so that immediately after the main function is called, the titlescreen is set to the top of the callback table, which allows us to exit our new "intro". As it is though, it isn't much. However, with a lot of coding it could be very easily possible to code out an entire custom introduction. However, there are a few limitations:
  1. You have to severely fragment your routine in order to prevent your branch-links from failing. BL's can only travel so far. I have already had this happen, and it seems that the only way around this is to split your ASM into multiple callbacks, which would require manual editing of each callback to link into eachother properly. It just makes it messy and difficult to work with, and even worse to debug.
  2. Coding an entire intro in ASM can be difficult, so finding a way to do it in C would be more optimal. DevKitARM seems to have this issue where it decides to assume your compiling it as a GBA ROM, which means it has a bunch of initiation stuff making it near impossible to exit. If anyone could find a way to simply compile the C directly to it's ASM equivalent, that would help a lot.
  3. This skips past everything after the copyright screen until the title screen, which may or may not be what you want. I personally wanted to replace only the GameFreak screen, and keep the other half of the introduction.
  4. Since your basing it off the callback system, you have to play by the rules of Fire Red. This means that you have to use their functions for things that you would normally do straightforward with ASM. For example, Fire Red uses DRM to copy the colors to the VRAM. As such, you have to use their color pallet at 0x020371F8. A few other important offsets are listed below:

Code:
vram:				.word 0x020370B8 		@Variable RAM (variables 0x8000 and up. I decided to use these since I could at least garantee that I wouldn't screw over gameplay by using a different RAM location)
bgpal:				.word 0x020371F8 		@Fire Red palette buffer
objpal:				.word 0x020371F8 + (0x200) 	@Fire Red palette buffer, plus 0x200 to get OBJ pallets
oam:				.word 0x03003128		@Fire Red OAM buffer
There are, however, some advantages to using ASM. The first and possibly best advantage is direct hooks into the functions in Fire Red. So as an example of exactly what you can do with both access to the GBA hardware as well as the functions on the ROM, I've made a video of my custom introduction that I'll most likely be using in any of my future hacks:

If your interested in the source code, you can download it from my GitHub here.

Implementation
So you want to create a new intro for your hack. Well, let's just say that it isn't exactly for the faint of heart, and it requires a lot of work, planning, and research. However, I will give you a basic layout of how I did mine.

To start, I fragmented my routine into easy, managable chunks of assembly, one file for one purpose. My file structure looked something like this:
callback.asm
graphics.asm
constants.asm
timeconstants.asm
quagsireintro.asm
images/
img_bins/
Each file did exactly as it described. callback.asm was the main callback function, graphics.asm stored several image binaries (more on that later), constants.asm stored needed constants for loading and storing data, as well as several key functions, timeconstants.asm stored constants related to timing, and quagsireintro.asm stored the actual function called in constants.asm.

So to include all these files as if it were one giant ASM file, you simply do something like this:
Code:
.include "quagsireintro.asm"
.include "timeconstants.asm"
.include "constants.asm"
.include "graphics.asm"
So how do we time this so that we go to the right function at the right time? Well, a function named increment_timer is called every frame. The GBA renders it's graphics at an exact 60Hz, or 60 times a second. Knowing this, we can make a constant called second with it's value set to 0x35, since there are 60 frames per second, minus a few if you call certain functions.

So essentially, you want to create several constants for each length of time that you want to call your function, and if it exceeds it, call the next function and so on. Here's a clip of code that I used to control events in my callback.asm:
Code:
ldr r1, companyname
cmp r0, r1
ble shinyquagsire

ldr r1, companyname2
cmp r0, r1
ble shinyquagsire_2

ldr r1, companyname3
cmp r0, r1
ble shinyquagsire_3

ldr r1, companyname4
cmp r0, r1
ble shinyquagsire_4

ldr r1, pausebetween1
cmp r0, r1
ble returnmain

ldr r1, a_flash
cmp r0, r1
ble update_a_step

ldr r1, grass_pan
cmp r0, r1
ble update_gpan_step

bgt titlescreen
and the corresponding constants:
Code:
companyname:			.word (second * 4) + (second / 4) + (second / 16)
companyname2:			.word (second * 5) + (second / 4) + (second / 16)
companyname3:			.word (second * 6) + (second / 2)
companyname4:			.word (second * 6) + (second / 2) + (second * 2)
pausebetween1:			.word (second * 6) + (second / 2) + (second * 2) + (second * 2) - (second / 2)
a_flash:			.word (second * 6) + (second / 2) + (second * 2) + (second * 2) - (second / 2) + (second * 3)
grass_pan:			.word (second * 6) + (second * 2) + (second * 2) + (second * 2) - (second / 2) + (second * 3) + (second * 3)
So basically we compare r0, which contains the timer value, to each of these time constants, and if it's less than it we go to a particular function which controls the callback for that duration of time.

This is the C as you can see.
C what I did there?

OK, lame puns aside, let's imagine that you want to create an introductory sequence that replicates that of Crystal. Well, it just so happens that I did that very thing:

Full source code and compilation instructions available at the FR-CrystalIntro project on my GitHub

It initially started as an experiment that was originally coded in ASM. I posted some screens of the unown A scene and it generated enough interest to make me want to continue it to the end of the Crystal intro. It took several days to finish though, and it was not an easy feat my any means.

One of the problems I ran into when coding this intro was the difficulty of writing in pure ASM, which I explained prior to this section. So I instead searched out a way to code it in C and with the help of Bond697 I was able to do just that. Here's the basis of how it worked. First I had to design a way to get my C code in without the issue of unneeded code or crashing. To do this, I came up with what I like to call a bootloader. Basically this piece of ASM is based off of the same routine we had before, but instead of calling a piece of ASM located in another file, we bl to the end of the file, which is where we will insert our C code. Then we do our thing in the C code, return, and the ASM takes up back home into the Fire Red main loop.

Here's the base code for the bootloader:
Code:
.text
.global callback

.thumb
.thumb_func
.align 2

main:
push {r0-r7, lr}
bl main_func
returnmain:
bl call_back3
bl sub_080704D0
pop {r0-r7}
ldr r0, returnloc
bx r0

call_back3:
ldr r4, callback3_address
bx r4

sub_080704D0:
ldr r4, loc_080704D0
bx r4

main_func:
push {lr}
bl C_hook
b returnmain

call_fadescreen:
mov r0, #0x1
neg r0, r0
mov r3, r2
mov r2, r1
mov r1, #0x0
str r1, [sp,#0x0]
mov r1, #0x0
ldr r4, =0x08070589;
bx r4

.align 2
callback3_address:		.word 0x08077578+1
loc_080704D0:			.word 0x080704D0+1
sub_0800CC7A:		.word 0x0800CC7A+1
unfadescreen_trigger:	.word 0x020370B6
returnloc:			        .word 0x0800053a+1
			                .word 0x00000000
   		                        .word 0x00000000

C_hook:
Now to compile the C code it's a tiny bit more complex. DevKitARM has this bad habit of assuming that if we're compiling something that's C based that we're going to want it to be a full on GBA executable, which is not the case for us. What we want is a bare-bones C->binary executable that we can insert into the ROM with ease. Admittedly, it's not near as easy as compiling ASM, but with some compilation scripts and a Java JAR we can make it dead simple.

The first issue at hand is that if we tell DevKitARM's linker to turn our C object into a workable file for objcopy, it auto-links several built-in apis and several other things. Since we're going to be hacking this into a ROM, having all of those APIs will start to increase the size of our routine drastically and make it more difficult to return from our routine back to the bootloader. So instead, we provide our own linker file called linker.lsc.
Code:
OUTPUT_ARCH(arm)
 
MEMORY {
 
        rom     : ORIGIN = 0x08790058, LENGTH = 32M
        ewram   : ORIGIN = 0x02000000, LENGTH = 4M - 4k
}
 
 
SECTIONS {
        .text : {
               
                FILL (0xABCD)
               
                __text_start = . ;
                *(.init)
                *(.text)
                *(.ctors)
                *(.dtors)
                *(.rodata)
                *(.fini)
                *(COMMON)
                __text_end  = . ;
               
                __bss_start__ = . ;
                *(.bss)
                __bss_end__ = . ;
        _end = __bss_end__ ;
        __end__ = __bss_end__ ;
        } >rom = 0xff
}
What this file does it it tells linker two things: Where is our executable based, and how do we want our executable formatted. So for example, let's say we're inserting this routine at 0x790000. Since the bootloader takes up 0x58 bytes, we want the linker to start all of the pointers from 0x08790058 (08 is the beginning of the ROM in the GBA's memory).

Installing DevKitPro
OK, so now that we know how to compile, we're going to download the tools to do so. DevKitARM is a package inside of the mega DevKitPro SDK, which you can download here if your a Windows user. If your a Linux or Mac user, I trust that you can figure out how to install it on your own. Be patient, it can take quite a while to install. If you want to speed it up, you can untick the DevKitPSP and DevKitPPC toolkits. the important thing is that you have the DevKitARM toolkit since that's what we're going to be working with in this tutorial.

Once you have that installed, download IntroTemplate.zip from my Dropbox and extract the files.


A Few Notes about the Emerald Port
Thanks to diegoisawesome and I, the existing codebase for my intro has been ported to Emerald. However, there are a few key differences:

On Emerald, a bootloader isn't actually required. However, specific functions like the fadescreen are required to be exported to an ASM routine due to a bug in DevKitARM which causes register r3 to be overwritten with the offset to the routine your going to. So instead, compile the bootloader and insert it separate from your main routine and take note of the address for later in this tutorial. As for the C, just compile your code and insert it and place the pointer (+1) at 0x16CE78. The difference between the FireRed code and the Emerald port is actually nearly the same and it was designed this way so as to keep code portable between games. If there is an important difference though, I'll have a little spoiler pointing this out.


Now, go into the folder for the desired version, be it Fire Red or Emerald. First assemble the bootloader.asm using HackMew's batch script so that you get the bootloader.bin file. Next, double click the file called compile.bat, check to see if any errors appeared (which hopefully won't happen) and congrats! Your coding setup is now complete!

Now it's time to test our code out. Open bootloader.bin, intro.bin, and your ROM in your favorite hex editor. Now if you didn't change the offsets in useful.h and linker.lsc, you might want to do that now. Just open linker.lsc and edit the line that says rom : ORIGIN = 0x08790058, LENGTH = 32M to say rom : ORIGIN = 0x08<freespace+0x58>, LENGTH = 32M. Now open useful.h and find the line that has this offset: 0x08790025 (0x08790001 for Emerald) and change it to be 0x08<freespace+25> (or just freespace + 1 for Emerald).

Once you have that fixed, recompile and insert your bootloader into the offset you specified earlier, or if you didn't edit the files, just insert it at 0x790000. Now copy the contents from intro.bin and place it directly after the last byte of the bootloader. If your inserting this on Emerald, take note of this address as it will be needed. Now to hack in your routine, just place the reversed pointer to your bootloader (+1) at 0x0EC5D0. For Emerald, insert the pointer to your C code (+1), not the bootloader, at 0x16CE78.

Now save and open your ROM. do you see a little Quagsire walking across your screen? Congrats! You just replaced the intro! Now press A and your game should be taken to the title screen.


One Last Note Before Coding

All of the files in the ZIP were written in a Linux text editor, meaning that they use the Linux line ending format. This will cause it to look like there are no newlines in NotePad. To solve this, either use WordPad or download NotePad++ which has excellent syntax highlighting to help you keep organized while you code.


Making teh Codes

Still coming soon...

Resources and More
If your looking for some more info on how to do raw GBA ASM, I'd recommend these few documents:

JPAN's Thumb Tutorial Doc
A Bit of ASM by thethethethe (can't find at the moment, I'll try to find it on the web archive)
HackMew's Knowledge
GBATek
Patater's GBA ASM Tutorial
__________________





My Works:

Coming Soon/Working On:
  • Pokemon Amethyst
  • ?Using HMs via Items?
  • Custom GUIs using ASM (and soon) C


Shiny Quagsire is offline  
Likes ProClifo, Elsa liked this post
Sponsored Links
Old 14th December 2013, 04:02 AM   #2
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default

I asked you to finish it, not post it somewhere else....:P

Well, I like it. Good job so far. Now I still need to learn C, cause Python is too easy. Haha, no freakin semi-colons.
karatekid552 is offline  
Old 7th February 2014, 08:20 AM   #3
circaoffire
Newbie
 
circaoffire's Avatar
 
Join Date: Feb 2014
Posts: 6
circaoffire
Default

You are a star, shinyquagsire! That is really impressive work! I'm surprised you were willing to public post it, but that seems to be just how you work. Good luck with all the rest of your work in ASM especially the cool work involving C!
__________________
circaoffire is offline  
 

Tags
gamefreak, intro, replacing, [Document]

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 04:22 PM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2018, vBulletin Solutions, Inc. User Alert System provided by Advanced User Tagging (Lite) - vBulletin Mods & Addons Copyright © 2018 DragonByte Technologies Ltd.
Feedback Buttons provided by Advanced Post Thanks / Like (Lite) - vBulletin Mods & Addons Copyright © 2018 DragonByte Technologies Ltd.
Pokémon characters and images belong to Pokémon USA, Inc. and Nintendo.
Pokémon Hackers Online (PHO) is in no way affiliated with or endorsed by Nintendo LLC, Creatures, GAMEFREAK inc,
The Pokémon Company, Pokémon USA, Inc., The Pokémon Company International, or Wizards of the Coast.
All forum/site content (unless noted otherwise) and site designs are © 2006-2013 Pokémon Hackers Online (PHO).
Green Charizard Christos TreeckoLv100

"Black 2" by ARTPOP. Kyurem artwork by XOUS.

no new posts