1+
2+ We're going to start off by decompiling
3+ the simplest type of function in the game:
4+ A function that does nothing.
5+
6+ An empty function is a function that has no logic.
7+ It literally doesn't do anything.
8+ In C++ a function that does nothing could look like this:
9+
10+ #grid(
11+ columns: (50%, 50%),
12+ ```cpp
13+ void DoNothing()
14+ {
15+ }
16+ ```,
17+ ```cpp
18+ void DoNothing()
19+ {
20+ return;
21+ }
22+ ```
23+ )
24+
25+ Note that we can choose whether or not to write the `return;` keyword.
26+ It doesn't make a difference whether we write it out or not.
27+
28+ #grid(
29+ columns: (50%, 50%),
30+ [
31+ Since the function is void, nothing is returned,
32+ so both of these functions
33+ are compiled down to a single `blr` assembly instruction:
34+ ],
35+ image("nothing/nothing.png")
36+ )
37+
38+ What does the assembly instruction `blr` mean?
39+
40+ #v(0.5cm)
41+ / blr: "Branch to Link Register". Jumps to lr. Used to end a subroutine.
42+ #v(0.5cm)
43+
44+ What is a Link Register?
45+
46+ _A link register (LR for short) is a register which holds the address to return to when a subroutine call completes. This is more efficient than the more traditional scheme of storing return addresses on a call stack, sometimes called a machine stack. The link register does not require the writes and reads of the memory containing the stack which can save a considerable percentage of execution time with repeated calls of small subroutines.
47+ The IBM POWER architecture, and its PowerPC and Power ISA successors, have a special-purpose link register, into which subroutine call instructions put the return address._
48+ #footnote("https://en.wikipedia.org/wiki/Link_register")
49+
50+ So the `lr` register is special and holds the return address for the CPU to
51+ jump back to when the current subroutine finishes.
52+ This means that every function in the game is going to end with a `blr` instruction.
53+
54+ This example function `DoNothing` is only made up of one instruction.
55+ Since every PowerPC assembly instruction is 4 bytes in size,
56+ The total size of this function is 4 since it is just one `blr` instruction.
57+ Size 4 functions are the smallest functions you will find.
58+
59+ Fun fact: There are 318 functions in the game that do nothing.
60+ If we add up their size,
61+ we get a total size of $318 * 4 = 1272$ bytes.
62+ One other way of saying this is that
63+ $0.07%$ of the entire game's code does nothing!
64+
65+ Since we have a general idea of what an empty function is like,
66+ let's now decompile an empty function in BFBB.
67+
68+ ==== Our First Function -- NPCWidget::Reset()
69+ We are going to look at the function `NPCWidget::Reset()`.
70+ It is a size 4 function located in the file
71+ `/Game/zNPCSupport.cpp`.
72+
73+ If we open up `zNPCSupport` in Objdiff and click on `NPCWidget::Reset()`,
74+ we can see the original assembly on the left, and nothing on the right.
75+
76+ #image("nothing/reset.png")
77+
78+ We can see that the original assembly on the left just has one instruction,
79+ a `blr` instruction like we expect.
80+ When the right side says "Missing", this means that
81+ we have yet to define the function in our game source file.
82+ This is to be expected at this point because we haven't decompiled this function yet.
83+
84+ Let's go ahead and define this function in our C++ source file `zNPCSupport.cpp`:
85+
86+ ```cpp
87+ void NPCWidget::Reset()
88+ {
89+ }
90+ ```
91+
92+ Great! We've defined our function.
93+ When we go back and look at Objdiff,
94+ it looks like there was an error compiling `zNPCSupport.cpp`:
95+
96+ ```
97+ ### mwcceppc.exe Compiler:
98+ # File: src\SB\Game\zNPCSupport.cpp
99+ # ------------------------------------
100+ # 25: void NPCWidget::Reset()
101+ # Error: ^^^^^^^^^
102+ # undefined identifier 'NPCWidget'
103+ # Too many errors printed, aborting program
104+ ```
105+
106+ The error is pretty straight-forward.
107+ The compiler can't find any kind of definition for the type `NPCWidget`.
108+
109+ *Classes and Member Functions*
110+
111+ This function isn't _quite_ as simple as our example `DoNothing` function we saw earlier.
112+ In this case, `Reset()` is actually a member function of the `NPCWidget` class,
113+ which is why we see the scope resolution operator, AKA double-colon (`::`) in `NPCWidget::Reset()`.
114+
115+ We need to define the type of `NPCWidget`.
116+
117+ // TODO: replace this link with the proper dwarf section
118+ We can search all of the files in the BFBB repository and we can see that
119+ we haven't yet defined `NPCWidget` in any of our source headers.
120+ This means that we need to add it ourselves.
121+
122+ But how do we know what the type of `NPCWidget` is?
123+ To answer this question, we're going to reference the PS2 DWARF Data.
124+ If we search for `NPCWidget` in the PS2 DWARF data file, (defined in Section TODO)
125+ We can see the definition for the class here:
126+
127+ ```cpp
128+ class NPCWidget {
129+ // total size: 0xC
130+ public:
131+ enum en_NPC_UI_WIDGETS idxID; // offset 0x0, size 0x4
132+ class xBase * base_widge; // offset 0x4, size 0x4
133+ class zNPCCommon * npc_ownerlock; // offset 0x8, size 0x4
134+ };
135+ ```
136+
137+ We can simply copy and paste this into the accompanying header file `zNPCSupport.h`.
138+ After we do this, Objdiff thanks us for our effort by greeting us with a new error:
139+
140+ ```
141+ ### mwcceppc.exe Compiler:
142+ # In: src\SB\Game\zNPCSupport.h
143+ # From: src\SB\Game\zNPCSupport.cpp
144+ # ------------------------------------
145+ # 22: enum en_NPC_UI_WIDGETS idxID; // offset 0x0, size 0x4
146+ # Error: ^^^^^
147+ # undefined identifier 'en_NPC_UI_WIDGETS'
148+ # Too many errors printed, aborting program
149+ ```
150+
151+ Again, the error message is pretty clear.
152+ We need to define `en_NPC_UI_WIDGETS`.
153+ Once again, we can't find this enum defined anywhere in
154+ our source header files yet, so it means that it hasn't
155+ been copied over yet.
156+ Let's do that now.
157+ Heading back over to the PS2 DWARF file, we can search for
158+ `en_NPC_UI_WIDGETS` and we find this enum:
159+
160+ ```cpp
161+ enum en_NPC_UI_WIDGETS {
162+ NPC_WIDGE_TALK = 0,
163+ NPC_WIDGE_NOMORE = 1,
164+ NPC_WIDGE_FORCE = 2,
165+ };
166+ ```
167+
168+ Once again, we want to copy this type over into our `zNPCSupport.h` file.
169+ We have to make sure that we put this definition before the definition of
170+ `NPCWidget`, as that class references the enum.
171+
172+ Back in Objdiff, unsurprisingly, we have another error:
173+ ```
174+ ### mwcceppc.exe Compiler:
175+ # File: src\SB\Game\zNPCSupport.cpp
176+ # ------------------------------------
177+ # 26: {
178+ # Error: ^
179+ # undefined identifier 'Reset'
180+ # Too many errors printed, aborting program
181+ ```
182+
183+ This one is simple though. It's just saying that
184+ the `Reset()` function isn't defined in `NPCWidget`.
185+ We can fix this by defining the `Reset` method with a `void` return type on our class:
186+
187+ ```cpp
188+ class NPCWidget
189+ {
190+ // total size: 0xC
191+ public:
192+ enum en_NPC_UI_WIDGETS idxID; // offset 0x0, size 0x4
193+ class xBase* base_widge; // offset 0x4, size 0x4
194+ class zNPCCommon* npc_ownerlock; // offset 0x8, size 0x4
195+ void Reset();
196+ };
197+ ```
198+
199+ When we save this and look at Objdiff now, we finally have no more error messages.
200+ Not only that, but now the function is a 100% match!
201+
202+ #image("nothing/OK.png")
203+
204+ We've done it!
205+ We have successfully decompiled our first function in Battle For Bikini Bottom.
206+ It's a humble little function, but we should be proud nonetheless.
207+
208+ Throughout this process, we have learned quite a few things:
209+
210+ + How to use Objdiff to view and compare assembly code
211+ + What our first assembly instruction `blr` means and why it is generated
212+ + How to copy over the correct class and enum types from the PS2 DWARF data
213+ + How to define a new class member function
214+
215+ This all might have seemed like a lot of work just to decompile a function
216+ that does literally nothing.
217+
218+ At this point you may have a few questions, such as
219+
220+ 1. Is it always this much trouble to simply get a function that does nothing to compile?
221+
222+ The answer to this is that it depends.
223+ It depends largely on the function that you're decompiling and a combination of factors.
224+ If the function is a member of a class, and that class is not defined in a header file
225+ yet, then you will have to define it like we did in this example.
226+ In this case, we also had to recursively define the properties which were included
227+ in the class we added.
228+ You can imagine that in some cases this could become a bit of work.
229+
230+ The same idea also applies for functions that do nothing but accept parameters
231+ who's types are not defined yet.
232+ Consider this example:
233+ ```cpp
234+ void xDebugAddTweak(const char*, xVec3*, const tweak_callback*, void*, U32)
235+ {
236+ }
237+ ```
238+ This function accepts types such as `xVec3*`, and `tweak_callback*` which are non-primitive types.
239+ We already have these types included in the project header files,
240+ so solving the type errors would be as simple as including the appropriate headers:
241+ ```cpp
242+ #include "xVec3.h"
243+ #include "zFX.h"
244+ ```
245+ If we didn't already have those types defined in those header files,
246+ you would have to copy the types for these parameters from
247+ the PS2 DWARF again.
248+ This is needed to get the file to compile
249+ despite the fact that the function itself does nothing.
250+
251+ Then of course there are functions like `NPCWidget_Shutdown`
252+ which are not part of a class nor accept any parameters,
253+ so decompiling them is as simple as just writing:
254+
255+ ```cpp
256+ void NPCWidget_Shutdown()
257+ {
258+ }
259+ ```
260+
261+ And that's that.
262+
263+ 2. Why are functions that do nothing even in the game?
264+
265+ This is a commonly asked question
0 commit comments