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

Notices

Archive Old threads that serve no purpose are here.

 
 
Thread Tools Display Modes
Old 10th August 2013, 04:03 PM   #1
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default REAL Runtime Player Customization [PKMN] [GBA]

Okay, for this first research, I am looking to improve on JPAN's runtime trainer customization so that it actually loads a new hero/heroine sprite, in real time. I have looked over the code that prepares OWs, and then the code that actually loads them. The latter of the two only runs on warp, or when an NPC is just offscreen and needs to be loaded. The one which prepares the OWs runs every frame.

The preparer is @0x0805F2C8. This is the code that JPAN branched in his hack, but since it's effects are only used on warp, or to hide/show sprites (It is also used by the hidesprite/showsprite commands) its effects are useless.

I have found that by writing directly to the OAMT states (@0x0202063C) I can modify the sprites in real time, with only one frame of delay. So, what I am thinking, is modifying JPAN's code to run like this:

Code:
.align 2
.thumb

get_ow_addr:	push {r2-r4}
		lsl r1, r0, #0x10
		lsr r1, r1, #0x18
		lsl r0, r0, #0x18Change this to "mov r8, r0" in order to log what sprite was being loaded before we check the var.
		lsr r0, r0, #0x18nop
		ldr r4, new_ow_table
		cmp r1, #0xff
		beq special_decrypter
more_check:	cmp r1, #0xff
		bgt go_old
		cmp r1, #0x0
		bne end_check
		cmp r0, #0xef
		bgt go_old
		bl multi_check
end_check:	lsl r1, r1, #0x2
		add r3, r4, r1
		ldr r2, [r3]
		cmp r2, #0x0
		bne location_real
		ldr r2, [r4]
location_real:	lsl r0, r0, #0x2 Here the NPC data is gathared from the rom.
		add r3, r2, r0
		ldr r0, [r3]
		cmp r0, #0x0
		bne end_ow_change I'm thinking of changing this line to point to a different set of code.
		ldr r3, [r4]
		add r3, #0x40
		ldr r0, [r3] 
end_ow_change:	pop {r2-r4}
		pop {r1}
		bx r1
special_decrypter:	cmp r0, #0xf 
			bgt more_check
			ldr r1, var_4080
			add r0, r1, r0
			bl call_var_decrypt
			ldrb r1, [r0, #0x1]
			ldrb r0, [r0]
			b end_check
						
new_ow_table:	.word 0x081a2000
var_4080:	.word 0x00004080
go_old:		pop {r2-r4}
		ldr r1, loader_call
		bx r1			

multi_check: 	push {lr}
		push {r0, r1}
		cmp r0, #0xD
		bgt no_change
		cmp r0, #0x7
		blt hero_change
		sub r0, #0x7	
hero_change:	ldr r1, var_Hero
		add r0, r0, r1
		bl call_var_decrypt
		b end_multi_check
no_change:	pop {r0,r1}
		pop {pc}

end_multi_check: ldrh r1, [r0]
		cmp r1, #0x0
		beq no_change
		lsl r0, r1, #0x18
		lsr r0, r0, #0x18
		lsr r1, r1, #0x8
		pop {r2,r3}
		pop {pc}
.hword 0x0000
var_Hero: .word 0x00004054
loader_call: 	.word 0x0805f2d5 		


call_var_decrypt: ldr r2, var_decrypt
		  bx r2
var_decrypt:	.word 0x0806E455
Full original source:
Spoiler:
.align 2
.thumb

/*third and final change for the number of overworlds is
at 0x0805f2ca. In order to mantain the original functions
from the game, slots 0ef->0ff are not able to hold original
OW's, but 16 slots are nothing compared with the remainder

get_ow_change: ldr r1, get_new_ow
bx r1

get_new_ow: pointer

or, compiled
00 00 00 49 08 47 Pointer

to be placed at 0x0805f2de to complement the code
ef_call: mov r1, #0x0
ldr r2, end_check_addr
bx r2

end_check_addr: pointer

or,compiled
00 21 00 4a 10 47 Pointer

this code also reflects the possibility of change on the
hero OW, performing a simple check on the designated addresses.*/

get_ow_addr: push {r2-r4}
lsl r1, r0, #0x10
lsr r1, r1, #0x18
lsl r0, r0, #0x18
lsr r0, r0, #0x18
ldr r4, new_ow_table
cmp r1, #0xff
beq special_decrypter
more_check: cmp r1, #0xff
bgt go_old /*to allow for a multi-sized Table table*/
cmp r1, #0x0
bne end_check
cmp r0, #0xef
bgt go_old
bl multi_check
end_check: lsl r1, r1, #0x2
add r3, r4, r1
ldr r2, [r3]
cmp r2, #0x0
bne location_real
ldr r2, [r4]
location_real: lsl r0, r0, #0x2
add r3, r2, r0
ldr r0, [r3]
cmp r0, #0x0
bne end_ow_change
ldr r3, [r4]
add r3, #0x40
ldr r0, [r3]
end_ow_change: pop {r2-r4}
pop {r1}
bx r1
special_decrypter: cmp r0, #0xf /*change here for more variables usable*/
bgt more_check
ldr r1, var_4080
add r0, r1, r0
bl call_var_decrypt
ldrb r1, [r0, #0x1]
ldrb r0, [r0]
b end_check

new_ow_table: .word /*0x081a2000*/ 0xffffffff
var_4080: .word /*0x00004080*/ 0xffffffff
go_old: pop {r2-r4}
ldr r1, loader_call
bx r1

/*this is the check that allows for trainer OW switch
How it works: Vars 4054-59 must have a value. 0x0 for the
default sprite, 0xyyyy for the new sprite*/
multi_check: push {lr}
push {r0, r1}
cmp r0, #0xD
bgt no_change
cmp r0, #0x7
blt hero_change
sub r0, #0x7
hero_change: ldr r1, var_Hero
add r0, r0, r1
bl call_var_decrypt
b end_multi_check
no_change: pop {r0,r1}
pop {pc}

end_multi_check: ldrh r1, [r0]
cmp r1, #0x0
beq no_change
lsl r0, r1, #0x18
lsr r0, r0, #0x18
lsr r1, r1, #0x8
pop {r2,r3}
pop {pc}
.hword 0x0000
var_Hero: .word /*0x00004054*/0xffffffff
loader_call: .word 0x0805f2d5
/*to increase the palettes, simply repoint 0x083a5158 to a new location*/

call_var_decrypt: ldr r2, var_decrypt
bx r2
var_decrypt: .word 0x0806E455



So, if I change that line to point to new code, I am thinking what it would do is check r8 and make sure it is one of the hero/heroine sprites. Then, if it is, run a check on the OAMT states to see if the data loaded here is different then what is in the OAMT states. If it is, then write in the new data, and then return. If not, just return without writing. This prevents the issues and possible lag of writing every frame, as well as removing the warp requirement.

The only issue is the palette. I can't tell what writes it, but it is written to every frame and this routine doesn't affect it.

Edit: Break down of OAMT states thanks to Knizz's IDB file.
Spoiler:

Last edited by karatekid552; 10th August 2013 at 04:19 PM.
karatekid552 is offline  
Likes Elsa liked this post
Sponsored Links
Old 10th August 2013, 09:55 PM   #2
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default

MUAHAHAHAHAA! All palettes are buffered on warp to 0x020375F8 in order. So, 0x020377F8 is where palette slot 0 is, which is the Hero/Heroine. Every frame, this buffer is DMA copied into the palette registers and displayed on screen. SOOOOOOO, we just load the palette to 0x020377F8 and let the game do the work for us!
karatekid552 is offline  
Old 11th August 2013, 02:46 AM   #3
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default

Sorry to triple post, but here is the WIP routine. Please note that the rom offsets presented here are specific to my rom and will be different on every other rom:

Code:
.text
.align 2
.thumb
.thumb_func
.global runtime_player_customization_fix


main:
	ldr r2, [r4]
	lsl r0, r0, #0x2
	add r3, r2, r0
	ldr r0, [r3]
	cmp r0, #0x0
	bne not_equal
equal:
	ldr r3, [r4]
	add r3,#0x40
	ldr r0,[r3]
	ldr r1, return
	bx r1
not_equal:
	push {r0-r3}
	mov r2, r8
	cmp r2, #0xD
	ble hero
	pop {r0-r3}
	ldr r1, return
	bx r1

hero:
	mov r2, r0
	ldr r0, [r0,#0x1C]
	ldr r1, oamt_state_hero
	push {r0-r1}
	ldr r1, [r1,#0xC]
	cmp r0, r1
	beq end_now
	pop {r0-r1}
	str r0, [r1,#0xC] /*Store GFX table in OAMT state*/
	mov r0, r2
	ldr r0, [r0,#0x18]
	ldr r1, oamt_state_hero /*Store animation table in OAMT state*/
	str r0, [r1,#0x8]
	mov r0, r2
	ldrb r0, [r0,#0x2]
	ldr r1, palette_table /*prepare to search for palette*/
	add r1, r1, #0x4
	b skip
loop:
	add r1, r1, #0x8 /*continue searching for palette in palette table*/
skip:	
	ldrb r3, [r1]
	cmp r0, r3
	bne loop
end_loop: /*Found palette*/
	sub r1, r1, #0x4
	ldr r1, [r1]
	mov r2, r1
	ldr r1, [r1]
	ldr r0, palette_buffer_slot_1
	mov r3, #0x0
str_loop: /*store all of palette in palette buffer*/
	add r3, r3, #0x1
	str r1, [r0]
	add r1, r2, #0x4
	add r2, r2, #0x4
	ldr r1, [r1]
	add r0, r0, #0x4
	cmp r3, #0x8
	beq end_str_loop
	b str_loop
end_str_loop:
	mov r2, #0x0
	mov r8, r2
	pop {r0-r3}
	ldr r1, return
	bx r1
	
end_now:
	pop {r0-r1}
	b end_str_loop
	
	
.align 2
oamt_state_hero:	.word	0x0202063C
palette_table:	.word	0x0871FDC0
palette_buffer_slot_1:	.word	0x020377F8
return:	.word	0x0806D4E5
Diego, just for testing purposes and simplicity, it doesn't change the hero's palette slot.

Edit: This code works well except it causes a lot of issues while warping. Outside of that, except for there is the rare occasion when the player leaves the OAMT #1 slot. I'll find a fix eventually, but does anyone know how I can determine when the player is being loaded to the screen just after a warp? My best guess is to check and see if all of the bg palettes are black, and then just make sure the code doesn't run if they are. Since this code is meant to be instant changes, it wouldn't need to run under a black veil anyways.

Last edited by karatekid552; 11th August 2013 at 04:44 AM.
karatekid552 is offline  
Old 11th August 2013, 06:36 AM   #4
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default

/me feels SUPER awkward quadruple posting, but whatever. I finished!


Full Source Code!!!! Is the replacement for "The Overworld Replacer.txt" in JPAN's hack tool folder's source code.

To insert the following, just compile the ASM below and insert it in your rom. Then, go to 0x5F2C8 in your rom and look for the first pointer (I think it is 0x5F2D0, but I'm not sure) and change it to match the location of the new ASM +1.

Code:
.align 2
.thumb

.org 0x08XXXXXX   /*Offset you plan to place this ASM. Works 
like #Dynamic in scripting in order to create dynamic labels 
that work. Otherwise, you will get bad pointers and such.*/

/*third and final change for the number of overworlds is
at 0x0805f2ca. In order to mantain the original functions
from the game, slots 0ef->0ff are not able to hold original
OW's, but 16 slots are nothing compared with the remainder

get_ow_change:   ldr r1, get_new_ow	
		 bx r1

get_new_ow: pointer

or, compiled
00 00 00 49 08 47 Pointer

to be placed at 0x0805f2de to complement the code
ef_call:	mov r1, #0x0
		ldr r2, end_check_addr
		bx r2

end_check_addr: pointer

or,compiled 
00 21 00 4a 10 47 Pointer

this code also reflects the possibility of change on the
hero OW, performing a simple check on the designated addresses.*/

get_ow_addr:	push {r2-r4}
		lsl r1, r0, #0x10
		lsr r1, r1, #0x18
		mov r11, r0
		nop
		lsl r0, r0, #0x18
		lsr r0, r0, #0x18
		ldr r4, new_ow_table
		cmp r1, #0xff
		beq special_decrypter
more_check:	cmp r1, #0xff
		bgt go_old	/*to allow for a multi-sized Table table*/
		cmp r1, #0x0
		bne end_check
		cmp r0, #0xef
		bgt go_old
		bl multi_check
end_check:	lsl r1, r1, #0x2
		add r3, r4, r1
		ldr r2, [r3]
		cmp r2, #0x0
		bne location_real
		ldr r2, [r4]
location_real: ldr r1, main_loc
		bx r1

end_ow_change:	pop {r2-r4}
		pop {r1}
		bx r1
special_decrypter:	cmp r0, #0xf /*change here for more variables usable*/
			bgt more_check
			ldr r1, var_4080
			add r0, r1, r0
			bl call_var_decrypt
			ldrb r1, [r0, #0x1]
			ldrb r0, [r0]
			b end_check
						
new_ow_table:	.word /*0x081a2000*/ 0xffffffff
var_4080:	.word /*0x00004080*/ 0xffffffff
main_loc:	.word	main + 1
go_old:		pop {r2-r4}
		ldr r1, loader_call
		bx r1			

/*this is the check that allows for trainer OW switch
How it works: Vars 4054-59 must have a value. 0x0 for the
default sprite, 0xyyyy for the new sprite*/
multi_check: 	push {lr}
		push {r0, r1}
		cmp r0, #0xD
		bgt no_change
		cmp r0, #0x7
		blt hero_change
		sub r0, #0x7	
hero_change:	ldr r1, var_Hero
		add r0, r0, r1
		bl call_var_decrypt
		b end_multi_check
no_change:	pop {r0,r1}
		pop {pc}

end_multi_check: ldrh r1, [r0]
		cmp r1, #0x0
		beq no_change
		lsl r0, r1, #0x18
		lsr r0, r0, #0x18
		lsr r1, r1, #0x8
		pop {r2,r3}
		pop {pc}
.hword 0x0000
var_Hero: .word /*0x00004054*/0xffffffff
loader_call: 	.word 0x0805f2d5 		
/*to increase the palettes, simply repoint 0x083a5158 to a new location*/

call_var_decrypt: ldr r2, var_decrypt
		  bx r2
var_decrypt:	.word 0x0806E455

.align 2
/*.global runtime_player_customization_fix*/


main:
	lsl r0, r0, #0x2
	add r3, r2, r0
	ldr r0, [r3]
	cmp r0, #0x0
	bne not_equal
equal:
	ldr r3, [r4]
	add r3,#0x40
	ldr r0,[r3]
	ldr r1, return
	bx r1
not_equal:
	push {r0-r3}
	mov r2, r11
	cmp r2, #0xD
	ble hero
	pop {r0-r3}
	ldr r1, return
	bx r1

hero:
	mov r2, r0
	ldr r0, [r0,#0x1C]
	ldr r1, oamt_state_hero
	push {r0-r1}
	ldr r1, [r1,#0xC]
	cmp r0, r1
	beq end_now
	ldr r0, .0x05000000
	ldr r0, [r0]
	mov r1, #0x0
	cmp r0, r1
	beq end_now
	pop {r0-r1}
	str r0, [r1,#0xC] /*Store GFX table in OAMT state*/
	mov r0, r2
	ldr r0, [r0,#0x18]
	ldr r1, oamt_state_hero /*Store animation table in OAMT state*/
	str r0, [r1,#0x8]
	mov r0, r2
	ldrb r0, [r0,#0x2]
	ldr r1, OW_palette_table /*prepare to search for palette*/
	add r1, r1, #0x4
	b skip
loop:
	add r1, r1, #0x8 /*continue searching for palette in palette table*/
skip:	
	ldrb r3, [r1]
	cmp r0, r3
	bne loop
end_loop: /*Found palette*/
	sub r1, r1, #0x4
	ldr r1, [r1]
	mov r2, r1
	ldr r1, [r1]
	ldr r0, palette_buffer_slot_1
	mov r3, #0x0
str_loop: /*store all of palette in palette buffer*/
	add r3, r3, #0x1
	str r1, [r0]
	add r1, r2, #0x4
	add r2, r2, #0x4
	ldr r1, [r1]
	add r0, r0, #0x4
	cmp r3, #0x8
	beq end_str_loop
	b str_loop
end_str_loop:
	mov r2, #0x0
	mov r11, r2
	pop {r0-r3}
	ldr r1, return
	bx r1
	
end_now:
	pop {r0-r1}
	b end_str_loop
	
	
.align 2
oamt_state_hero:	.word	0x0202063C
OW_palette_table:	.word	0x08FFFFFF
palette_buffer_slot_1:	.word	0x020377F8
return:	.word	end_ow_change + 1
.0x05000000:	.word	0x05000000
Right now, there is only one issue: sprites need to be the same size. So, no switching between 16x32 and 32x32, etc. I will look into fixing this, but that really isn't bad. Enjoy!


Just so you guys know, I also didn't include the OAMT check. I figured out how to do it, but never inserted. All you have to do is check byte 2E of the OAMT state. That will give you the NPCID which can be compared with the byte at 0x0203707D. If they are equal, you are editing the correct the OAMT, if not, jump up to the next one, which is +0x44 bytes. The player rarely ever leaves OAMT #1, if ever, so that isn't something to concern yourself with.

I had something else I wanted to share, but I can't remember. If I think of it, I'll edit this.:P

Last edited by karatekid552; 1st September 2013 at 09:00 PM. Reason: I made a mistake..... Fixed it though.:)
karatekid552 is offline  
Likes daniilS, DrFuji liked this post
Old 31st October 2013, 02:45 AM   #5
Diegoisawesome
Oh god the bees
Ex-Staff
 
Diegoisawesome's Avatar
 
Join Date: Jul 2013
Location: :noitacoL
Age: 21
Posts: 60
Diegoisawesome
Default

Sorry to destroy all your hard work, but I found out that all you need to do is this:
Code:
writebytetooffset 0x1 0x2037079
Put this just after setting the variable and the sprite will instantly change.
No joke. It's that simple.

EDIT: Well, my mistake. The routine that reads this byte only activates once the script ends. No worries, though, I'll find what it activates and call that directly instead.

EDIT 2: Found it. Instead of what I have above, call this:
Code:
callasm 0x805BE61
This refreshes the player sprite immediately upon calling it. Keep in mind this will load whatever sprite you have set for walking.

Last edited by Diegoisawesome; 31st October 2013 at 12:53 PM.
Diegoisawesome is offline  
Old 31st October 2013, 10:37 AM   #6
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default

Quote:
Originally Posted by diegoisawesome View Post
Sorry to destroy all your hard work, but I found out that all you need to do is this:
Code:
writebytetooffset 0x1 0x2037079
Put this just after setting the variable and the sprite will instantly change.
No joke. It's that simple.

EDIT: Well, my mistake. The routine that reads this byte only activates once the script ends. No worries, though, I'll find what it activates and call that directly instead.

EDIT 2: Found it. Instead of what I have above, call this:
Code:
callasm 0x805BE61
This refreshes the player sprite immediately upon calling it. Keep in mind this is only if the player's walking.
What ever.:P Mine works instantly no matter what, and doesn't need to be called every script. Choices are awesome, so let the hacker chose.
karatekid552 is offline  
Likes Elsa liked this post
Old 31st October 2013, 12:59 PM   #7
Diegoisawesome
Oh god the bees
Ex-Staff
 
Diegoisawesome's Avatar
 
Join Date: Jul 2013
Location: :noitacoL
Age: 21
Posts: 60
Diegoisawesome
Default

Quote:
Originally Posted by karatekid552 View Post
What ever.:P Mine works instantly no matter what, and doesn't need to be called every script. Choices are awesome, so let the hacker chose.
I didn't mean to somehow diminish the coolness of what you did. There's just some things that I didn't particularly like about yours, like the fact that it overwrites palette 0, that you couldn't change between sprite sizes, and that the GFX tables and palette are being written to memory every frame. I also like reusing pre-existing in-game routines in my code because that assures me that all necessary changes are being taken care of.
Diegoisawesome is offline  
Old 31st October 2013, 02:08 PM   #8
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default

Quote:
Originally Posted by diegoisawesome View Post
I didn't mean to somehow diminish the coolness of what you did. There's just some things that I didn't particularly like about yours, like the fact that it overwrites palette 0, that you couldn't change between sprite sizes, and that the GFX tables and palette are being written to memory every frame. I also like reusing pre-existing in-game routines in my code because that assures me that all necessary changes are being taken care of.
/me has a better idea....

What if we combine both? Just call that function instead of the extra code I added. It does the same thing. My GFX tables, if I remember correctly, are only written when they change.
karatekid552 is offline  
Old 31st October 2013, 11:08 PM   #9
Diegoisawesome
Oh god the bees
Ex-Staff
 
Diegoisawesome's Avatar
 
Join Date: Jul 2013
Location: :noitacoL
Age: 21
Posts: 60
Diegoisawesome
Default

Quote:
Originally Posted by karatekid552 View Post
/me has a better idea....

What if we combine both? Just call that function instead of the extra code I added. It does the same thing. My GFX tables, if I remember correctly, are only written when they change.
I'm fairly certain that this routine actually calls the routine you've hacked, so it would be a recursive loop.
Diegoisawesome is offline  
Old 1st November 2013, 12:05 PM   #10
karatekid552
What does this button do?.....
Ex-Staff
 
karatekid552's Avatar
 
Join Date: Feb 2013
Location: Stalker.......
Posts: 229
karatekid552 karatekid552
Default

Quote:
Originally Posted by diegoisawesome View Post
I'm fairly certain that this routine actually calls the routine you've hacked, so it would be a recursive loop.
So, the routine calls the loader, which is what I hacked, and then calls a refresher? Damm.... I honestly tried looking for the refresher. I never thought to backtrack, lol. I always went forward. When I couldn't find it, I just wrote my own crappy one, haha.
karatekid552 is offline  
 

Tags
customization, gba, pkmn, player, real, runtime, trainer

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 05:26 PM.

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

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

no new posts