Researches on Battle History Password

Battle History Password

At the Battle History page (accessed from the Extras menu after completing the game at least once), hold down Select for a couple of seconds. A password will appear, which doesn’t do anything. In the Japanese version, players could submit this to the official site for a contest of sorts.

From: Hints and Secrets - Serenes Forest

Backup from chat messages history for easy search in the future.








3 Likes

What does this mean for the layman in the audience?

Most new documentation doesn’t really matter to you; the vast majority of the important stuff has some documentation already. You don’t need to follow #documentation if you aren’t interested.

Personally I find this interesting, but I don’t really expect any hacks to get a meaningful use out of it.

@Vesly You misunderstood me, I find this extremely interesting as well. When I asked “what does this meant for the layman in the audience ?” I meant “can you translate this for me since I’m not savy on the topic” It just seemed very interesting but I couldn’t follow since I’m not versed in hacking. Naturally the “layman” referring to just me, or anyone else interested.

In a word, it is an encrypted game time.

Oh okay. Forgive me, I know I sound like a complete scrub but what does the encryption do/access or what can this potentially unveil?

In the Japanese version, players could submit this password to the official site for a contest of sorts. I wonder what it is.

Got it, thanks for explaining

This documentation is not to tell you how to do something for hacking. It is to unveil a secret in game which I found by hacking instead.

2 Likes

(Slightly more than that, the other 15 values it runs sub_809E298 with are obtained using the same function used by the Battle History menu to obtain the information to display, and considering the game time value it’s using only starts counting from startup and isn’t file time it’s likely only included to make it harder to reverse-engineer the encryption using the output code and the rest of the data is what is actually meant to be sent)

1 Like

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

file

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.

1 Like