Some more notes (offsets are all FE7U)
- Proc handling battle history screen is at
0x8CC5AF0
- Function within that proc that makes the password is
func_809E25E
; this does a large amount of stuff, but there are 3 function calls within it near the end relevant to making the password:
-
func_809DFC4(mode,difficulty)
gets the relevant battle history data directly from sram and obfuscates it
-
func_809D7B4(5,11)
does some Math with the given values and stores the results various places; unsure if this is relevant or what it’s actually doing, but
-
func_809DBD8(func_809DDE4)
is the meat of the encoding process, with 4 subroutines of its own and a 5th as the argument:
func_809DDE4
is the hash function mentioned in the prior discord screenshot, this should be the pairs for source value locations & constants passed into func_809D880
(also shown in the prior screenhot):
20144E3, 0x3
20144E4, 0x3
20144E5, 0x3
20144E6, 0x3
20144E7, 0x3
20144E9, 0x8
20144E8, 0x6
20144EC, 0xA
20144EE, 0x6
20144EF, 0x6
20144EB, 0x8
20144F0, 0x18
func_809D800
, func_809D9A4
, func_809D82C
and func_809D9E4
get called a few times each throughout the function as well, all after it calls the hash function. func_809D82C
can be seen in the prior discord screenshot as well.
Then here’s some rough pseudocode functions:
void func_809D880( u32 pointer1, u32* pointer2, u32 data, u32 constant) {
//pointer1 -> stack
//pointer2 -> r10
//data -> r8
//constant -> r9
data += func_809D82C();
data = data & (1 << constant);
u32 iterator = 0;
if (constant >= iterator) {
while (iterator < constant) {
pointer1 += (pointer2 / (word @ 20143FC));
u32 tempVar1 = ((1 << iterator) & data) >> iterator;
u32 tempVar2 = pointer2 % (word @ 20143FC);
if (tempVar2 < iterator) tempVar2 = (tempVar2 * 2) + 7;
tempVar2 = tempVar2 >> 3;
tempVar2 = tempVar2 << 3;
tempVar2 = (pointer2 % (word @ 20143FC)) - tempVar2;
tempVar1 = tempVar1 << tempVar2;
(byte @ pointer1) = (byte @ pointer1) | tempVar1;
pointer2 ++;
iterator ++;
}
}
}
u16 func_809D82C() {
//despite being treated as a halfword, value @ 2014434 is read & stored as a word
u16 @ 2014434 *= 13;
u16 @ 2014434 ++;
return u16 @ 2014434;
}
func_809DBD8((void*)(func)(u32)(u32)) {
//initializes some RAM values used in the encoding process
//var -> r3
//store 0 to allocated stack space
//2014438 -> r6
//2014404 -> r5
u32 iterator = $20144D7;
while (iterator >= 2014438) {
(byte @ iterator) = 0;
iterator --;
}
int i = 0;
func(&i, (2014438 + [word @ 2014404])); //func_809DDE4($3007D58,$201445E) this is the part that makes the initial code
//it has the initial values after this call that are referenced below, so this populates this area I think
[halfword @ 20144DE] = u16 func_809D800(i);
[halfword @ 20144DA] = u16 func_809D9A4([word @ 2014404] + 2014438, [halfword @ 20144DE]);
[halfword @ 20144D8] = ([halfword @ 20144DA] + (GetGameClock() >> 3)) & 0x3FF;
//continues at this point from 809DC30, should be doing all the encoding
}
func_809DFC4(const u32 var1, u32 var2) { //populates struct @ 20144E0
//var1 -> r5
//var2 -> r6
[sp + 0x18] = 0x0000;
CpuSet([sp + 0x18], $20144E0, 0x100000A); //zeroes out the struct this populates
if (func_809F224(sp,var1,var2) != 0) {
//func_809F224 definitely populates sp with the values referenced henceforth
[byte @ 20144E0] = var1;
[byte @ 20144E2] = var2;
[byte @ 20144E3] = [byte @ sp] & 0x70;
[byte @ 20144E4] = [halfword @ sp] & 0x7E;
[byte @ 20144E5] = ([byte @ sp+1] & 0x18;
[byte @ 20144E6] = [byte @ sp+1] & 0xE0;
[byte @ 20144E7] = [byte @ sp+1] & 0xE0;
[byte @ 20144E9] = ([halfword @ sp+2] >> 7) & 0xFF;
[byte @ 20144E8] = [halfword @ sp+10] & 0x7F0<U+202C>;
[byte @ 20144E1] = [byte @ sp+2] & 0x60;
[halfword @ 20144EC] = [word @ sp+4] & 0x01FF80;
[byte @ 20144EE] = [byte @ sp+6] & 0x7E;
[byte @ 20144EF] = [halfword @ sp+6] & 0x1F80;
[byte @ 20144F0] = ((([word @ sp+8] & 0x001FFFFF) << 3) | [byte @ sp+7] >> 5);
[byte @ 20144EA] = [byte @ sp+23];
[byte @ 20144EB] = (([byte @ sp+4] & 0x7F) << 1) | ([byte @ sp+3] >> 7);
return 1;
} else {
return 0;
}
}
bool func_809F224(destPtr,var1,var2) {
//r6 = destPtr
//r7 = var1
//r5 = var2
//r4 = 0
CPUSet([sp+0x94],destPtr,0x0100000C); //initializes the area we're going to put stuff temporarily for this function
CPUSet(... initalizes the destination ...);
//func_809F0D8(&sp) gets battle history data from sram
if (func_809F0D8(&sp) != 0) {
//assuming var1 and var2 are used to index the battle history data
//since that function just grabs all of it from sram
u32 temp = var2 << 1;
temp += var2;
temp += var1;
u32 temp2 = temp << 1;
temp2 += temp;
temp2 = temp2 << 3;
//does some ldmia/stmia schenanigans I don't feel like decoding right now,
//presumably just grabbing the specific subset of data we asked for
//things that this gets called with for args:
//[0,0] - Lyn normal mode
//[1,0] - Eliwood normal mode
//[2,0] - Hector normal mode
//[0,1] - Lyn hard mode
//[1,1] - Eliwood hard mode
//[2,1] - Hector hard mode
//So, var1 is mode and var2 is difficulty
//and this is just returning the section of raw data from sram
return true;
} else {
return false;
}
}
bool func_809F0D8(u32 dest) {
if (IsSramWorking()) {
if (dest == 0) dest = gGenericBuffer;
gReadSramFast(0x0E007044, dest, 0x7044);
} else {
return false;
}
return true;
}
If you want to try and trace through some stuff, here’s an example of the data returned by func_809F0D8
and the page made from it:
49 19 09 68 0D 4A 89 18 0B F0 0E FA 20 6B 80 68 E1 6A 40 31 09 78 C9 00 49 19 89 88 10 31 49 01 20 22 F9 F7 8F F9 E1 6A 20 6B C8 62 60 6B C8 87 30 BC 01 BC 00 47 00 00 8C A5 02 02 00 00 01 06 10 B5 04 1C E0 6A 81 6C 00 29 0B D0 20 6B 00 7E 08 86 E0 6A 80 6C 00 21 FC F7 C2 FC E0 6A 80 6C 88 F0 A0 FF E0 6A 40 6C 00 28 01 D0 88 F0 9A FF 10 BC 01 BC 00 47 00 00 30 B5 04 1C 0D 1C 04 48 21 1C FC F7 67 FB C4 62 45 63 30 BC 01 BC 00 47 80 09 B9 08 10 B5 00 22
func_809F0D8(mode,difficulty)
is used every time you change the page on the Battle History screen with the mode & difficulty of whatever page it’s loading. When you generate a password, it uses the mode & difficulty of the current page.