-
Notifications
You must be signed in to change notification settings - Fork 2
/
loader.cpp
423 lines (358 loc) · 10.7 KB
/
loader.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
//#include <vector>
//#include <string>
//using std::array;
//using std::memcpy;
//using std::memcmp;
//using std::memset;
//using std::string;
#define dcbst(_val) asm volatile("dcbst 0, %0" \
: \
: "r"(_val))
#define dcbf(_val) asm volatile("dcbf 0, %0" \
: \
: "r"(_val))
#define icbi(_val) asm volatile("icbi 0, %0" \
: \
: "r"(_val))
#define call(addr) ((void (*)(...))addr)
#define CODEHANDLER 0x800018A8
#define GCT_MAGIC 0x00D0C0DE
#define __start call(0x4948494C)
using u32 = unsigned int;
using u16 = unsigned short;
using u8 = unsigned char;
using s32 = int;
using s16 = short;
using s8 = char;
using f32 = float;
using f64 = double;
__attribute__((noreturn)) int main();
struct CodeList
{
u16 mBaseASM;
u16 mUpperBase;
u16 mOffsetASM;
u16 mLowerOffset;
};
struct Info
{
const u32 allocsize;
const u32 loaderSize;
const u32 handlerSize;
const u32 codeSize;
const u32 *codehandlerHook;
const u32 crypted;
};
class DiscHeader
{
public:
enum class TVMODE
{
NTSC,
PAL,
DEBUG,
DEBUGPAL,
MPAL,
PAL60
};
struct MetaData
{
const u8 mDiscID; //0x0000
const u16 mGameCode; //0x0001
const u8 mRegionCode; //0x0003
const u16 mMakerCode; //0x0004
const u8 mDiscNumber; //0x0006
const u8 mDiscVersion; //0x0007
const u8 mAudioStreaming; //0x0008
const u8 mStreamBufferSize; //0x0009
const u8 _00[12]; //0x000A
const u32 mWiiMagic; //0x0018
const u32 mGCNMagic; //0x001C
const u32 mNinBootCode; //0x0020
const u32 mAppVersion; //0x0024
const u32 mPhysicalRAMSize; //0x0028
const u32 mBoardModel; //0x002C
u8 *mOSArenaLo; //0x0030
u8 *mOSArenaHi; //0x0034
u32 *mFstStart; //0x0038
u32 mFstSize; //0x003C
u32 mDebuggerPresent; //0x0040
const u32 mDebuggerExceptionMask; //0x0044
void *mExceptionHookDest; //0x0048
const u32 mExceptionReturn; //0x004C
u32 _01[0x10 / 4]; //0x0050
u32 mDebuggerHook[0x24 / 4]; //0x0060
u32 _02[0x3C / 4]; //0x0084
u32 mCurrentOSContext; //0x00C0
u32 mPreviousOSMask; //0x00C4
u32 mCurrentOSMask; //0x00C8
DiscHeader::TVMODE mTVMode; //0x00CC
u32 mARAMSize; //0x00D0
void *mCurOSContextLogical; //0x00D4
void *mDefaultOSThreadLogical; //0x00D8
u32 *mThreadQueueHead; //0x00DC
u32 *mThreadQueueTail; //0x00E0
u32 *mCurrentOSThread; //0x00E4
u32 mDebuggerSize; //0x00E8
u32 *mDebuggerMonitorLoc; //0x00EC
u32 mSimulatedMemSize; //0x00F0
u8 *mBi2HeaderLoc; //0x00F4
u32 mBusClockSpeed; //0x00F8
u32 mCPUClockSpeed; //0x00FC
u32 _04[0x3010 / 4]; //0x0100
u8 *mWiiHeap; //0x3110
};
static MetaData sMetaData;
enum class CONSOLETYPE
{
Gamecube,
Wii,
Unknown
};
inline u32 getGameID() { return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | ((u32)sMetaData.mRegionCode); }
inline u16 getMakerID() { return sMetaData.mMakerCode; }
inline u8 getDiscNumber() { return sMetaData.mDiscNumber; }
inline u8 getDiscVersion() { return sMetaData.mDiscVersion; }
CONSOLETYPE detectHomeConsole()
{
if (sMetaData.mGCNMagic)
{
return CONSOLETYPE::Gamecube;
}
else if (sMetaData.mWiiMagic)
{
return CONSOLETYPE::Wii;
}
return CONSOLETYPE::Unknown;
}
inline void setHeap(u32 alloc)
{
if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi)
{
sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc;
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
{
sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc;
}
}
else
{
if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
{
sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
sMetaData.mWiiHeap -= alloc;
}
else
{
sMetaData.mOSArenaHi -= alloc;
}
}
}
};
static DiscHeader sDisc;
Info gpModInfo = {
0x48454150,
0x4C53495A,
0x4853495A,
0x4353495A,
(const u32 *)0x484F4F4B,
0x43525054,
};
inline u32 extractBranchAddr(u32 *bAddr)
{
s32 offset;
if (*bAddr & 0x2000000)
offset = (*bAddr & 0x3FFFFFD) - 0x4000000;
else
offset = *bAddr & 0x3FFFFFD;
return (u32)bAddr + offset;
}
namespace Memory
{
static void memcpy(u8 *to, u8 *from, s32 size)
{
for (s32 i = 0; i < size; ++i)
{
*to++ = *from++;
}
}
namespace Cache
{
static inline void flushAddr(void *addr)
{
dcbf(addr);
icbi(addr);
}
static void flushRange(u8 *addr, s32 size)
{
size += 31 + (((u32)addr & 31) > 0);
for (u32 i = 0; i < (size >> 5); ++i)
{
flushAddr((void *)(addr + (i << 5)));
}
}
static void storeAddr(void *addr)
{
dcbst(addr);
icbi(addr);
}
static void storeRange(u8 *addr, s32 size)
{
size += 31 + (((u32)addr & 31) > 0);
for (u32 i = 0; i < (size >> 5); ++i)
{
storeAddr((void *)(addr + (i << 5)));
}
}
} // namespace Cache
namespace Direct
{
template <typename T>
static inline void write(T *addr, T value)
{
*addr = value;
Cache::flushAddr(addr);
}
/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/
static inline void branch(void *addr, void *to, bool lk)
{
Direct::write<u32>((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | lk));
}
} // namespace Direct
namespace Search
{
static u32 *array(u32 *start, u32 *end, u32 arrayLength, const u32 *hookData)
{
u32 index = 0;
/*Loop through the games RAM, make sure we don't find our own hook data by accident*/
for (u32 i = 0; &start[i] < end; ++i)
{
/*If the data matches, increase the index counter and continue search,
else set index to 0 and continue searching*/
if (start[i] == hookData[index])
++index;
else
index = 0;
/*If the data has matched the whole array, return the address of the match*/
if (index >= (arrayLength) && (&start[i] < (u32 *)&gpModInfo || &start[i] > (u32 *)&gpModInfo + sizeof(Info)))
return &start[i];
}
return nullptr;
}
template <typename T>
static T *single(T *start, T *end, T match)
{
for (u32 i = 0; &start[i] < end; ++i)
{
if (start[i] == match)
{
return &start[i];
}
}
return nullptr;
}
/*Call this after viHook, finds the address of the first instance
of targetVal, and hooks it to the pointer hookTo*/
static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, bool lk)
{
Direct::branch(Search::single<u32>(start, start + 0x500, targetVal), hookTo, lk);
}
} // namespace Search
class Crypt
{
private:
u32 key;
u32 getKey()
{
u32 b1 = (this->key >> 24) & 0xFF;
u32 b2 = (this->key >> 16) & 0xFF;
u32 b3 = (this->key >> 8) & 0xFF;
u32 b4 = this->key & 0xFF;
b1 ^= b2;
b2 ^= b3;
b3 ^= b4;
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
}
void setKey(u32 key)
{
u32 b1 = key & 0xFF;
u32 b2 = (key >> 8) & 0xFF;
u32 b3 = (key >> 16) & 0xFF;
u32 b4 = (key >> 24) & 0xFF;
b3 ^= b4;
b2 ^= b3;
b1 ^= b2;
this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
}
public:
Crypt(u32 key)
{
this->key = key;
}
inline void xorCrypt(u32 *dest, u32 *buffer, u32 size)
{
auto key = this->getKey();
for (u32 i = 0; i < size; ++i)
{
dest[i] = buffer[i] ^ key;
key += i << 3;
}
}
};
enum class Space : u32
{
Start = 0x80000000,
End = 0x81800000,
Size = 0x1800000
};
} // namespace Memory
Memory::Crypt gpCryptor = {0x43595054};
static void initMods()
{
sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/
/*Change codelist pointer to the new address in the allocation*/
CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC);
codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF;
codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF;
/*Copy codelist to the new allocation*/
if (gpModInfo.crypted)
{
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize);
gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2);
}
else
{
Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize);
}
/*Get codehandler hook resources*/
auto fillInField = Memory::Search::single<u32>((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE);
auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook);
auto ppc = *gpModInfo.codehandlerHook;
/*Write hook branch*/
Memory::Direct::branch((void *)gpModInfo.codehandlerHook, (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); //entryhook
/*Temporary nop*/
*fillInField = 0x60000000;
/*Flush the cache so that the instructions update*/
Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize);
/*Call the codehandler*/
call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))();
/*Write original instruction or translate offset data if a branch*/
if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C)
{
Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1);
}
else
{
Memory::Direct::write(fillInField, ppc);
}
/*Write branch back to the hook address + 4*/
Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return
}
int main()
{
if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown)
{
initMods();
}
__start();
}