Emulator Issues #11869
Riivolution doesn't boot
0%
Description
Searched around, doesn't look like there's already an issue about this. Figured I'd make one in case anyone follows down this rabbit hole.
As stated, Riivolution (v1.06) doesn't boot (tested on 5.0-10914). I'm booting straight from the elf.
It has 3 sections
* 0x80003400, A little bootstrap to set some hwreg, and jump to section 2
* 0x80D13040+, Decryption/decoding code
* "Encrypted" payload
Section 2 is something along the lines of this (taken from https://gist.github.com/NWPlayer123/d564229724636a7b7131db7077ac0ec1)
#Python doesn't support do while, so I do "while True" -> inverse check (if var1 == 1: break) from struct import unpack, pack import sys bits_left = 0 #0x816107EC decrypt_byte = 0 bytes_read = 0 DAT_816107F8 = 0 write_addr = 0 read_addr = 0 DAT_81610804 = 0 payload_start = 0 DAT_8161080C = 0 DAT_81610810 = 0 #no xrefs? DAT_81610814 = 0 DAT_81610818 = 0 DAT_8161081C = 0 payload_entry = 0 table1 = [0] * 0x4EA #short, 2 bytes long table2 = [0] * 0x4EA #short table3 = [0] * 0x275 #short table4 = [0] * 0x275 #short table5 = [0] * 6 #word, 4 bytes long table6 = [0] * 6 #short table7 = [0] * 6 #word # entry, flush_cache #----------------------------------------------------- def read_byte(f): #u8 read_byte(void) global read_addr byte = ord(f.read(1)) #*(payload_start + read_addr) read_addr += 1 return byte #----------------------------------------------------- def write_byte(byte, o): #void write_byte(u8 byte) global write_addr o.write(chr(byte)) #*(payload_entry + write_addr) write_addr += 1 #----------------------------------------------------- def get_bit(f): #u8 get_bit(void) global bits_left, decrypt_byte, bytes_read bits_left -= 1 if bits_left == -1: #read another byte decrypt_byte = read_byte(f) bits_left = 7 bytes_read += 1 bit = decrypt_byte >> 7 decrypt_byte <<= 1 return bit & 1 #----------------------------------------------------- def FUN_80D131C4(r3, r4): #void FUN_80D131C4(short r3, short r4) global table1, table2, table3, table4 while True: var1 = table1[r3] table2[var1] = table2[r3] + table2[r4] if var1 == 1: break r4 = table4[table1[var1]] if (r4 == var1): r4 = table3[table1[var1]] r3 = var1 if var1 == 1: break #do while var1 != 1 if table2[1] != 2000: return for i in range(1, 0x4EA): table2[var1] >>= 1 var1 += 1 #----------------------------------------------------- def FUN_80D1326C(value): #void FUN_80D1326C(short value) global table1, table2, table3, table4 value += 0x275 r10 = table1[value] table2[value] += 1 if r10 == 1: return r4 = table4[r10] if (r4 == value): r4 = table3[r10] FUN_80D131C4(value, r4) while True: r10_val = table1[r10] r10_val2 = r10_val r4 = table4[r10_val2] if r4 == r10: r4 = table3[r10_val2] if table2[r4] < table2[value]: r9 = table4 if table4[r10_val2] == r10: r9 = table3 r9[r10_val2] = value r10_val2 = table4[r10] #overwrites 1 if we don't have another var if r10_val2 == value: table4[r10] = r4 r10_val2 = table3[r10] else: table3[r10] = r4 table1[r4] = r10 table1[value] = r10_val FUN_80D131C4(r4, r10_val2) value = r4 value = table1[value] r10 = table1[value] if r10 == 1: break #do while r10 != 1 #----------------------------------------------------- def FUN_80D133D0(value, f): #short FUN_80D133D0(short value) ret = 0 uVar1 = 1 if 0 < value: while True: bit = get_bit(f) if bit != 0: ret |= uVar1 value -= 1 uVar1 <<= 1 if value == 0: break #do while value != 0 return ret #----------------------------------------------------- def get_short(f): #short get_short(void) global table3, table4 r29 = 1 while r29 < 0x275: if get_bit(f) == 0: r29 = table4[r29] else: r29 = table3[r29] FUN_80D1326C(r29 - 0x275) return r29 - 0x275 #----------------------------------------------------- def setup_tables(): #checks out global bits_left, decrypt_byte, bytes_read, DAT_816107F8, \ write_addr, read_addr, DAT_81610804, DAT_8161080C, \ DAT_81610814, DAT_81610818, DAT_8161081C, table1, \ table2, table3, table4, table5, table6, table7 for i in range(6): table6[i] = 4 + (2 * i) DAT_8161081C = 3 write_addr = 0 DAT_81610814 = 0 DAT_81610818 = 0 bits_left = 0 decrypt_byte = 0 DAT_816107F8 = 0 bytes_read = 0 read_addr = 0 for i in range(2, 0x4EA): table1[i] = i >> 1; table2[i] = 1; for i in range(1, 0x275): table3[i] = (i * 2) + 1 table4[i] = i * 2 l = 0 for i in range(6): table5[i] = l l += 1 << table6[i] table7[i] = l - 1 DAT_81610804 = l - 1 DAT_8161080C = l + 0x3F #----------------------------------------------------- def decrypt_payload(f, o): global DAT_816107F8, DAT_8161080C, table5, table6 buffer = [0] * 0x5600 #but *why* bytes_wrote = 0 setup_tables() short = get_short(f) while short != 0x100: if short < 0x100: write_byte(short, o) buffer[bytes_wrote] = short bytes_wrote += 1 DAT_816107F8 += 1 if bytes_wrote == DAT_8161080C: bytes_wrote = 0 #looks like debug? else: iVar1 = (short + -0x101) / 0x3E iVar6 = short + iVar1 * -0x3E + -0xFE short = FUN_80D133D0(table6[iVar1], f) uVar8 = bytes_wrote - (short + iVar6 + table5[iVar1]) if uVar8 < 0: uVar8 += DAT_8161080C #looks like debug? uVar7 = bytes_wrote short = iVar6 if 0 < iVar6: while True: write_byte(buffer[uVar8], o) short -= 1 buffer[uVar7] = buffer[uVar8] uVar7 += 1 uVar8 += 1 uVar5 = uVar8 ^ DAT_8161080C uVar3 = uVar5 >> 31 uVar2 = uVar7 ^ DAT_8161080C uVar4 = uVar2 >> 31 DAT_816107F8 += 1 uVar8 &= ((uVar3 - (uVar3 ^ uVar5)) >> 31) uVar7 &= ((uVar4 - (uVar4 ^ uVar2)) >> 31) if short == 0: break #do while short != 0 bytes_wrote += iVar6 if DAT_8161080C <= bytes_wrote: bytes_wrote -= DAT_8161080C short = get_short(f) #load new #----------------------------------------------------- #FUN_80D1376C global something def entry2(f, o): global payload_start, payload_entry payload_start = 0x80D13820 payload_entry = 0x80A00000 decrypt_payload(f, o) #flush_cache(0x80000000, 0x1800000); //flush entire MEM1 #(*(void*)0x80A00000)(); //they blrl to fuck with people(?) #----------------------------------------------------- with open("boot.elf", "rb") as f: with open("output.bin", "wb") as o: f.seek(0xD00) #too lazy to extract entry2(f, o)
A little digging shows it's a Quake 64 audio decompression algorithm? https://github.com/jombo23/N64-Tools/blob/master/N64%20Sound%20Tool/N64SoundListToolUpdated/N64SoundLibrary/QuakeDecoder.cpp#L24-L47
Code flow is, run elf, 0x80003400 -> entry2, which decodes the payload to 0x80A00000, and then it jumps to 0x80A00000 (see Screenshot_139). Something like
void FUN_80A00020(void) { __init_memory(); __init_registers(); __init_hardware(); FUN_80a204a8(); FUN_80b5c1c4(); if (true) { //some check in source code memset(0x80D13000, 0, 0x580); memset(0x80D13580, 0, 0xDB740); } mymain(); }
Just running the elf in Dolphin, it decodes fine, and eventually hits this function (see Screenshot_140), and gets stuck waiting for a bit to clear that never does. I dug around and there's no documentation for that hardware register? (Note: I haven't touched Wii IPC bs yet)
void sub_80B61CF8(long chan) { do { x = *(u32*)(0xCD00680C + (chan * 0x14)) & 1; while (x != 0); //... }
Based on my limited knowledge, it looks like an EXI mirror? EXI0/1/2 is 0x14, and EXI is 0xCC006800 (+0xC is the Control Register). That's what I've run up to. Have to figure out what that register is and how to emulate it before Riivolution is bootable.
History
#1
Updated by pokechu22 over 1 year ago
To my understanding, Riivolution requires patching IOS, and dolphin doesn't emulate IOS at all, so there's not going to be much chance of it running successfully even if it loaded. I might be wrong about that though.