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


Questions & Answers Do you have a problem you need a straight answer for? Ask a question here and get an answer.

Thread Tools Display Modes
Prev Previous Post   Next Post Next
Old 25th January 2017, 03:20 AM   #1
DoomInAJar's Avatar
Join Date: Jan 2017
Location: Canada
Posts: 4
Default Changing wild Pokemon's species in memory

I'm working on a lua script for Pokemon FireRed which changes certain values of wild Pokemon when you meet them (for example, turning a wild pokemon into a Bulbasaur, or changing its moveset). My current issue is with changing the species of the Pokemon.

To do this, I read in the data of the wild Pokemon, decrypt it, find the growth substructure, change the first 2 bytes to the new Pokemon's id, calculate the checksum, re-encrypt the data, and then write it all back to memory. See "Pokémon data substructures in Generation III" on Bulbapedia.

It mostly works fine. I can encounter the modified Pokemon, capture it, and fight with it. The only problem is that when I try to view the Pokemon's summary page, the game usually crashes (I say "usually" because it seems to depend on the species of the Pokemon that was modified: out of the one's I've tried, it consistently crashes for Weedle, Caterpie, Kakuna, and Rattata, and works for Pidgey). Any ideas as to what would cause this? The only thing I can think of is that there's some species specific value that I'm neglecting to change.

For anyone who is inclined, the following is a lua script which reproduces the error. I'm using Pokemon FireRed US V1.1 and the VBA-ReRecording emulator. Save the script as a .lua file, run it in the emulator, and every wild Pokemon you meet (and the first Pokemon in enemy trainers' parties) should become a Bulbasaur.


G = 1
A = 2
E = 3
M = 4
substructureOrders = {
    [0] = {G,A,E,M},
    [1] = {G,A,M,E},
    [2] = {G,E,A,M},
    [3] = {G,E,M,A},
    [4] = {G,M,A,E},
    [5] = {G,M,E,A},
    [6] = {A,G,E,M},
    [7] = {A,G,M,E},
    [8] = {A,E,G,M},
    [9] = {A,E,M,G},
    [10] = {A,M,G,E},
    [11] = {A,M,E,G},
    [12] = {E,G,A,M},
    [13] = {E,G,M,A},
    [14] = {E,A,G,M},
    [15] = {E,A,M,G},
    [16] = {E,M,G,A},
    [17] = {E,M,A,G},
    [18] = {M,G,A,E},
    [19] = {M,G,E,A},
    [20] = {M,A,G,E},
    [21] = {M,A,E,G},
    [22] = {M,E,G,A},
    [23] = {M,E,A,G}
enemyPokemonPointer = 0x0202402C -- pointer to the enemy pokemon's data structure
dataStructureOffset = 32
checksumOffset = 28
substructureSize = 12
pokemonId = 1 -- bulbasaur

function run()
    print('battle started')
    otId = memory.readdword(enemyPokemonPointer+4)
    personality = memory.readdword(enemyPokemonPointer)
    encryptionKey = bit.bxor(otId, personality)
    substructureOrder = substructureOrders[personality%24]

    -- read the data in 12 byte chunks, one chunk for each substructure
    encryptedData = {}
    for i=1,4 do
        encryptedData[i] = memory.readbyterange(enemyPokemonPointer+dataStructureOffset+(i-1)*substructureSize,substructureSize)
    -- decrypt the data
    decryptedData = {}
    for i=1,4 do
        decryptedData[i] = xor(encryptedData[i], encryptionKey)
    -- organize the data
    growth = decryptedData[indexOf(substructureOrder,G)]
    attacks = decryptedData[indexOf(substructureOrder,A)]
    ev = decryptedData[indexOf(substructureOrder,E)]
    misc = decryptedData[indexOf(substructureOrder,M)]
    -- set the pokemon species
    growth[1] =, 0xFF)
    growth[2] = bit.rshift(pokemonId, 8)
    -- reassemble the data
    newUnencryptedData = {}
    newUnencryptedData[indexOf(substructureOrder,G)] = growth
    newUnencryptedData[indexOf(substructureOrder,A)] = attacks
    newUnencryptedData[indexOf(substructureOrder,E)] = ev
    newUnencryptedData[indexOf(substructureOrder,M)] = misc
    -- calculate the checksum
    checksum = calculateChecksum(newUnencryptedData)
    -- re-encrypt the data
    newEncryptedData = {}
    for i=1,4 do
        newEncryptedData[i] = xor(newUnencryptedData[i], encryptionKey)
    -- write the new data
    for i=1,4 do
        for j=1,#newEncryptedData[i] do
            memory.writebyte(enemyPokemonPointer + dataStructureOffset + (i-1)*substructureSize + (j-1), newEncryptedData[i][j])
    memory.writeword(enemyPokemonPointer+checksumOffset, checksum)

function calculateChecksum(unencryptedData)
    sum = 0
    for i=1,#unencryptedData do
        for j=1,#unencryptedData[i],2 do
            sum = sum +
                unencryptedData[i][j] +
    return, 0xFFFF)

-- xor a table of bytes with a double word repeatedly
function xor(byteTable, val)
    valTable = {,0xFF),
        bit.ror(,bit.rol(0xFF,8)), 8),
        bit.ror(,bit.rol(0xFF,16)), 16),
        bit.ror(,bit.rol(0xFF,24)), 24)
    ret = {}
    for i=1,#byteTable do
        ret[i] = bit.bxor(byteTable[i], bit.bxor(valTable[(i-1)%4+1]))
    return ret

-- get the index of val in table. Return 0 if not found
function indexOf(table, val)
    for i=1,#table do
        if table[i] == val then
            return i
    return 0

print('script started')
memory.registerexec(0x08010672,run) -- address is executed whenever a battle starts

Last edited by DoomInAJar; 26th January 2017 at 04:05 AM. Reason: wrapping code in spoiler block for readability
DoomInAJar is offline   Reply With Quote
Sponsored Links

changing, memory, pokemon, species, wild

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 11:29 AM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2019, vBulletin Solutions, Inc. User Alert System provided by Advanced User Tagging (Lite) - vBulletin Mods & Addons Copyright © 2019 DragonByte Technologies Ltd.
Feedback Buttons provided by Advanced Post Thanks / Like (Lite) - vBulletin Mods & Addons Copyright © 2019 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