Files
psopeeps-newserv/system/quests/hidden/q8853x-gc.s
T
2024-08-17 10:39:10 -07:00

215 lines
6.2 KiB
ArmAsm

// This function copies an inline handler for the B2 command (function call)
// to an unused area of memory, and inserts it into the game's command handler
// table, thus making the B2 command fully functional as it is on most other
// versions of the game.
// We could do the code copy and callsite modification directly in the quest
// script, but that would restrict us to only using addresses that end in 00.
// Furthermore, doing it this way provides an example of how to embed native
// code in a quest script and run it from within the script.
start:
mflr r11
bl get_handle_B2_ptr
handle_B2:
# Arguments:
# r3 = TProtocol* proto (we use this to call the send function)
# r4 = void* data
# Returns: void
mflr r0
stwu [r1 - 0x40], r1
stw [r1 + 0x44], r0
# Stack:
# [r1+08] = B3 XX 0C 00
# [r1+0C] = code section's return value
# [r1+10] = checksum
# [r1+14] = saved ctx argument
# [r1+18] = saved data argument
stw [r1 + 0x14], r3
stw [r1 + 0x18], r4
# Set up the reply header (B3 XX 0C 00, where XX comes from the B2 command)
lbz r5, [r4 + 1]
rlwinm r5, r5, 16, 8, 15
oris r5, r5, 0xB300
ori r5, r5, 0x0C00
stw [r1 + 0x08], r5
# If there's no code section, skip it. We also write the code section size to
# the return value field (which will be overwritten later if the size is not
# zero). This is because I'm lazy and this gives the behavior we want: the
# code return value is always zero if the code section size is zero.
li r6, 4
lwbrx r5, [r4 + r6] # r5 = code_size
stw [r1 + 0x0C], r5 # response.code_return_value = code_size
cmplwi r5, 0
beq handle_B2_skip_code
# Get the code section base and footer addresses
addi r6, r4, 0x10 # r6 = code base address
add r7, r6, r5
subi r7, r7, 0x20 # r7 = footer address (code base + code size - 0x20)
# Check if there are relocations to do
lwz r8, [r7 + 4] # r8 = num relocations
cmplwi r8, 0
beq handle_B2_skip_relocations
# Execute the relocations
mtctr r8
lwz r8, [r7] # r8 = relocations list offset
add r8, r8, r6 # r8 = relocations list address
subi r8, r8, 2 # Back up one space so we can use lhzu in the loop
mr r10, r6 # relocation pointer = code base address
handle_B2_relocate_again:
lhzu r9, [r8 + 2]
rlwinm r9, r9, 2, 0, 29 # r9 = next_relocation_offset * 4
add r10, r10, r9 # relocation pointer += next_relocation_offset * 4
lwz r9, [r10]
add r9, r9, r6
stw [r10], r9 # (*relocation pointer) += code base address
bdnz handle_B2_relocate_again
handle_B2_skip_relocations:
# Invalidate the caches appropriately for the newly-copied code
lis r0, 0x8000
ori r0, r0, 0xC274
mr r3, r6
mr r4, r5
bl call_flush_code # flush_code(code_base_addr, code_section_size)
# Call the code section and put the return value (byteswapped) on the stack
# Note: flush_code only uses r3, r4, and r5, so we don't need to reload r7
# after the above call
lwz r8, [r7 + 0x10]
lwzx r8, [r8 + r6]
mtctr r8
bctrl
li r8, 0x0C
stwbrx [r1 + r8], r3
handle_B2_skip_code:
# Get the checksum function args
lwz r4, [r1 + 0x18]
li r5, 0x08
lwbrx r3, [r4 + r5] # checksum addr
li r5, 0x0C
lwbrx r4, [r4 + r5] # checksum size
bl crc32 # crc32(checksum_addr, checksum_size)
li r8, 0x10
stwbrx [r1 + r8], r3
# Send the response (B3 command)
lwz r3, [r1 + 0x14]
lwz r4, [r3 + 0x18]
lwz r4, [r4 + 0x28]
mtctr r4
addi r4, r1, 0x08
li r5, 0x0C
bctrl # TProtocol::send_command(ctx, &reply_data, 0x0C)
# Clean up stack and return
lwz r0, [r1 + 0x44]
addi r1, r1, 0x40
mtlr r0
blr
crc32:
subi r3, r3, 1 # So we can use lbzu
add r4, r3, r4 # r4 = end ptr (also adjusted for lbzu, implicitly)
li r5, -1 # r5 = result value (0xFFFFFFFF initially)
lis r7, 0xEDB8
ori r7, r7, 0x8320 # 1-bit xor value
li r8, 8 # Number of bits per byte
crc32_again:
cmpl r3, r4
beq crc32_done
lbzu r9, [r3 + 1]
xor r5, r5, r9 # result ^= next_input_value
mtctr r8
crc32_next_bit:
rlwinm r6, r5, 0, 31, 31 # r6 = low bit of result
rlwinm r5, r5, 31, 1, 31 # result >>= 1
neg r6, r6
and r6, r6, r7
xor r5, r5, r6 # result ^= (0xEDB88320 if low bit was 1, else 0)
bdnz crc32_next_bit
b crc32_again
crc32_done:
xoris r3, r5, 0xFFFF
xori r3, r3, 0xFFFF
blr # return (result ^ 0xFFFFFFFF)
call_flush_code:
lis r5, 0x8000
ori r5, r5, 0xC274
mtctr r5
lhz r0, [r5 + 6]
cmplwi r0, 0xFFF1
beqctr
addi r5, r5, 0xB0 # 8000C324
mtctr r5
bctr
get_handle_B2_ptr:
mflr r9 # r9 = &handle_B2
bl get_handle_B2_end_ptr
get_handle_B2_end_ptr:
mflr r10
subi r10, r10, 8 # r10 = pointer to end of handle_B2
# Copy handle_B2 to 8000B0E0, which is normally unused by the game
lis r12, 0x8000
ori r12, r12, 0xB0E0 # r12 = 0x8000B0E0
sub r7, r10, r9
rlwinm r7, r7, 30, 2, 31 # r7 = number of words to copy
mtctr r7
subi r8, r12, 4 # r8 = r12 - 4 (so we can use stwu)
subi r9, r9, 4 # r9 = r9 - 4 (so we can use lwzu)
copy_handle_B2_word_again:
lwzu r0, [r9 + 4]
stwu [r8 + 4], r0
bdnz copy_handle_B2_word_again
# Invalidate the caches appropriately for the newly-copied code
mr r3, r12
rlwinm r4, r7, 30, 2, 31
bl call_flush_code # flush_code(copied_B2_handler, copied_B2_handler_bytes)
# Replace the command handler table entry for command 0E (which is an unused
# legacy command and has very broken behavior) with our B2 implementation
li r0, 0x00B2
lis r6, 0x804C
ori r5, r6, 0x4E08 # US v1.2
lwz r3, [r5]
cmplwi r3, 0x000E
beq patch_main_handlers_write
ori r5, r6, 0x5530 # JP v1.5
lwz r3, [r5]
cmplwi r3, 0x000E
beq patch_main_handlers_write
lis r6, 0x8045
subi r5, r6, 0x097C # US Ep3
lwz r3, [r5]
cmplwi r3, 0x000E
beq patch_main_handlers_write
ori r5, r6, 0x1A3C # EU Ep3
lwz r3, [r5]
cmplwi r3, 0x000E
bne done
patch_main_handlers_write:
stw [r5], r0
stw [r5 + 0x0C], r12
done:
mtlr r11
blr