Emulator Issues #11869
closedRiivolution 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.
Files
Updated by pokechu22 about 5 years 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.
Updated by JMC4789 over 4 years ago
- Status changed from New to Questionable
Just marking as questionable since we don't know if dolphin will ever support patched IOSes. If someone implements them, maybe.
Updated by JMC4789 about 2 years ago
- Status changed from Questionable to Won't fix
I'm not saying the IOS-LLE will never happen, but for Riivolution support, we now have an HLE implementation.