• 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.

Research: Items

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
Warning: this post's examples and code snippets were taken and\or written for US Fire Red version. Because the engine is virtually the same between all Gen III games, the inner workings should be similar. But you need to find their equivalents in all other versions if you wish to use them.

Also I wish for this topic to gather knowledge for all advance generation games. As such, feel free to post findings on other versions. Moving on...

I find it incredible that we know so little about such a large part of the games we hack. I'm talking about, as the title implies, Items. How do they work, and more importantly, how to make them do what we want? So, for a while now, every now and then, I tackle the subject by examining the code for some simple items: the MasterBall, the Potion and the FireStone. Up until now, I have got little to almost none information out of it, as there are several routines required for them to work. So, I decided to change items a little and tried to decompile Repel: After some tries, I have found that it can actually be changed and allows for the creation of a new items. But first, I start with what items are:
The item data is composed of:
Code:
14 bytes for the name, a string in the same encoding as all in-game strings, finished by 0xff
2 bytes for the item number, that is used for some integrity checks
2 bytes for the item market price
1 byte that I know not it's use
1 byte used for item usage values, such as potion HP recovery or Repel steps
4 bytes for a pointer to a small description of the item, as seen in the market and in the bag screen
2 bytes for something else
1 byte for the number of the pocket the item is stored in (tm case and berry bag included)
1 byte that usually allow to distinguish between between types of item
4 bytes pointer for item's THUMB routine start.
1 byte for item usage location
3 bytes for something else
4 bytes for the battle routine start.
4 bytes for other uses
This data, as one can see, still has many holes and unfound data, even being so small. In here, I hope we can find the total of their workings.
But, most importantly, finding out how they work, and if they can be manipulated, really falls down to examine the two ASM work pointers. I will start with the success case, the REPEL routine. Repel sets its item value to variable 0x4020, that will be decreased every step you take. Here's the routines:
Code:
THE REPEL CALLED ROUTINE
Code:
[FONT=Calibri]ROM:080A1998                 PUSH    {R4,LR}[/FONT]
[FONT=Calibri]ROM:080A199A                 LSL     R0, R0, #0x18[/FONT]
[FONT=Calibri]ROM:080A199C                 LSR     R4, R0, #0x18[/FONT]
[FONT=Calibri]ROM:080A199E                 LDR     R0, =0x4020[/FONT]
[FONT=Calibri]ROM:080A19A0                 BL      sub_806E568  ;loads variable and retrives the value [/FONT]
[FONT=Calibri]ROM:080A19A4                 LSL     R0, R0, #0x10[/FONT]
[FONT=Calibri]ROM:080A19A6                 CMP     R0, #0  ;if there is at least a step remaining, goes to exit function[/FONT]
[FONT=Calibri]ROM:080A19A8                 BNE     loc_80A19CC[/FONT]
[FONT=Calibri]ROM:080A19AA                 MOV     R0, #0x29[/FONT]
[FONT=Calibri]ROM:080A19AC                 BL      sub_80722CC[/FONT]
[FONT=Calibri]ROM:080A19B0                 LDR     R0, =0x3005090 ;this is the routine pointer for later execution (note 1)[/FONT]
[FONT=Calibri]ROM:080A19B2                 LSL     R1, R4, #2[/FONT]
[FONT=Calibri]ROM:080A19B4                 ADD     R1, R1, R4[/FONT]
[FONT=Calibri]ROM:080A19B6                 LSL     R1, R1, #3[/FONT]
[FONT=Calibri]ROM:080A19B8                 ADD     R1, R1, R0[/FONT]
[FONT=Calibri]ROM:080A19BA                 LDR     R0, =(loc_80A19E8+1)[/FONT]
[FONT=Calibri]ROM:080A19BC                 STR     R0, [R1]  ;stores the second routine for later execution[/FONT]
[FONT=Calibri]ROM:080A19BE                 B       loc_80A19D8[/FONT]
[FONT=Calibri]ROM:080A19BE ; ---------------------------------------------------------------------------[/FONT]
[FONT=Calibri]ROM:080A19C0 dword_80A19C0   DCD 0x4020              ; DATA XREF: ROM:080A199Er[/FONT]
[FONT=Calibri]ROM:080A19C4 dword_80A19C4   DCD 0x3005090           ; DATA XREF: ROM:080A19B0r[/FONT]
[FONT=Calibri]ROM:080A19C8 off_80A19C8     DCD loc_80A19E8+1       ; DATA XREF: ROM:080A19BAr[/FONT]
[FONT=Calibri]ROM:080A19CC ; ---------------------------------------------------------------------------[/FONT]
[FONT=Calibri]ROM:080A19CC[/FONT]
[FONT=Calibri]ROM:080A19CC loc_80A19CC                             ; CODE XREF: ROM:080A19A8j[/FONT]
[FONT=Calibri]ROM:080A19CC                 LDR     R2, =unk_841659E ; this is the "already in use sentence"[/FONT]
[FONT=Calibri]ROM:080A19CE                 LDR     R3, =(loc_810A1F8+1) ;this loads the message to be executed in the bag.[/FONT]
[FONT=Calibri]ROM:080A19D0                 ADD     R0, R4, #0[/FONT]
[FONT=Calibri]ROM:080A19D2                 MOV     R1, #2[/FONT]
[FONT=Calibri]ROM:080A19D4                 BL      sub_8108E70[/FONT]
[FONT=Calibri]ROM:080A19D8[/FONT]
[FONT=Calibri]ROM:080A19D8 loc_80A19D8                             ; CODE XREF: ROM:080A19BEj[/FONT]
[FONT=Calibri]ROM:080A19D8                 POP     {R4}[/FONT]
[FONT=Calibri]ROM:080A19DA                 POP     {R0}[/FONT]
[FONT=Calibri]ROM:080A19DC                 BX      R0[/FONT]
[FONT=Calibri]ROM:080A19DC ; ---------------------------------------------------------------------------[/FONT]
[FONT=Calibri]ROM:080A19DE                 DCB    0[/FONT]
[FONT=Calibri]ROM:080A19DF                 DCB    0[/FONT]
[FONT=Calibri]ROM:080A19E0 off_80A19E0     DCD unk_841659E         ; DATA XREF: ROM:loc_80A19CCr[/FONT]
[FONT=Calibri]ROM:080A19E4 off_80A19E4     DCD loc_810A1F8+1       ; DATA XREF: ROM:080A19CEr[/FONT]
 
[FONT=Calibri]THE CHARGE REPEL ROUTINE[/FONT]
[FONT=Calibri]ROM:080A19E8 loc_80A19E8                             ; DATA XREF: ROM:off_80A19C8o[/FONT]
[FONT=Calibri]ROM:080A19E8                 PUSH    {R4-R6,LR}[/FONT]
[FONT=Calibri]ROM:080A19EA                 LSL     R0, R0, #0x18[/FONT]
[FONT=Calibri]ROM:080A19EC                 LSR     R6, R0, #0x18[/FONT]
[FONT=Calibri]ROM:080A19EE                 BL      sub_80723E0[/FONT]
[FONT=Calibri]ROM:080A19F2                 LSL     R0, R0, #0x18[/FONT]
[FONT=Calibri]ROM:080A19F4                 CMP     R0, #0[/FONT]
[FONT=Calibri]ROM:080A19F6                 BNE     loc_80A1A2A[/FONT]
[FONT=Calibri]ROM:080A19F8                 LDR     R4, =0x203AD30 ;used item number location[/FONT]
[FONT=Calibri]ROM:080A19FA                 LDRH    R2, [R4][/FONT]
[FONT=Calibri]ROM:080A19FC                 LDR     R3, =0xFFFF[/FONT]
[FONT=Calibri]ROM:080A19FE                 MOV     R0, #4[/FONT]
[FONT=Calibri]ROM:080A1A00                 MOV     R1, #0[/FONT]
[FONT=Calibri]ROM:080A1A02                 BL      sub_80A2294[/FONT]
[FONT=Calibri]ROM:080A1A06                 LDR     R5, =0x4020[/FONT]
[FONT=Calibri]ROM:080A1A08                 LDRH    R0, [R4][/FONT]
[FONT=Calibri]ROM:080A1A0A                 BL      sub_809A948     ;this is the routine that gets the actual repel value[/FONT]
[FONT=Calibri]ROM:080A1A0E                 ADD     R1, R0, #0      [/FONT]
[FONT=Calibri]ROM:080A1A10                 LSL     R1, R1, #0x18[/FONT]
[FONT=Calibri]ROM:080A1A12                 LSR     R1, R1, #0x18[/FONT]
[FONT=Calibri]ROM:080A1A14                 ADD     R0, R5, #0[/FONT]
[FONT=Calibri]ROM:080A1A16                 BL      sub_806E584     ;this is the function that stores values in a variable[/FONT]
[FONT=Calibri]ROM:080A1A1A                 BL      sub_80A1A44     ;this one buffers the string to print "[player] used [Item]"[/FONT]
[FONT=Calibri]ROM:080A1A1E                 LDR     R2, =0x2021D18[/FONT]
[FONT=Calibri]ROM:080A1A20                 LDR     R3, =(loc_810A1F8+1) ; the same function as in the exit. ends with a return to the bag screen. [/FONT]
[FONT=Calibri]ROM:080A1A22                 ADD     R0, R6, #0[/FONT]
[FONT=Calibri]ROM:080A1A24                 MOV     R1, #2[/FONT]
[FONT=Calibri]ROM:080A1A26                 BL      sub_8108E70[/FONT]
[FONT=Calibri]ROM:080A1A2A[/FONT]
[FONT=Calibri]ROM:080A1A2A loc_80A1A2A                             ; CODE XREF: ROM:080A19F6j[/FONT]
[FONT=Calibri]ROM:080A1A2A                 POP     {R4-R6}[/FONT]
[FONT=Calibri]ROM:080A1A2C                 POP     {R0}[/FONT]
[FONT=Calibri]ROM:080A1A2E                 BX      R0[/FONT]
[FONT=Calibri]ROM:080A1A2E ; ---------------------------------------------------------------------------[/FONT]
[FONT=Calibri]ROM:080A1A30 dword_80A1A30   DCD 0x203AD30           ; DATA XREF: ROM:080A19F8r[/FONT]
[FONT=Calibri]ROM:080A1A34 dword_80A1A34   DCD 0xFFFF              ; DATA XREF: ROM:080A19FCr[/FONT]
[FONT=Calibri]ROM:080A1A38 dword_80A1A38   DCD 0x4020              ; DATA XREF: ROM:080A1A06r[/FONT]
[FONT=Calibri]ROM:080A1A3C dword_80A1A3C   DCD 0x2021D18           ; DATA XREF: ROM:080A1A1Er[/FONT]
[FONT=Calibri]ROM:080A1A40 off_80A1A40     DCD loc_810A1F8+1       ; DATA XREF: ROM:080A1A20r[/FONT]
[FONT=Calibri]ROM:0809A948 sub_809A948                             ; CODE XREF: sub_803ECEC+C2p[/FONT]
[FONT=Calibri]ROM:0809A948                                         ; sub_803ECEC+10Cp ...[/FONT]
[FONT=Calibri]ROM:0809A948                 PUSH    {R4,LR}[/FONT]
[FONT=Calibri]ROM:0809A94A                 LSL     R0, R0, #0x10[/FONT]
[FONT=Calibri]ROM:0809A94C                 LSR     R0, R0, #0x10[/FONT]
[FONT=Calibri]ROM:0809A94E                 LDR     R4, =unk_83DB028     ;item table pointer[/FONT]
[FONT=Calibri]ROM:0809A950                 BL      sub_809A8A4        ;checks for item_validity (note 2)[/FONT]
[FONT=Calibri]ROM:0809A954                 LSL     R0, R0, #0x10[/FONT]
[FONT=Calibri]ROM:0809A956                 LSR     R0, R0, #0x10[/FONT]
[FONT=Calibri]ROM:0809A958                 MOV     R1, #0x2C[/FONT]
[FONT=Calibri]ROM:0809A95A                 MUL     R0, R1[/FONT]
[FONT=Calibri]ROM:0809A95C                 ADD     R0, R0, R4[/FONT]
[FONT=Calibri]ROM:0809A95E                 LDRB    R0, [R0,#0x13][/FONT]
[FONT=Calibri]ROM:0809A960                 POP     {R4}[/FONT]
[FONT=Calibri]ROM:0809A962                 POP     {R1}[/FONT]
[FONT=Calibri]ROM:0809A964                 BX      R1[/FONT]
(note 1): this is the location in the RAM the game keeps the pointers for the functions it must execute next. It's like a small queue that allows the game to run several things "at once". This is very important because this allows for things like separate screens to exist, such as the normal map, town map, trainer card or bag. The routine for the bag screen to come forward is at 0x08108F0C.
(note 2): this function is called on every piece of code that concerns items. This code checks if they are the e-berries, or even if they are legal. To increase the number of items, one needed to expand the number in here (currently 0x176) first. But in theory, that would allow us to have 65535 items allowed.
In order for an item to disappear after use, you need to use a disappearing code such as the one in 0x0809A1D8. This routine is, in fact, called for every item removal. It receives r0 as the item number and R1 as the number of items to remove.
The Items in the bag, in the RAM, are stored in a very annoying way, made to throw cheaters away from editing them. Just like the pokémon box data, they are stored dynamically, and are accessed by going to the pointers indicated in 0x0203988c + (pocket_number * 8) , adding to that pointer the item slot, and finally going to 0x300500c, add to the pointer there f20, and performing an exclusive OR between the two values.
Here, I shall present an example of a new functional item, a portable pokecenter. It will always be spent when used, even if the pokémon were fully healed. But if you can follow that routine, you most likely can come up with a solution to that problem if you wish to use it. Also, the message loaded is the normal "repel is already on" message, so feel free to change it.
Code:
.align 2
.thumb
     push {r4, lr}
     ldr r0, item_location
     ldrh r0, [r0]
     mov r1, #0x1
     bl call_erase_me
     bl call_special0
     mov r0, #0x0
     mov r1, #0x2
     ldr r2, phrase
     ldr r3, message_manage
     bl call_loader
     pop {r4, pc}
    .hword 0x0000
item_location: .word 0x0203ad30
phrase: .word 0x841659e
message_manage: .word 0x0810a1f9
 
call_erase_me: ldr r4, erase_me 
                            bx r4
erase_me: .word 0x0809A1D9
 
call_special0: ldr r0, special_0
                          bx r0
special_0: .word 0x080a0059
 
call_loader: ldr r4, loader_addr 
                       bx r4
loader_addr: .word 0x08108E71
And this is it for now. With this thread I have started an overlook on the Item world, that remains dark for too long. I hope that you who understand this can help understand the Items in all their workings.


So, I ask you to share any information you have on the subject, namely, these three main topics (for starters):
  1. How can we create new pokeball behaviours, such as new catch rates and specific pokemon targeting (any of the custom balls);
  2. How are the Pokemon submenu items (potions, berries, stones, TM's) organized, so we can create more of the above items;
  3. (ultimate goal) It is possible, but how can we create items that execute scripts.
 

ZodiacDaGreat

Working on a Mobile System
429
Posts
17
Years
I've tried to make one item that executes scripts. I called a script from ASM in a item, if the type is set to others - the script doesn't work but when it was set to 'Use-Of-Select' type. The bag exited and the script worked but it kept running. So, I guess the bag's environment isn't compatible with the scripts - this is what I first thought too until I saw Juan's explanation of script execution. I've never initialised the script structure. So, I'll try again and post some discoveries.

Ah, I found a way to show a message box from examining the coin case.
Code:
	push {r4-r5, lr}

	mov r0, #0x0 @ Textbox left position*
	mov r1, #0x0 @ Textbox top position*
	ldr r2, =0x0890000B @ Text data**
	ldr r5, .DisplayTextFunc
	bl .BranchLink

	pop {r4-r5}
	pop {r0}
	bx r0

.BranchLink:
	bx r5
* I'm not sure about this as I haven't really messed with it. It could be the other way round.
** The text data must end with '\c\h09'. The wait for keypress code or else it'll display the text and close down.
 
Last edited:

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
I have sucessfully managed to execute a script through an item. But it is still quite buggy. First, I bring you what matters most, the code:
Code:
/*this routine loads a fixed script and runs it*/
New_item_start:  push {r4,lr}
                        ldr r0, script
                        bl script_executer
                        ldr R1, Script_flag_set
                        mov R0, #0x1
                        strb R0, [R1]
                        bl bag_exiter
                        pop {r4,pc}

script: .word 0x08801000 /*the script to execute after leaving the bag*/
Script_flag_set: .word 0x03000f9c
 
script_executer:  ldr r1, exe_addr
                        bx r1
exe_addr: .word 0x08069AE5
 
bag_exiter:  ldr r1, exit_code
                 bx r1
exit_code: .word 0x080a103d
(ps: this code does not delete the item used.)
This will sucessfully run a piece of code through the map, just like a normal script. This code has been tested for all pockets, and only works if it's type byte is not 2. In fact, the type byte 2 seems to be used by the game to determine if a code is stored for posterior execution. Items with 2 on it that try to leave the bag will trigger a condition that calls for another type of exit.
As for the bugs, I must say, it all comes down to one: the Start menu.
The game exits the bag and re-opens the menu, a convenient feature most of the time. But, when the game exits the bag and asks to run a script, the game will run both at once. That, besides being graphically ugly (see attatchment), is also very bad for us, as the control scheme for the menu overwrite the normal world controls, and pressing A will automatically open the bag menu once more. Depending on how you start the script, or how you encode your Item, this bug may not be a problem. Using any scripting function that overwrites the Overworld screen, or that refreshes the map entirely (and there are many, refreshing with warps, loading the PC screens, the pokemon selection menu, the town map special...) the bug is no longer important. Also, being a fully functional script, it also allows trainer battles and set wild battles, but message boxes, multichoice boxes (like the Yes\no one) and the like will not work until the map is refreshed somehow.
And here is where I ask for your help. I can't seem to find the location the Menu opens after the return for the OW map (as all of the OW functions located at the function execution table at 0x3005090 seem to be the same when it's on or not, only one byte changes from 0xff to something else, and changing it will crash the game). So, I will place here some of the bag_exit routine, and hope someone will be able to find where to change it to prevent the menu when one of our items runs.
Code:
ROM:080A103C Bag_exit                                ; CODE XREF: Bicycle_func+AAp
ROM:080A103C                                         ; ROM:080A1352p ...
ROM:080A103C                 PUSH    {LR}
ROM:080A103E                 LSL     R0, R0, #0x18
ROM:080A1040                 LSR     R2, R0, #0x18
ROM:080A1042                 LDR     R1, =0x3005090
ROM:080A1044                 LSL     R0, R2, #2
ROM:080A1046                 ADD     R0, R0, R2
ROM:080A1048                 LSL     R0, R0, #3
ROM:080A104A                 ADD     R0, R0, R1
ROM:080A104C                 MOV     R1, #0xE
ROM:080A104E                 LDRSH   R0, [R0,R1]
ROM:080A1050                 CMP     R0, #1
ROM:080A1052                 BEQ     loc_80A1070
ROM:080A1054                 LDR     R1, =0x3005020
ROM:080A1056                 LDR     R0, =(loc_80A1084+1)
ROM:080A1058                 STR     R0, [R1]
ROM:080A105A                 ADD     R0, R2, #0
ROM:080A105C                 BL      sub_80A0FBC
ROM:080A1060                 B       loc_80A107A
ROM:080A1060 ; ---------------------------------------------------------------------------
ROM:080A1062                 DCB    0
ROM:080A1063                 DCB    0
ROM:080A1064 dword_80A1064   DCD 0x3005090           ; DATA XREF: Bag_exit+6r
ROM:080A1068 dword_80A1068   DCD 0x3005020           ; DATA XREF: Bag_exit+18r
ROM:080A106C off_80A106C     DCD loc_80A1084+1       ; DATA XREF: Bag_exit+1Ar
ROM:080A1070 ; ---------------------------------------------------------------------------
ROM:080A1070
ROM:080A1070 loc_80A1070                             ; CODE XREF: Bag_exit+16j
ROM:080A1070                 LDR     R0, =0x2039998
ROM:080A1072                 LDR     R1, [R0]
ROM:080A1074                 ADD     R0, R2, #0
ROM:080A1076                 BL      sub_81E3BAC
ROM:080A107A
ROM:080A107A loc_80A107A                             ; CODE XREF: Bag_exit+24j
ROM:080A107A                 POP     {R0}
ROM:080A107C                 BX      R0
ROM:080A107C ; End of function Bag_exit
ROM:080A107C
ROM:080A107C ; ---------------------------------------------------------------------------
ROM:080A107E                 DCB    0
ROM:080A107F                 DCB    0
ROM:080A1080 dword_80A1080   DCD 0x2039998           ; DATA XREF: Bag_exit:loc_80A1070r
ROM:080A1084 ; ---------------------------------------------------------------------------
ROM:080A0FBC sub_80A0FBC                             ; CODE XREF: Bag_exit+20p
ROM:080A0FBC                                         ; sub_80A16D0+6p
ROM:080A0FBC                 PUSH    {R4,R5,LR}
ROM:080A0FBE                 LSL     R0, R0, #0x18
ROM:080A0FC0                 LSR     R5, R0, #0x18
ROM:080A0FC2                 LDR     R1, =0x203AD30
ROM:080A0FC4                 LDRH    R0, [R1]
ROM:080A0FC6                 CMP     R0, #0xAF
ROM:080A0FC8                 BNE     loc_80A0FE0
ROM:080A0FCA                 LDR     R0, =0x3005090
ROM:080A0FCC                 LSL     R1, R5, #2
ROM:080A0FCE                 ADD     R1, R1, R5
ROM:080A0FD0                 LSL     R1, R1, #3
ROM:080A0FD2                 ADD     R1, R1, R0
ROM:080A0FD4                 LDRB    R0, [R1,#0x10]
ROM:080A0FD6                 B       loc_80A0FE6
ROM:080A0FD6 ; ---------------------------------------------------------------------------
ROM:080A0FD8 dword_80A0FD8   DCD 0x203AD30           ; DATA XREF: sub_80A0FBC+6r
ROM:080A0FDC dword_80A0FDC   DCD 0x3005090           ; DATA XREF: sub_80A0FBC+Er
ROM:080A0FE0 ; ---------------------------------------------------------------------------
ROM:080A0FE0
ROM:080A0FE0 loc_80A0FE0                             ; CODE XREF: sub_80A0FBC+Cj
ROM:080A0FE0                 LDRH    R0, [R1]
ROM:080A0FE2                 BL      get_item_type
ROM:080A0FE6
ROM:080A0FE6 loc_80A0FE6                             ; CODE XREF: sub_80A0FBC+1Aj
ROM:080A0FE6                 SUB     R0, #1
ROM:080A0FE8                 LSL     R0, R0, #0x18
ROM:080A0FEA                 LSR     R4, R0, #0x18
ROM:080A0FEC                 LDR     R0, =0x203AD30
ROM:080A0FEE                 LDRH    R0, [R0]
ROM:080A0FF0                 BL      sub_809A260
ROM:080A0FF4                 LSL     R0, R0, #0x18
ROM:080A0FF6                 LSR     R0, R0, #0x18
ROM:080A0FF8                 CMP     R0, #5
ROM:080A0FFA                 BNE     loc_80A1018
ROM:080A0FFC                 LDR     R0, =off_83E2954
ROM:080A0FFE                 LSL     R1, R4, #2
ROM:080A1000                 ADD     R1, R1, R0
ROM:080A1002                 LDR     R0, [R1]
ROM:080A1004                 BL      sub_813D934
ROM:080A1008                 ADD     R0, R5, #0
ROM:080A100A                 BL      sub_813D808
ROM:080A100E                 B       loc_80A1032
ROM:080A100E ; ---------------------------------------------------------------------------
ROM:080A1010 dword_80A1010   DCD 0x203AD30           ; DATA XREF: sub_80A0FBC+30r
ROM:080A1014 off_80A1014     DCD off_83E2954         ; DATA XREF: sub_80A0FBC+40r
ROM:080A1018 ; ---------------------------------------------------------------------------
ROM:080A1018
ROM:080A1018 loc_80A1018                             ; CODE XREF: sub_80A0FBC+3Ej
ROM:080A1018                 LDR     R0, =off_83E2954
ROM:080A101A                 LSL     R1, R4, #2
ROM:080A101C                 ADD     R1, R1, R0
ROM:080A101E                 LDR     R0, [R1]
ROM:080A1020                 BL      sub_8108EE0
ROM:080A1024                 CMP     R4, #1
ROM:080A1026                 BNE     loc_80A102C
ROM:080A1028                 BL      sub_8108CB4
ROM:080A102C
ROM:080A102C loc_80A102C                             ; CODE XREF: sub_80A0FBC+6Aj
ROM:080A102C                 ADD     R0, R5, #0
ROM:080A102E                 BL      sub_8108B50
ROM:080A1032
ROM:080A1032 loc_80A1032                             ; CODE XREF: sub_80A0FBC+52j
ROM:080A1032                 POP     {R4,R5}
ROM:080A1034                 POP     {R0}
ROM:080A1036                 BX      R0
ROM:080A1036 ; End of function sub_80A0FBC
ROM:080A1036
ROM:080A1036 ; ---------------------------------------------------------------------------
ROM:080A1038 off_80A1038     DCD off_83E2954         ; DATA XREF: sub_80A0FBC:loc_80A1018r
 
219
Posts
16
Years
I have sucessfully managed to execute a script through an item. But it is still quite buggy. First, I bring you what matters most, the code:
Code:
/*this routine loads a fixed script and runs it*/
New_item_start:  push {r4,lr}
                        ldr r0, script
                        bl script_executer
                        ldr R1, Script_flag_set
                        mov R0, #0x1
                        strb R0, [R1]
                        bl bag_exiter
                        pop {r4,pc}
 
script: .word 0x08801000 /*the script to execute after leaving the bag*/
Script_flag_set: .word 0x03000f9c
 
script_executer:  ldr r1, exe_addr
                        bx r1
exe_addr: .word 0x08069AE5
 
bag_exiter:  ldr r1, exit_code
                 bx r1
exit_code: .word 0x080a103d
(ps: this code does not delete the item used.)
Well,I'm not sure,but what I saw of the mach bike,is running the 0x080a103d before it works in the map.
I don't have my own computer for now,so I can't research deeper,sorry...
Hope this helps...
 

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
I created an alternative way to run the script through an item, using a really cheap method.
The return is executed as normally would, and the script starts executing as soon as it leaves the bag.
But the script it runs is not the one the hacker placed therem but one at a fixed ROM position.
A "header" that forces the BAG window to close before starting to execute the main code.
This is the final result asm source file, the version I shall make available in my next hack release:
Code:
.align 2
.thumb
/*this is the final result of the first stage of item usage. This code will allow you to run scripts through an item.
 
Usage Requirements:
Compiled key_change_hack;
compiled special 0xc9 (force key), even if not as a special
 
to use:
1 - place the pointer to this routine in the OW execution word of the item of your choice
2 - Place the Script pointer in the last word (right next to the name of the next item)
2.5 - this code does not remove the item, to make it easier to code Key items. Just place in your script somewhere a "removeitem item_num 0x1" to destroy it.
This has been made to run trainer battles, map display, pokemon selection screen and some specials. seems to work well with all scripting commands.
*/
New_item_start:  push {r4,lr}
                        ldr r0, RAM_item
                        ldrh r0, [r0]
                        mov r1, #0x2c
                        mul r0, r1
                        ldr r1, item_addr
                        add r0, r0, r1
                        ldr r4, [r0]            /*loads item script*/
 
                        ldr r2, script_area
                        str r4, [r2]
                        mov r4, #0x5
                        sub r2, #0x1
                        strb r4, [r2]       /*creates a bypass for our real script*/
                        ldr r0, script_pos /*the starter script with the menu shut*/
                        bl script_executer
                        ldr     R1, Script_flag_set
                        mov  R0, #0x1
                        strb R0, [R1]
                        bl bag_exiter
                        pop {r4,pc}
 
RAM_item: .word 0x0203ad30
item_addr: .word 0x083db050
Script_flag_set: .word 0x03000f9c
 
script_executer: ldr r1, exe_addr
                       bx r1
exe_addr: .word 0x08069AE5
 
bag_exiter:  ldr r1, exit_code
                 bx r1
 
exit_code: .word 0x080a103d
 
script_area: .word 0x0203f3ec
script_pos: .word 0x00000000
 
/*
This script must be compiled and put somewhere in the rom, and the pointer to this placed in script_pos. 
Compiled version already comes with it compiled, but you need to put the pointer in the 4 zero bytes area in the middle of the code.
 
copyvar 0x8012 0x8004
copyvar 0x8013 0x8005
setvar 0x8004 0x2
setvar 0x8005 0x7
pause 0x16
special 0xC9 'or callasm @location_you_put_key_presser
pause 0x1
copyvar 0x8004 0x8012
copyvar 0x8005 0x8013
goto 0x203F3EB
*/
And the dependencies
Code:
.align 2
.thumb
/*
This code will be replacing the two lines at 0x80005e8, the Key loader
This will, at an end, allow for us to input keys of our own, to create several key-manipulation techniques. Examples:
 key inibition; key replacement.
to apply, place a bx r0 (00 47) at 0x080005ec and replace the pointer at 0x08000624 to this routine's one.
 
Stuff this will do:
type of change will depend on 0203f4ec contents.
bit set -> effect
0 -> change value to and with, so it makes the game believe a certain key is pressed.
       This key value is placed at 0x0203f4de.
1 -> makes sure a key is not detected. However, that key is allways detected by the key specials.
      Allows you to delete the access to the help menu, or start, etc.
      That keymap must be placed at 0203f4fc.
2 -> if the player is in the OW, forces the script at 0203f4f4 to run when a key in 
       the position 0x0203f4ee
*/
Key_wrecker_start: push {r4-r7} 
                           ldr r0, key_addr
                           ldrh r0, [r0]
                           ldr r4, switches
                           ldrb r1, [r4]
                           cmp r1, #0x0
                           beq nothing_to_do
                           bl check_for_script
                           bl check_for_forced
                           bl check_for_cancel
nothing_to_do:       pop {r4-r7}
                           add r1, r0, #0x0
                           ldr r0, return_place
                           bx r0
.hword 0x0000
key_addr: .word 0x04000130
switches: .word 0x0203f4ec
return_place: .word 0x080005ef
 
check_for_script: push {r0-r1,lr}
                        mov r2, #0x4       /*check for flag*/
                        and r2, r1
                        cmp r2, #0x0
                        beq end_all
                        ldr r5, script_on    /*check if a script is executing*/
                        ldrb r3, [r5]
                        cmp r3, #0x1
                        beq end_all
                        ldrh r2, [r4, #0x2]  /*check if the keys are pressed*/
                        and r2, r0
                        cmp r2, #0x0
                        bne end_all
 
                        ldr r3, first_function    /*checks if we are in the OW*/
                        ldr r2, [r3]
                        ldr r3, OW_function
                        cmp r2, r3
                        bne end_all
 
                        mov r2, #0x1 /*if all is ok, run script*/
                        strb r2, [r5]
                        ldr  r0, [r4, #0x8]
                        bl script_executer
end_all:              pop {r0-r1,pc}
 
script_on: .word 0x03000f9c
first_function: .word 0x03005090
OW_function: .word 0x08079e0d
 
check_for_forced: push {lr}
                         mov r2, #0x1 /*check for flag*/
                         and r2, r1
                         cmp r2, #0x0
                         beq end_all_2
                         ldrb r2, [r4, #0x1] /*check if it should apply it*/
                         cmp r2, #0x0
                         beq end_all_2
 
                         sub r2, #0x1
                         strb r2, [r4, #0x1] /*updates counter*/
                         sub r4, #0xE
                         ldrh r2, [r4]     /*gets keymap to force*/
                         add r4, #0xe
                         mvn r2, r2
                         and r0, r2         /*keys changed*/
end_all_2:           pop {pc}
 
check_for_cancel: push {lr}
                         mov r2, #0x2 /*check for flag*/
                         and r2, r1
                         cmp r2, #0x0
                         beq end_all_3
 
                         ldr r2, [r4, #0x10] /*loads key to ignore*/
                         orr r0, r2 /*ignored*/
end_all_3:           pop {pc}
 
script_executer: ldr r1, exe_addr
                       bx r1
exe_addr: .word 0x08069AE5
The special is only a setter for the Force B flag. If you understood the above code, you could probably do the same with writebytes
Code:
.align 2
.thumb
/*this code forces one keyMap stored on your ROM to run for the asked number of times
Special c9 receives:
0x8004 is the keys to set
0x8005 is the times to set it to
returns nothing
*/
Special_c9:  push {r0-r2, lr}
                 ldr r0, key_store_addr
                 ldr r2, var_8004
                 ldrh r1, [r2]
                 strh r1, [r0]
                 ldrh r1, [r2, #0x2]
                 add r0, #0xe
                 strb r1, [r0, #0x1]
                 ldrb r1, [r0]
                 mov r2, #0x1
                 orr r1, r2
                 strb r1, [r0]
                 pop {r0-r2, pc}
 
.hword 0x0000
 
key_store_addr: .word 0x0203f4de
var_8004: .word 0x020370c0
This method will only not work if the player holds down the B button at the same time it exits the bag.
It could be made certain that B button was not pressed at least for one check, but probably wouldn't be worth it.

And that's it for Script Items. Just as mentioned in the code above, this is a generic item for any OW activity, and as such, is only guaranteed to work at the Overworld. Next step, finding the battle items code and usages.
 
219
Posts
16
Years
How are the Pokemon submenu items (potions, berries, stones, TM's) organized, so we can create more of the above items;

I trace two potions for days and got some useful information.

There is a group of pointers stored at 0x2528BC in the ROM,about items,which includes potions.
These pointers are just base pointers.
Trace with you debugger at 0x08126C9C.
There is a algorithm about pointers.
And finally,the HP points the potion can restore is store in 0x03007D68 which will be a pointer in SP when run at 0x08041C58.
BTW,as a little hack profit,change the value at 0x2526F2 in ROM can change the value that normal potion restored.

This is for now.
Sorry the routine is so complex that I can't figure it out...

Edit:
The key point around different potions is at 0x08126C68.
When run to here,this is what happened:
Code:
08126C68 b530 push {r4,r5,lr}
08126C6A 0400 lsl r0, r0, #0x10
08126C6C 0c01 lsr r1, r0, #0x10
08126C6E 1c0a add r2, r1, #0x0
08126C70 3a0d sub r2, #0xd
08126C72 0410 lsl r0, r2, #0x10
08126C74 0c00 lsr r0, r0, #0x10
08126C76 28a5 cmp r0, #0xa5
08126C78 d900 bls $08126c7c
08126C7C 29af cmp r1, #0xaf
08126C7E d109 bne $08126c94
08126C94 490b ldr r1, [$08126cc4] (=$082528bc)
08126C96 0090 lsl r0, r2, #0x02
08126C98 1840 add r0, r0, r1
 
Last edited:

kittopian

Source engine coder
11
Posts
14
Years
I am sorry if I am off topic but for the "ITEMS RUNNING SCRIPTS" ASM code, could someone explain how to convert it to PKMN RUBY (AXVE)? Any help will be greatly appreciated (PS, I do not know much about ASM). Also how do I insert them?
 
Last edited:

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
After studying battle scripts (here), I've come across the code that kept coming around to bug me, the capture rate function. Turns out, the reason we couldn't find it on the item function was because the ball function is, in fact, a simple generic "load item" function with a "search if there's room for more pokemon" function attached. The battle script function was the one that did all the work.
But this function alone was not enough. The game has two checks preventing an item to be used in battle if not real. The first one was the one who loaded the script. An extensive search for the script assumed address linked to a table, and that table linked to a function that kept all allowed items to run scripts for. The function is too big (and largely irrelevant), so only part of it will be posted:
Code:
ItemScript_Activator
ROM:08016418                 PUSH    {R4-R7,LR}
ROM:0801641A                 MOV     R7, R10
ROM:0801641C                 MOV     R6, R9
ROM:0801641E                 MOV     R5, R8
ROM:08016420                 PUSH    {R5-R7}
ROM:08016422                 LDR     R4, =0x2023D6B
ROM:08016424                 LDR     R2, =0x2023D6C
ROM:08016426                 LDR     R1, =0x2023BDE
ROM:08016428                 LDR     R0, =0x2023BE2
ROM:0801642A                 LDRB    R0, [R0]
ROM:0801642C                 ADD     R0, R0, R1
ROM:0801642E                 LDRB    R0, [R0]
ROM:08016430                 STRB    R0, [R2]
ROM:08016432                 STRB    R0, [R4]
ROM:08016434                 LDR     R0, =0x2022974
ROM:08016436                 MOV     R1, #0
ROM:08016438                 STRH    R1, [R0]
ROM:0801643A                 LDR     R0, =0x2022976
ROM:0801643C                 STRH    R1, [R0]
ROM:0801643E                 LDRB    R0, [R4]
ROM:08016440                 BL      sub_801CFE4
ROM:08016444                 LDR     R5, =0x2023D68
ROM:08016446                 LDR     R2, =0x20233C4
ROM:08016448                 LDRB    R1, [R4]
ROM:0801644A                 LSL     R1, R1, #9
ROM:0801644C                 ADD     R0, R2, #1
ROM:0801644E                 ADD     R0, R1, R0
ROM:08016450                 LDRB    R3, [R0]
ROM:08016452                 ADD     R2, #2
ROM:08016454                 ADD     R1, R1, R2
ROM:08016456                 LDRB    R0, [R1]
ROM:08016458                 LSL     R0, R0, #8
ROM:0801645A                 ORR     R3, R0
ROM:0801645C                 STRH    R3, [R5]
ROM:0801645E                 ADD     R1, R3, #0
ROM:08016460                 CMP     R1, #0xC     [B];number of pokeball types[/B]
ROM:08016462                 BHI     loc_801649C     ;larger means other item
ROM:08016464                 LDR     R2, =0x2023D74
ROM:08016466                 LDR     R1, =PokeBall_Toss
ROM:08016468                 LDRH    R0, [R5]
ROM:0801646A                 LSL     R0, R0, #2
ROM:0801646C                 ADD     R0, R0, R1
ROM:0801646E                 LDR     R0, [R0]
ROM:08016470                 STR     R0, [R2]
ROM:08016472                 B       loc_80164FC
ROM:08016472 ; ---------------------------------------------------------------------------
ROM:08016474 dword_8016474   DCD 0x2023D6B           ; DATA XREF: ROM:08016422r
ROM:08016478 dword_8016478   DCD 0x2023D6C           ; DATA XREF: ROM:08016424r
ROM:0801647C dword_801647C   DCD 0x2023BDE           ; DATA XREF: ROM:08016426r
ROM:08016480 dword_8016480   DCD 0x2023BE2           ; DATA XREF: ROM:08016428r
ROM:08016484 dword_8016484   DCD 0x2022974           ; DATA XREF: ROM:08016434r
ROM:08016488 dword_8016488   DCD 0x2022976           ; DATA XREF: ROM:0801643Ar
ROM:0801648C dword_801648C   DCD 0x2023D68           ; DATA XREF: ROM:08016444r
ROM:08016490 dword_8016490   DCD 0x20233C4           ; DATA XREF: ROM:08016446r
ROM:08016494 dword_8016494   DCD 0x2023D74           ; DATA XREF: ROM:08016464r
ROM:08016498 off_8016498     DCD PokeBall_Toss       ; DATA XREF: ROM:08016466r
ROM:0801649C ; ---------------------------------------------------------------------------
ROM:0801649C
ROM:0801649C loc_801649C                             ; CODE XREF: ROM:08016462j
ROM:0801649C                 ADD     R0, R3, #0
ROM:0801649E                 SUB     R0, #0x50
ROM:080164A0                 LSL     R0, R0, #0x10
ROM:080164A2                 LSR     R0, R0, #0x10
ROM:080164A4                 CMP     R0, #1 [B];0x50 and 0x51 are the pokedoll and fluffy tail[/B] [B]items[/B]
ROM:080164A6                 BHI     loc_80164C4
ROM:080164A8                 LDR     R0, =0x2023D74
ROM:080164AA                 LDR     R1, =off_81D99FC
ROM:080164AC                 LDR     R1, [R1]
ROM:080164AE                 STR     R1, [R0]
ROM:080164B0                 LDR     R1, =0x2023BE3
ROM:080164B2                 MOV     R10, R1
ROM:080164B4                 B       loc_801671E
ROM:080164B4 ; ---------------------------------------------------------------------------
ROM:080164B6                 DCB    0
ROM:080164B7                 DCB    0
ROM:080164B8 dword_80164B8   DCD 0x2023D74           ; DATA XREF: ROM:080164A8r
ROM:080164BC off_80164BC     DCD off_81D99FC         ; DATA XREF: ROM:080164AAr
ROM:080164C0 dword_80164C0   DCD 0x2023BE3           ; DATA XREF: ROM:080164B0r
ROM:080164C4 ; ---------------------------------------------------------------------------
ROM:080164C4
ROM:080164C4 loc_80164C4                             ; CODE XREF: ROM:080164A6j
ROM:080164C4                 MOVL    R0, 0x15E  [B];check for the pokeflute[/B]
ROM:080164C8                 CMP     R1, R0
ROM:080164CA                 BNE     loc_80164E8
ROM:080164CC                 LDR     R0, =0x2023D74
ROM:080164CE                 LDR     R1, =off_81D99FC
ROM:080164D0                 LDR     R1, [R1,#4]
ROM:080164D2                 STR     R1, [R0]
ROM:080164D4                 LDR     R2, =0x2023BE3
ROM:080164D6                 MOV     R10, R2
ROM:080164D8                 B       loc_801671E
ROM:080164D8 ; ---------------------------------------------------------------------------
ROM:080164DA                 DCB    0
ROM:080164DB                 DCB    0
ROM:080164DC dword_80164DC   DCD 0x2023D74           ; DATA XREF: ROM:080164CCr
ROM:080164E0 off_80164E0     DCD off_81D99FC         ; DATA XREF: ROM:080164CEr
ROM:080164E4 dword_80164E4   DCD 0x2023BE3           ; DATA XREF: ROM:080164D4r
This code is still quite big, so focus on the item-indicating lines. Directly below them there are some lines of code that load a script onto the BattleScript Pointer (0x2023D74). But those scripts are limited for the balls, pointing to a table of 12 positions. What is their content? This is it:
Code:
ROM:081D99B0 PokeBall_Toss   DCD unk_81D9A14         
ROM:081D99B4                 DCD unk_81D9A14
ROM:081D99B8                 DCD unk_81D9A14
ROM:081D99BC                 DCD unk_81D9A14
ROM:081D99C0                 DCD unk_81D9A14
ROM:081D99C4                 DCD unk_81D9A3C
ROM:081D99C8                 DCD unk_81D9A14
ROM:081D99CC                 DCD unk_81D9A14
ROM:081D99D0                 DCD unk_81D9A14
ROM:081D99D4                 DCD unk_81D9A14
ROM:081D99D8                 DCD unk_81D9A14
ROM:081D99DC                 DCD unk_81D9A14
ROM:081D99E0                 DCD unk_81D9A14
As you can see, apart from one, they are the same script. The different one is the Safari Ball.
Now, imagine you want a new type of ball. Repointing the table would work if it wasn't for a simple detail: Potions (0xd) are handy items, but adding one entry would replace their behaviour for this one. Unless you're interested in a potion that captures pokemon and does not heal them, this wouldn't be the best solution. Instead, let's repoint the whole relevant part of this function and work from it. Here's my take:
Code:
.align 2
.thumb
/*This function is meant to replace the one at 08016460, where the choice
of which item is delegated to. 
08016460 to 08016463 will be replaced by a null op (0s), 08016464 stays as-is and 08016466 will be replaced by a bx r2 (10 47). At 0x08016494, place pointer to this*/
/*r1,r3 are the item, r5 is the item temp storage addr*/
/*this code will simulate the interesting part of the code, not using redundant functions.*/
Item_get:  cmp r1, #0x5
               beq Is_safari_ball
               cmp r1, #0xc
               bls Is_ball
               /*add new balls here, example given with item 0x3d*/
               cmp r1, #0x3d
               beq Is_ball
               cmp r1, #0x50
               beq dolly
               cmp r1, #0x51
               beq dolly
               mov r0, #0xaf
               lsl r0, r0, #0x1
               cmp r1, r0
               beq flute
               ldr r0, return_addr
               bx r0
.hword 0x0000
 
Is_safari_ball:   ldr r1, safari_script
                     b script_ender
 
Is_ball:           ldr r1, ball_script
                     b script_ender
 
dolly:              ldr r1, doll_script
                     b script_ender
 
flute:  ldr r1, flute_script
 
script_ender: ldr r0, script_ptr
                  str r1, [r0]
                  ldr r1, be3_addr
                  mov r10, r1
                  ldr r0, end_addr
                  bx r0
 
safari_script: .word 0x081D9A3c
ball_script:  .word 0x081D9A14
doll_script: .word 0x081D9B7C
flute_script: .word 0x081D9B86
script_ptr: .word 0x02023d74
be3_addr: .word 0x02023be3
return_addr:  .word 0x080164e9
end_addr: .word 0x0801671f
This code example shows a previously added 0x3d item as a ball. To add it yourselves, simply copy-paste the pokeball item on this new ball, changing the index to 0x3d.
Now, if you try to use it as-is, you'll have a funny scene where the player throws <insert ball name here> and it always fails to capture the target. At least it's a working ball, right? Also, note the graphics shown are those of the normal pokeball. The game defaults to that when the graphics to the corresponding ball do not exist. I will not address this problem yet.
So, it would be nice if our ball could capture something, right? Well, that's the area of another function, the one mentioned at the beggining of this post, the EF battle command, Pokemon catching function. Located at 0x0802D434, it's even bigger than the one before. It starts out by calculating lots of stuff, but let's focus only on the part that matters:
Code:
loc_802D4DC                             ; CODE XREF: ROM:0802D4BCj
ROM:0802D4DC                 LDR     R0, =0x2023D68 ;item address
ROM:0802D4DE                 LDRH    R0, [R0]
ROM:0802D4E0                 CMP     R0, #5 [B];is safari ball[/B]
ROM:0802D4E2                 BNE     loc_802D508
ROM:0802D4E4                 LDR     R0, =0x2023FE8 ;[B]special safari ball case[/B]
ROM:0802D4E6                 LDR     R0, [R0]
ROM:0802D4E8                 ADD     R0, #0x7C
ROM:0802D4EA                 LDRB    R0, [R0]
ROM:0802D4EC                 LSL     R1, R0, #2
ROM:0802D4EE                 ADD     R1, R1, R0
ROM:0802D4F0                 LSL     R0, R1, #8
ROM:0802D4F2                 SUB     R0, R0, R1
ROM:0802D4F4                 MOV     R1, #0x64
ROM:0802D4F6                 BL      sub_81E4018
ROM:0802D4FA                 LSL     R0, R0, #0x18
ROM:0802D4FC                 LSR     R5, R0, #0x18
ROM:0802D4FE                 B       loc_802D520
ROM:0802D4FE ; ---------------------------------------------------------------------------
ROM:0802D500 dword_802D500   DCD 0x2023D68           ; DATA XREF: ROM:loc_802D4DCr
ROM:0802D504 dword_802D504   DCD 0x2023FE8           ; DATA XREF: ROM:0802D4E4r
ROM:0802D508 ; ---------------------------------------------------------------------------
ROM:0802D508
ROM:0802D508 loc_802D508                             ; CODE XREF: ROM:0802D4E2j
ROM:0802D508                 LDR     R3, =Base_Stats
ROM:0802D50A                 LDR     R2, =0x2023BE4
ROM:0802D50C                 LDRB    R1, [R6]
ROM:0802D50E                 MOV     R0, #0x58
ROM:0802D510                 MUL     R0, R1
ROM:0802D512                 ADD     R0, R0, R2
ROM:0802D514                 LDRH    R1, [R0]
ROM:0802D516                 LSL     R0, R1, #3
ROM:0802D518                 SUB     R0, R0, R1
ROM:0802D51A                 LSL     R0, R0, #2
ROM:0802D51C                 ADD     R0, R0, R3
ROM:0802D51E                 LDRB    R5, [R0,#8]
ROM:0802D520
ROM:0802D520 loc_802D520                             ; CODE XREF: ROM:0802D4FEj
ROM:0802D520                 LDR     R2, =0x2023D68
ROM:0802D522                 LDRH    R0, [R2]
ROM:0802D524                 CMP     R0, #5
ROM:0802D526                 BHI     loc_802D52A
ROM:0802D528                 B       loc_802D620 ;[B]is regular ball[/B]
ROM:0802D52A ; ---------------------------------------------------------------------------
ROM:0802D52A
ROM:0802D52A loc_802D52A                             ; CODE XREF: ROM:0802D526j
ROM:0802D52A                 SUB     R0, #6 ;[B]is special ball[/B]
ROM:0802D52C                 CMP     R0, #6
ROM:0802D52E                 BLS     loc_802D532
ROM:0802D530                 B       loc_802D62A
ROM:0802D532 ; ---------------------------------------------------------------------------
ROM:0802D532
ROM:0802D532 loc_802D532                             ; CODE XREF: ROM:0802D52Ej
ROM:0802D532                 LSL     R0, R0, #2
ROM:0802D534                 LDR     R1, =off_802D54C
ROM:0802D536                 ADD     R0, R0, R1
ROM:0802D538                 LDR     R0, [R0]
ROM:0802D53A                 MOV     PC, R0
ROM:0802D53A ; ---------------------------------------------------------------------------
ROM:0802D53C off_802D53C     DCD Base_Stats          ; DATA XREF: ROM:loc_802D508r
ROM:0802D540 dword_802D540   DCD 0x2023BE4           ; DATA XREF: ROM:0802D50Ar
ROM:0802D544 dword_802D544   DCD 0x2023D68           ; DATA XREF: ROM:loc_802D520r
ROM:0802D548 off_802D548     DCD off_802D54C         ; DATA XREF: ROM:0802D534r
ROM:0802D54C off_802D54C     DCD loc_802D568         ; DATA XREF: ROM:off_802D548o
ROM:0802D550                 DCD loc_802D598
ROM:0802D554                 DCD loc_802D5AA
ROM:0802D558                 DCD loc_802D5D8
ROM:0802D55C                 DCD loc_802D608
ROM:0802D560                 DCD loc_802D5CA
ROM:0802D564                 DCD loc_802D5CA
The way the capture is calculated for individual balls is not relevant here, so check them out if you want to know how they did it. First let's take a look at this code. Once again, balls are chosen for being below 0xc. the last piece of code perform a jump based on the ball chosen. If we replace that function with one of our own, we can jump to our own functions with our own balls of choice. But that is irrelevant if we know nothing of how to calculate the catch rate. Well, to learn that the easy way, let's check the simple pokeball codes (master ball not included):
Code:
loc_802D620                             ; CODE XREF: ROM:0802D528j
ROM:0802D620                 LDR     R1, =unk_8250892
ROM:0802D622                 LDRH    R0, [R2]
ROM:0802D624                 SUB     R0, #2
ROM:0802D626                 ADD     R0, R0, R1
ROM:0802D628                 LDRB    R4, [R0]
 
ROM:08250892 unk_8250892     DCB 0x14                ; DATA XREF: ROM:off_802D694o
ROM:08250893                 DCB  0xF
ROM:08250894                 DCB  0xA
This piece of code is the answer to our problems. It basically subtracts two from the item number (so ultra ball is 0x0), and goes to a small table to get the catch rate value, that is stored in R4. Basically, in Fire Red, pokeballs have a catch multiplier of 10, great balls a mutiplier of 15 and ultra balls have a catch rate of 20, meaning that 5 is the true base value, even though no balls use it. Now, we know how to change the catch rate of a pokeball: simply place a different value in r4 before going to the actual catch chance calculation.
With all of the above in mind, I created a small example for a replacement function that uses the old functions for all old pokeballs, but uses our own for the ones we add.
Code:
.align 2
.thumb
/*this code is to be inserted at 0802d52c, implementing a simple bx r1(08 47), and deleting 0802d52a (00 00)
This piece of code will replace the old check for balls larger than 5 (6 to c) and determine
a new catch rate for other balls, for any new balls we wish to add.*/
 
new_code:    cmp r0, #0xc
                   bgt not_normal_ball /*Our balls go here*/
                   sub r0, #0x6
                   lsl r0, r0, #0x2
                   ldr r1, old_ball_checks
                   add r0, r0, r1
                   ldr r0, [r0]
                   mov pc, r0
old_ball_checks: .word 0x0802d54c
 
not_normal_ball:  cmp r0, #0x3d
                        beq check_this_ball
                        mov r4, #0x0
                        ldr r0, return_me
                        bx r0
/*our simple ball function, ball rate depends on a value we set in the RAM address 0x0203fe00*/
 
check_this_ball: ldr r0, a_RAM_addr
                       ldrb r4, [r0]
                       ldr r0, return_me
                       bx r0
.hword 0x0000
a_RAM_addr: .word 0x0203fe00 
return_me: .word 0x0802d62b
With this, we are now able to build any type of ball we want, as long as we know how to check for the condition.
I know this might be a bit confusing, but I encourage those who know to compile it and see that it works. With this, ball hacking is possible. Last entry here will be the "how to change the throw ball graphics and the in-game status screen ball graphics".
 

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
It's been a long time since I last posted, but I'm back with some new information on Items. Today, I'll be updating you on how Party-screen Items work.

Party screen Items are a variety of items that are used directly on your pokemon on the party screen, or that are used in battle and affect your party pokemon. These include Healing items, Rare candy, Stones, Vitamins and stat-boosting items (like X-attack). TM's use a different system.

Party-items use the same routines to be executed, but differ in the routine called. Namely, they are:
Spoiler:

Putting this UI differences aside, the code beneath is pretty much the same. It runs two functions, one at 080413E4, that actually executes the code, and one at 08042414, that is called before, when the item is to be used on the pokemon, to determine if it will work at all. If either of them fails a "It Won't work" message is displayed.
Each of this functions border the 4KB in size, so I won't be posting them here. But they work by reading from a table of Item Flags, and working on each.
ItemFlags are stored in a table starting at 082528BC, starting with the value for the potion, and going all the way to Sitrus Berry, placing 0 on the items that have no party use. Those flags are a group of 6 to 10 bytes(depends on the flags present). Their layout is as follows:
Spoiler:

So, what can we do with this information? Well, we can start designing new items with uses, such as new types of potions, new evolution stones, or new battle items. However, please note that you are limited to using items in the range between Potion(0d) and Sitrus berry (8e). To change that, we can erase the limiters at 0x08042508 (for the check routine) and the one at 0x0804152C (for the actual item usage). Replace each of those locations with 00 00 00 00. That will replace the jump with nop operations, erasing the check. Remember to repoint the entire table to a safe location if you do.

But the best part about this find is that we now know all items of this type call two functions to determine what to do. If we change these functions, we can make items do whatever we want to the pokemon in our party.
The system in place right now gives you some freedom to combine effects. But it will not allow you to do much. Even something as simple as an item that heals both PP and HP is impossible (without changing the routine).

To end, an example of a new item, a candy that boosts happiness and heals 10 hp to your pokemon:
The script would be
Code:
00 00 00 00 04 e0 0a 0a 06 04
Which means happiness boosted by 10 if less than 99, 6 if between 100 and 199 and 4 if over 200.
Placing this item immediately before HP-up, you would:
- place this in the rom in some location
- place the address to this location at 08252980
- overwrite the item above HP-up (Starting at 083dbad0), with all the usual data (index, name, price...)
- place as the Normal usage pointer the healing item function.
 

knizz

192
Posts
16
Years
  • Seen Oct 28, 2020
I'm sorry I can't post anything on-topic now. But I'll look into this thread again when I have my own computer back. (~4 days) I'm happy you're back and posting, JPAN. I have a IDA-Database for firered. It's worth taking a look at. I have at least some basic notes about items in there too. Also all asm-hackers are invited to join Darthatrons and my IRC-Channel (irc.irchighway.net #pokehack) which is like this thread just with faster responses. ;)

EDIT: I hope this helps a bit.
Code:
080A1998 @ =============== S U B R O U T I N E =======================================
080A1998
080A1998
080A1998 repel:                                  @ DATA XREF: ROM:item_super_repelo
080A1998                                         @ ROM:083DBE98o ...
080A1998                 PUSH    {R4,LR}
080A199A                 LSLS    R0, R0, #0x18
080A199C                 LSRS    R4, R0, #0x18
080A199E                 LDR     R0, =0x4020
080A19A0                 BL      var_load
080A19A4                 LSLS    R0, R0, #0x10
080A19A6                 CMP     R0, #0
080A19A8                 BNE     loc_080A19CC
080A19AA                 MOVS    R0, #0x29
080A19AC                 BL      music_play
080A19B0                 LDR     R0, =callback3table
080A19B2                 LSLS    R1, R4, #2
080A19B4                 ADDS    R1, R1, R4
080A19B6                 LSLS    R1, R1, #3
080A19B8                 ADDS    R1, R1, R0
080A19BA                 LDR     R0, =(c3_repel+1)
080A19BC                 STR     R0, [R1]
080A19BE                 B       loc_080A19D8
080A19BE @ ---------------------------------------------------------------------------
080A19C0 dword_080A19C0: .long 0x4020            @ DATA XREF: repel+6r
080A19C4 off_080A19C4:   .long callback3table    @ DATA XREF: repel+18r
080A19C8 off_080A19C8:   .long c3_repel+1        @ DATA XREF: repel+22r
080A19CC @ ---------------------------------------------------------------------------
080A19CC
080A19CC loc_080A19CC:                           @ CODE XREF: repel+10j
080A19CC                 LDR     R2, =aButTheEffectsOfARepelLingeredF @ "But the effects of a REPEL lingered fro"...
080A19CE                 LDR     R3, =(bag_print+1)
080A19D0                 MOVS    R0, R4
080A19D2                 MOVS    R1, #2
080A19D4                 BL      sub_08108E70
080A19D8
080A19D8 loc_080A19D8:                           @ CODE XREF: repel+26j
080A19D8                 POP     {R4}
080A19DA                 POP     {R0}
080A19DC                 BX      R0
080A19DC @ End of function repel
080A19DC
080A19DC @ ---------------------------------------------------------------------------
080A19DE                 .byte    0
080A19DF                 .byte    0
080A19E0 off_080A19E0:   .long aButTheEffectsOfARepelLingeredF
080A19E0                                         @ DATA XREF: repel:loc_080A19CCr
080A19E0                                         @ "But the effects of a REPEL lingered fro"...
080A19E4 off_080A19E4:   .long bag_print+1       @ DATA XREF: repel+36r
080A19E8
080A19E8 @ =============== S U B R O U T I N E =======================================
080A19E8
080A19E8
080A19E8 c3_repel:                               @ DATA XREF: repel+22o
080A19E8                                         @ repel:off_080A19C8o
080A19E8                 PUSH    {R4-R6,LR}
080A19EA                 LSLS    R0, R0, #0x18
080A19EC                 LSRS    R6, R0, #0x18
080A19EE                 BL      sub_080723E0
080A19F2                 LSLS    R0, R0, #0x18
080A19F4                 CMP     R0, #0
080A19F6                 BNE     loc_080A1A2A
080A19F8                 LDR     R4, =var_800E   @ Used item number location
080A19FA                 LDRH    R2, [R4]
080A19FC                 LDR     R3, =0xFFFF
080A19FE                 MOVS    R0, #4
080A1A00                 MOVS    R1, #0
080A1A02                 BL      sub_080A2294
080A1A06                 LDR     R5, =0x4020
080A1A08                 LDRH    R0, [R4]
080A1A0A                 BL      item_get_quality
080A1A0E                 MOVS    R1, R0
080A1A10                 LSLS    R1, R1, #0x18
080A1A12                 LSRS    R1, R1, #0x18
080A1A14                 MOVS    R0, R5
080A1A16                 BL      var_set
080A1A1A                 BL      sub_080A1A44
080A1A1E                 LDR     R2, =displayed_string
080A1A20                 LDR     R3, =(bag_print+1)
080A1A22                 MOVS    R0, R6
080A1A24                 MOVS    R1, #2
080A1A26                 BL      sub_08108E70
080A1A2A
080A1A2A loc_080A1A2A:                           @ CODE XREF: c3_repel+Ej
080A1A2A                 POP     {R4-R6}
080A1A2C                 POP     {R0}
080A1A2E                 BX      R0
080A1A2E @ End of function c3_repel
080A1A2E
080A1A2E @ ---------------------------------------------------------------------------
080A1A30 off_080A1A30:   .long var_800E          @ DATA XREF: c3_repel+10r
080A1A34 dword_080A1A34: .long 0xFFFF            @ DATA XREF: c3_repel+14r
080A1A38 dword_080A1A38: .long 0x4020            @ DATA XREF: c3_repel+1Er
080A1A3C off_080A1A3C:   .long displayed_string  @ DATA XREF: c3_repel+36r
080A1A40 off_080A1A40:   .long bag_print+1       @ DATA XREF: c3_repel+38r
080A1A44

08452EB8 brm_bottom_right_menu:seisof <aUse, [b]brm_use[/b]+1>@ 0 @ DATA XREF: mega_func_3+28Ao
08452EB8                                         @ ROM:off_08109BA4o ...
08452EB8                 seisof <aToss, sub_08109CC0+1>@ 1 @ "USE"
08452EB8                 seisof <aRegister_0, sub_0810A000+1>@ 2
08452EB8                 seisof <aGive, sub_0810A0A8+1>@ 3
08452EB8                 seisof <aCancel_0, brm_cancel+1>@ 4
08452EB8                 seisof <aUse, sub_0810A324+1>@ 5
08452EB8                 seisof <aCheck, brm_use+1>@ 6
08452EB8                 seisof <aOpen, brm_use+1>@ 7
08452EB8                 seisof <aOpen, sub_0810A324+1>@ 8
08452EB8                 seisof <aWalk, brm_use+1>@ 9
08452EB8                 seisof <aDeselect, sub_0810A000+1>@ 10
08452EB8                 seisof <unk_084161CD, 0>@ 11

08109C50 @ =============== S U B R O U T I N E =======================================
08109C50
08109C50
08109C50 brm_use:                                @ DATA XREF: ROM:brm_bottom_right_menuo
08109C50                 PUSH    {R4,R5,LR}
08109C52                 LSLS    R0, R0, #0x18
08109C54                 LSRS    R4, R0, #0x18
08109C56                 LDR     R5, =var_800E
08109C58                 LDRH    R0, [R5]
08109C5A                 BL      sub_0809AA20
08109C5E                 CMP     R0, #0
08109C60                 BEQ     loc_08109CB4
08109C62                 MOVS    R0, #0xA
08109C64                 BL      sub_0810BA3C
08109C68                 MOVS    R0, #6
08109C6A                 BL      sub_0810BA3C
08109C6E                 MOVS    R0, #0
08109C70                 BL      sub_08003FA0
08109C74                 MOVS    R0, #1
08109C76                 BL      sub_08003FA0
08109C7A                 MOVS    R0, #0
08109C7C                 BL      sub_080F67A4
08109C80                 BL      count_pokemon
08109C84                 LSLS    R0, R0, #0x18
08109C86                 CMP     R0, #0
08109C88                 BNE     loc_08109CA4
08109C8A                 LDRH    R0, [R5]
08109C8C                 BL      sub_0809A9FC
08109C90                 LSLS    R0, R0, #0x18
08109C92                 LSRS    R0, R0, #0x18
08109C94                 CMP     R0, #1
08109C96                 BNE     loc_08109CA4
08109C98                 MOVS    R0, R4
08109C9A                 BL      sub_0810A170
08109C9E                 B       loc_08109CB4
08109C9E @ ---------------------------------------------------------------------------
08109CA0 off_08109CA0:   .long var_800E          @ DATA XREF: brm_use+6r
08109CA4 @ ---------------------------------------------------------------------------
08109CA4
08109CA4 loc_08109CA4:                           @ CODE XREF: brm_use+38j
08109CA4                                         @ brm_use+46j
08109CA4                 LDR     R0, =var_800E
08109CA6                 LDRH    R0, [R0]
08109CA8                 BL      sub_0809AA20
08109CAC                 MOVS    R1, R0
08109CAE                 MOVS    R0, R4
08109CB0                 BL      bx_r1           @ [b]Call item function[/b]
08109CB4
08109CB4 loc_08109CB4:                           @ CODE XREF: brm_use+10j
08109CB4                                         @ brm_use+4Ej
08109CB4                 POP     {R4,R5}
08109CB6                 POP     {R0}
08109CB8                 BX      R0
08109CB8 @ End of function brm_use
08109CB8
08109CB8 @ ---------------------------------------------------------------------------
08109CBA                 .byte    0
08109CBB                 .byte    0
08109CBC off_08109CBC:   .long var_800E          @ DATA XREF: brm_use:loc_08109CA4r
08109CC0

EDIT 2:
JPAN, I have two of your wanted offsets.
03005074 is the number of the NPC that is executing a script.
03005090 is a table of structures with a function pointer that is called every frame. Think of it as pokemons multithreading system. It's called callback3 in my DB.
 
Last edited:
416
Posts
11
Years
  • Age 34
  • Seen Feb 10, 2024
Spoiler:

I just wanted to point out that these are bit fields...

Its not if byte 0 has a value of 80, its if bit 0 of byte 0 is 1 (or on, or true or however you wanna say it)...

I'm sure this is understood by most, but some may have missed it...

ALSO, if byte 6 is not needed, it is not present, byte 7 becomes byte 6, and so on. Same with bytes 7 8 and 9. they are all not required and only the needed ones are placed at the end of the first 0-5 bytes (in the described order).
SOOO if you are programaticly checking which items increase happiness, keep in mind that many times byte 6 will be the 0-99 happiness byte.
 
Last edited:

Lance32497

LanceKoijer of Pokemon_Addicts
792
Posts
9
Years
I have sucessfully managed to execute a script through an item. But it is still quite buggy. First, I bring you what matters most, the code:
Code:
/*this routine loads a fixed script and runs it*/
New_item_start:  push {r4,lr}
                        ldr r0, script
                        bl script_executer
                        ldr R1, Script_flag_set
                        mov R0, #0x1
                        strb R0, [R1]
                        bl bag_exiter
                        pop {r4,pc}

script: .word 0x08801000 /*the script to execute after leaving the bag*/
Script_flag_set: .word 0x03000f9c
 
script_executer:  ldr r1, exe_addr
                        bx r1
exe_addr: .word 0x08069AE5
 
bag_exiter:  ldr r1, exit_code
                 bx r1
exit_code: .word 0x080a103d

all it does is restart my game, I compiled the script in 0x800001
Here is my script:
#dynamic 0x800000

#org @start
msgbox @1 0x6
release
end

#org @1
= TEST!
and then.....
Here is your routine I compiled:
New_item_start: push {r4,lr}
ldr r0, script
bl script_executer
ldr R1, Script_flag_set
mov R0, #0x1
strb R0, [R1]
bl bag_exiter
pop {r4,pc}

script: .word 0x08800001
Script_flag_set: .word 0x03000f9c

script_executer: ldr r1, exe_addr
bx r1
exe_addr: .word 0x08069AE5

bag_exiter: ldr r1, exit_code
bx r1
exit_code: .word 0x080a103d

then I put in Field script the exact offset of wher the routine is compiled
 

ipatix

Sound Expert
145
Posts
15
Years
A Restarting game most likely means something has crashed.
Have you checked the debugger where the code ends up?
 
416
Posts
11
Years
  • Age 34
  • Seen Feb 10, 2024
all it does is restart my game, I compiled the script in 0x800001
Here is my script:

and then.....
Here is your routine I compiled:


then I put in Field script the exact offset of wher the routine is compiled
The tool "complete item editor" allows you to run scripts from items with a few clicks... just throwing that out there
 

Lance32497

LanceKoijer of Pokemon_Addicts
792
Posts
9
Years
The tool "complete item editor" allows you to run scripts from items with a few clicks... just throwing that out there

WOW! O! WOW!
Im gonna try that out!

A Restarting game most likely means something has crashed.
Have you checked the debugger where the code ends up?

By the way, How do I debug a game?
What function in VBA?
 
Last edited:

ipatix

Sound Expert
145
Posts
15
Years
Well, since the regular version of VBA doesn't allow you directly to debug and the VBA-SDL-H is pretty glitchty I prefer to use the No$gba Debugger (It's free now ->http://problemkaputt.de/gba.htm)
You can find a rough overview from its functions here: http://problemkaputt.de/gbahlp.htm

I'd set a breakpoint in the beginning of your injected code and follow instruction by instruction where the game code ends up. If it branches into undefined or BIOS area the game usually ends up resetting or glitching.
If you found out where the game starts getting glitch you'd need to find your own way to find out why exactly the misbehaviour happens.
 
19
Posts
9
Years
  • Age 28
  • Seen Feb 26, 2024
So, I ask you to share any information you have on the subject, namely, these three main topics (for starters):
How can we create new pokeball behaviours, such as new catch rates and specific pokemon targeting (any of the custom balls);


So, in theory, we could be able to catch fainted pokemon?
 
Last edited:

daniilS

busy trying to do stuff not done yet
409
Posts
10
Years
  • Age 24
  • Seen Jan 29, 2024
So, I ask you to share any information you have on the subject, namely, these three main topics (for starters):
How can we create new pokeball behaviours, such as new catch rates and specific pokemon targeting (any of the custom balls);


So, in theory, we could be able to catch fainted pokemon?

www.pokecommunity.com/showthread.php?t=326311
That's my thread regarding ball hacking. If you read through it you'll see that although the ball behaviours are entirely custom, they are still called through the built-in function, apart from special effects, which are slight modifications of other existing funcs. Catching fainted pokes would require a more in-depth hack to prevent fainting from ending the battle and instead giving the player an option to throw a ball.
 
Back
Top