Hacking New Classic Controller Hacks

  • Thread starter Thread starter Vague Rant
  • Start date Start date
  • Views Views 252,217
  • Replies Replies 690
  • Likes Likes 42
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?
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.

Update #2 on the Wiimote remapper: I tried Wii Music, a game that uses KPADRead and it still does not work. Very strange
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 use 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

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

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)?

OriginalModified
Code:
:unpack
wadunpacker %wad%

if exist "0001000157324d45" (
  set region=USA
  set titleid=0001000157324d45
) else if exist "0001000157324d50" (
  set region=EUR
  set titleid=0001000157324d50
) else (
  echo.
  echo Unsupported WAD. Please provide a WAD for Blaster Master: Overdrive.
  goto exit
)
Code:
:unpack
wadunpacker %wad%
pause
if exist "0001000157324d45" (
  set region=USA
  set titleid=0001000157324d45
) else if exist "0001000157324d50" (
  set region=EUR
  set titleid=0001000157324d50
) else (
  echo.
  echo Unsupported WAD. Please provide a WAD for Blaster Master: Overdrive.
  goto exit
)

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.

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

Don't what going on unable get the Potioner to move at all in the menus in game at all.
Post automatically merged:
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.

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

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!
I'll cover Nunchuk stick support in the next reply since newhinton also asked about it. 👍

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

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. :D

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.

From 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?
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 get 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.

(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?)
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 (+) 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
So this loop will run that 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.

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

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

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



SI_Wii_LegoIndianaJones2_enGB_image1600w.jpg


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)

  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/NunchukClassic ControllerFunction
Wiimote HomeHome
Remember that B is your A button
Open/Close Home Menu
Wiimote D-PadD-Pad
Right Stick
Menus
Navigation
Gameplay
Camera Control
Wiimote ABMenus
Confirm
Gameplay
Jump
Wiimote BY
ZR
Menus
Cancel
Gameplay
Attack
Wiimote 1LGameplay
Cycle Characters
Wiimote 2RGameplay
Cycle Characters
Wiimote PlusPlusGameplay
Pause
Wiimote MinusMinusNot used?
Wiimote PointerLeft StickGameplay
Aiming
Home Button Menu
Navigation
Nunchuk StickLeft StickMenus
Navigation
Gameplay
Movement
Nunchuk CXGameplay
Switch Character
Ride Vehicles
Nunchuk ZA
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:
  • C2 in WiiPad_Update(): force Classic to be recognized as Nunchuk
  • C2 in calc_dpd_variable(): fake IR pointer when in pointer contexts/Home Button Menu
  • 04 and C2 in read_kpad_stick(): redirect Classic Left Stick to Nunchuk, D-Pad emulation on Right Stick
  • C2, 04 and 04 in KPADRead(): store player index, dropped input fix
  • C2 in read_kpad_button(): button injector
For this one, I'm going to be posting the full source as well as some additional notes, since I think there's a good chance it'll be helpful to anybody who wants to look at other Lego games. First up are my notes, basically written for myself to read the next time I'm looking at a Lego game.

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
So ultimately, I had to store 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,
Would it be possible to make a gecko code to add nunchuk support to Monster Hunter G?
It only has support for classic controller.
 
Last edited by WiiML,
Would it be possible to make a gecko code to add nunchuk support to Monster Hunter G?
It only has support for classic controller.
It should be possible but it would be very complicated. Nunchuk hacks need you to set all these breakpoints and such to find the bytes the Nunchuk has.
Post automatically merged:

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.
I cant seem to find any bctrl stuff in read_kpad_stick. Do you know how to handle this part of hacking if its a game like Excite Truck? (Handles KPAD differently)
The symbol maps might not be correctly listed, but this is what read_kpad_stick looks like.
Screenshot 2025-01-27 063809.png
Screenshot 2025-01-27 063754.png
 
Last edited by awesomeee,
  • Like
Reactions: WiiML
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.
@Vague Rant If you point me to a git-repository, i am more than happy to help out with that. Maybe something like hugo or jekyll will work great. Just give me a pointer to what you decide and i'll gladly help out!

Before we start, I want to mention that Lego Star Wars III: The Clone Wars has a debug symbol map on the disc.
Funny that you say that. I did check out the link with the games that do have debug-maps, but it seems that the wbfs i created from my disk does not contain that file. I will have to re-dump it to check the iso.
 
  • Like
Reactions: Vague Rant
Have you been able to investigate Mario Sluggers yet? I might give that game a look for simple horizontal NES controls as an early release before you are able to get the Nunchuk version out.

It seems EXTREMELY complicated though since you need to investigate how the controls selector works though. Usually you can select what control scheme you want to use, but the Extension data is going to complicate it.
 
Thats strange, but please dont share wads, since it would be piracy.
Post automatically merged:


Excellent work! Im also curious about some of this, how do you redirect the CC stick into the Nunchuk stick? Hopefully we get knowledge lol

As for C and Z, the values go into the button assembly, with 2000 being Z, and 4000 being C. We still need knowledge on how to trick the game saying “Im a nunchuk.”
Post automatically merged:

@Vague Rant Good news! I know you arent interested in Twilight Princess and such, so I decided to give it a look. It handles KPAD differently. A lot differently. I checked your Excite Trucks technical notes, and using some of my logic I had, I managed to find where the buttons are injected! Ill keep you updated on the hack 👍

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!

Additionally, the game has a map leftover on the disc, so I know all the functions im patching. To clarify what I asked before, do you know any symbols that are often involved with Nunchuk checks?

Of course, im not trying to push you into doing a collab code.

Yeah i know it. I just want someone to share a patched pal nyxquest wad in mp.
 
God, I am seriously going to cry if I look at this god awful symbol map any longer. Ive tried a few addresses and I cannot for the love of god get this extension issue fixed. I even tried GetExtType, a direct function that looks like it handles extension data, but NOPE! Wont work. I made sure to inject at the cmpwi thing, and no luck. Is there anything else I should know?
 
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.


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

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)?

[TABLE=full]
[TR]
[TH]Original[/TH]
[TH]Modified[/TH]
[/TR]
[TR]
[TD]
Code:
:unpack
wadunpacker %wad%

if exist "0001000157324d45" (
  set region=USA
  set titleid=0001000157324d45
) else if exist "0001000157324d50" (
  set region=EUR
  set titleid=0001000157324d50
) else (
  echo.
  echo Unsupported WAD. Please provide a WAD for Blaster Master: Overdrive.
  goto exit
)
[/TD]
[TD]
Code:
:unpack
wadunpacker %wad%
pause
if exist "0001000157324d45" (
  set region=USA
  set titleid=0001000157324d45
) else if exist "0001000157324d50" (
  set region=EUR
  set titleid=0001000157324d50
) else (
  echo.
  echo Unsupported WAD. Please provide a WAD for Blaster Master: Overdrive.
  goto exit
)
[/TD]
[/TR]
[/TABLE]

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


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.


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.


I'll cover Nunchuk stick support in the next reply since newhinton also asked about it. 👍


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.

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. :D

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 get 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 (+) 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
So this loop will run that 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.

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.



View attachment 482823

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)

  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​

[TABLE=full]
[TR]
[TH]Wii Remote/Nunchuk[/TH]
[TH]Classic Controller[/TH]
[TH]Function[/TH]
[/TR]
[TR]
[TD]Wiimote Home[/TD]
[TD]Home
Remember that B is your A button[/TD]
[TD]Open/Close Home Menu[/TD]
[/TR]
[TR]
[TD]Wiimote D-Pad[/TD]
[TD]D-Pad
Right Stick[/TD]
[TD]Menus
Navigation
Gameplay
Camera Control[/TD]
[/TR]
[TR]
[TD]Wiimote A[/TD]
[TD]B[/TD]
[TD]Menus
Confirm
Gameplay
Jump[/TD]
[/TR]
[TR]
[TD]Wiimote B[/TD]
[TD]Y
ZR[/TD]
[TD]Menus
Cancel
Gameplay
Attack[/TD]
[/TR]
[TR]
[TD]Wiimote 1[/TD]
[TD]L[/TD]
[TD]Gameplay
Cycle Characters[/TD]
[/TR]
[TR]
[TD]Wiimote 2[/TD]
[TD]R[/TD]
[TD]Gameplay
Cycle Characters[/TD]
[/TR]
[TR]
[TD]Wiimote Plus[/TD]
[TD]Plus[/TD]
[TD]Gameplay
Pause[/TD]
[/TR]
[TR]
[TD]Wiimote Minus[/TD]
[TD]Minus[/TD]
[TD]Not used?[/TD]
[/TR]
[TR]
[TD]Wiimote Pointer[/TD]
[TD]Left Stick[/TD]
[TD]Gameplay
Aiming
Home Button Menu
Navigation[/TD]
[/TR]
[TR]
[TD]Nunchuk Stick[/TD]
[TD]Left Stick[/TD]
[TD]Menus
Navigation
Gameplay
Movement[/TD]
[/TR]
[TR]
[TD]Nunchuk C[/TD]
[TD]X[/TD]
[TD]Gameplay
Switch Character
Ride Vehicles[/TD]
[/TR]
[TR]
[TD]Nunchuk Z[/TD]
[TD]A
ZL[/TD]
[TD]Gameplay
Interact
(Build, Turn Cranks, etc.)[/TD]
[/TR]
[/TABLE]

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:
  • C2 in WiiPad_Update(): force Classic to be recognized as Nunchuk
  • C2 in calc_dpd_variable(): fake IR pointer when in pointer contexts/Home Button Menu
  • 04 and C2 in read_kpad_stick(): redirect Classic Left Stick to Nunchuk, D-Pad emulation on Right Stick
  • C2, 04 and 04 in KPADRead(): store player index, dropped input fix
  • C2 in read_kpad_button(): button injector
For this one, I'm going to be posting the full source as well as some additional notes, since I think there's a good chance it'll be helpful to anybody who wants to look at other Lego games. First up are my notes, basically written for myself to read the next time I'm looking at a Lego game.

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
So ultimately, I had to store 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
Found fixed for right stick not working did unmap classic controller and remapping it to IR pointer on wii remote itself basic fixed it.
 
  • Like
Reactions: Vague Rant
Fair warning, there's no hack at the end of this post, I'm just replying to stuff.

Would it be possible to make a gecko code to add nunchuk support to Monster Hunter G?
It only has support for classic controller.
That sounds like a fun thing to try, like a reverse Classic Controller hack. Not guaranteeing it can or will get done, but I'm interested. Do you have any thoughts about how the controls would work? Looking at Monster Hunter Tri, that one uses a bunch of motion controls to trigger different "buttons", which I'm definitely not confident I could replicate, so ideally everything would need to work with just the 8 buttons you get on Wiimote/Nunchuk.

I've never played MHG but I did play Tri. Assuming they're relatively similar, that could be a problem because Tri uses all 10 buttons on the Classic Controller. I'm not sure how you'd get everything to fit into less. That said, I know the the big addition to Tri was the underwater gameplay, so G probably doesn't use the underwater rise/sink buttons, so maybe there are only 8 buttons needed? Hard to find much about the game on Google since it was Japan-only.

I cant seem to find any bctrl stuff in read_kpad_stick. Do you know how to handle this part of hacking if its a game like Excite Truck? (Handles KPAD differently)
The symbol maps might not be correctly listed, but this is what read_kpad_stick looks like.
View attachment 482861View attachment 482860
Those clamp_stick() calls are what you're looking for. In very early games like Excite Truck and this, instead of the bctrl which allows branching to either the circle- or square-clamped analog range, games just used a hardcoded clamp_stick() function. The offsets look different compared to "modern" games, so uh ... it's not quite in the screenshot, but just above the first shot (literally one instruction off the top, I'm guessing), you should see addi r3, r30, 108. That's the offset where the Left Stick is writing to. You want to change that instruction to addi r3, r30, 96, which is 387E0060 in machine code.

So the final code you want to redirect the Left Stick into the Nunchuk Stick should be:
Code:
04388CD0 387E0060

@Vague Rant If you point me to a git-repository, i am more than happy to help out with that. Maybe something like hugo or jekyll will work great. Just give me a pointer to what you decide and i'll gladly help out!


Funny that you say that. I did check out the link with the games that do have debug-maps, but it seems that the wbfs i created from my disk does not contain that file. I will have to re-dump it to check the iso.
I have a bit of experience using Jekyll but none at all setting it up, so if you'd be willing to help with that, that would be great. I just started a GitHub organization ClassicHacks and the classichacks.github.io repo (currently empty and set to private). If you're also newhinton on GitHub, I'll add you to the org if you like.

Something like the Academic Pages theme (while maybe not quite thematically what we're doing!) seems like a sensible choice since it would allow us to have tabs along the top separating hacks from hacking guides and stuff. But if you're willing to get it set up I'll use whatever you're happy with, because as I say, I don't know what I'm doing.

Re: the symbol map for Clone Wars, it could definitely be that they only left behind the symbol map in the specific revision that I've got (USA Rev 0). They're not supposed to leave those laying around, so it definitely might be something they corrected in the next disc pressing. If that's the case, personally I would switch to hacking that revision, then figure out porting to the map-less revisions later.

Most games don't really change that much across revisions, so you can often just eyeball things or use known functions as a guide. e.g. If you find KPADRead() in your revision and the one with the symbol map, and they're 0x200 bytes apart, then you can just add 0x200 bytes to any other offset you're looking at and you'll be in the rough ballpark of the same code and should be able to recognize when it's the same stuff. You can have two Dolphin windows up at the same time if you need to compare directly.

Have you been able to investigate Mario Sluggers yet? I might give that game a look for simple horizontal NES controls as an early release before you are able to get the Nunchuk version out.

It seems EXTREMELY complicated though since you need to investigate how the controls selector works though. Usually you can select what control scheme you want to use, but the Extension data is going to complicate it.
Mario Sluggers I did get partway into, but hit a bit of a snag because there are lots of separate places where it checks which extension controller you have in, so it would seem to be working (menu would show Nunchuk inserted) but actually not be (gameplay controls were still sideways Wiimote). Definitely a complicated one as you say. I haven't given up on it, it just went on my list of games to come back to and try again.

God, I am seriously going to cry if I look at this god awful symbol map any longer. Ive tried a few addresses and I cannot for the love of god get this extension issue fixed. I even tried GetExtType, a direct function that looks like it handles extension data, but NOPE! Wont work. I made sure to inject at the cmpwi thing, and no luck. Is there anything else I should know?
I can't really think of anything else, getting rid of Nunchuk errors is definitely just something that can be really tough. Sometimes the extension check will be right after the KPADRead() call, so going to the parent function and scrolling down a bit to see if there's an extension check can work, but other times the check happens somewhere completely unrelated in code that you just have to locate through general reverse engineering. Stuff like finding the error text in memory "Please insert the Nunchuk"--could be plain ASCII, could be UTF-16, and tracking what reads that can help but you have to roughly inderstand what each function on the way backward is doing to really know what you're looking at.

So for example, if the game uses UTF-16 text (Japanese-made games are especially likely to use it), you might search for a hex byte string something like the bottom row.
Code:
   P    l    e    a    s    e         i    n    s    e    r    t         t    h    e         N    u    n    c    h    u    k
0050 006c 0065 0061 0073 0065 0020 0069 006e 0073 0065 0072 0074 0020 0074 0068 0065 0020 004e 0075 006e 0063 0068 0075 006b

If you find it, you'd then read breakpoint the address at the beginning of the string in memory so you can find what code is loading the error text. Most Wii games are multi-language, so it's likely that the function you find is something that handles grabbing the English error text specifically, by passing a specific integer or pointer for the string it wants to print into a localization function. So then you need to find what passed that specific value (the Nunchuk error message) to the localization function, then find what called the function that called that function ...

Obviously, a major problem with this is that games are reading and printing different strings all the time, so you can't just find what called the localization function and be certain that that's always running because it's printing the error message. So you need to use the conditional breakpoints in Dolphin to only break when the thing you're tracking is happening. For example, if there's 10 different strings currently being printed on screen but the Nunchuk error one is located at 80543210 and is passed to the function that reads it in r3, then you can execute breakpoint the start of that function and set a condition r3 == 0x80543210. Now, this breakpoint will only fire if r3 matches that value when it gets hit.

Ultimately, you're looking for some code that doesn't run at all when the Nunchuk is connected, but does run when a Classic Controller is connected. Then you can track backward to find what is calling that code, since it's clearly something related to the Nunchuk error state. There's no generic way to resolve the issue for games, because every developer has their own way of handling the wrong extension being inserted.
 
  • Like
Reactions: awesomeee
Thank you for the Nunchuk gecko code, I appreciate the help. Ill be sure to keep you updated

I might make the hack more of like a Bully: Scholarship one like you said earlier. The players must switch to the Nunchuk when there is something I wasnt able to implement 👍

Post automatically merged:

@Vague Rant Testing results for Max And the Magic Marker:
USA: Gameplay and controls works, audio is corrupted
EUR: Works aswell (YAY, no need to redo annoying extension error work) Audio is corrupted aswell
JPN: Not tested
 
Last edited by awesomeee,
I want to use the Classic Controller in Newer SMBW, so I tried the code from New Super Mario Bros., but the shake button doesn't work properly. When I press the shake button, the right input on the D-pad and the shake are both registered simultaneously. I want to modify the code so it works in Newer SMBR, but I'm having trouble with the modification. Can someone help me fix the code?
 
I want to use the Classic Controller in Newer SMBW, so I tried the code from New Super Mario Bros., but the shake button doesn't work properly. When I press the shake button, the right input on the D-pad and the shake are both registered simultaneously. I want to modify the code so it works in Newer SMBR, but I'm having trouble with the modification. Can someone help me fix the code?
I think Newer already has its own Classic Controller support, doesn't it? This might be the two separate CC hacks colliding with each other. If I'm remembering correctly, I'd say you just shouldn't use my hack with Newer, instead using its built-in CC support.

Since you did Spider-Man friend or foe, web of shadows or spider-man 3 would be amazing to play with a classic controller
They're definitely two that I'd love to do, but they're on the more complicated end as far as all the different motions they expect you to perform that would have to be simulated. I always wanted a proper open-world Spider-Man on Wii that could be played with traditional controls. Technically, there's Spider-Man 2 for GameCube, but I've played that a thousand times.



pix-cars.png


The original Cars video game, released alongside the Wii as a launch title in 2006. Coming out several months after Rainbow Studios' previous-gen versions (GameCube included), this port was developed by Incinerator Studios. Rather than creating a straight port, Incinerator remastered the graphics for the slightly-more-powerful Wii hardware, arguably resulting in the best version of the title. Like the later games in the Cars (1) trilogy, this is a mix of racing and minigames, taking place in open-world recreations of environments from the first movie.

Highlight for @Riot20052 who was hoping to see Cars 1 get CC support.
Cars makes relatively minimal use of motion controls (shake to jump), except in Piston Cup races, which have motion-controlled QTEs to tune up your vehicle when you make a pitstop. I did not directly handle the motion control in these sections, instead hacking the minigame itself to automatically register the pitstop QTEs as having been performed successfully.

You will need to use this Skip Pitstop Motions code to complete the game using a Classic Controller. The same code works on all regions of the game.

Code:
Skip Pitstop Motions [Vague Rant]
04062CAC 4800001C

In addition to the Classic Controller hack, I've also included a separate code to correct the widescreen rendering. If you know what vert- means then that was the issue and I fixed the game to run hor+. If you want more details, see the General Notes.

USAEUR (En,Es) / EUR (De,It)EUR (Fr,Nl)JPN

  1. Code:
    Classic Controller Support v1.1 [Vague Rant]
    060DA314 00000008
    989E0070 989E0071
    C214139C 00000002
    28000001 41820008
    28000002 00000000
    0419F2AC 48000048
    C22A8CC8 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C89F0
    7D8903A6 4E800421
    C03F0514 00000000
    C22A8FCC 00000002
    28000001 41820008
    28000002 00000000
    04380020 48000024
    0438007C 4E800020
    C22F6BC8 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F7858 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 803621E8
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F8244 38630060
    C22F6428 00000003
    A0040000 70050200
    41820008 60000040
    60000000 00000000
    C22F6470 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000
    Code:
    Fix Widescreen FOV [Vague Rant]
    C203C34C 0000000C
    C03F0000 800D99C8
    2C000002 40820050
    48000019 802DCBAC
    802DC220 3FAAAAAB
    3C0EFA39 42E52EE1
    7FE802A6 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 00000000
    C218F9B4 0000000F
    800D99C8 2C000002
    40820068 9101000C
    93E10008 48000019
    802DCBAC 802DC220
    3F400000 3C0EFA39
    42E52EE1 7FE802A6
    C0280000 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 8101000C
    D0280000 83E10008
    C03D0008 00000000
  2. Code:
    Classic Controller Support v1.1 [Vague Rant]
    060DA31C 00000008
    98DF0070 98DF0071
    C21413B0 00000002
    28000001 41820008
    28000002 00000000
    0419F300 48000048
    C22A8D7C 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C8AA4
    7D8903A6 4E800421
    C03F0514 00000000
    C22A9080 00000002
    28000001 41820008
    28000002 00000000
    04380090 48000024
    043800EC 4E800020
    C22F6C38 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F78C8 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 80362258
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F82B4 38630060
    C22F6498 00000003
    A0040000 70050200
    41820008 60000040
    60000000 00000000
    C22F64E0 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000
    Code:
    Fix Widescreen FOV [Vague Rant]
    C203C34C 0000000C
    C03F0000 800D99C8
    2C000002 40820050
    48000019 802DCC14
    802DC288 3FAAAAAB
    3C0EFA39 42E52EE1
    7FE802A6 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 00000000
    C218FA08 0000000F
    800D99C8 2C000002
    40820068 9101000C
    93E10008 48000019
    802DCC14 802DC288
    3F400000 3C0EFA39
    42E52EE1 7FE802A6
    C0280000 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 8101000C
    D0280000 83E10008
    C03D0008 00000000
  3. Code:
    Classic Controller Support v1.1 [Vague Rant]
    060DA320 00000008
    98DF0070 98DF0071
    C21413B4 00000002
    28000001 41820008
    28000002 00000000
    0419F304 48000048
    C22A8D80 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C8AA8
    7D8903A6 4E800421
    C03F0514 00000000
    C22A9084 00000002
    28000001 41820008
    28000002 00000000
    04380090 48000024
    043800EC 4E800020
    C22F6C38 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F78C8 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 80362258
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F82B4 38630060
    C22F6498 00000003
    A0040000 70050200
    41820008 60000040
    60000000 00000000
    C22F64E0 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000
    Code:
    Fix Widescreen FOV [Vague Rant]
    C203C34C 0000000C
    C03F0000 800D99C8
    2C000002 40820050
    48000019 802DCC18
    802DC28C 3FAAAAAB
    3C0EFA39 42E52EE1
    7FE802A6 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 00000000
    C218FA0C 0000000F
    800D99C8 2C000002
    40820068 9101000C
    93E10008 48000019
    802DCC18 802DC28C
    3F400000 3C0EFA39
    42E52EE1 7FE802A6
    C0280000 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 8101000C
    D0280000 83E10008
    C03D0008 00000000
  4. Code:
    Classic Controller Support v1.1 [Vague Rant]
    060DA2FC 00000008
    989E0070 989E0071
    C2141444 00000002
    28000001 41820008
    28000002 00000000
    0419F73C 48000048
    C22A9438 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C9160
    7D8903A6 4E800421
    C03F0514 00000000
    C22A973C 00000002
    28000001 41820008
    28000002 00000000
    04380780 48000024
    043807DC 4E800020
    C22F7328 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F7FB8 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 80362948
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F89A4 38630060
    C22F6498 00000003
    A0040000 70050200
    41820008 60000040
    60000000 00000000
    C22F6BD0 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000
    Code:
    Fix Widescreen FOV [Vague Rant]
    C203C34C 0000000C
    C03F0000 800D99C8
    2C000002 40820050
    48000019 802DD30C
    802DC980 3FAAAAAB
    3C0EFA39 42E52EE1
    7FE802A6 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 00000000
    C218FE44 0000000F
    800D99C8 2C000002
    40820068 9101000C
    93E10008 48000019
    802DD30C 802DC980
    3F400000 3C0EFA39
    42E52EE1 7FE802A6
    C0280000 C01F000C
    EC210032 819F0000
    7D8903A6 4E800421
    C01F0008 EC210032
    819F0004 7D8903A6
    4E800421 C01F0010
    EC210032 8101000C
    D0280000 83E10008
    C03D0008 00000000

Button Mapping​

In addition to the Classic Controller mapping, this hack also adds a camera toggle button on Wiimote 1 when playing with a real Nunchuk. Controls for the Wiimote/Nunchuk are otherwise unmodified.

Wii Remote/NunchukClassic ControllerFunction
Wiimote HomeHome
Remember that X is your A button
Open/Close Home Menu
Wiimote D-PadD-PadMenus
Navigation
Wiimote D-Pad LeftRGameplay
Powerslide
Wiimote D-Pad RightLGameplay
Tilt Vehicle
Wiimote AXHome Button Menu
Confirm
Menus
Replay Cutscene
Gameplay
E-Brake
Wiimote BZRMenus
Show Stats
Gameplay
Select Event
Nitro Boost
Wiimote 1BMenus
Cancel
Wiimote 2AMenus
Confirm
YGameplay
Switch Camera
Wiimote PlusPlusGameplay
Pause
Wiimote MinusMinusGameplay
Open Map (Overworld)
Wiimote ShakeZLGameplay
Jump
Wiimote PointerLeft StickHome Button Menu
Navigation
Nunchuk StickLeft StickMenus
Navigation
Gameplay
Steering
Nunchuk CBGameplay
Brake/Reverse
Nunchuk ZAGameplay
Accelerate

General Notes​

  • If you're using this to create a Wii VC injection on Wii U, you must enable the Force Classic Controller connected option. Thanks to @KeinesR for testing. Also note, if you allow the Wiimote connection to drop (no inputs for five minutes), upon reconnection the controller will be recognized as a Wii Remote without Classic Controller attached. To "re-attach the CC", press the Home button twice.

  • As mentioned in the Gecko Codes spoiler, you will need to use the Skip Pitstop Motions code to play through the game with a Classic Controller. These are brief motion-control QTEs encountered in Piston Cup races and no support for them is implemented in the CC hack. Use the skip code to bypass these sections instead.

  • UPDATE: I've added a widescreen fix code above. Cars was originally developed and released for sixth-gen consoles and was exclusively meant to be played in 4:3. When updating the game for the Wii, Incinerator opted to keep the same horizontal viewing area, cropping the vertical area to make a 16:9 image. This is what some people call "vert-", where a game is converted to widescreen by reducing the viewport vertically. The alternative is "hor+", where the vertical area remains the same, but the viewport is extended on the sides. The latter is generally the preferred option as people don't expect to see less on a wider TV.

    • Since the game was never meant to display wider, this does cause a very minor issue in the main menu only, where the static background doesn't extend all the way to the edge of the (16:9) screen. This isn't a clipping/culling issue, where graphics outside of the 4:3 area are not rendering, they literally just didn't put any geometry out there. It's not a big deal, I promise you won't die.

  • Originally, the game defaulted to the horizontal Wiimote control scheme (Preset 1), regardless of the attached controller. This has been switched to the Wiimote/Nunchuk control scheme (Preset 2) in this hack. However, it is still possible to find yourself in Preset 1 if you load an existing save configured to use that scheme or if you manually go into the control settings and switch it. If this happens, you won't be able to steer and your D-Pad layout will be rotated until you switch back to Preset 2. This is perfectly achievable with a rotated D-Pad, just mildly disorientating.

  • Once again, controls here are adapted from the PlayStation 2 version and are essentially the same as Mater-National (but not Race-O-Rama, which has a unique control scheme). This means you get the PS2 layout except face buttons are rotated to Nintendo-style and a Jump button has been added on ZL, which is unused on Sony (L2).

    • The original button layout on Wii is weirdly nothing like the Mater-National setup, including absolutely nonsensical choices like 1 and 2 for Cancel/Confirm even when playing with the vertical layout. Excuse me? In contrast, the Classic Controller layout here is quite normal. You've got your B/A buttons for Cancel/Confirm and traditional Nintendo Brake/Accelerate. That said, the messed up default controls actually make this an ideal game to remap if you want (code in Technical Notes). Because Cancel and Confirm (1/2) are mapped separately to the gameplay controls, you can remap everything else any way you like as long as you leave those alone and you'll have sensible menu controls no matter what.

  • The big news feature here is the camera control button (Classic Y/Nunchuk 1), used to toggle between the three camera views (Default, Chase and First-Person) during gameplay. This is a feature included on every other console's port of Cars which was lost in the transition to Wii--presumably because Incinerator ran out of buttons on the Wiimote/Nunchuk combo. By comparing against the GameCube version, I was able to reinstate this feature, which still mostly existed in the code but without any button assigned to perform it.

    • To be clear, the Wii version still supports camera controls, but normally you have to pause the game and select a camera setting in the menu. The game has an annoying habit of switching back to the Default camera when loading an event, so if you preferred one of the others, switching via the menu got tedious fast. Having a button to instantly switch is a huge convenience boost.

  • Hey! Listen! Before you start playing, create a new profile and name it CHMPION (champion, without an A). This switches the story mode to the secret Champion difficulty, which is like the Normal difficulty on a game that's not aimed at small children. Otherwise, this game is so overwhelmingly easy that it becomes frustrating. It's mostly still pretty easy, but nothing ridiculous, you just might not come first with a 30+ second lead in every single race.

Changelog​

  • v1.1
    • added a camera toggle button for (real) Nunchuks on Wiimote 1
    • no actual Classic Controller changes

  • Fix Widescreen FOV
    • no changes to the Classic Controller hack here, but I've added a separate code to fix a widescreen bug in the original game

Technical Notes​

The unbelievably helpful devs at Rainbow and Incinerator left behind unstripped ELFs on both the GameCube and Wii discs. This was instrumental in implementing both the pitstop skip and restoring the camera control button from the GameCube version. Give a round of applause to both teams.

Code breakdown:
  • 06 in CarsSettings::Reset(): set Preset 2 as the default control scheme (for both players)
  • C2 in CarsUIControllerSettings::HandleOK(): bypass Nunchuk error
  • 04 in CarsVehiclePhysics::SetControlInputs(): disable leftover double-tap boost we don't need
  • C2 and C2 in WIIJoystickDevice::Update(): add an extra button (Wiimote 0x40) to the in-game mapper to handle camera control, read Classic as a Nunchuk-type extension controller
  • 04 and 04 in setKpad(): disable native Classic Controller support in the Home Button Menu, which clashes with emulated Wiimote/Nunchuk support
  • C2 in read_kpad_acc(): fake accelerometer for Shake (Jump)
  • C2 in calc_dpd_variable(): simulate IR pointer
  • 04 in read_kpad_stick(): redirect Classic Left into Nunchuk Stick
  • C2 in read_kpad_button(): button injector
Code:
; read_kpad_button
; super early sdk, like excite truck
; 802F6470 for USA
; 802F64E0 for EUR (En,Es)/EUR (Fr,Nl)/EUR (De,It)
; 802F6BD0 for JPN
  lhz r5, 0x2A(r4)  ; classic bitfield

    CLASSIC_HOME:
      andi. r4, r5, 0x800
      beq- CLASSIC_UP
      ori r0, r0, 0x8000    ; home

    CLASSIC_UP:
      andi. r4, r5, 0x1
      beq- CLASSIC_DOWN
      ori r0, r0, 0x8       ; up (v) / left (h)

    CLASSIC_DOWN:
      andi. r4, r5, 0x4000
      beq- CLASSIC_LEFT
      ori r0, r0, 0x4       ; down (v) / right (h)

    CLASSIC_LEFT:
      andi. r4, r5, 0x2
      beq- CLASSIC_RIGHT
      ori r0, r0, 0x1       ; left (v) / down (h)

    CLASSIC_RIGHT:
      andi. r4, r5, 0x8000
      beq- CLASSIC_A
      ori r0, r0, 0x2       ; right (v) / up (h)

    CLASSIC_A:
      andi. r4, r5, 0x10
      beq- CLASSIC_B
      ori r0, r0, 0x2100    ; z (2000) + 2 (0100)

    CLASSIC_B:
      andi. r4, r5, 0x40
      beq- CLASSIC_X
      ori r0, r0, 0x4200    ; c (4000) + 1 (0200)

    CLASSIC_X:
      andi. r4, r5, 0x8
      beq- CLASSIC_Y
      ori r0, r0, 0x800     ; a

    CLASSIC_Y:
      andi. r4, r5, 0x20
      beq- CLASSIC_L
      ori r0, r0, 0x40      ; camera

    CLASSIC_L:
      andi. r4, r5, 0x2000
      beq- CLASSIC_R
      ori r0, r0, 0x2       ; right (v) / up (h)

    CLASSIC_R:
      andi. r4, r5, 0x200
      beq- CLASSIC_ZL
      ori r0, r0, 0x1       ; left (v) / down (h)

    CLASSIC_ZL:
      andi. r4, r5, 0x80
      beq- CLASSIC_ZR
      ori r0, r0, 0x80      ; shake

    CLASSIC_ZR:
      andi. r4, r5, 0x4
      beq- CLASSIC_PLUS
      ori r0, r0, 0x400     ; b

    CLASSIC_PLUS:
      andi. r4, r5, 0x400
      beq- CLASSIC_MINUS
      ori r0, r0, 0x10      ; plus

    CLASSIC_MINUS:
      andi. r4, r5, 0x1000
      beq- RETURN
      ori r0, r0, 0x1000    ; minus

RETURN:
  stw r0, 0x0(r3)
  mr r0, r5
 
Last edited by Vague Rant,
I think Newer already has its own Classic Controller support, doesn't it? This might be the two separate CC hacks colliding with each other. If I'm remembering correctly, I'd say you just shouldn't use my hack with Newer, instead using its built-in CC support.


They're definitely two that I'd love to do, but they're on the more complicated end as far as all the different motions they expect you to perform that would have to be simulated. I always wanted a proper open-world Spider-Man on Wii that could be played with traditional controls. Technically, there's Spider-Man 2 for GameCube, but I've played that a thousand times.



View attachment 483557

The original Cars video game, released alongside the Wii as a launch title in 2006. Coming out several months after Rainbow Studios' previous-gen versions (GameCube included), this port was developed by Incinerator Studios. Rather than creating a straight port, Incinerator remastered the graphics for the slightly-more-powerful Wii hardware, arguably resulting in the best version of the title. Like the later games in the Cars (1) trilogy, this is a mix of racing and minigames, taking place in open-world recreations of environments from the first movie.

Highlight for @Riot20052 who was hoping to see Cars 1 get CC support.
Cars makes relatively minimal use of motion controls (shake to jump), except in Piston Cup races, which have motion-controlled QTEs to tune up your vehicle when you make a pitstop. I did not directly handle the motion control in these sections, instead hacking the minigame itself to automatically register the pitstop QTEs as having been performed successfully.

You will need to use this Skip Pitstop Motions code to complete the game using a Classic Controller. The same code works on all regions of the game.

Code:
Skip Pitstop Motions [Vague Rant]
04062CAC 4800001C

USAEUR (En,Es) / EUR (De,It)EUR (Fr,Nl)JPN

  1. Code:
    Classic Controller Support [Vague Rant]
    060DA314 00000008
    989E0070 989E0071
    C214139C 00000002
    28000001 41820008
    28000002 00000000
    0419F2AC 48000048
    C22A8CC8 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C89F0
    7D8903A6 4E800421
    C03F0514 00000000
    C22A8FCC 00000002
    28000001 41820008
    28000002 00000000
    04380020 48000024
    0438007C 4E800020
    C22F6BC8 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F7858 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 803621E8
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F8244 38630060
    C22F6470 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000
  2. Code:
    Classic Controller Support [Vague Rant]
    060DA31C 00000008
    98DF0070 98DF0071
    C21413B0 00000002
    28000001 41820008
    28000002 00000000
    0419F300 48000048
    C22A8D7C 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C8AA4
    7D8903A6 4E800421
    C03F0514 00000000
    C22A9080 00000002
    28000001 41820008
    28000002 00000000
    04380090 48000024
    043800EC 4E800020
    C22F6C38 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F78C8 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 80362258
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F82B4 38630060
    C22F64E0 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000
  3. Code:
    Classic Controller Support [Vague Rant]
    060DA320 00000008
    98DF0070 98DF0071
    C21413B4 00000002
    28000001 41820008
    28000002 00000000
    0419F304 48000048
    C22A8D80 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C8AA8
    7D8903A6 4E800421
    C03F0514 00000000
    C22A9084 00000002
    28000001 41820008
    28000002 00000000
    04380090 48000024
    043800EC 4E800020
    C22F6C38 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F78C8 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 80362258
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F82B4 38630060
    C22F64E0 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000
  4. Code:
    Classic Controller Support [Vague Rant]
    060DA2FC 00000008
    989E0070 989E0071
    C2141444 00000002
    28000001 41820008
    28000002 00000000
    0419F73C 48000048
    C22A9438 00000006
    801F0504 FC40F890
    C022B204 7FE3FB78
    54040672 38A0001D
    3D80802A 618C9160
    7D8903A6 4E800421
    C03F0514 00000000
    C22A973C 00000002
    28000001 41820008
    28000002 00000000
    04380780 48000024
    043807DC 4E800020
    C22F7328 00000008
    801C005C 2C000002
    4082002C 4800000D
    C059999A BF800000
    7CA802A6 C0050004
    809C0004 70800080
    41820008 C0050000
    D01C03B8 C03C03B4
    60000000 00000000
    C22F7FB8 00000029
    90010024 2C040000
    4082013C 8003005C
    2C000002 40820130
    A0BD0112 38A50001
    7C05C000 408200E4
    48000021 80362948
    3FAAAAAB 3D4CCCCD
    00000000 00000000
    3DCCCCCD 3F800000
    7CA802A6 90A1000C
    56C7083C 38E7000C
    C0030060 FC000210
    C0230064 FC200A10
    FC00082A C0450014
    FC001040 4080001C
    7CC53AAE 28060000
    408100C4 80030000
    70000800 4182000C
    38C0012C 48000008
    38C6FFFF 7CC53B2E
    38C00002 98C30084
    80650000 28030001
    40810018 90A1000C
    7C6803A6 4E800021
    80A1000C 90650000
    2C030001 7FE3FB78
    C0450004 40820008
    EC4200B2 C0650008
    C0030020 C0230060
    FC211024 4800002D
    D0030020 C0030024
    C0230064 FC200850
    48000019 D0030024
    80010024 7C0803A6
    38210020 4E800020
    FC0100FA C0250018
    FC000800 4180000C
    FC000890 48000014
    FC200850 FC000800
    41810008 FC000890
    4E800020 00000000
    042F89A4 38630060
    C22F6BD0 00000019
    A0A4002A 70A40800
    41820008 60008000
    70A40001 41820008
    60000008 70A44000
    41820008 60000004
    70A40002 41820008
    60000001 70A48000
    41820008 60000002
    70A40010 41820008
    60002100 70A40040
    41820008 60004200
    70A40008 41820008
    60000800 70A40020
    41820008 60000040
    70A42000 41820008
    60000002 70A40200
    41820008 60000001
    70A40080 41820008
    60000080 70A40004
    41820008 60000400
    70A40400 41820008
    60000010 70A41000
    41820008 60001000
    90030000 7CA02B78
    60000000 00000000

Button Mapping​

[TABLE=full]
[TR]
[TH]Wii Remote/Nunchuk[/TH]
[TH]Classic Controller[/TH]
[TH]Function[/TH]
[/TR]
[TR]
[TD]Wiimote Home[/TD]
[TD]Home[/TD]
[TD]Open/Close Home Menu[/TD]
[/TR]
[TR]
[TD]Wiimote D-Pad[/TD]
[TD]D-Pad[/TD]
[TD]Menus
Navigation[/TD]
[/TR]
[TR]
[TD]Wiimote D-Pad Left[/TD]
[TD]R[/TD]
[TD]Gameplay
Powerslide[/TD]
[/TR]
[TR]
[TD]Wiimote D-Pad Right[/TD]
[TD]L[/TD]
[TD]Gameplay
Tilt Vehicle[/TD]
[/TR]
[TR]
[TD]Wiimote A[/TD]
[TD]X[/TD]
[TD]Home Button Menu
Confirm
Menus
Replay Cutscene
Gameplay
E-Brake[/TD]
[/TR]
[TR]
[TD]Wiimote B[/TD]
[TD]ZR[/TD]
[TD]Menus
Show Stats
Gameplay
Select Event
Nitro Boost[/TD]
[/TR]
[TR]
[TD]Wiimote 1[/TD]
[TD]B[/TD]
[TD]Menus
Cancel[/TD]
[/TR]
[TR]
[TD]Wiimote 2[/TD]
[TD]A[/TD]
[TD]Menus
Confirm[/TD]
[/TR]
[TR]
[TD][/TD]

[TD]Y[/TD]
[TD]Gameplay
Switch Camera[/TD]
[/TR]
[TR]
[TD]Wiimote Plus[/TD]
[TD]Plus[/TD]
[TD]Gameplay
Pause[/TD]
[/TR]
[TR]
[TD]Wiimote Minus[/TD]
[TD]Minus[/TD]
[TD]Gameplay
Open Map (Overworld)[/TD]
[/TR]
[TR]
[TD]Wiimote Shake[/TD]
[TD]ZL[/TD]
[TD]Gameplay
Jump[/TD]
[/TR]
[TR]
[TD]Wiimote Pointer[/TD]
[TD]Left Stick[/TD]
[TD]Home Button Menu
Navigation[/TD]
[/TR]
[TR]
[TD]Nunchuk Stick[/TD]
[TD]Left Stick[/TD]
[TD]Menus
Navigation
Gameplay
Steering[/TD]
[/TR]
[TR]
[TD]Nunchuk C[/TD]
[TD]B[/TD]
[TD]Gameplay
Brake/Reverse[/TD]
[/TR]
[TR]
[TD]Nunchuk Z[/TD]
[TD]A[/TD]
[TD]Gameplay
Accelerate[/TD]
[/TR]
[/TABLE]

General Notes​

  • As mentioned in the Gecko Codes spoiler, you will need to use the Skip Pitstop Motions code to play through the game with a Classic Controller. These are brief motion-control QTEs encountered in Piston Cup races and no support for them is implemented in the CC hack. Use the skip code to bypass these sections instead.

  • Originally, the game defaulted to the horizontal Wiimote control scheme (Preset 1), regardless of the attached controller. This has been switched to the Wiimote/Nunchuk control scheme (Preset 2) in this hack. However, it is still possible to find yourself in Preset 1 if you load an existing save configured to use that scheme or if you manually go into the control settings and switch it. If this happens, you won't be able to steer and your D-Pad layout will be rotated until you switch back to Preset 2. This is perfectly achievable with a rotated D-Pad, just mildly disorientating.

  • Once again, controls here are adapted from the PlayStation 2 version and are essentially the same as Mater-National (but not Race-O-Rama, which has a unique control scheme). This means you get the PS2 layout except face buttons are rotated to Nintendo-style and a Jump button has been added on ZL, which is unused on Sony (L2).
    • The original button layout on Wii is weirdly nothing like the Mater-National setup, including absolutely nonsensical choices like 1 and 2 for Cancel/Confirm even when playing with the vertical layout. Excuse me? In contrast, the Classic Controller layout here is quite normal. You've got your B/A buttons for Cancel/Confirm and traditional Nintendo Brake/Accelerate. That said, the messed up default controls actually make this an ideal game to remap if you want (code in Technical Notes). Because Cancel and Confirm (1/2) are mapped separately to the gameplay controls, you can remap everything else any way you like as long as you leave those alone and you'll have sensible menu controls no matter what.

  • The big news feature here is the camera control button (Classic Y), used to toggle between the three camera views (Default, Chase and First-Person) during gameplay. This is a feature included on every other console's port of Cars which was lost in the transition to Wii--presumably because Incinerator ran out of buttons on the Wiimote/Nunchuk combo. By comparing against the GameCube version, I was able to reinstate this feature, which still mostly existed in the code but without any button assigned to perform it.
    • To be clear, the Wii version still supports camera controls, but normally you have to pause the game and select a camera setting in the menu. The game has an annoying habit of switching back to the Default camera when loading an event, so if you preferred one of the others, switching via the menu got tedious fast. Having a button to instantly switch is a huge convenience boost.

  • Hey! Listen! Before you start playing, create a new profile and name it CHMPION (champion, without an A). This switches the story mode to the secret Champion difficulty, which is like the Normal difficulty on a game that's not aimed at small children. Otherwise, this game is so overwhelmingly easy that it becomes frustrating. It's mostly still pretty easy, but nothing ridiculous, you just might not come first with a 30+ second lead in every single race.

Technical Notes​

The unbelievably helpful devs at Rainbow and Incinerator left behind unstripped ELFs on both the GameCube and Wii discs. This was instrumental in implementing both the pitstop skip and restoring the camera control button from the GameCube version. Give a round of applause to both teams.

Code breakdown:
  • 06 in CarsSettings::Reset(): set Preset 2 as the default control scheme (for both players)
  • C2 in CarsUIControllerSettings::HandleOK(): bypass Nunchuk error
  • 04 in CarsVehiclePhysics::SetControlInputs(): disable leftover double-tap boost we don't need
  • C2 and C2 in WIIJoystickDevice::Update(): add an extra button (Wiimote 0x40) to the in-game mapper to handle camera control, read Classic as a Nunchuk-type extension controller
  • 04 and 04 in setKpad(): disable native Classic Controller support in the Home Button Menu, which clashes with emulated Wiimote/Nunchuk support
  • C2 in read_kpad_acc(): fake accelerometer for Shake (Jump)
  • C2 in calc_dpd_variable(): simulate IR pointer
  • 04 in read_kpad_stick(): redirect Classic Left into Nunchuk Stick
  • C2 in read_kpad_button(): button injector
Code:
; read_kpad_button
; super early sdk, like excite truck
; 802F6470 for USA
; 802F64E0 for EUR (En,Es)/EUR (Fr,Nl)/EUR (De,It)
; 802F6BD0 for JPN
  lhz r5, 0x2A(r4)  ; classic bitfield

    CLASSIC_HOME:
      andi. r4, r5, 0x800
      beq- CLASSIC_UP
      ori r0, r0, 0x8000    ; home

    CLASSIC_UP:
      andi. r4, r5, 0x1
      beq- CLASSIC_DOWN
      ori r0, r0, 0x8       ; up (v) / left (h)

    CLASSIC_DOWN:
      andi. r4, r5, 0x4000
      beq- CLASSIC_LEFT
      ori r0, r0, 0x4       ; down (v) / right (h)

    CLASSIC_LEFT:
      andi. r4, r5, 0x2
      beq- CLASSIC_RIGHT
      ori r0, r0, 0x1       ; left (v) / down (h)

    CLASSIC_RIGHT:
      andi. r4, r5, 0x8000
      beq- CLASSIC_A
      ori r0, r0, 0x2       ; right (v) / up (h)

    CLASSIC_A:
      andi. r4, r5, 0x10
      beq- CLASSIC_B
      ori r0, r0, 0x2100    ; z (2000) + 2 (0100)

    CLASSIC_B:
      andi. r4, r5, 0x40
      beq- CLASSIC_X
      ori r0, r0, 0x4200    ; c (4000) + 1 (0200)

    CLASSIC_X:
      andi. r4, r5, 0x8
      beq- CLASSIC_Y
      ori r0, r0, 0x800     ; a

    CLASSIC_Y:
      andi. r4, r5, 0x20
      beq- CLASSIC_L
      ori r0, r0, 0x40      ; camera

    CLASSIC_L:
      andi. r4, r5, 0x2000
      beq- CLASSIC_R
      ori r0, r0, 0x2       ; right (v) / up (h)

    CLASSIC_R:
      andi. r4, r5, 0x200
      beq- CLASSIC_ZL
      ori r0, r0, 0x1       ; left (v) / down (h)

    CLASSIC_ZL:
      andi. r4, r5, 0x80
      beq- CLASSIC_ZR
      ori r0, r0, 0x80      ; shake

    CLASSIC_ZR:
      andi. r4, r5, 0x4
      beq- CLASSIC_PLUS
      ori r0, r0, 0x400     ; b

    CLASSIC_PLUS:
      andi. r4, r5, 0x400
      beq- CLASSIC_MINUS
      ori r0, r0, 0x10      ; plus

    CLASSIC_MINUS:
      andi. r4, r5, 0x1000
      beq- RETURN
      ori r0, r0, 0x1000    ; minus

RETURN:
  stw r0, 0x0(r3)
  mr r0, r5
Wonder if people like a Ōkami classic controller patch mean don't focused on it probably lower tier needed to get done many more wii exclusives should put are work into. Reason say this because Ōkami basic every modern system and on android emulate the Nintendo switch ver quite will and or ps2 ver quite will so personality don't see needed classic controller patch on wii. That more up to you if what to add it.
Post automatically merged:

Wonder if people like a Ōkami classic controller patch mean don't focused on it probably lower tier needed to get done many more wii exclusives should put are work into. Reason say this because Ōkami basic every modern system and on android emulate the Nintendo switch ver quite will and or ps2 ver quite will so personality don't see needed classic controller patch on wii. That more up to you if what to add it.
Also don't be rube guy make watse time on Radom 3nd party games really dont neeeded Classic controller patch at all really.
 
Wonder if people like a Ōkami classic controller patch mean don't focused on it probably lower tier needed to get done many more wii exclusives should put are work into. Reason say this because Ōkami basic every modern system and on android emulate the Nintendo switch ver quite will and or ps2 ver quite will so personality don't see needed classic controller patch on wii. That more up to you if what to add it.
Post automatically merged:


Also don't be rube guy make watse time on Radom 3nd party games really dont neeeded Classic controller patch at all really.
Yeah, another complicating factor is that it doesnt use KPADRead at all so it would require a low level WPAD hack which seems EXTREMELY complicated, and then add in the complicating factors of the IR pointer, motion controls, it would be too much.
 
  • Like
Reactions: Vague Rant
Yeah, another complicating factor is that it doesnt use KPADRead at all so it would require a low level WPAD hack which seems EXTREMELY complicated, and then add in the complicating factors of the IR pointer, motion controls, it would be too much.
Yeah not main reason not be done more just fact that already outher system as is.
 
  • Like
Reactions: awesomeee
Re: Sonic Riders (AGAIN)

I believe GCN pad inputs are managed by a symbol “PADRead”. Im not entirely sure if this is accurate, but im pretty sure the GCN pad is just codenamed PAD.

It might be like Excite Trucks handling where it uses different instructions and values. Hopefully it can be figured out!
 
Last edited by awesomeee,
  • Like
Reactions: Vague Rant
@newhinton I would be willing to help with the Github aswell. I could make a video about how to find KPAD Read using WPADProbe. Any idea when the respository or page will be public?
 
Does anyone has a zip or a rar of this thread with all codes in it? I looked a few pages but not all 22.
Post automatically merged:

Does anyone has a zip or a rar of this thread with all codes in it? I looked a few pages but not all 22.
 

Site & Scene News

Popular threads in this forum