Project

General

Profile

Emulator Issues #11869

Riivolution doesn't boot

Added by nwplayer123 about 2 months ago. Updated about 2 months ago.

Status:
New
Priority:
Normal
Assignee:
-
% Done:

0%

Operating system:
N/A
Issue type:
Bug
Milestone:
Regression:
No
Relates to usability:
No
Relates to performance:
No
Easy:
No
Relates to maintainability:
No
Regression start:
Fixed in:

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.

Screenshot_139.png (464 KB) Screenshot_139.png nwplayer123, 10/01/2019 04:12 AM
Screenshot_140.png (53.5 KB) Screenshot_140.png nwplayer123, 10/01/2019 04:16 AM
7583
7584

History

#1 Updated by pokechu22 about 2 months 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.

Also available in: Atom PDF