Jump to content

Trainer Pokemon Nature Adjustor

Recommended Posts

The first post will be the patch and instructions and the second will be a look into how I managed to do this.

Using this will be a little bit complex for now until the new version of Kazo's trainer editor shows up. I don't know if he'll be doing it or if I'll be doing it, but it will be available at some point soon-ish. Until then, there are basic instructions in the text file and obviously you can ask questions in here.

So what we've got here is the first implementable hack for 5th gen Rom hacks. For now it will just be for B2W2, but will eventually be for BW as well. As the title states, it lets you pick the nature for opposing trainers' pokes.


Also, here's a pic of what I was talking about in the text file:


The red square is byte 0x4B, the one you're supposed to edit.

Edited by Bond697
Link to comment
Share on other sites

reserved for explanation.

so this is just a basic runthrough of how this came about.

**see here for the different types of trainer pokes: http://projectpokemon.org/forums/showthread.php?24589-B2W2-General-ROM-Info&p=161021&viewfull=1#post161021

i had a few issues to tackle. for one, all the code needed to fit into some very small spaces. the function that makes trainerpokes leaves very small windows to actually do what i needed to do- no more than 6 or 8 bytes each time. second, there are 4 different kinds of trainerpokes and each type jumps to the trainerpoke stat creation function just a bit differently. so in the small amounts of room i had, i had to account for each of the 4 sets of code being different/using different registers. an early thought i had was to use a few of my own functions that do basically the same thing, but with different registers. that was my plan for quite awhile, but i managed to come up with a way that allowed me to route all 4 sets of code to the same nature creation function.

one of the challenges here was finding space for me to use to put my code. first, the code HAD to go in the arm9. i didn't have enough room to really go crazy with overlays and i didn't want to anyway. an overlay loading system is something i have planned for later with user-made code and i didn't want that to conflict with this code. i used a good idea i had for nesting code instead:

romhacks will never run in dsi mode, right? they can't. there's no dsi mode dump of white 2 U and even if there was, editing the rom would likely break necessary data that allowed for dsi mode booting. so what i could do is look for places where the game checked to see if it was in dsi mode. i found code that was set to ONLY run in dsi mode and removed it with the (correct) assumption that it will simply never run. after that i wrote my own code in THUMB which allows for fitting a greater amount of code in a smaller area. this is a picture of the code in action: (green is ARM, red is THUMB)


note the STMFD at the top(which is ARM) and if you follow it does, you'll see where isDSi runs to check for dsi mode. that call will never be true, so right after that my code begins in THUMB(test_hack) at the MOVS and runs through the POP near the bottom. note the bright blue sets of bytes next to the ARM and THUMB code. there's 4 next to the ARM and 2 next to the THUMB, which is mine. so there's smaller THUMB code nested inside the larger ARM code. when the main function there runs starting at the STMFD, it will jump past my code and continue on and when my code runs, it ends just before the ARM starts again.

so figuring that part out wasn't too bad. finding enough space in unused code wasn't difficult since my function was small and was done in THUMB to keep it tiny. i ended up using 52 bytes total there, which is good because now i can fit more code in the rest of that space as needed.

so that worked out well. the real problem though was in getting the code that makes trainerpokes to cooperate. this is my planning notes: http://pastebin.com/PYFdK3Mj

the very, very first thing i did was fix the level loading code for all 4 types of trainerpoke. gamefreak treats level as a 16-bit number here, likely for the sake of keeping alignment to 2 or 4-bytes. what i did was edit some code so that it went from this:

LDRH    R0, [R0,#2]     ; load level

to this:

LDRB    R0, [R0,#2]     ; load level

the top version loads a halfword(16-bit) while the bottom loads a byte(8-bit) and leaves the other byte for me.

i wanted to get the first of the 4 working and then i figured i would move on to the rest. that starts as:

RAM_ARM9:02030A4C EB F7 20 FD                 BL      createPKM
RAM_ARM9:02030A50 0D 9A                       LDR     R2, [sP,#0x98+current_tr_poke_ptr] this load a pointer to the trpoke poke's file to r2
RAM_ARM9:02030A52 0D 9B                       LDR     R3, [sP,#0x98+current_tr_poke_ptr] same into r3
RAM_ARM9:02030A54 D2 88                       LDRH    R2, [R2,#6]     ; form                    byte 7 is the form to use in the trpoke file
RAM_ARM9:02030A56 5B 78                       LDRB    R3, [R3,#1]     ; gender_abil             byte 2 is the gender/abil byte
RAM_ARM9:02030A58 04 98                       LDR     R0, [sP,#0x98+trainer_idx] ; trainer_idx  this is unused but would be a massive pain to do anything with
RAM_ARM9:02030A5A 21 1C                       MOVS    R1, R4          ; pPkm
RAM_ARM9:02030A5C 00 F0 E6 F9                 BL      setTrainerPkmStats

so my first thought is that LDR R2 and LDR R3 do the same thing there so right away we can trim out 1 of those instructions for use in a new branch. THUMB instructions are 2 bytes, but THUMB branch with links are 4 bytes, so i would need to make room and get rid of a few instructions for that AND make sure the next function has the arguments it needs. so getting rid of that leaves 1 more + arguments i need for my function and the next. the second thing i can do is move the ldrh and ldrb there into my function at the very end so that they're available to the next function. that frees up 4 more bytes to use for preparation. the final thing i ended up doing is something that is extremely unconventional and not a legal/acceptable move for nintendo's compiler because it breaks programming standards. it helps me because it frees up more rooom though. see where it says "MOVS R1, R4 ; pPkm"? i need that value for my function. i went to look and for all 4 types that value is always in R4. so i don't pass it into my function, i simply assume it's in r4 and take it as i need it. one other nice thing i take advantage of is that "LDR R0, [sP,#0x98+trainer_idx] ; trainer_idx" is completely unused in "setTrainerPkmStats" for some reason, so i can ignore it completely. taking advantage of all these optimizations gets me this:

RAM_ARM9:02030A50 0D 9A       LDR     R2, [sP,#0x98+current_tr_poke_ptr]
RAM_ARM9:02030A52 10 1C       MOVS    R0, R2
RAM_ARM9:02030A54 C0 46       NOP
RAM_ARM9:02030A56 29 F0 A1 FF BL      test_hack

where i managed to optimize so much that i actually had room left over and had to use a NOP for the extra space. this is true for one other type also. the first and third have extra space, but the other 2 use it all.

the last one presented a problem because it looks like this:

RAM_ARM9:02030D80 FA 88                       LDRH    R2, [R7,#6]     ; form
RAM_ARM9:02030D82 7B 78                       LDRB    R3, [R7,#1]     ; gender_abil
RAM_ARM9:02030D84 04 98                       LDR     R0, [sP,#0x98+trainer_idx] ; trainer_idx
RAM_ARM9:02030D86 21 1C                       MOVS    R1, R4          ; pPkm
RAM_ARM9:02030D88 00 F0 50 F8                 BL      setTrainerPkmStats

note how the loading is done from R7, but i assume everywhere else that the loading is done via R2. that means that the first thing i have to do i move the value in r7 over to r2 and go from there so that i can keep using the 1 function to set natures and preserve space. BUT i only have 6 bytes to work with and that includes a couple of moves and a branch, so that's at least 8. i solved this by being a little bit clever here. the other 3 calls to test_hack have the same value in r0 and r2. it's always the same in both and i use both eventually. so my thought was to allow the move from r7 to r2 and then add a bit to test_hack that moves the value in r2 into r0. the other 3 are always equal already, so it won't affect them and it lets the last type fit and use the same function. this was the result:

RAM_ARM9:02030D80 3A 1C       MOVS    R2, R7                          ; form
RAM_ARM9:02030D82 29 F0 0B FE BL      test_hack

and this is the final version of test_hack:

RAM_ARM9:0205A99C 10 1C       MOVS    R0, R2
RAM_ARM9:0205A99E F0 B5       PUSH    {R4-R7,LR}
RAM_ARM9:0205A9A0 07 1C       MOVS    R7, R0
RAM_ARM9:0205A9A2 26 1C       MOVS    R6, R4
RAM_ARM9:0205A9A4 15 1C       MOVS    R5, R2
RAM_ARM9:0205A9A6 FC 78       LDRB    R4, [R7,#3]
RAM_ARM9:0205A9A8 00 2C       CMP     R4, #0
RAM_ARM9:0205A9AA 06 D0       BEQ     loc_205A9BA
RAM_ARM9:0205A9AC 19 2C       CMP     R4, #0x19
RAM_ARM9:0205A9AE 04 DC       BGT     loc_205A9BA                     ; pPkm
RAM_ARM9:0205A9B0 30 1C       MOVS    R0, R6                          ; pPkm
RAM_ARM9:0205A9B2 61 1E       SUBS    R1, R4, #1                      ; nature
RAM_ARM9:0205A9B4 C2 F7 2A FE BL      setNatureResetStats       
RAM_ARM9:0205A9B8 06 E0       B       loc_205A9C8
RAM_ARM9:0205A9BA             loc_205A9BA                             ; max
RAM_ARM9:0205A9BA 19 20       MOVS    R0, #0x19
RAM_ARM9:0205A9BC AA F7 C4 FE BL      randMain64
RAM_ARM9:0205A9C0 01 1C       MOVS    R1, R0                          ; nature
RAM_ARM9:0205A9C2 30 1C       MOVS    R0, R6                          ; pPkm
RAM_ARM9:0205A9C4 C2 F7 22 FE BL      setNatureResetStats
RAM_ARM9:0205A9C8             loc_205A9C8
RAM_ARM9:0205A9C8 2A 1C       MOVS    R2, R5
RAM_ARM9:0205A9CA 53 78       LDRB    R3, [R2,#1]
RAM_ARM9:0205A9CC D2 88       LDRH    R2, [R2,#6]
RAM_ARM9:0205A9CE 31 1C       MOVS    R1, R6
RAM_ARM9:0205A9D0 F0 BD       POP     {R4-R7,PC}

note that the very first instruction moves r2 into r0. usually THUMB functions begin with PUSH and end with POP(or don't use PUSH and POP at all), so this is unconventional, but it solves my problem.

Edited by Bond697
Link to comment
Share on other sites

  • 1 month later...
  • 4 weeks later...

I need help patching the rom some. I haven't been able to patch with your xdelta file. I've tried various versions of xdelta and they all give a different error. I have made sure it's clean rom. I've tried applying other patches and had no problems.

xdelta3: target window checksum mismatch: XD3_INVALID_INPUT


xdelta.exe: C:\DS\5\trainer_nature_white2.xdelta: bad magic number: not a valid delta


xdelta.exe: unrecognized command

xdelta.exe: usage: xdelta.exe COMMAND [OPTIONS] [ARG1 ...]

xdelta.exe: COMMAND is one of:

xdelta.exe: delta Produce a delta from ARG1 to ARG2 producing ARG3

xdelta.exe: info List details about delta ARG1

xdelta.exe: patch Apply patch ARG1 using file ARG2 producing ARG3

xdelta.exe: OPTIONS are:

xdelta.exe: -v, --version Print version information

xdelta.exe: -V, --verbose Print verbose error messages

xdelta.exe: -h, --help Print this summary

xdelta.exe: -n, --noverify Disable automatic MD5 verification

xdelta.exe: -p, --pristine Disable automatic GZIP decompression

xdelta.exe: -m, --maxmem=SIZE Set the buffer size limit, e.g. 640K, 16M

xdelta.exe: -[0-9] ZLIB compress level: 0=none, 1=fast, 6=default, 9=best

xdelta.exe: -s=BLOCK_SIZE Sets block size (power of 2), minimum match length

xdelta.exe: In-core memory requirement is (FROM_LEN * 8) / BLOCK_SIZE

Link to comment
Share on other sites

I found an interesting side effect of that patch. It breaks PRC(Pokemon Rom Changer). It can no longer properly load the values and throws lots of errors if you try. Transferring the narcs isn't too bad though(for what I do with the editor). Just a/0/2/1 and a/0/1/6.

Link to comment
Share on other sites

I found an interesting side effect of that patch. It breaks PRC(Pokemon Rom Changer). It can no longer properly load the values and throws lots of errors if you try. Transferring the narcs isn't too bad though(for what I do with the editor). Just a/0/2/1 and a/0/1/6.

prc just loads data from a static offset. if you alter anything sizewize (changing narcs/overlays) you'll break the reading since it will read from the wrong position.

Link to comment
Share on other sites

  • 1 month later...

At the risk of sounding like an idiot I can't seem to get this working. I've patched the roms successfully and edited trainer files with Kazo's tool to make sure it's being done correctly, but the game still seems to be reading it as a secondary level byte and just screws the levels instead of setting a nature. Tried both inserting arm9s on my own hacks and just using the basic roms, but nothing worked.

Tried both the Black 2 and White 2 ones (this applies to both these and the ones you sent me in PM, bond). I'm stumped tbh.

Link to comment
Share on other sites

i just tried the arm9 i sent you, expecting that i had somehow made a minor error, following the result in a debugger. i unpacked a copy of white 2, subbed in my arm9, and re-packed it. it worked perfectly. the only thing that threw me off for a minute was that i always play in challenge mode, so the poke's level was bumped up a bit compared to what's written to the pkm at first. that has nothing to do with me, though.

i could see there maybe being a problem with black 2 because i just threw that one together, as opposed to the white 2 one which i did all the work in.

Link to comment
Share on other sites

Hmm, I tried both Black 2 and White 2 and both failed for me. I might take another crack at it tomorrow, suppose I might have messed a file up or something. I'm assuming setting a nature is literally just using Kazo's tool to do it once the arm9 file is in the rom, so yeah. I dunno how I'm mucking it up lol.

I'll focus on trying to get the White 2 one to work I guess, will report back with results when I can.

Link to comment
Share on other sites

  • 2 years later...
  • 5 years later...

Thx for the quick reply, but I'm afraid it's not working for me.
When I click on http://hack.thundaga.com/trainer_nature.7z I get a connection refused error, 
Can you maybe post another download link if you have the tool available or do you know what the issue could be? 


edit: got it to work now from my laptop, no idea what the issue was.. 
But another question: Was this ever tried/ developed to work with Black White or only with Black/ White 2? Because Bond does say in his post that it will eventually be for B/W as well


Edited by doms1313
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Create New...