• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Completed Infinite Shield (P1 Only)

Mimerme

Smash Rookie
Joined
Feb 9, 2018
Messages
3
Pretty self explanatory. Not sure if it's been posted here before, but nothing showed up on Google. The code can be easily modified to support other players as well.

C206B824 00000006
7C0803A6 3CA08045
60A53130 80C50000
38C6002C 80A60000
38A51998 3CC04270
60C60000 90C50000
4E800020 00000000

Code:
mtlr 0
lis r5, 0x8045
ori r5, r5, 0x3130     ;Load the character entity data pointer
lwz r6, 0(r5)         ;dereference the pointer
addi r6, r6, 0x2c     ;Add the offset to the character data struct
lwz r5, 0(r6)         ;dereference the pointer to the player character data
addi r5, r5, 0x1998 ;Add the offset to point to the shield size
lis r6, 0x4270
ori r6, r6, 0x0000     ;Load 60 into r6
stw r6, 0(r5)        ;Permanently set the shield size to the value stored in r6
blr
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
423
Nice code! It’s good to see more ASM hackers around here. Thanks for sharing your source, too.

If you’re open to suggestions, I have a little advice about lis instructions, and load/store displacements. I can also tell you how to make this code support multiple players without increasing the code size:
When you use an li or lis instruction, the bits that aren’t specified in the instruction are automatically set to 0.

So if you want to load the float “60” as hex 0x42700000 into r6, the single lis instruction is sufficient.

Code:
lis r6, 0x4270 ;  r6 = 0x42700000
When loading or storing values, you can also specify a displacement offset in the range of a signed immediate value.

So for example, instead of adding addi r5, r5, 0x1998, and then storing stw r6, 0(r5) -- you could combine them into one instruction using

Code:
stw r6, 0x1998(r5)
---

If you haven’t already -- grab the Community Symbol Map! It’s very informative, and will help a lot when searching for good context when making an injection code.

The function you’ve hooked to the end of is a player function that updates player buttons and IASA each frame. This means that it uses the same player data pointer you’ve built to do something with each player, each frame. It’s a useful context to exploit.

If you move your hook to before the epilog, you’ll be able to steal the context of r31 = player data to make the code apply to everyone.



So, if you inject @ 8006b80c instead, you may skip most of the setup you’ve done in order to apply to every character that uses the host function.

---

A code that does all of these things would end up looking something like the following
Code:
; INJ @ 8006b80c -- for each player, each frame
; r31 = contextual player data start address
lis r6, 0x4270       ; create FP 60.0
stw r6, 0x1998(r31)  ; store r6 in offset 0x1998 of "this" player data table
lwz r0, 0x007C (sp)  ; original hook instruction
; gecko tools will compile a return branch to the line after your injection hook
This hits all players because "this" player refers to the player referenced by the host function; which operates on each player once per frame.

Hope to see more of your stuff!
 
Last edited:

Mimerme

Smash Rookie
Joined
Feb 9, 2018
Messages
3
Nice code! It’s good to see more ASM hackers around here. Thanks for sharing your source, too.

If you’re open to suggestions, I have a little advice about lis instructions, and load/store displacements. I can also tell you how to make this code support multiple players without increasing the code size:
When you use an li or lis instruction, the bits that aren’t specified in the instruction are automatically set to 0.

So if you want to load the float “60” as hex 0x42700000 into r6, the single lis instruction is sufficient.

Code:
lis r6, 0x4270 ;  r6 = 0x42700000
When loading or storing values, you can also specify a displacement offset in the range of a signed immediate value.

So for example, instead of adding addi r5, r5, 0x1998, and then storing stw r6, 0(r5) -- you could combine them into one instruction using

Code:
stw r6, 0x1998(r5)
---

If you haven’t already -- grab the Community Symbol Map! It’s very informative, and will help a lot when searching for good context when making an injection code.

The function you’ve hooked to the end of is a player function that updates player buttons and IASA each frame. This means that it uses the same player data pointer you’ve built to do something with each player, each frame. It’s a useful context to exploit.

If you move your hook to before the epilog, you’ll be able to steal the context of r31 = player data to make the code apply to everyone.



So, if you inject @ 8006b80c instead, you may skip most of the setup you’ve done in order to apply to every character that uses the host function.

---

A code that does all of these things would end up looking something like the following
Code:
; INJ @ 8006b80c -- for each player, each frame
; r31 = contextual player data start address
lis r6, 0x4270       ; create FP 60.0
stw r6, 0x1998(r31)  ; store r6 in offset 0x1998 of "this" player data table
lwz r0, 0x007C (sp)  ; original hook instruction
; gecko tools will compile a return branch to the line after your injection hook
This hits all players because "this" player refers to the player referenced by the host function; which operates on each player once per frame.

Hope to see more of your stuff!
Thanks a lot for the criticism, but I was wondering the steps you originally went through to deduce that r31 contained the player address. I've never had any formal training so I'm still new to this.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
423
Thanks a lot for the criticism, but I was wondering the steps you originally went through to deduce that r31 contained the player address. I've never had any formal training so I'm still new to this.
The Wiibrew Assembly Tutorial has a section at the end that explains the "Application Binary Interface" -- which is a very useful thing to understand about PowerPC and the stack frame.

Basically, it explains how the registers are divided into "volatile" and "saved" groups:
r0 volatile, may be used by function linkage
r1 stack pointer
r2 reserved for system
r3 .. r4 volatile, pass 1st - 2nd int args, return 1st - 2nd ints
r5 .. r10 volatile, pass 3rd - 8th int args
r11 .. r12 volatile, may be used by function linkage
r13 small data area pointer
r14 .. r31 saved
f0 volatile
f1 volatile, pass 1st float arg, return 1st float
f2 .. f8 volatile, pass 2nd - 8th float args
f9 .. f13 volatile
f14 .. f30 saved
f31 saved, static chain if needed.
lr volatile, return address
ctr volatile
xer volatile
fpscr volatile
cr0 volatile
cr1 volatile
cr2 .. cr4 saved
cr5 .. cr7 volatile
When a function makes a call, it will use r3-r10 to pass arguments to a new function. On return however, all "volatile" registers will be considered garbage -- they are not persistent unless used to pass arguments or return values.

So, when a function needs to use variables both before and after a call, it will store the information that is currently occupying a saved register into what's called the "runtime stack" by making a "stack frame" that persists for the duration of the function. The stack frame represents a chunk of space that you can throw things in temporarily.

You can see functions do this in the "prolog" and "epilog" of the function, as it operates on a special register called "sp" which is a nickname for r1.

Here's the prolog in the function you're using:



After it has stored r31 in the stack frame using the stack pointer, the function is then free to use r31 however it likes because it's been "saved." Before the function finishes, it has an epilog that restores the saved values:



Once a stack frame is collapsed like this, everything saved in it becomes garbage -- to be overwritten by the next stack frame.

---

Most player functions like this one are passed a "player entity" as an argument -- usually in r3.

We can see the function setting up r31 using r3 just after the prolog. It's using the character data pointer you created in your code's setup:



---

The function also stores the player entity in r30. You can check this for an entity class ID from either r3 or r30 using a breakpoint at the beginning of the function.

If offset 0x0 contains the hword 0x0004
and offset 0x7 contains the byte 0x04
Then it's usually a good indication that you're looking at a player:

 
Last edited:

Mimerme

Smash Rookie
Joined
Feb 9, 2018
Messages
3
Words can't explain how grateful I am for this, especially since it's so clear and well written!
 

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
673
The Wiibrew Assembly Tutorial has a section at the end that explains the "Application Binary Interface" -- which is a very useful thing to understand about PowerPC and the stack frame.

Basically, it explains how the registers are divided into "volatile" and "saved" groups:


When a function makes a call, it will use r3-r10 to pass arguments to a new function. On return however, all "volatile" registers will be considered garbage -- they are not persistent unless used to pass arguments or return values.

So, when a function needs to use variables both before and after a call, it will store the information that is currently occupying a saved register into what's called the "runtime stack" by making a "stack frame" that persists for the duration of the function. The stack frame represents a chunk of space that you can throw things in temporarily.

You can see functions do this in the "prolog" and "epilog" of the function, as it operates on a special register called "sp" which is a nickname for r1.

Here's the prolog in the function you're using:



After it has stored r31 in the stack frame using the stack pointer, the function is then free to use r31 however it likes because it's been "saved." Before the function finishes, it has an epilog that restores the saved values:



Once a stack frame is collapsed like this, everything saved in it becomes garbage -- to be overwritten by the next stack frame.

---

Most player functions like this one are passed a "player entity" as an argument -- usually in r3.

We can see the function setting up r31 using r3 just after the prolog. It's using the character data pointer you created in your code's setup:



---

The function also stores the player entity in r30. You can check this for an entity class ID from either r3 or r30 using a breakpoint at the beginning of the function.

If offset 0x0 contains the hword 0x0004
and offset 0x7 contains the byte 0x04
Then it's usually a good indication that you're looking at a player:

How come he got images in his "Intro to PPC" post and all I got was a boring text post?

:grin::grin::grin::grin::grin:
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
423
How come he got images in his "Intro to PPC" post and all I got was a boring text post?

:grin::grin::grin::grin::grin:
LOL. God, I cover like basically the same thing there hahaha.

To be fair, he DID ask a question with a directly available example. It helps that it involves the function I wrote my first code in, too
:p:p:p:p:p

BTW -- congrats on over a year of ASM hacking, UnclePunch UnclePunch ! The community has seen a lot of great new things since you've picked up powerPC.

---

Words can't explain how grateful I am for this, especially since it's so clear and well written!
I'm happy to help if I can. If you have more questions about projects, you can ask them in threads like these. It's also common to ask general questions in threads like this one. People are usually happy to answer what they know.

Sometimes though, people don't have the answer to questions posted around here. For powerPC, the hyperlinks DRGN DRGN mentioned are very useful:
I think the best way to learn more about this stuff is probably to just observe functions in the game that you want to understand. By consulting valuable resources like the Wiibrew wiki, the Gekko User Manual, the Programmer’s Reference Guide, and the Compiler Writer’s Guide--many things can be learned by observing examples and tested through experimentation.
I already mentioned the wiibrew assembler guide, but the other links are invaluable sources of information. If you're ever stumped, a ctrl+F in any of those documents might give you an answer nobody else can. I still use them to this day.
 
Last edited:
Top Bottom