• This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn more.
  • Announcement - March 5th 12:17 PM GMT

    Hi there Guest!
    Thanks for checking out Silph Co.! Right now things are very much still in development with things like themes, guidelines, rules and most importantly content, still being a WIP. The staff and our members are actively working to make the community more homey for you. In the mean time, we are welcoming feedback and suggestions if you have them in the Feedback section.
    Please read the forum rules before posting.

Other Miscellaneous Fire Red Edits

Compatible Base ROMs
PKMN FireRed
#1
Disabling the Security Key
The Security Key is an unsigned 32-bit integer used as a weak form of symmetric encryption. To disable it entirely, just do the following:

Code:
0x0804C13C: 00 24
Some things are encrypted by xoring them with the Security Key. Because xor functions as its own inverse, these things can be decrypted by xoring them with the Security Key a second time. I do not have a comprehensive list of everything encrypted in this fashion, but it includes things such as the player's money, the quantity of items in the bag, and the amount of stored Berry Powder.

The Security Key is found at *(0x0300500C) + 0xF20. It changes with every map refresh.

The above change modifies the code responsible for choosing the next Security Key, ensuring it will always be zero. This takes advantage of the fact that an xor with zero is a no-op, and thus disables the encryption.

DMA Negation
FRLG introduced a anti-cheat measure in the form of DMA-protected memory. In short, much of the data will move around in RAM, rather than staying in one static location, meaning you have to access that data indirectly through the pointers at 0x03005008, 0x0300500C, and 0x03005010.

This stops it from moving around.

Code:
0x0804C064: 00 21
Here's an incomplete FR map of the DMA-protected memory in their new static locations:

Code:
[0x0202552C]    2b    Camera X-position
[0x0202552E]    2b    Camera Y-position
[0x02025530]    1b    Current map.
[0x02025531]    1b    Current map bank.

-------------------------------------------------------------------------

[0x02029314] Box data

-------------------------------------------------------------------------

[0x02024588]    8b    Character name including terminator, padded to end with 0xFFs
[0x02024590]    1b    Gender (00/01 m/f)
[0x02024591]    1b    Unknown
[0x02024592]    2b    Trainer ID
[0x02024594]    2b    Secret ID (halfword)
[0x02024596]    2b    Playtime (hours)
[0x02024598]    1b    Playtime (minutes)
[0x02024599]    1b    Playtime (seconds)
[0x0202459A]    1b    Playtime (frames)
[0x0202459B]    1b    Unknown
[0x0202459C]    2b    Options // this and above thanks to hackmew's asm tut pt. 1
...
[0x020245A2]    1b    If 0xDA, then National Dex is enabled.
...
[0x020254A8]    4b    Security Key
[0x020254AC]        End (byte after)

-------------------------------------------------------------------------

[0x020257BC]    4b    Player's current money.
[0x02000490]    4b    Copy of player's current money. Doesn't seem to do anything if changed.

Credit to Missingyep for the original RAM map.

The pointers to the DMA-protected memory will still be set correctly, so anything that accesses that memory through them will still work, but of course now that they're static, that's unnecessary.

No PC Item Storage
The PC Item Storage is fairy useless, so why not remove it? This will remove it from the Player's PC menu, rendering it inaccessible.

Code:
0x080EB80C: F0
0x080EB8B4: F0

0x080EB6C0: 02
0x080EB710: 02

0x08402253: 09 04
The PC's Item Storage is located at *(0x03005008) + 0x298. It has room for 30 items, which is 120 bytes. If you remove the free Potion you're given in the beginning of the game, you can use that space for something else.

New Game PC Items
You start the game with a Potion in your PC. The relevant table is at 0x08402220. Each entry in the table is four bytes; two for the item's id, followed by two for the quantity of the item. The end of the table is marked with 4 bytes of zero.

If you want to give more than one distinctive item, you'll have to repoint the table. If you don't want to give any items, just put the four bytes of zero marking the end of the table at the beginning of the table.

Automatically Enable Running
Code:
0x0805BA3A: 01 20 C0 46
This makes it so you don't need flag 0x82F to be set to be able to run. Pair it with Hackmew's Run Anywhere hack, and you'll get the same running mechanics as Gen VI and Gen VII (as in, run anywhere and start out with the ability to run).

No Healing Eggs
Did anyone ask for a generation difference that almost nobody noticed or cared about?

When healing at a Pokemon Center, the healing machine will light up based on how many Pokemon are being healed. Starting in Diamond and Pearl, eggs don't count. In Emerald and prior, they did. This gives FR the behavior from the later games.

Code:
0x08083B8E : 08 F0 D5 FA

Automatic National Dex
Code:
0x0806E25C: 01 20 70 47
This basically just overwrites the function responsible for checking if you have the National Dex with return true;, so you'll always have it. Equivalent to immediately invoking special 0x16F at the beginning of the game.

To be clear, this does not add the Pokedex to the start menu.

Disable Trade Restrictions
There are three distinct aspects to FR's trade restrictions:
  • You can't trade or be traded an egg, even an egg of a Kanto Pokemon.
  • You can't be traded a Pokemon not in the Kanto Dex, even if it's from another FRLG.
  • You can't trade with any of the Hoenn based games.
The first two are lifted upon getting the National Dex. The third is lifted upon completing the Sevii Island postgame story.

However, you can get rid of those restrictions entirely:

Code:
0x08009754: C0 46 01 20
0x0800975C: C0 46 01 20
0x0804FA4E: C0 46 01 20

Enable Foreign Evolutions Without National Dex
FR has an annoying feature that stops any evolutions that would result in a foreign Pokemon (a Pokemon not in the Kanto Dex) if you don't have the National Dex. This applies equally for something like Golbat -> Crobat as something like Cyndaquil -> Quilava. You could simply give out the National Dex at the beginning of the game to avoid this, but that isn't necessary.

Code:
0x08043156: 02 E0
0x080CE90C: 1C E0
0x080CF566: 17 E0
0x08126C26: C0 46 C0 46 C0 46 C0 46
0x08126C4C: 08 E0

Keep Held Item After Held Item Trade Evolutions
When, for example, an Onix is traded while holding the Metal Coat, it will evolve into Steelix, and the Held Item will be used up in the process. If you want to change this so that the Held Item is still required, but won't be used up:

Code:
0x0804316A: C0 46 C0 46

Can't Forget HM08
Code:
0x08125AB6: 07
FRLG turned HM08 into effectively a reusable TM, meaning you can freely forget the move it teaches (Dive). This makes it like the rest of the HMs.

Constant Base Power For Hidden Power
Code:
0x0802B722: C0 46
This just nulls out the strh for Hidden Power's base power, forcing it to read it out of the move data table, like a normal move. FRLG have its power set to 1 (this is actually so it'll show a line next to its power, rather than a number, because its power is supposed to vary), so you'll need to change that to 60 (as it is in XY and later).

(Not So) Hidden Power
Forces the game to calculate and display the actual type of Hidden Power. Assemble with armips.

Code:
// unhidden-power.s

// modify these constants as needed.
infile equ "firered.gba"
outfile equ "test.gba"
free_space equ 0x08800000
move_data equ 0x08250C04

// ----------------------------------------------------------------------------

pkmn_status_data equ 0x0203B140
battle_slot_mapping equ 0x02023BCE
party_player equ 0x02024284

write_type equ 0x0803098E|1
display_type equ 0x081368D6|1
pokemon_getattr equ 0x0803FBE8|1

hp_effect_index equ 0x87

// ----------------------------------------------------------------------------

.gba
.thumb

.create outfile, 0x08000000
.import infile

.org free_space

write_type_hook:                    // r1 := move_id
    push {r3-r7}
    mov r7, lr
   
    lsl r0, r1, #2
    lsl r1, r1, #3
    add r0, r1                        // r0 := sizeof(move_t) * move_id
    ldr r1, =move_data
    add r1, r0                        // [r1] := data for current move
    ldrb r0, [r1, #2]                // r0 := recorded type
   
    ldrb r2, [r1, #0]                // r2 := move effect id
    cmp r2, #hp_effect_index
    bne return0
   
    ldrb r0, [r5]                    // r0 := slot
    lsl r0, #1                        // r0 := slot << 1
    ldr r1, =battle_slot_mapping
    add r0, r1
    ldrb r0, [r0]                    // r0 := index in party
    mov r1, #100
    mul r0, r1                        // r0 := offset from party_player
    ldr r1, =party_player
    add r0, r1                        // [r0] := pokemon
   
    bl hp_type_decode
   
return0:
    mov lr, r7
    pop {r3-r7}
    ldr r1, =write_type
    bx r1

// return value on r1
display_type_hook:                    // r2, r5 := move_id, move_data
    push {r0, r3-r7}
    mov r7, lr
   
    lsl r0, r2, #2
    lsl r1, r2, #3
    add r0, r1                        // r0 := 12 * move_id
    add r0, r5                        // [r0] := data for current move
   
    ldrb r1, [r0, #2]                // r1 := type
    ldrb r2, [r0, #0]                // r2 := effect_id
    cmp r2, #hp_effect_index
    bne return1
   
    ldr r0, =pkmn_status_data
    ldr r0, [r0]
    ldr r1, =0x3290
    add r0, r1                        // [r0] := pokemon
    bl hp_type_decode
    mov r1, r0

return1:
    mov lr, r7
    pop {r0, r3-r7}
    ldr r2, =display_type
    bx r2

// uint8_t hp_type_decode(pokemon_t*)
hp_type_decode:
    push {r4-r7, lr}
    mov r6, r0                                    // [r6] := pokemon
    mov r4, #0                                    // r4 := type calculation
    mov r7, #0                                    // r7 := iv index
    ldr r5, =pokemon_getattr
    // b test
   
loop:
    mov r0, r6                                    // [r0] := pokemon
    mov r1, #0x27                                // GET_HP_IV
    add r1, r7
    bl call
    mov r1, #1
    and r0, r1                                  // r0 := LSB of IV
    lsl r0, r7
    orr r4, r0
    add r7, #1
   
test:
    cmp r7, #6
    bne loop
   
floor:
    mov r0, #15
    mul r0, r4
    mov r1, #63
    swi #0x6
   
decode:                        // add 2 if below 8, 1 otherwise
    cmp r0, #8
    blo L1
    add r0, #1
   
L1:
    add r0, #1
    pop {r4-r7, pc}
   
call:
    bx r5
   
.pool

.org 0x08030984
ldr r0, =write_type_hook|1
bx r0
.pool

.org 0x081368CC
ldr r1, =display_type_hook|1
bx r1
.pool

.close

This affects the type of the move shown when selecting a move in battle, and when viewing a Pokemon's moveset on the status screen. It will still show up as Normal-type in the TM Case, because that's not tied to a particular Pokemon, so it just relies on what the type move_data lists for it. It's not necessary at all, but you might want to consider redefining HP as ???-type instead of Normal-type, so it's more accurate (a Normal-type HP is actually impossible).

Powder Jar Notes
By participating in the Berry Crush minigame, you earn Berry Powder, which can be turned into items such as Energy Powder and Calcium. The amount of Berry Powder in your jar takes up two bytes at *(0x300500C) + 0xAF8. Keep in mind that it is encrypted with the Security Key.

Smart Automatic Double Battles
This changes the way the game decides whether an NPC trainer battle should be a Single or a Double Battle. By default, there is a byte in the trainer data alongside their name and Pokemon that marks whether it should be a Single or Double Battle.

With this, it first consults a scripting flag. If the flag isn't set, it falls back to the vanilla behavior. If it is set, however, it will check if both you and the NPC have two eligible Pokemon, and if so, you'll have a Double Battle, otherwise, you'll have a Single Battle.

Assemble with armips.

Code:
// autodouble.s

// adjusts these constants as needed
filename equ "firered.gba"
free_space equ 0x08800000
autodouble_flag equ 0x828

// ----------------------------------------------------------------------------

trainer_something_823EAC8 equ 0x0823EAC8
battle_type_flags equ 0x02022B4C

checkflag equ 0x0806E6D0|1
dbl_btl_teamcheck equ 0x8040CC4|1
returnptr equ 0x0801169C|1

// ----------------------------------------------------------------------------

.gba
.thumb
.open filename, 0x08000000

.org free_space

autodouble:
                                            // r3 := tid
    ldr r1, =trainer_something_823EAC8
    ldr r4, [sp, #0x20]                     // r4 := 4 * tid
    add r0, r3, r4                          // r0 := 5 * tid
    lsl r0, r0, #3                          // r0 := 40 * tid
    add r0, r1                              // r0 := data
    ldr r3, =checkflag
   
    push {r0}
    ldr r0, =autodouble_flag
    bl call
    mov r3, r0
    pop {r0}
    cmp r3, #0
    beq noautodouble
   
    // NPC having less than 2 Pokemon -> single battle
    mov r1, #0
    mov r2, #0x20
    add r2, r0
    ldrb r2, [r2]                           // r2 := party_size
    cmp r2, #2
    blo return
   
    push {r1}
    ldr r3, =dbl_btl_teamcheck
    bl call
    pop {r1}
    cmp r0, #0
    bne return
    mov r1, #1
   
return:
                                            // r1 := double ? 1 : 0
    ldr r2, =battle_type_flags
    ldr r0, [r2]
    orr r0, r1
    str r0, [r2]
    ldr r0, =returnptr
    bx r0
   
noautodouble:
    ldrb r1, [r0, #0x18]
    b return
   
call:
    bx r3
   
.pool

.org 0x08011686
hook:
    ldr r0, =autodouble|1
    bx r0
   
    .pool
   
.close

By default the flag to activate is set to 0x828, which is the flag to add Pokemon to the Start Menu, so it'll effectively be always active. But of course, you can change the flag to something otherwise unused and set/unset the flag to activate/deactivate it at will.

Class Based Poke Balls
In Sun and Moon, NPC trainers started using different types of Poke Balls. Specifically, it's tied to their trainer class - for example, Elite Four members and Ace Trainers keep their Pokemon in Ultra Balls. Prior to this, NPC trainers only ever used basic Poke Balls.

Repo is here. I'll probably switch it to using the make.py script at some point, but for now it's the old build and insert scripts. It's small enough that I may even rewrite in THUMB with armips, not sure.

Emerald's Pickup Mechanics
The Pickup mechanics were overhauled in Emerald. In RS and FRLG, there's simply a static item list that you get items from. Emerald made it so the items you can Pickup vary with level. Later games kept Emerald's mechanics, merely changing the item lists.

Repo is here. I'll probably switch it to using the make.py script at some point, but for now it's the old build and insert scripts.

Expand Mart Inventory By Earning Badges
Gen IV introduced an incredibly useful feature where the inventories of the Poke Marts would gradually get bigger as you go through the game, meaning that if you happen to go somewhere that was available much earlier on, the Poke Marts there aren't completely worthless.

This allows you to set up item lists depending on how many badges you have (from none all the way to all eight). This however doesn't break statically set item lists - those will still function normally. If you want to use the dynamic list, you have to use "pokemart 0" in your script; this is so you could do the later game thing of having two clerks, one with the standard items, and one with items unique to that town with a static item list.

Repo is here. I'll probably switch it to using the make.py script at some point, but for now it's the old build and insert scripts.
 
Last edited:
OP
OP
Sagiri
#3
Dynamic Trade Animation Names

special 0xFE is the special for trading Pokemon. It trades the Pokemon stored at 0x0202402C for the Pokemon in slot var_8005 of the player's party. However, if the Pokemon at 0x0202402C doesn't match up with the var_8004th entry in the in-game trade table, the messages during the trade will be wrong, because it consults that to get the nickname and trainer's name so it can set the buffers to say "REYLEY sent over MIMIEN".

This fixes it so it consults the Pokemon's data for those names instead.

Repo is here.

By changing this, you could, for example, have a Wonder Trade implementation that generates a random Pokemon and actually have the trade animation with it.
 
OP
OP
Sagiri
#4
Wonder Trade with NPC Trainers

Basically, this is a WT system that uses the NPC trainer data as the pool of potential trades. It picks a random trainer, and a random slot from that trainer's party, and that's what you get traded.

The repo is here.

In case it's not obvious, this includes the actual trade animation, and the Pokemon in question will keep the NPC trainer's OT ID and OT Name, as well as any held item and custom moves it may have assigned to it.

Additionally, if the Pokemon you receive evolves by trade, it will evolve when you get it. Trade + Held Item evolutions would work as well, but I doubt there are many NPC trainers that have Scythers with Metal Coats (for example).

A few things to keep in mind if you decide to use this:

  • You could end up with a Nat'l Dex Pokemon early. You'll probably want to enable foreign Pokemon trading and evolution, assuming you aren't simply giving out the Nat'l Dex at the beginning of the game.
  • Some NPC trainers such as Giovanni have names that are too long to fit in the OT Name field. These trainers are excluded from the pool.
  • Any Pokemon that isn't included in a team of an eligible trainer can't be received at all.
  • It is possible to get the same Pokemon multiple times.
  • You'll probably trade with a lot of people named GRUNT.