- Joined
- Aug 7, 2008
- Messages
- 2,538
- Solutions
- 1
- Reaction score
- 1,520
- Trophies
- 2
- Location
- Melbourne
- Website
- vaguerant.tumblr.com
- XP
- 4,165
- Country

I'll answer the first part after, but as for the Nunchuk buttons, they shouldn't hurt anything if you just leave them sitting there (since you're never going to press the Nunchuk buttons in a game that doesn't use the Nunchuk), but you could remove them as you suggested if you want to save a few lines in the code.Okay, thank you! Ill look into remapping Rhythm Heaven to support the Horizontal remote soon as a test.
@Vague Rant Bad news, your KPAD Wiimote mapper does not work. I tried changing the bytes for Rhythm Heaven Fever (A read_kpad_button game) And it does not seem to care, it just stays as it is, the original controls. Is the insertion address 8016c4d4 correct for that game?
If a game does not use the Nunchuk inputs, do I delete both nunchuk assembly lines and make WIIMOTE_MINUS branch to WIIMOTE_DONE?
This is really odd. It wasn't tested when I initially wrote it, but I just tried it on my end and it worked perfectly for remapping the buttons on the Wiimote. I also tested a version for newer SDKs that useUpdate #2 on the Wiimote remapper: I tried Wii Music, a game that uses KPADRead and it still does not work. Very strange
read_kpad_button() and that worked fine as well. Were you using both codes simultaneously maybe, Wiimote remapper and Classic Controller hack? They both get injected into the same place, so whichever code gets hit second will overwrite the first one. That would reset the Wiimote controls back to default if you had the CC hack after it. Anyway, here's the newer SDK version in case it works any differently:
Code:
; read_kpad_button
; r4 holds extType
; r6 holds wiimote bitfield
; r7 holds wiimote+nunchuk bitfield
; r8 holds classic bitfield
li r6, 0 ; blank out real wiimote buttons
WIIMOTE_HOME:
andi. r0, r7, 0x8000
beq- WIIMOTE_UP
ori r6, r6, 0x8000 ; home
WIIMOTE_UP:
andi. r0, r7, 0x8
beq- WIIMOTE_DOWN
ori r6, r6, 0x8 ; up (v) / left (h)
WIIMOTE_DOWN:
andi. r0, r7, 0x4
beq- WIIMOTE_LEFT
ori r6, r6, 0x4 ; down (v) / right (h)
WIIMOTE_LEFT:
andi. r0, r7, 0x1
beq- WIIMOTE_RIGHT
ori r6, r6, 0x1 ; left (v) / down (h)
WIIMOTE_RIGHT:
andi. r0, r7, 0x2
beq- WIIMOTE_A
ori r6, r6, 0x2 ; right (v) / up (h)
WIIMOTE_A:
andi. r0, r7, 0x800
beq- WIIMOTE_B
ori r6, r6, 0x800 ; a
WIIMOTE_B:
andi. r0, r7, 0x400
beq- WIIMOTE_1
ori r6, r6, 0x400 ; b
WIIMOTE_1:
andi. r0, r7, 0x200
beq- WIIMOTE_2
ori r6, r6, 0x200 ; 1
WIIMOTE_2:
andi. r0, r7, 0x100
beq- WIIMOTE_PLUS
ori r6, r6, 0x100 ; 2
WIIMOTE_PLUS:
andi. r0, r7, 0x10
beq- WIIMOTE_MINUS
ori r6, r6, 0x10 ; plus
WIIMOTE_MINUS:
andi. r0, r7, 0x1000
beq- NUNCHUK_C
ori r6, r6, 0x1000 ; minus
NUNCHUK_C:
andi. r0, r7, 0x4000
beq- NUNCHUK_Z
ori r6, r6, 0x4000 ; c
NUNCHUK_Z:
andi. r0, r7, 0x2000
beq- WIIMOTE_DONE
ori r6, r6, 0x2000 ; z
WIIMOTE_DONE:
mr r7, r6
RETURN:
andi. r0, r6, 0x9FFF
Sorry, I did try to reproduce the issue but so far I haven't managed to trigger the problem, so I'm not sure what's going wrong. Even though we're both using verified WADs (checked mine in Dolphin too), I wonder if there's some difference between them that the unpacker tool doesn't like.Ever get a chance to look at Blaster Master: Overdrive again btw? If not no worries!
and thanks again for all that you've done.
This won't help with getting it to actually rebuild, but maybe we can sort of diagnose it a bit if you edit the patcher script (
apply_patch.bat). Batch files are just plain text, you can open them in Notepad or whatever is convenient for you. Near the bottom is the :unpack section. Could you edit it like this (changed version on the right)?| Original | Modified |
|---|---|
|
Code:
|
Code:
|
If you could put a
pause on the line right after wadunpacker %wad%, then the script will pause there and wait for you to press a key. At that point you can have a look in the unpacked WAD directory and check whether the 00000007.app file is missing, or zero bytes or anything weird which indicates that the unpacking didn't work correctly.I would say it's more accurate to say that I listen to requests; I don't want anybody to get their hopes up that I'll definitely hack every game that gets asked about, but I'll 100% add this to my list of games to investigate.Edit: if you guys do requests, can I request for the shake feature in one piece grand adventure to be changed to a button? the game uses with nunchuck and the shaking is with the remote, and thanks for everything

This is weird. Since you're playing in Dolphin, do you have any other control mappings that might be conflicting, like the Right Stick also controls the real IR pointer and it's fighting with the hack? I'm not doing anything special with the pointer in this game, it works the same as in basically every other hack that has the Right Stick as pointer.Don't what going on unable get the Potioner to move at all in the menus in game at all.
Post automatically merged:
If I've got a symbol map, I'll usually just try searching for any functions that have "Nunchuk" or "Extension" in the name, because that's generally what they'll call any dedicated functions that check whether the right extension is attached currently. There doesn't seem to be a specific name that multiple devs share or anything, it's just whatever that team decided to call the function.Are there any specific functions / symbols that games use for extension errors? I might be lucky and get a direct function to patch already
If i do end up getting lucky, this might be my first game that uses the nunchuk lol
I'll cover Nunchuk stick support in the next reply since newhinton also asked about it.I currently need to learn more knowledge about disabling the Nunchuk check and redirecting the Stick input to the CC stick. This might really be my first complex game!

Impressively done! Before we start, I want to mention that Lego Star Wars III: The Clone Wars has a debug symbol map on the disc. This should be a huge help to you. Go to the right click Properties screen > Filesystem and right click mapfile.map. Choose Extract File... and save the file to wherever seems sensible to you. Now go to the top bar Symbols > Load Other Map File... and load in the file you just extracted, then do a Symbols > Save Symbol Map to save the new symbols back to Dolphin's database for this game.@Vague Rant I did it! Kinda!
I have written a small gecko code to sidestep the nunchuck-check. I found multiple candidates that looked like a check, but none had the desired outcome when changed.
However, at some point i had the idea to not look at the "reader", but the "writer" of the extension-bits, and that lead me back to WPADProbe. I patched this to always return an attached nunchuk. That probably has some undesired sideeffects. (I guess with this gecko code enabled, ONLY classic controllers will be supported. But i'll check that before i post it)
Now to the interesting parts: Since the button-injector works, i want to move on to the movement-controls. In the original game that is handled by the nunchuk.
For Classic-to-Nunchuk stick redirection, I actually covered that back in this post, which goes to your point about how this would work much better as a web site than a 20-page forum thread.
To summarize the relevant part of that post, you need to locate
read_kpad_stick() (or ext in newer games), scroll down to the two bctrl instructions which are where the Left Stick and Right Stick write out, respectively, and then look just before the first bctrl to where you see addi r3, r30, 12.
This is where the address where the Left Stick should write to in memory is set up:
r30 represents the start of the extension portion of the KPADStatus struct, which contains all the input (sticks, accelerometers, pointer, buttons), and r30 + 12 is where the Classic Controller Left Stick gets written to.
The Nunchuk Stick instead writes to the very first dword (double word: two words, 8 bytes), i.e.
r30 + 0. We want to change that addi instruction to make the Classic Left Stick write to there as well. We do this by replacing that instruction with an mr r3, r30 (move register to r3 from r30, without adding anything). In machine code, that looks like 7FC3F378.To insert just that one instruction, we take the address where the
addi instruction was and replace the first two digits (80) with 04, then our replacement code. Putting that all together, if we want to replace the instruction at 8001A114 with mr r3, r30, that looks like this:
Code:
0401A114 7FC3F378
With your current solution where
WPADProbe() always writes that a Nunchuk is attached, I don't think this will work on its own, because when read_kpad_stick() checks the current extension, it will see 01 (Nunchuk) and skip reading the actual Classic Controller sticks. You're definitely on the right track though. I actually just looked at a related game and the Technical Notes for this current hack might be useful for Clone Wars.awesomeee covered the Nunchuk buttons, but yeah, that's usually very simple, you just map them the same way as Wiimote buttons in the injector. The Wiimote and Nunchuk ultimately getFrom what i got in your button-mapper example, i need to write the cc-left-stick values to the register that would normally contain the nunchuck stick values. (You mentioned r7) I'd also need to write C and Z from one of the CLASSIC_?-Button-registers.
How do i do that?
I assume after the injected mapping, r8 contains the pressed buttons from the cc, and r0 should contain some bitfield made from those button-presses (A dummy-register only used to create the flag for the next beq- check?). r6 seems to be the target register for our conversion.
When i breakpoint the next instruction after the injected function returns, all those registers are 0. Is this because the emulation-window does not register my button presses since "clicking" start to step takes over window-focus? Or do i have to do something else?
or'ed and write their inputs into the same address, so in some senses, Nunchuk buttons are Wiimote buttons. Occasionally, games are real jerks about it and will filter out the Nunchuk buttons if a Nunchuk isn't specifically attached (requiring another Nunchuk check to be patched out), but most games will just accept that if you pressed a Nunchuk button, then you pressed a Nunchuk button.The registers coming out as 0 might be because of your modification of
WPADProbe() forcing it to report a Nunchuk. The button injector checks that the extension type is 2 before it starts, so if the injector thinks you have a Nunchuk attached, it won't do any of the Classic Controller button redirection. You mean specifically the minus symbol? The minus and pluses on the end of branch instructions in PPC assembly are for CPU hinting, they potentially optimize performance by telling the CPU whether the instruction is more likely to branch ((In that case, is there an exhaustive wii-assembly-document? I found the powerpc assembly guide helpful, but not quite easy to browse. For example i couldn't figure out what beq- means. Is that a negation for branch-if-equal?)
+) or not branch (-). What we're doing has such a microscopic impact on performance that it really doesn't matter much, I just use - habitually even though technically I should be using + sometimes. So beq- means:branch-if-equal, but it's more likely that we won't branch, so if there's any low-level optimizations you (the CPU) want to perform based on the probability that we're not going to branch, do them now.
The most common place you'll see
+ used is when code is running a loop. e.g. If something runs five times in a row then continues, you might use a decrementing counter. So your loop would look something like this:
Code:
li r6, 5 ; init the counter
START:
; some code that runs five times goes here
nop
; end of loop
subic. r6, r6, 1 ; subtract 1 and update the condition register
bgt+ START ; if r6 > 0, goto START
nop instruction (no operation, exactly what it sounds like), decrement r6 by 1 and then hit the bgt+. The first four times bgt+ gets hit, it will take the branch, because r6 will be 4, 3, 2, 1. The fifth time, r6 will be 0 and the bgt+ will not be taken (no branch). This means that four out of five times (80%), when that instruction gets hit, it will branch. Hence, it makes sense to use bgt+ in this scenario.For assembly info, I use IBM's PowerPC documentation. It's not Wii-specific in any way, but it has (fairly dry and technical) descriptions of every assembly instruction in the architecture. It's worth noting that the Wii CPU is basically from the '90s (it's roughly a PPC 750 from 1997), so there are instructions in that doc which were added to the architecture in subsequent generations that are not supported on Wii. If you try to use an instruction and CodeWrite throws an error, it's probably just not supported.
Yeah, the Right Stick setup should work in a pinch, but I agree that the Attack + Left Stick approach is much nicer and more in line with how Lego games work on PC and traditional consoles. That is how I handled the pointer emulation in Lego Batman and the hack below. It does require a bit of extra work (outside of the SDK stuff which normally makes up most of these hacks) to identify when the game is looking for pointer input and switch up the Left Stick functionality, but I think it really makes the Lego games feel more like they were meant to be played this way.After i figure out the motion, i will need to figure out the pointer. I assume that will be "easy" in the first step, since i can just reuse the second stick of the cc. In the final version though, i want to emulate the pc-control-scheme. It activates the pointer when the stick moves AND the action-button is pressed, which i quite like.
Again, I'd say check out the Technical Notes for the hack below. I'll go more in-depth than usual, since it seems like we're dealing with essentially all of the same problems.

This definitely seems like a great idea. I asked very early on in the thread (I dunno, page 2 or something) if anybody would be interested in that, but at the time it didn't get any response, as I don't think anybody was looking to get into making CC hacks themselves that far back. Obviously there's a few of us now and it would be much better to have the info somewhere more navigable than a forum thread, where nobody's going to read 20+ pages to find what they're looking for.When i am done, i need to properly document on what i learned, and what i did. I was thinking of using a git-repository for that. Would it make sense to create a shared one where you can collect all of the codes you wrote, their details and issues, guides and so on? It should be rather easy to publish that as a standalone website that is easily browseable. What do you think?
TT Games returns to the Harrison Fordiverse with Lego Indiana Jones 2: The Adventure Continues. The first game in the Lego series to use TT's second-generation engine, Indy 2 adds the dynamic split-screen multiplayer feature, which became an iconic part of Lego games over the following decade. Other additions include much larger hub worlds with their own challenges, a full level editor and high jumps for women. Outside of this, Lego Indy 2 is sometimes regarded as the black sheep of the Lego family. It removed the familiar Minikit and Red Brick hunting, placed a much heavier focus on combat and driving missions and most levels take place in a single arena instead of having players traverse through a few scenes of the relevant movie. The bonus levels feel similarly limited, seemingly built using the level editor system rather than being unique and original creations.
Highlight for @NestorM, who was interested in Lego games.
USA (Rev 1) / EUR (Rev 1)
-
Code:
Classic Controller Support v1.1 [Vague Rant] C215F094 00000003 28000002 4082000C 38000001 901D0000 28000001 00000000 C2019248 00000034 90010024 2C040000 40820190 8803005C 2C000002 40820184 2C190001 40820140 48000021 8088DEC8 00000000 00000000 8005E364 3FAAAAAB 3D4CCCCD 3F800000 7CA802A6 81030068 5504083C 7C842A14 80E50000 5500103A 7CE70214 80E70000 2C070000 4182003C A8E70858 A8C40004 B0E40004 2C070080 4182001C 2C070086 41820014 2C070089 4182000C 2C070090 40820010 7C073000 41820044 48000024 80C50008 80830004 70808000 41820020 68C60001 90C50008 2C060001 418200A0 38000000 90030020 90030024 2C060001 4182000C 98C3005E 48000084 38000002 9803005E 8065000C 28030001 40810018 90A1000C 7C6803A6 4E800021 80A1000C 9065000C 2C030001 7FE3FB78 C0450010 40820008 EC4200B2 C0650014 C0030020 C0230060 FC211024 48000045 D0030020 80030020 68000001 90030020 C0030024 C0230064 FC200850 48000025 D0030024 80030024 68000001 90030024 80010024 7C0803A6 38210020 4E800020 FC0100FA C0250018 FC000800 4180000C FC000890 48000014 FC200850 FC000800 41810008 FC000890 4E800020 60000000 00000000 0401A114 7FC3F378 C201A14C 00000012 4E800421 2C190001 40820080 4800000D 3F000000 00000000 7CA802A6 C0450000 C0650004 809EFFA0 80BEFFA4 80DEFFA8 C03E0014 38E00001 48000021 C03E0018 38E00004 48000015 909EFFA0 90BEFFA4 90DEFFA8 48000034 FC000A10 FC001040 4D800020 FC011840 41800008 54E7083C 7CC03839 40820008 7CA53B78 7C843B78 7CC63879 4E800020 60000000 00000000 C201A8E4 00000002 933A0068 3AE00000 60000000 00000000 0401ADB8 60000000 0401AF14 60000000 C20182A4 00000021 7D2802A6 4800000D 00000000 00000000 7D4802A6 7D2803A6 81230068 5529083C 7D4A4A14 2808FFFF 4082000C A10A0000 48000014 2C040002 408200C4 38800001 B10A0000 71090800 41820008 60E78000 71090001 41820008 60E70008 71094000 41820008 60E70004 71090002 41820008 60E70001 71098000 41820008 60E70002 71090010 41820008 60E72000 71090040 41820008 60E70800 71090008 41820008 60E74000 71090020 41820008 60E70400 71092000 41820008 60E70200 71090200 41820008 60E70100 71090080 41820008 60E72000 71090004 41820008 60E70400 71090400 41820008 60E70010 71091000 41820008 60E71000 7CC63B78 70C99FFF 60000000 00000000
Button Mapping
| Wii Remote/Nunchuk | Classic Controller | Function |
|---|---|---|
| Wiimote Home | Home Remember that B is your A button | Open/Close Home Menu |
| Wiimote D-Pad | D-Pad Right Stick | Menus Navigation Gameplay Camera Control |
| Wiimote A | B | Menus Confirm Gameplay Jump |
| Wiimote B | Y ZR | Menus Cancel Gameplay Attack |
| Wiimote 1 | L | Gameplay Cycle Characters |
| Wiimote 2 | R | Gameplay Cycle Characters |
| Wiimote Plus | Plus | Gameplay Pause |
| Wiimote Minus | Minus | Not used? |
| Wiimote Pointer | Left Stick | Gameplay Aiming Home Button Menu Navigation |
| Nunchuk Stick | Left Stick | Menus Navigation Gameplay Movement |
| Nunchuk C | X | Gameplay Switch Character Ride Vehicles |
| Nunchuk Z | A ZL | Gameplay Interact (Build, Turn Cranks, etc.) |
General Notes
- Same as Lego Batman, only the Rev 1 releases of the game were hacked. If you have a first-pressing, these hacks will almost certainly not be compatible.
- It's another one. This uses the same control scheme as Lego Star Wars and Batman before it, which once again means Y/B for Cancel/Confirm in order to match the standard gameplay controls of the Lego franchise.
Changelog
- v1.1 fixes an error in the dropped connection fix
Technical Notes
Code breakdown:
Note that I basically wrote these as I went, so some of the process is slightly redundant as I was learning more about how the engine worked. Also, anything that involves tracking down a function shouldn't be necessary for Clone Wars since it has a map file.
Next, forcing the Classic Controller to be recognized by the game as a Nunchuk. This allows button inputs and analog stick movement to be recognized. This comes right after the
Because this patch comes after
Now, the IR pointer. This is the most complex part in Lego games. The parts that need attention in adjusting this to other Lego games are the following:
Ordinarily, that
I don't know how much you're enjoying the "figuring it out" part so I'll put this stuff in a spoiler. If you prefer, you can find this stuff yourself. Note that these are all from the USA Rev 0 game, so if you're not using that version, use these only as a rough guide to tracking down the equivalent values in your version. Also note, I haven't actually checked any of this stuff is correct, I'm just working by analogy to Indiana Jones 2, so I'm sorry if any of this is wrong or misleading.
Now,
Now we're on to
In many SDK versions (both before and after Indy 2), the player index is preserved all the way to
So ultimately, I had to store
I've explained this before here and there, but I'll say it again since we're going in-depth: It's possible for extension controllers to briefly lose their connection to the Wiimote itself. This is generally something you'll encounter due to electrical interference with the cables themselves but can also be caused by software bugs. This is minimally noticeable in gameplay, because KPAD will read in the previous frame's inputs during any dropped frames so it seems like you just kept holding whatever you were holding before.
However, because we're rearranging where different things get written to (like the Classic Controller analog stick), KPAD is now reading in the previous frame's values from the wrong location. We need to fix that behavior, so we
Lastly, we come to
C2inWiiPad_Update(): force Classic to be recognized as NunchukC2incalc_dpd_variable(): fake IR pointer when in pointer contexts/Home Button Menu04andC2inread_kpad_stick(): redirect Classic Left Stick to Nunchuk, D-Pad emulation on Right StickC2,04and04inKPADRead(): store player index, dropped input fixC2inread_kpad_button(): button injector
Note that I basically wrote these as I went, so some of the process is slightly redundant as I was learning more about how the engine worked. Also, anything that involves tracking down a function shouldn't be necessary for Clone Wars since it has a map file.
Code:
===============================================================================
Process for hacking a "modern" Lego game
Lego Indiana Jones 2 and forward
===============================================================================
First up, let's handle everything except the pointer.
1) Do all the normal "tracking down the SDK functions" stuff.
Find KPADRead() as the unrecognized ("---") caller of WPADProbe().
2) Make the usual insertions and replacements.
nop lag frame fix, insert button injector, redirect stick, emulate d-pad.
We'll deal with DPD (the pointer) later.
3) Track the Nunchuk analog stick values (0x60 in KPADStatus)
[!] You might want to skip ahead to step 5 instead of doing it this way.
Eventually you'll land on the function nuwii.master::WiiPad_GetStickX().
lwzx r0, r3, r0 ; extension type
cmplwi r0, 0x1
bne- NO_NUNCHUK
mulli r0, r4, 0xB0
lis r3, 0x8083
lfs f1, -0x72CC(r2)
subi r3, r3, 0x1138 ; lol george lucas
add r3, r3, r0
lfs f0, 0x60(r3) ; nunchuk x axis
b CONTINUE
NO_NUNCHUK:
Most values likely won't match, confirm the extension type and analog read.
4) Write breakpoint the extension type word. You should land in WPADProbe().
5) Go to the caller, which should be nuwii.master::WiiPad_Update().
This function is also the caller for KPADRead(). You could just find that.
After WPADProbe() should be an extension check. Force Classic to Nunchuk:
cmplwi r0, 2
bne- RETURN
li r0, 1
stw r0, 0x0(r29)
RETURN:
cmplwi r0, 1
Make sure the registers and distances are correct.
6) You should now have analog control and buttons working.
-------------------------------------------------------------------------------
Now it's time to get the IR pointer working. There are a few moving parts here.
1) Locate legogame.master::Move_CHARACTER().
I don't ... really know how to find this, this was just hours of RE for me.
2) Find the child function legoapi.master::Boomerang_MoveCode().
About two thirds of the way down. There should be context checks after.
By context checks, I mean something like:
lha r0, 0x858(r19)
cmpwi r0, -0x1
[...]
cmpwi r0, 0xA
Registers, distances and contexts may not match.
3) Inside Boomerang_MoveCode(), breakpoint this instruction near the beginning:
lha r3, 0x858(r28)
Again, the register and distance are not guaranteed. Should be a halfword.
This will give us the player context once per frame. Disable the breakpoint.
4) Test every projectile or other aiming-based weapon.
Get into aiming mode (hold B) then enable the breakpoint.
Note down which values correspond to contexts that require aiming.
5) Locate the pointer to the current player, legoapi.master::Player.
This is another thing I don't really have a quick route to.
Instead, track r3 backward from legoapi.master::Boomerang_MoveCode().
The pointer will eventually be loaded in via immediates.
6) Plug legoapi.master::Player and the aiming contexts into your DPD function.
7) You should now have aiming working.
Next, forcing the Classic Controller to be recognized by the game as a Nunchuk. This allows button inputs and analog stick movement to be recognized. This comes right after the
WPADProbe() call inside WiiPad_Update(). In human terms, we're checking if the extension type returned by WPADProbe() was 2 (Classic Controller). If not, we don't do anything. If it was 2, we replace it with 1 (Nunchuk) and write it back so that any other functions which also read this value (namely the WiiPad_GetStickX/Y() functions and button reader I've forgotten the name of) will think we have a Nunchuk attached.Because this patch comes after
KPADRead() has run (using the real extension type of 2), it doesn't impact any of that functionality, but now when the game's own code checks the extension it's reading our replaced value.
Code:
; WiiPad_Update
; 8015F094 for USA (Rev 1)
cmplwi r0, 2
bne- RETURN
li r0, 1
stw r0, 0x0(r29)
RETURN:
cmplwi r0, 1
Now, the IR pointer. This is the most complex part in Lego games. The parts that need attention in adjusting this to other Lego games are the following:
Code:
PLAYER: .int 0x8088DEC8 ; USA (Rev 1) 0x8088DEC8
[...]
GETASPECT: .int 0x8005E364 ; USA (Rev 1) 0x8005E364
[...]
lha r7, 0x858(r7)
lha r6, TARGETING-MAGIC(r4)
sth r7, TARGETING-MAGIC(r4)
cmpwi r7, 0x80 ; gun
beq- NEED_POINTER
cmpwi r7, 0x86 ; creator
beq- NEED_POINTER
cmpwi r7, 0x89 ; whip
beq- NEED_POINTER
cmpwi r7, 0x90 ; banana
bne- CHECK_HOME
Ordinarily, that
PLAYER pointer is a lot of effort to find, but since Clone Wars has a map file, you can just look it up in there. That's a pointer to all of the info about the current player (player 1 and 2 are right next to each other, the DPD code handles checking which player you are). Inside the player data is the current context, e.g. aiming a projectile weapon, which is what the next part does.lha r7, 0x858(r7) is right for Indy 2 but 0x858 will be different for Clone Wars. That reads in the current context, and then we're checking if the current context is one which requires aiming. If it is one of those contexts, we jump straight to enabling the pointer. If it's not any of those contexts, we branch out to check if the Home Button Menu was opened (since the pointer is always needed in there).I don't know how much you're enjoying the "figuring it out" part so I'll put this stuff in a spoiler. If you prefer, you can find this stuff yourself. Note that these are all from the USA Rev 0 game, so if you're not using that version, use these only as a rough guide to tracking down the equivalent values in your version. Also note, I haven't actually checked any of this stuff is correct, I'm just working by analogy to Indiana Jones 2, so I'm sorry if any of this is wrong or misleading.
Code:
00063ba0 000020 80931fa0 009294a0 8 Player legoapi.master.a players.o
^^^^^^^^ here's the PLAYER offset
007f2920 000054 807f9100 007f52e0 16 SCGetAspectRatio sc.a scapi.o
^^^^^^^^ here's the GETASPECT offset
And by checking the code of Boomerang_MoveCode(), this looks like the context read:
80564fc0 lha r0, 0x02D4 (r3)
So you probably want lha r7, 0x2D4(r7) in Clone Wars.
I suspect this will be the same across all revisions and regions.
Code:
; calc_dpd_variable
; 80019248 for USA (Rev 1)
stw r0, 0x24(sp)
; player using real pointer? (gravity already valid)
cmpwi r4, 0x0
bne- NO_CLASSIC
; check for Classic Controller
lbz r0, 0x5C(r3)
cmpwi r0, 0x2
bne- NO_CLASSIC
; last poll
cmpwi r25, 0x1
bne- RETURN
; magic
bl GRAB
MAGIC:
PLAYER: .int 0x8088DEC8 ; USA (Rev 1) 0x8088DEC8
TARGETING: .int 0
HOME: .int 0
GETASPECT: .int 0x8005E364 ; USA (Rev 1) 0x8005E364
ASPECT: .float 1.3333333333
MULTIPLIER: .float 0.05 ; pointer speed
MAX_RANGE: .float 1.0
GRAB:
mflr r5
; you reading this, this is an instruction, not explaining what the code is doing
; you should check if r27 or other high registers are already tracking player #
; if yes, you can skip reading the player number in and just use ~r27 in the slwi
lwz r8, 0x68(r3)
slwi r4, r8, 1
add r4, r4, r5
lwz r7, PLAYER-MAGIC(r5)
slwi r0, r8, 2 ; offset by player
add r7, r7, r0
lwz r7, 0x0(r7)
cmpwi r7, 0x0
beq- CHECK_HOME
lha r7, 0x858(r7)
lha r6, TARGETING-MAGIC(r4)
sth r7, TARGETING-MAGIC(r4)
cmpwi r7, 0x80 ; gun
beq- NEED_POINTER
cmpwi r7, 0x86 ; creator
beq- NEED_POINTER
cmpwi r7, 0x89 ; whip
beq- NEED_POINTER
cmpwi r7, 0x90 ; banana
bne- CHECK_HOME
NEED_POINTER:
cmpw r7, r6
beq- DELTA
b RESET_POINTER
; has somebody opened home menu?
CHECK_HOME:
lwz r6, HOME-MAGIC(r5)
; toggle pointer when Home pressed
lwz r4, 0x4(r3)
andi. r0, r4, 0x8000
beq- HOME_OPEN
xori r6, r6, 1
stw r6, HOME-MAGIC(r5)
cmpwi r6, 1
beq- RETURN
RESET_POINTER:
li r0, 0x0
stw r0, 0x20(r3)
stw r0, 0x24(r3)
HOME_OPEN:
cmpwi r6, 1
beq- DELTA
stb r6, 0x5E(r3)
b RETURN
DELTA:
li r0, 2
stb r0, 0x5E(r3)
lwz r3, GETASPECT-MAGIC(r5) ; SCGetAspectRatio()
cmplwi r3, 0x1
ble- CHECK_ASPECT
; run aspect ratio check
stw r5, 0x0C(sp) ; save magic pointer
mtlr r3
blrl
lwz r5, 0x0C(sp) ; restore magic pointer
stw r3, GETASPECT-MAGIC(r5)
CHECK_ASPECT:
cmpwi r3, 0x1
mr r3, r31 ; restore input pointer
lfs f2, ASPECT-MAGIC(r5)
bne- FULLSCREEN
fmuls f2, f2, f2 ; widescreen
FULLSCREEN:
lfs f3, MULTIPLIER-MAGIC(r5)
; pointer X axis
lfs f0, 0x20(r3)
lfs f1, 0x60(r3) ; nunchuk stick X
fdiv f1, f1, f2 ; divide by aspect ratio
bl RANGE_CHECK
stfs f0, 0x20(r3)
lwz r0, 0x20(r3)
xori r0, r0, 1
stw r0, 0x20(r3)
; pointer Y axis
Y_AXIS:
lfs f0, 0x24(r3)
lfs f1, 0x64(r3) ; nunchuk stick Y
fneg f1, f1
bl RANGE_CHECK
stfs f0, 0x24(r3)
lwz r0, 0x24(r3)
xori r0, r0, 1
stw r0, 0x24(r3)
; this commented section is for handling the vacuum suit in lego batman
; cmpwi r7, 0x52
; bne- RETURN
; right stick is nunchuk stick temporarily
; lfs f0, 0x74(r3)
; stfs f0, 0x60(r3)
; lfs f0, 0x78(r3)
; stfs f0, 0x64(r3)
RETURN:
lwz r0, 0x24(sp)
mtlr r0
addi sp, sp, 0x20
blr
RANGE_CHECK:
fmadd f0, f1, f3, f0
lfs f1, MAX_RANGE-MAGIC(r5)
fcmpu cr0, f0, f1
blt CHECK_NEGATIVE
fmr f0, f1
b IN_RANGE
CHECK_NEGATIVE:
fneg f1, f1
fcmpu cr0, f0, f1
bgt IN_RANGE
fmr f0, f1
IN_RANGE:
blr
NO_CLASSIC:
Now,
read_kpad_stick(), with our stick redirection on the addi instruction before the Classic Controller Left Stick bctrl (second one in the entire function but first for CC; you should see the Right Stick one immediately below it), and then our D-Pad emulation on the second bctrl.
Code:
; read_kpad_stick
; stick redirection
; USA (Rev 1)
0401A114 7FC3F378
; d-pad emulation
; 8001A14C for USA (Rev 1)
bctrl
cmpwi r25, 0x1
bne- RETURN
; magic
bl GRAB
MAGIC:
THRESHOLD: .float 0.5
ZERO: .float 0.0
GRAB:
mflr r5
lfs f2, THRESHOLD-MAGIC(r5)
lfs f3, ZERO-MAGIC(r5)
lwz r4, -0x60(r30) ; currently held
lwz r5, -0x5C(r30) ; pressed this frame
lwz r6, -0x58(r30) ; released this frame
lfs f1, 0x14(r30) ; right stick x axis
li r7, 0x1
bl STICK_EMULATION
lfs f1, 0x18(r30) ; right stick y axis
li r7, 0x4
bl STICK_EMULATION
stw r4, -0x60(r30)
stw r5, -0x5C(r30)
stw r6, -0x58(r30)
b RETURN
STICK_EMULATION:
fabs f0, f1
fcmpo cr0, f0, f2 ; is stick more than halfway?
bltlr-
fcmpo cr0, f1, f3 ; positive or negative direction?
blt- AXIS_SKIP
slwi r7, r7, 0x1 ; left->right / down->up
AXIS_SKIP:
and. r0, r6, r7 ; was this button already being held?
bne- NOT_NEW
or r5, r5, r7 ; add to buttons newly pressed
NOT_NEW:
or r4, r4, r7 ; add to buttons held
andc. r6, r6, r7 ; remove from buttons released
blr
RETURN:
Now we're on to
KPADRead(). In the first part, we're storing the current player number into KPADStatus. It's not normally part of KPADStatus, but we need that info later in the DPD code, so that we can read from the current player's pointer instead of just the hardcoded player 1 pointer. KPADRead() is always called with which player's inputs are being handled currently, so this was an effective way to get that value.In many SDK versions (both before and after Indy 2), the player index is preserved all the way to
calc_dpd_variable(), so if one of the high registers (usually r27) is already tracking the current player, you can substitute that instead of doing all of this. If it is necessary though, the registers are probably not accurate for other games (Lego or otherwise), so you just want to look for the general pattern at the beginning of KPADRead(). You're looking for something like this:
Code:
lbz r0, -0x8C(r13)
lis r8, -0x7F88
mulli r9, r3, 0x578
or r25, r3, r3 ; player index just got moved into r25
cmpwi r0, 0x0
subi r8, r8, 0x4600
or r29, r4, r4
or r24, r5, r5
or r30, r6, r6
or r31, r7, r7
add r26, r8, r9 ; pointer to KPADStatus just got generated
li r23, 0x0 ; this is where i put my injection
li r22, 0x0
r25 into 0x68(r26)--0x68 is normally the "Classic Controller released buttons" field, but we're not storing that information any more because we don't write out CC buttons. Below is what that looks like. In case you're unfamiliar with Gecko codes, a code like this (where you give it an address and some code to insert) replaces the instruction that was originally at the address where you made the insertion. That's why my injected code here includes li r23, 0, because the injection clobbered that original instruction, so I need to restore it in the code. You can see this being done in any of the other codes as well, the original instruction added into the injected code.
Code:
; KPADRead
; save player index
; 8001A8E4 for USA (Rev 1)
stw r25, 0x68(r26)
li r23, 0
I've explained this before here and there, but I'll say it again since we're going in-depth: It's possible for extension controllers to briefly lose their connection to the Wiimote itself. This is generally something you'll encounter due to electrical interference with the cables themselves but can also be caused by software bugs. This is minimally noticeable in gameplay, because KPAD will read in the previous frame's inputs during any dropped frames so it seems like you just kept holding whatever you were holding before.
However, because we're rearranging where different things get written to (like the Classic Controller analog stick), KPAD is now reading in the previous frame's values from the wrong location. We need to fix that behavior, so we
nop out the original "bad input" reads in KPADRead() and replace them with our own inside read_kpad_button(). Right before calling read_kpad_button(), games check if the Classic Controller button value is 0xFFFF (bad) and reads in the previous frame's inputs. So we find the two places that read_kpad_button() is called and look before that to disable the original reads (replacing them with 60000000 aka nop.
Code:
0401ADB8 60000000
0401AF14 60000000
Lastly, we come to
read_kpad_button() and our trusty button injector. As mentioned, this now includes the handling for dropped frames along with the actual button injections. You can see how the C and Z buttons are injected here just like every other button, as well.
Code:
; read_kpad_button
; 800182A4 for USA (Rev 1)
; r4 holds extType
; r6 holds wiimote bitfield
; r7 holds wiimote+nunchuk bitfield
; r8 holds classic bitfield
; magic
mflr r9
bl GRAB
MAGIC:
HELD: .double 0
GRAB:
mflr r10
mtlr r9
lwz r9, 0x68(r3)
slwi r9, r9, 1
add r10, r10, r9
; check dropped extension
cmplwi r8, 0xFFFF
bne- CLASSIC
lhz r8, HELD-MAGIC(r10)
b DROPPED_CONNECTION
CLASSIC:
cmpwi r4, 0x2
bne- RETURN
li r4, 0x1 ; i'm a nunchuk
sth r8, HELD-MAGIC(r10)
DROPPED_CONNECTION:
CLASSIC_HOME:
andi. r9, r8, 0x800
beq- CLASSIC_UP
ori r7, r7, 0x8000 ; home
CLASSIC_UP:
andi. r9, r8, 0x1
beq- CLASSIC_DOWN
ori r7, r7, 0x8 ; up (v) / left (h)
CLASSIC_DOWN:
andi. r9, r8, 0x4000
beq- CLASSIC_LEFT
ori r7, r7, 0x4 ; down (v) / right (h)
CLASSIC_LEFT:
andi. r9, r8, 0x2
beq- CLASSIC_RIGHT
ori r7, r7, 0x1 ; left (v) / down (h)
CLASSIC_RIGHT:
andi. r9, r8, 0x8000
beq- CLASSIC_A
ori r7, r7, 0x2 ; right (v) / up (h)
CLASSIC_A:
andi. r9, r8, 0x10
beq- CLASSIC_B
ori r7, r7, 0x2000 ; z
CLASSIC_B:
andi. r9, r8, 0x40
beq- CLASSIC_X
ori r7, r7, 0x800 ; a
CLASSIC_X:
andi. r9, r8, 0x8
beq- CLASSIC_Y
ori r7, r7, 0x4000 ; c
CLASSIC_Y:
andi. r9, r8, 0x20
beq- CLASSIC_L
ori r7, r7, 0x400 ; b
CLASSIC_L:
andi. r9, r8, 0x2000
beq- CLASSIC_R
ori r7, r7, 0x200 ; 1
CLASSIC_R:
andi. r9, r8, 0x200
beq- CLASSIC_ZL
ori r7, r7, 0x100 ; 2
CLASSIC_ZL:
andi. r9, r8, 0x80
beq- CLASSIC_ZR
ori r7, r7, 0x2000 ; z
CLASSIC_ZR:
andi. r9, r8, 0x4
beq- CLASSIC_PLUS
ori r7, r7, 0x400 ; b
CLASSIC_PLUS:
andi. r9, r8, 0x400
beq- CLASSIC_MINUS
ori r7, r7, 0x10 ; plus
CLASSIC_MINUS:
andi. r9, r8, 0x1000
beq- CLASSIC_DONE
ori r7, r7, 0x1000 ; minus
CLASSIC_DONE:
or r6, r6, r7
RETURN:
andi. r9, r6, 0x9FFF
Last edited by Vague Rant,










