From fdca3dacf3917c46b692b2cc292a422fd957fb1d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 09:13:10 -0700 Subject: [PATCH 01/51] Improve hsd_3A94 card block writers Restructure fn_803AE7F8, fn_803AF3F0, fn_803B0120, and fn_803B0E9C: hoist the repeated queue-command blocks into helpers, split the verify/write results into their own locals, and correct the stack padding. fn_803AE7F8 96.64 -> 98.34, fn_803AF3F0 92.95 -> 97.80, fn_803B0120 95.55 -> 96.48, fn_803B0E9C 97.28 -> 98.27. Co-authored-by: ford-bubba-ai --- src/sysdolphin/baselib/hsd_3A94.c | 472 +++++++++++++++--------------- 1 file changed, 233 insertions(+), 239 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 5381c36e2f..b8ce882e81 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3036,6 +3036,43 @@ static inline void fn_803AE7F8_close(CardState* state) } } +static inline s32 fn_803AE7F8_queue_write(CardState* state, s32 block, + s32 logical, s32 seq, void* data, + s32 size, s32 file_id) +{ + s32 cmd[9]; + s32 init_cmd[9]; + s32 ofs = fn_803ACBE8(state, block); + s32 result; + + if (block == 0) { + s32 zero = 0; + + if (logical != 0) { + return -0x101; + } + init_cmd[0] = 2; + init_cmd[1] = (s32) state; + init_cmd[4] = zero; + init_cmd[5] = zero; + init_cmd[6] = zero; + init_cmd[8] = zero; + init_cmd[7] = ofs; + fn_803AC168(init_cmd); + } + cmd[0] = 1; + cmd[1] = (s32) state; + cmd[3] = block; + cmd[4] = logical; + cmd[5] = seq; + cmd[6] = (s32) data; + cmd[8] = size; + cmd[7] = ofs; + cmd[2] = file_id; + result = fn_803AC168(cmd); + return result; +} + static inline void fn_803AE7F8_calc_file_blocks(s32 file_size, s32 file_idx, CardState* state, s32* file_blocks, @@ -3084,7 +3121,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 total_blocks; s32 verify_failed; u8* data; - PAD_STACK(44); + PAD_STACK(36); repair_result = 0; verify_failed = 0; @@ -3197,15 +3234,15 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) pass = 0; map = block_map_ptr; - result = 0; for (; pass < 2; pass++, map += 64) { s32 remaining = file_size; data = (u8*) arg2; for (i = 0; i < file_blocks && remaining > 0; i++) { s32 phys; - s32 chunk = state->x8 - 0x20; + s32 chunk = state->x8; + chunk -= 0x20; if ((u32) remaining > (u32) chunk) { phys = map[i]; if (phys >= 0) { @@ -3231,8 +3268,11 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return cmd_result; } } else { - result = fn_803ACC0C(arg0, phys, blocks_before + i, - current_seq, data, chunk); + s32 verify_result = + fn_803ACC0C(arg0, phys, blocks_before + i, + current_seq, data, chunk); + + result = verify_result; if (result < 0) { fn_803AE7F8_close(state); return result; @@ -3287,32 +3327,30 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } - if (result <= 0) { - if (arg3 != 0) { - s32 cmd[9]; - s32 cmd_result; + if (arg3 != 0) { + s32 cmd[9]; + s32 cmd_result; - cmd[0] = 6; - cmd[1] = (s32) arg0; - cmd_result = fn_803AC168(cmd); - if (cmd_result < 0) { - fn_803AE7F8_rewind(entries); - return cmd_result; - } - } else { - s32 retries; - for (retries = 0; retries < 10; retries++) { - result = CARDClose(&state->file_info); - if (result != -1) { - break; - } + cmd[0] = 6; + cmd[1] = (s32) arg0; + cmd_result = fn_803AC168(cmd); + if (cmd_result < 0) { + fn_803AE7F8_rewind(entries); + return cmd_result; + } + } else { + s32 retries; + for (retries = 0; retries < 10; retries++) { + result = CARDClose(&state->file_info); + if (result != -1) { + break; } + } - if (result < 0) { - return -267; - } - return 1; + if (result < 0) { + return -267; } + return 1; } } @@ -3334,36 +3372,9 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) phys = map[i]; if (phys >= 0) { if (arg3 != 0) { - s32 ofs = fn_803ACBE8(arg0, phys); - s32 cmd_result; - if (phys == 0 && blocks_before + i != 0) { - cmd_result = -0x101; - } else { - if (phys == 0) { - s32 cmd[9]; - cmd[0] = 2; - cmd[1] = (s32) arg0; - cmd[4] = 0; - cmd[5] = 0; - cmd[6] = 0; - cmd[8] = 0; - cmd[7] = ofs; - fn_803AC168(cmd); - } - { - s32 cmd[9]; - cmd[0] = 1; - cmd[1] = (s32) arg0; - cmd[3] = phys; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = chunk; - cmd[7] = ofs; - cmd[2] = arg1; - cmd_result = fn_803AC168(cmd); - } - } + s32 cmd_result = fn_803AE7F8_queue_write( + arg0, phys, blocks_before + i, current_seq, + data, chunk, arg1); if (cmd_result < 0) { fn_803AE7F8_rewind(entries); return cmd_result; @@ -3390,36 +3401,9 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) phys = map[i]; if (phys >= 0) { if (arg3 != 0) { - s32 ofs = fn_803ACBE8(arg0, phys); - s32 cmd_result; - if (phys == 0 && blocks_before + i != 0) { - cmd_result = -0x101; - } else { - if (phys == 0) { - s32 cmd[9]; - cmd[0] = 2; - cmd[1] = (s32) arg0; - cmd[4] = 0; - cmd[5] = 0; - cmd[6] = 0; - cmd[8] = 0; - cmd[7] = ofs; - fn_803AC168(cmd); - } - { - s32 cmd[9]; - cmd[0] = 1; - cmd[1] = (s32) arg0; - cmd[3] = phys; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = remaining; - cmd[7] = ofs; - cmd[2] = arg1; - cmd_result = fn_803AC168(cmd); - } - } + s32 cmd_result = fn_803AE7F8_queue_write( + arg0, phys, blocks_before + i, current_seq, + data, remaining, arg1); if (cmd_result < 0) { fn_803AE7F8_rewind(entries); return cmd_result; @@ -3469,6 +3453,75 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return repair_result; } +static inline s32 fn_803AF3F0_chunk_size(CardState* state) +{ + return state->x8 - 0x20; +} + +static inline s32 fn_803AF3F0_queue_verify(CardState* state, s32 block, + s32 logical, s32 seq, void* data, + s32 size) +{ + s32 result; + + if (size == 0) { + return 0; + } + { + s32 cmd[9]; + s32 ofs = fn_803ACBE8(state, block); + cmd[0] = 5; + cmd[1] = (s32) state; + cmd[4] = logical; + cmd[5] = seq; + cmd[6] = (s32) data; + cmd[8] = size; + cmd[7] = ofs; + result = fn_803AC168(cmd); + } + return result; +} + +static inline s32 fn_803AF3F0_queue_write(CardState* state, s32 block, + s32 logical, s32 seq, void* data, + s32 size, s32 file_id) +{ + s32 ofs = fn_803ACBE8(state, block); + s32 result; + + if (block == 0) { + if (logical != 0) { + return -0x101; + } + { + s32 cmd[9]; + s32 zero = 0; + cmd[0] = 2; + cmd[1] = (s32) state; + cmd[4] = zero; + cmd[5] = zero; + cmd[6] = zero; + cmd[8] = zero; + cmd[7] = ofs; + fn_803AC168(cmd); + } + } + { + s32 cmd[9]; + cmd[0] = 1; + cmd[1] = (s32) state; + cmd[3] = block; + cmd[4] = logical; + cmd[5] = seq; + cmd[6] = (s32) data; + cmd[8] = size; + cmd[7] = ofs; + cmd[2] = file_id; + result = fn_803AC168(cmd); + } + return result; +} + static inline void fn_803AF3F0_rewind(CardBufEntry* entries) { s32 snap = hsd_804D7998; @@ -3496,6 +3549,11 @@ static inline void fn_803AF3F0_close(CardState* state) } } +static inline s32 fn_803AF3F0_queue_cmd(s32* cmd) +{ + return fn_803AC168(cmd); +} + s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { CardBufEntry* entries = (CardBufEntry*) hsd_804D1138; @@ -3504,13 +3562,13 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 blocks_before; s32 file_blocks; s32 file_size; - s32 total_blocks; + s32 result; s32 current_seq; s32 secondary_count; s32 free_count; s32 i; s32 remaining; - s32 result; + s32 total_blocks; u8* data; PAD_STACK(32); @@ -3531,7 +3589,11 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) blocks_before = fn_803AC6B8_blocks_before(state, arg1); - if ((file_size = state->x4C[arg1]) <= 0) { + { + s32 size = state->x4C[arg1]; + file_size = size; + } + if (file_size <= 0) { file_blocks = 0; } else if (arg1 == 0) { s32 hdr = state->x24; @@ -3560,9 +3622,9 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 != 0) { hsd_804D7998 = hsd_804D7984; } else { - s32 ofs = state->x20; - s32 fd = state->x4; s32 retries; + s32 fd = state->x4; + s32 ofs = state->x20; s32 open_result; for (retries = 0; retries < 10; retries++) { open_result = CARDFastOpen(fd, ofs, &state->file_info); @@ -3620,73 +3682,47 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) remaining = file_size; data = (u8*) arg2; for (i = 0; i < file_blocks && remaining > 0; i++) { - s32 chunk = state->x8 - 0x20; - - if ((u32) remaining > (u32) chunk) { + if ((u32) remaining > (u32) fn_803AF3F0_chunk_size(state)) { if (arg3 != 0) { - s32 cmd_result; - if (chunk == 0) { - cmd_result = 0; - } else { - s32 cmd[9]; - s32 ofs = fn_803ACBE8(state, block_map[0][i]); - cmd[0] = 5; - cmd[1] = (s32) state; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = chunk; - cmd[7] = ofs; - cmd_result = fn_803AC168(cmd); - } + s32 cmd_result = fn_803AF3F0_queue_verify( + state, block_map[0][i], blocks_before + i, current_seq, + data, fn_803AF3F0_chunk_size(state)); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { - result = - fn_803ACC0C(state, block_map[0][i], blocks_before + i, - current_seq, data, chunk); - if (result < 0) { + s32 verify_result = fn_803ACC0C( + state, block_map[0][i], blocks_before + i, current_seq, + data, fn_803AF3F0_chunk_size(state)); + if (verify_result < 0) { fn_803AF3F0_close(state); - return result; + return verify_result; } - if (result > 0) { + if (verify_result > 0) { goto after_verify; } } - remaining -= chunk; - data += chunk; + remaining -= fn_803AF3F0_chunk_size(state); + data += fn_803AF3F0_chunk_size(state); } else { if (arg3 != 0) { - s32 cmd_result; - if (remaining == 0) { - cmd_result = 0; - } else { - s32 cmd[9]; - s32 ofs = fn_803ACBE8(state, block_map[0][i]); - cmd[0] = 5; - cmd[1] = (s32) state; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = remaining; - cmd[7] = ofs; - cmd_result = fn_803AC168(cmd); - } + s32 cmd_result = fn_803AF3F0_queue_verify( + state, block_map[0][i], blocks_before + i, current_seq, + data, remaining); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { - result = + s32 verify_result = fn_803ACC0C(state, block_map[0][i], blocks_before + i, current_seq, data, remaining); - if (result < 0) { + if (verify_result < 0) { fn_803AF3F0_close(state); - return result; + return verify_result; } - if (result > 0) { + if (verify_result > 0) { goto after_verify; } } @@ -3723,23 +3759,24 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (secondary_count > file_blocks) { for (i = file_blocks; i < secondary_count; i++) { if (arg3 != 0) { + s32 block = block_map[1][i]; s32 cmd_result; - s32 ofs = fn_803ACBE8(state, block_map[1][i]); - if (block_map[1][i] == 0) { + s32 ofs = fn_803ACBE8(state, block); + if (block == 0) { cmd_result = -257; } else { s32 cmd[9]; s32 zero = 0; cmd[0] = 1; cmd[1] = (s32) state; - cmd[3] = block_map[1][i]; + cmd[3] = block; cmd[4] = 0xFFFF; cmd[5] = zero; cmd[6] = zero; cmd[8] = zero; cmd[7] = ofs; cmd[2] = arg1; - cmd_result = fn_803AC168(cmd); + cmd_result = fn_803AF3F0_queue_cmd(cmd); } if (cmd_result < 0) { fn_803AF3F0_rewind(entries); @@ -3760,10 +3797,14 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) while (secondary_count < file_blocks && free_count > 0) { block_map[1][secondary_count++] = block_map[2][--free_count]; } - for (i = 0; secondary_count < file_blocks && i < file_blocks; i++) { - if (block_map[0][i] >= 0) { - block_map[1][secondary_count++] = block_map[0][i]; - block_map[0][i] = -1; + if (secondary_count < file_blocks) { + for (i = 0; i < file_blocks && secondary_count < file_blocks; i++) + { + s32 primary = block_map[0][i]; + if (primary >= 0) { + block_map[1][secondary_count++] = primary; + block_map[0][i] = -1; + } } } if (secondary_count < file_blocks) { @@ -3781,104 +3822,50 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) current_seq = (u8) next; } for (i = 0; i < file_blocks && remaining > 0; i++) { - s32 chunk = state->x8 - 0x20; - - if ((u32) remaining > (u32) chunk) { + if ((u32) remaining > (u32) fn_803AF3F0_chunk_size(state)) { if (arg3 != 0) { - s32 ofs = fn_803ACBE8(state, block_map[1][i]); - s32 cmd_result; - if (block_map[1][i] == 0 && blocks_before + i != 0) { - cmd_result = -0x101; - } else { - if (block_map[1][i] == 0) { - s32 cmd[9]; - s32 zero = 0; - cmd[0] = 2; - cmd[1] = (s32) state; - cmd[4] = zero; - cmd[5] = zero; - cmd[6] = zero; - cmd[8] = zero; - cmd[7] = ofs; - fn_803AC168(cmd); - } - { - s32 cmd[9]; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = block_map[1][i]; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = chunk; - cmd[7] = ofs; - cmd[2] = arg1; - cmd_result = fn_803AC168(cmd); - } - } + s32 block = block_map[1][i]; + s32 cmd_result = fn_803AF3F0_queue_write( + state, block, blocks_before + i, current_seq, data, + fn_803AF3F0_chunk_size(state), arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { - result = fn_803ACFC0(state, block_map[1][i], blocks_before + i, - current_seq, data, chunk, arg1); - if (result < 0) { + s32 write_result = fn_803ACFC0( + state, block_map[1][i], blocks_before + i, current_seq, + data, fn_803AF3F0_chunk_size(state), arg1); + if (write_result < 0) { state->x170[block_map[1][i]] = -0x7FFF; state->x270[block_map[1][i]] = 0; fn_803AF3F0_close(state); - return result; + return write_result; } state->x170[block_map[1][i]] = blocks_before + i; state->x270[block_map[1][i]] = current_seq; } - remaining -= chunk; - data += chunk; + remaining -= fn_803AF3F0_chunk_size(state); + data += fn_803AF3F0_chunk_size(state); } else { if (arg3 != 0) { - s32 ofs = fn_803ACBE8(state, block_map[1][i]); - s32 cmd_result; - if (block_map[1][i] == 0 && blocks_before + i != 0) { - cmd_result = -0x101; - } else { - if (block_map[1][i] == 0) { - s32 cmd[9]; - s32 zero = 0; - cmd[0] = 2; - cmd[1] = (s32) state; - cmd[4] = zero; - cmd[5] = zero; - cmd[6] = zero; - cmd[8] = zero; - cmd[7] = ofs; - fn_803AC168(cmd); - } - { - s32 cmd[9]; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = block_map[1][i]; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = remaining; - cmd[7] = ofs; - cmd[2] = arg1; - cmd_result = fn_803AC168(cmd); - } - } + s32 block = block_map[1][i]; + s32 cmd_result = fn_803AF3F0_queue_write( + state, block, blocks_before + i, current_seq, data, + remaining, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { - result = fn_803ACFC0(state, block_map[1][i], blocks_before + i, - current_seq, data, remaining, arg1); - if (result < 0) { + s32 write_result = + fn_803ACFC0(state, block_map[1][i], blocks_before + i, + current_seq, data, remaining, arg1); + if (write_result < 0) { state->x170[block_map[1][i]] = -0x7FFF; state->x270[block_map[1][i]] = 0; fn_803AF3F0_close(state); - return result; + return write_result; } state->x170[block_map[1][i]] = blocks_before + i; state->x270[block_map[1][i]] = current_seq; @@ -4049,7 +4036,11 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) blocks_before = fn_803AC6B8_blocks_before(state, arg1); - if ((file_size = state->x4C[arg1]) <= 0) { + { + s32 size = state->x4C[arg1]; + file_size = size; + } + if (file_size <= 0) { file_blocks = 0; } else if (arg1 == 0) { s32 hdr = state->x24; @@ -4176,8 +4167,9 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) remaining = file_size; data = (u8*) arg2; for (i = 0; i < file_blocks && remaining > 0; i++) { + s32 logical = blocks_before + i; s32 chunk; - if (blocks_before + i == 0) { + if (logical == 0) { chunk = fn_803B0120_first_chunk(state); } else { chunk = state->x8 - 0x20; @@ -4260,16 +4252,17 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) while (secondary_count > 0) { secondary_count--; if (arg3 != 0) { + s32 block = block_map[1][secondary_count]; s32 cmd_result; - s32 ofs = fn_803ACBE8(state, block_map[1][secondary_count]); - if (block_map[1][secondary_count] == 0) { + s32 ofs = fn_803ACBE8(state, block); + if (!block) { cmd_result = -257; } else { s32 cmd[9]; s32 zero = 0; cmd[0] = 1; cmd[1] = (s32) state; - cmd[3] = block_map[1][secondary_count]; + cmd[3] = block; cmd[4] = 0xFFFF; cmd[5] = zero; cmd[6] = zero; @@ -4283,8 +4276,8 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return cmd_result; } } else { - result = fn_803ACFC0(state, block_map[1][secondary_count], 0xFFFF, - 0, NULL, 0, arg1); + s32 block = block_map[1][secondary_count]; + result = fn_803ACFC0(state, block, 0xFFFF, 0, NULL, 0, arg1); state->x170[block_map[1][secondary_count]] = -0x7FFF; state->x270[block_map[1][secondary_count]] = 0; if (result < 0) { @@ -4339,8 +4332,10 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return cmd_result; } } else { - result = fn_803ACFC0(state, block_map[0][i], blocks_before + i, - current_seq, data, remaining, arg1); + s32 write_result = + fn_803ACFC0(state, block_map[0][i], blocks_before + i, + current_seq, data, remaining, arg1); + result = write_result; if (result < 0) { state->x170[block_map[0][i]] = -0x7FFF; state->x270[block_map[0][i]] = 0; @@ -4376,11 +4371,6 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return 0; } -static inline CARDFileInfo* fn_803B0E9C_file_info(struct CardState* arg0) -{ - return &arg0->file_info; -} - s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { u8 digest[0x30]; @@ -4509,9 +4499,9 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } else { s32 retries; u8* buf = arg0->x0; + u32 read_size = sector_size; for (retries = 0; retries < 10; retries++) { - result = - CARDRead(fn_803B0E9C_file_info(arg0), buf, sector_size, 0); + result = CARDRead(&arg0->file_info, buf, read_size, 0); if (result != -1) { break; } @@ -4611,10 +4601,14 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { s32 retries; - u32 write_size = arg0->x8; - u8* buf = arg0->x0; - s32 offset = write_size * block_idx; + s32 offset; + u32 write_size; + u8* buf; + write_size = arg0->x8; + (void) write_size; + buf = arg0->x0; + offset = write_size * block_idx; for (retries = 0; retries < 10; retries++) { result = CARDWrite(&arg0->file_info, buf, write_size, offset); if (result != -1) { From 5c23a27ec8b4df8ba7ea36e0219c34e97c01c54b Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 09:23:01 -0700 Subject: [PATCH 02/51] Hoist the fn_803B0E9C icon-read loop into a helper The target's loop exit is a bge/b pair rather than a folded conditional branch, which is how MWCC lowers an inlined early return. 98.27 -> 98.54; the remainder is register allocation. --- src/sysdolphin/baselib/hsd_3A94.c | 42 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index b8ce882e81..d4e1edf2bf 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4371,6 +4371,29 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return 0; } +static inline s32 fn_803B0E9C_read_icons(struct CardState* arg0, s32* cmd, + s32 file_id, s32 seq) +{ + s32 block_idx; + s32 result; + + for (block_idx = 0; + (u32) block_idx < (0x2F + arg0->x24 + arg0->x8) / arg0->x8; + block_idx++) + { + cmd[0] = 10; + cmd[1] = (s32) arg0; + cmd[3] = file_id; + cmd[4] = seq; + cmd[2] = block_idx; + result = fn_803AC168(cmd); + if (result < 0) { + return result; + } + } + return 0; +} + s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { u8 digest[0x30]; @@ -4395,23 +4418,8 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 == 0) { if (arg4 != 0) { - for (block_idx = 0; - (u32) block_idx < (0x2F + arg0->x24 + arg0->x8) / arg0->x8;) - { - cmd_read_icon[0] = 10; - cmd_read_icon[1] = (s32) arg0; - cmd_read_icon[3] = arg1_copy; - cmd_read_icon[4] = arg2; - cmd_read_icon[2] = block_idx; - result = fn_803AC168(cmd_read_icon); - if (result >= 0) { - block_idx++; - continue; - } - goto check_read_result; - } - result = 0; - check_read_result: + result = + fn_803B0E9C_read_icons(arg0, cmd_read_icon, arg1_copy, arg2); if (result < 0) { return result; } From 0f3221fb1f7d7727ee773ce879d018bbb7b058f8 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 09:39:58 -0700 Subject: [PATCH 03/51] Hoist the fn_803B0E9C card write retry loop into a helper Both CARDWrite retry blocks are identical; sharing one inline also drops the opacity cast the two copies needed. 98.54 -> 99.24. --- src/sysdolphin/baselib/hsd_3A94.c | 67 +++++++++++++------------------ 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index d4e1edf2bf..4cb9ac8d3c 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4394,6 +4394,24 @@ static inline s32 fn_803B0E9C_read_icons(struct CardState* arg0, s32* cmd, return 0; } +static inline s32 fn_803B0E9C_write_block(struct CardState* arg0, + s32 block_idx) +{ + s32 retries; + s32 result; + u32 write_size = arg0->x8; + u8* buf = arg0->x0; + s32 offset = write_size * block_idx; + + for (retries = 0; retries < 10; retries++) { + result = CARDWrite(&arg0->file_info, buf, write_size, offset); + if (result != -1) { + break; + } + } + return result; +} + s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { u8 digest[0x30]; @@ -4405,21 +4423,20 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 cmd_write_icon[9]; UNUSED u8 pad_cmd_write_icon[16]; s32 cmd_read_icon[9]; - UNUSED u8 pad_cmd_read_icon[20]; - s32 payload_pos; + UNUSED u8 pad_cmd_read_icon[12]; s32 remaining; + s32 payload_pos; s32 block_idx; s32 digest_idx; s32 result; u32 sector_size; - s32 arg1_copy = arg1; arg0->x24 = hsd_803AC340(&arg0->x3B0); if (arg3 == 0) { if (arg4 != 0) { result = - fn_803B0E9C_read_icons(arg0, cmd_read_icon, arg1_copy, arg2); + fn_803B0E9C_read_icons(arg0, cmd_read_icon, arg1, arg2); if (result < 0) { return result; } @@ -4433,7 +4450,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return result; } } else { - result = fn_803ACD58(arg0, (UNK_T) arg1_copy, (UNK_T) arg2); + result = fn_803ACD58(arg0, (UNK_T) arg1, (UNK_T) arg2); if (result < 0) { return result; } @@ -4489,7 +4506,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) cmd_write_icon[0] = 9; cmd_write_icon[1] = (s32) arg0; cmd_write_icon[2] = arg4; - cmd_write_icon[3] = arg1_copy; + cmd_write_icon[3] = arg1; cmd_write_icon[4] = arg2; result = fn_803AC168(cmd_write_icon); if (result < 0) { @@ -4526,13 +4543,13 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) switch (arg0->x3B0) { case 2: { void* dst = arg0->x0 + 0x40; - memcpy(dst, (UNK_T) arg1_copy, 0x1800); + memcpy(dst, (UNK_T) arg1, 0x1800); } payload_pos = 0x1840; break; case 1: { void* dst = arg0->x0 + 0x40; - memcpy(dst, (UNK_T) arg1_copy, 0xE00); + memcpy(dst, (UNK_T) arg1, 0xE00); } payload_pos = 0xE40; break; @@ -4546,11 +4563,6 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) while (remaining >= 0) { sector_size = arg0->x8; if ((u32) (payload_pos + remaining) > sector_size) { - s32 retries; - s32 offset; - u32 write_size; - u8* buf; - memcpy(arg0->x0 + payload_pos, (s32*) arg2, sector_size - payload_pos); sector_size = arg0->x8; @@ -4558,16 +4570,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) remaining -= sector_size - payload_pos; hsd_803B2B20(arg0->x0, sector_size, &digest[digest_idx]); - write_size = arg0->x8; - (void) write_size; - buf = arg0->x0; - offset = write_size * block_idx; - for (retries = 0; retries < 10; retries++) { - result = CARDWrite(&arg0->file_info, buf, write_size, offset); - if (result != -1) { - break; - } - } + result = fn_803B0E9C_write_block(arg0, block_idx); if (result < 0) { return result; @@ -4607,23 +4610,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) memcpy(arg0->x0 + payload_pos, digest, 0x30); remaining = -1; - { - s32 retries; - s32 offset; - u32 write_size; - u8* buf; - - write_size = arg0->x8; - (void) write_size; - buf = arg0->x0; - offset = write_size * block_idx; - for (retries = 0; retries < 10; retries++) { - result = CARDWrite(&arg0->file_info, buf, write_size, offset); - if (result != -1) { - break; - } - } - } + result = fn_803B0E9C_write_block(arg0, block_idx); if (result < 0) { return result; From 544b3384382a5d0cc18e297b823299e1bffa1101 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 10:09:22 -0700 Subject: [PATCH 04/51] Share a card read retry helper in fn_803B0E9C and order its locals The first CARDRead retry loop is an inline like the write helper, which colours its locals from the pool band; declaration order inside each helper then fixes the remaining register rotations. 99.24 -> 99.53. --- src/sysdolphin/baselib/hsd_3A94.c | 41 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 4cb9ac8d3c..0cc904c91e 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4397,11 +4397,11 @@ static inline s32 fn_803B0E9C_read_icons(struct CardState* arg0, s32* cmd, static inline s32 fn_803B0E9C_write_block(struct CardState* arg0, s32 block_idx) { - s32 retries; - s32 result; u32 write_size = arg0->x8; - u8* buf = arg0->x0; s32 offset = write_size * block_idx; + u8* buf = arg0->x0; + s32 retries; + s32 result; for (retries = 0; retries < 10; retries++) { result = CARDWrite(&arg0->file_info, buf, write_size, offset); @@ -4412,6 +4412,23 @@ static inline s32 fn_803B0E9C_write_block(struct CardState* arg0, return result; } +static inline s32 fn_803B0E9C_read_first(struct CardState* arg0, + u32 sector_size) +{ + u32 read_size = sector_size; + u8* buf = arg0->x0; + s32 retries; + s32 result; + + for (retries = 0; retries < 10; retries++) { + result = CARDRead(&arg0->file_info, buf, read_size, 0); + if (result != -1) { + break; + } + } + return result; +} + s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { u8 digest[0x30]; @@ -4423,7 +4440,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 cmd_write_icon[9]; UNUSED u8 pad_cmd_write_icon[16]; s32 cmd_read_icon[9]; - UNUSED u8 pad_cmd_read_icon[12]; + UNUSED u8 pad_cmd_read_icon[8]; s32 remaining; s32 payload_pos; s32 block_idx; @@ -4435,8 +4452,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 == 0) { if (arg4 != 0) { - result = - fn_803B0E9C_read_icons(arg0, cmd_read_icon, arg1, arg2); + result = fn_803B0E9C_read_icons(arg0, cmd_read_icon, arg1, arg2); if (result < 0) { return result; } @@ -4522,16 +4538,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 != 0) { memset(arg0->x0, 0, sector_size); } else { - s32 retries; - u8* buf = arg0->x0; - u32 read_size = sector_size; - for (retries = 0; retries < 10; retries++) { - result = CARDRead(&arg0->file_info, buf, read_size, 0); - if (result != -1) { - break; - } - } - + result = fn_803B0E9C_read_first(arg0, sector_size); if (result < 0) { return result; } @@ -4584,8 +4591,8 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 != 0) { memset(arg0->x0, 0, arg0->x8); } else { - s32 retries2; u32 read_size = arg0->x8; + s32 retries2; u8* buf = arg0->x0; for (retries2 = 0; retries2 < 10; retries2++) { From 289baa718600008db9e9fd59077a36f74829e4b7 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 10:20:38 -0700 Subject: [PATCH 05/51] Give fn_803B0E9C's header-patch flag function scope 99.53 -> 99.66. --- src/sysdolphin/baselib/hsd_3A94.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 0cc904c91e..0cdec22f9d 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4446,6 +4446,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 block_idx; s32 digest_idx; s32 result; + s32 has_blocks; u32 sector_size; arg0->x24 = hsd_803AC340(&arg0->x3B0); @@ -4497,8 +4498,6 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) cmd_clear[2] = zero; result = fn_803AC168(cmd_clear); } else { - s32 has_blocks; - if (arg0->x4C[0] > 0) { has_blocks = 1; } else { From 929f39aaa6dd6b35375235515b8331df28175b27 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 10:30:19 -0700 Subject: [PATCH 06/51] Rewind queued card commands through a block pointer The three rewind helpers address the entry through a pointer and advance the cursor before clearing, like cancelQueuedCardCommands. fn_803AE7F8 98.34 -> 99.10, fn_803AF3F0 97.80 -> 98.64, fn_803B0120 96.48 -> 97.27. --- src/sysdolphin/baselib/hsd_3A94.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 0cdec22f9d..bad555ef4f 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3015,11 +3015,12 @@ static inline void fn_803AE7F8_rewind(CardBufEntry* entries) if (snap >= 0) { s32 saved = snap; - s32 zero = 0; + s32 zero; while (saved != hsd_804D7984) { - entries[saved].x10 = zero; + CardBufEntry* queued = &entries[saved]; saved = (saved + 1) % 128; + queued->x10 = zero = 0; } hsd_804D7984 = snap; } @@ -3528,11 +3529,12 @@ static inline void fn_803AF3F0_rewind(CardBufEntry* entries) if (snap >= 0) { s32 saved = snap; - s32 zero = 0; + s32 zero; while (saved != hsd_804D7984) { - entries[saved].x10 = zero; + CardBufEntry* queued = &entries[saved]; saved = (saved + 1) % 128; + queued->x10 = zero = 0; } hsd_804D7984 = snap; } @@ -3916,11 +3918,12 @@ static inline void fn_803B0120_rewind(CardBufEntry* entries) if (snap >= 0) { s32 saved = snap; - s32 zero = 0; + s32 zero; while (saved != hsd_804D7984) { - entries[saved].x10 = zero; + CardBufEntry* queued = &entries[saved]; saved = (saved + 1) % 128; + queued->x10 = zero = 0; } hsd_804D7984 = snap; } From 519318d10a53ece7da5483820095f75b2973ed24 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 10:35:20 -0700 Subject: [PATCH 07/51] Read the file size inside fn_803AE7F8's block-count helper The caller keeps its own copy of the size, so the load CSEs and the copy into the caller's local shows up as the original's addi. 99.10 -> 99.22. --- src/sysdolphin/baselib/hsd_3A94.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index bad555ef4f..1716e61482 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3074,12 +3074,11 @@ static inline s32 fn_803AE7F8_queue_write(CardState* state, s32 block, return result; } -static inline void fn_803AE7F8_calc_file_blocks(s32 file_size, s32 file_idx, - CardState* state, +static inline void fn_803AE7F8_calc_file_blocks(s32 file_idx, CardState* state, s32* file_blocks, s32* total_blocks) { - if (file_size <= 0) { + if (state->x4C[file_idx] <= 0) { *file_blocks = 0; } else if (file_idx == 0) { u32 sector_size = state->x8; @@ -3097,8 +3096,8 @@ static inline void fn_803AE7F8_calc_file_blocks(s32 file_size, s32 file_idx, } } else { u32 sector_size = state->x8; - *file_blocks = - (u32) (file_size + sector_size - 0x21) / (sector_size - 0x20); + *file_blocks = (u32) (state->x4C[file_idx] + sector_size - 0x21) / + (sector_size - 0x20); } *total_blocks = fn_803AC7DC(state); @@ -3122,7 +3121,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 total_blocks; s32 verify_failed; u8* data; - PAD_STACK(36); + PAD_STACK(56); repair_result = 0; verify_failed = 0; @@ -3143,8 +3142,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) blocks_before = fn_803AC6B8_blocks_before(arg0, arg1); file_size = state->x4C[arg1]; - fn_803AE7F8_calc_file_blocks(file_size, arg1, arg0, &file_blocks, - &total_blocks); + fn_803AE7F8_calc_file_blocks(arg1, arg0, &file_blocks, &total_blocks); for (i = 0; i < file_blocks; i++) { block_map[0][i] = -1; @@ -4020,7 +4018,7 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 result; u8* data; - PAD_STACK(52); + PAD_STACK(56); needs_rewrite = 0; if (arg3 == 0) { From f6dec6225613ed1387e9abb34e79a500909329eb Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 10:38:18 -0700 Subject: [PATCH 08/51] Hoist fn_803AF3F0's block-count computation into a helper Mirrors fn_803AE7F8: the helper reads the file size itself and the caller keeps its own copy. 98.64 -> 98.74. --- src/sysdolphin/baselib/hsd_3A94.c | 64 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 1716e61482..357da8f990 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3554,6 +3554,37 @@ static inline s32 fn_803AF3F0_queue_cmd(s32* cmd) return fn_803AC168(cmd); } +static inline void fn_803AF3F0_calc_file_blocks(s32 file_idx, CardState* state, + s32* file_blocks, + s32* total_blocks) +{ + if (state->x4C[file_idx] <= 0) { + *file_blocks = 0; + } else if (file_idx == 0) { + s32 hdr = state->x24; + u32 sector_size = state->x8; + s32 sz0; + s32 rem; + hdr += 0x30; + sz0 = state->x4C[0]; + rem = (s32) ((u32) hdr % sector_size); + rem = (s32) (sector_size - 0x20) - rem; + rem = sz0 - rem; + if (rem <= 0) { + *file_blocks = 1; + } else { + *file_blocks = + (u32) (rem + sector_size - 0x21) / (sector_size - 0x20) + 1; + } + } else { + u32 sector_size = state->x8; + *file_blocks = (u32) (state->x4C[file_idx] + sector_size - 0x21) / + (sector_size - 0x20); + } + + *total_blocks = fn_803AC7DC(state); +} + s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { CardBufEntry* entries = (CardBufEntry*) hsd_804D1138; @@ -3570,7 +3601,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 total_blocks; u8* data; - PAD_STACK(32); + PAD_STACK(56); seq_match = 0; if (arg3 == 0) { @@ -3589,35 +3620,8 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) blocks_before = fn_803AC6B8_blocks_before(state, arg1); - { - s32 size = state->x4C[arg1]; - file_size = size; - } - if (file_size <= 0) { - file_blocks = 0; - } else if (arg1 == 0) { - s32 hdr = state->x24; - u32 sector_size = state->x8; - s32 sz0; - s32 rem; - hdr += 0x30; - sz0 = state->x4C[0]; - rem = (s32) ((u32) hdr % sector_size); - rem = (s32) (sector_size - 0x20) - rem; - rem = sz0 - rem; - if (rem <= 0) { - file_blocks = 1; - } else { - file_blocks = - (u32) (rem + sector_size - 0x21) / (sector_size - 0x20) + 1; - } - } else { - u32 sector_size = state->x8; - file_blocks = - (u32) (file_size + sector_size - 0x21) / (sector_size - 0x20); - } - - total_blocks = fn_803AC7DC(state); + file_size = state->x4C[arg1]; + fn_803AF3F0_calc_file_blocks(arg1, state, &file_blocks, &total_blocks); if (arg3 != 0) { hsd_804D7998 = hsd_804D7984; From 49f67ffed577ab5d31352daa1a0b9e84afe01b30 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 10:53:55 -0700 Subject: [PATCH 09/51] Match fn_803AF3F0's header-block arithmetic and open-block load order 98.74 -> 98.82. --- src/sysdolphin/baselib/hsd_3A94.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 357da8f990..82031cd164 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3561,15 +3561,13 @@ static inline void fn_803AF3F0_calc_file_blocks(s32 file_idx, CardState* state, if (state->x4C[file_idx] <= 0) { *file_blocks = 0; } else if (file_idx == 0) { - s32 hdr = state->x24; u32 sector_size = state->x8; - s32 sz0; + u32 usable; s32 rem; - hdr += 0x30; - sz0 = state->x4C[0]; - rem = (s32) ((u32) hdr % sector_size); - rem = (s32) (sector_size - 0x20) - rem; - rem = sz0 - rem; + + rem = state->x4C[0]; + usable = sector_size - 0x20; + rem = rem - (s32) (usable - (state->x24 + 0x30) % sector_size); if (rem <= 0) { *file_blocks = 1; } else { @@ -3627,9 +3625,12 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) hsd_804D7998 = hsd_804D7984; } else { s32 retries; - s32 fd = state->x4; - s32 ofs = state->x20; + s32 fd; + s32 ofs; s32 open_result; + + ofs = state->x20; + fd = state->x4; for (retries = 0; retries < 10; retries++) { open_result = CARDFastOpen(fd, ofs, &state->file_info); if (open_result != -1) { From 2ea838543374fe3d4b84b7aa469f00a7d2e439fe Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 11:12:12 -0700 Subject: [PATCH 10/51] Split fn_803B0E9C's final card write into its own helper The TU already pairs First/Final block helpers; giving the last write its own inline recovers the second site's register assignment. 99.66 -> 99.80. --- src/sysdolphin/baselib/hsd_3A94.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 82031cd164..66c37bbe83 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4418,6 +4418,24 @@ static inline s32 fn_803B0E9C_write_block(struct CardState* arg0, return result; } +static inline s32 fn_803B0E9C_write_block_final(struct CardState* arg0, + s32 block_idx) +{ + u32 write_size = arg0->x8; + s32 retries; + s32 result; + s32 offset = write_size * block_idx; + u8* buf = arg0->x0; + + for (retries = 0; retries < 10; retries++) { + result = CARDWrite(&arg0->file_info, buf, write_size, offset); + if (result != -1) { + break; + } + } + return result; +} + static inline s32 fn_803B0E9C_read_first(struct CardState* arg0, u32 sector_size) { @@ -4622,7 +4640,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) memcpy(arg0->x0 + payload_pos, digest, 0x30); remaining = -1; - result = fn_803B0E9C_write_block(arg0, block_idx); + result = fn_803B0E9C_write_block_final(arg0, block_idx); if (result < 0) { return result; From 454264796f0d601bd2fe59ffd0196b930e542e0a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 13:10:47 -0700 Subject: [PATCH 11/51] Lay out fn_803AE7F8's command buffers like the original The offset local sits between the two command buffers in the queue-write helper, which gives the target's uniform 40-byte stride, and the done command's buffer is a function-scope object above the verify buffers. 99.22 -> 99.22, but the done buffer and block map now land on their original stack slots. --- src/sysdolphin/baselib/hsd_3A94.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 66c37bbe83..3f0d909451 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3042,8 +3042,8 @@ static inline s32 fn_803AE7F8_queue_write(CardState* state, s32 block, s32 size, s32 file_id) { s32 cmd[9]; - s32 init_cmd[9]; s32 ofs = fn_803ACBE8(state, block); + s32 init_cmd[9]; s32 result; if (block == 0) { @@ -3108,6 +3108,8 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) CardState* state = arg0; CardBufEntry* entries = (CardBufEntry*) hsd_804D1138; s32 block_map[3][64]; + UNUSED u8 pad_block_map[32]; + s32 cmd_done[9]; s32* block_map_ptr; s32 blocks_before; s32 current_seq; @@ -3121,7 +3123,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 total_blocks; s32 verify_failed; u8* data; - PAD_STACK(56); + PAD_STACK(24); repair_result = 0; verify_failed = 0; @@ -3327,12 +3329,11 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } if (arg3 != 0) { - s32 cmd[9]; s32 cmd_result; - cmd[0] = 6; - cmd[1] = (s32) arg0; - cmd_result = fn_803AC168(cmd); + cmd_done[0] = 6; + cmd_done[1] = (s32) arg0; + cmd_result = fn_803AC168(cmd_done); if (cmd_result < 0) { fn_803AE7F8_rewind(entries); return cmd_result; From c982f93f173f5cd40778b4ceae1cbc59c451980a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 13:26:55 -0700 Subject: [PATCH 12/51] Build the card commands through CardCmd instead of word arrays Every nine-word command buffer in the file is a CardCmd, so the queue calls read as field assignments and the state and buffer pointers stop being laundered through s32. Byte-identical everywhere. --- src/sysdolphin/baselib/hsd_3A94.c | 452 +++++++++++++++--------------- 1 file changed, 226 insertions(+), 226 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 3f0d909451..80e057b803 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -851,24 +851,24 @@ static inline void rollbackCardCommands(CardContext* context, s32 snap) } hsd_804D7984 = snap; } -static inline void initHeaderBlockCommand(s32* buf, CardState* state, +static inline void initHeaderBlockCommand(CardCmd* buf, CardState* state, u32 block, s32 file_id, s32 seq_num, s32 version) { - buf[0] = 11; - buf[1] = (s32) state; - buf[2] = block; - buf[3] = file_id; - buf[4] = seq_num; - buf[5] = version; + buf->type = 11; + buf->state = state; + buf->x8 = block; + buf->xC = file_id; + buf->x10 = seq_num; + buf->x14 = version; } static inline s32 queueHeaderBlock(CardState* state, u32 block, s32 file_id, s32 seq_num, s32 version) { - s32 buf[9]; + CardCmd buf; - initHeaderBlockCommand(buf, state, block, file_id, seq_num, version); - return fn_803AC168(buf); + initHeaderBlockCommand(&buf, state, block, file_id, seq_num, version); + return fn_803AC168((s32*) &buf); } static inline s32 queueHeaderBlocks(CardState* state, s32 file_id, s32 seq_num, s32 version) @@ -1456,23 +1456,23 @@ s32 fn_803AC168(s32* cmd_buf) s32 fn_803AC258(CardState* card_state, s32 block_idx) { - s32 buf[9]; - buf[0] = 13; - buf[1] = (s32) card_state; - buf[4] = block_idx; - buf[5] = 0; - buf[6] = 0; - buf[8] = 0; - buf[7] = fn_803ACBE8(card_state, block_idx); - return fn_803AC168(buf); + CardCmd buf; + buf.type = 13; + buf.state = card_state; + buf.x10 = block_idx; + buf.x14 = 0; + buf.x18 = NULL; + buf.x20 = 0; + buf.x1C = fn_803ACBE8(card_state, block_idx); + return fn_803AC168((s32*) &buf); } s32 fn_803AC2A4(CardState* card_state) { - s32 buf[9]; - buf[0] = 14; - buf[1] = (s32) card_state; - return fn_803AC168(buf); + CardCmd buf; + buf.type = 14; + buf.state = card_state; + return fn_803AC168((s32*) &buf); } void fn_803AC2D4(void) @@ -2004,19 +2004,19 @@ s32 fn_803ACD58(CardState* state, void* icon_data, void* file_data) s32 fn_803ACF30(CardState* state, s32 file_id, s32 seq_num, s32 version) { - s32 buf[9]; + CardCmd buf; s32 ret; u32 i; PAD_STACK(4); for (i = 0; i < (0x2F + state->x24 + state->x8) / state->x8; i++) { - buf[0] = 11; - buf[1] = (s32) state; - buf[2] = i; - buf[3] = file_id; - buf[4] = seq_num; - buf[5] = version; - ret = fn_803AC168(buf); + buf.type = 11; + buf.state = state; + buf.x8 = i; + buf.xC = file_id; + buf.x10 = seq_num; + buf.x14 = version; + ret = fn_803AC168((s32*) &buf); if (ret < 0) { return ret; } @@ -2498,8 +2498,8 @@ s32 fn_803AD16C(CardState* state) s32 fn_803ADE4C(s32 card_state, s32 channel, s32 callback) { - s32 buf1[9]; - s32 buf2[9]; + CardCmd buf1; + CardCmd buf2; s32 result; s32 saved; s32 snap; @@ -2509,10 +2509,10 @@ s32 fn_803ADE4C(s32 card_state, s32 channel, s32 callback) PAD_STACK(16); hsd_804D7998 = hsd_804D7984; - buf1[0] = 12; - buf1[1] = card_state; - buf1[2] = channel; - result = fn_803AC168(buf1); + buf1.type = 12; + buf1.state = (CardState*) card_state; + buf1.x8 = channel; + result = fn_803AC168((s32*) &buf1); if (result < 0) { snap1 = hsd_804D7998; if (snap1 >= 0) { @@ -2526,9 +2526,9 @@ s32 fn_803ADE4C(s32 card_state, s32 channel, s32 callback) return result; } - buf2[0] = 17; - buf2[1] = card_state; - result = fn_803AC168(buf2); + buf2.type = 17; + buf2.state = (CardState*) card_state; + result = fn_803AC168((s32*) &buf2); if (result < 0) { snap = hsd_804D7998; if (snap >= 0) { @@ -3041,9 +3041,9 @@ static inline s32 fn_803AE7F8_queue_write(CardState* state, s32 block, s32 logical, s32 seq, void* data, s32 size, s32 file_id) { - s32 cmd[9]; + CardCmd cmd; s32 ofs = fn_803ACBE8(state, block); - s32 init_cmd[9]; + CardCmd init_cmd; s32 result; if (block == 0) { @@ -3052,25 +3052,25 @@ static inline s32 fn_803AE7F8_queue_write(CardState* state, s32 block, if (logical != 0) { return -0x101; } - init_cmd[0] = 2; - init_cmd[1] = (s32) state; - init_cmd[4] = zero; - init_cmd[5] = zero; - init_cmd[6] = zero; - init_cmd[8] = zero; - init_cmd[7] = ofs; - fn_803AC168(init_cmd); - } - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = block; - cmd[4] = logical; - cmd[5] = seq; - cmd[6] = (s32) data; - cmd[8] = size; - cmd[7] = ofs; - cmd[2] = file_id; - result = fn_803AC168(cmd); + init_cmd.type = 2; + init_cmd.state = state; + init_cmd.x10 = zero; + init_cmd.x14 = zero; + init_cmd.x18 = (void*) zero; + init_cmd.x20 = zero; + init_cmd.x1C = ofs; + fn_803AC168((s32*) &init_cmd); + } + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + cmd.x8 = file_id; + result = fn_803AC168((s32*) &cmd); return result; } @@ -3109,7 +3109,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) CardBufEntry* entries = (CardBufEntry*) hsd_804D1138; s32 block_map[3][64]; UNUSED u8 pad_block_map[32]; - s32 cmd_done[9]; + CardCmd cmd_done; s32* block_map_ptr; s32 blocks_before; s32 current_seq; @@ -3252,17 +3252,17 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (chunk == 0) { cmd_result = 0; } else { - s32 cmd[9]; + CardCmd cmd; s32 ofs = fn_803ACBE8(arg0, phys); - cmd[0] = 5; - cmd[1] = (s32) arg0; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = chunk; - cmd[7] = ofs; - cmd_result = fn_803AC168(cmd); + cmd.type = 5; + cmd.state = arg0; + cmd.x10 = blocks_before + i; + cmd.x14 = current_seq; + cmd.x18 = data; + cmd.x20 = chunk; + cmd.x1C = ofs; + cmd_result = fn_803AC168((s32*) &cmd); } if (cmd_result < 0) { fn_803AE7F8_rewind(entries); @@ -3294,17 +3294,17 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (remaining == 0) { cmd_result = 0; } else { - s32 cmd[9]; + CardCmd cmd; s32 ofs = fn_803ACBE8(arg0, phys); - cmd[0] = 5; - cmd[1] = (s32) arg0; - cmd[4] = blocks_before + i; - cmd[5] = current_seq; - cmd[6] = (s32) data; - cmd[8] = remaining; - cmd[7] = ofs; - cmd_result = fn_803AC168(cmd); + cmd.type = 5; + cmd.state = arg0; + cmd.x10 = blocks_before + i; + cmd.x14 = current_seq; + cmd.x18 = data; + cmd.x20 = remaining; + cmd.x1C = ofs; + cmd_result = fn_803AC168((s32*) &cmd); } if (cmd_result < 0) { fn_803AE7F8_rewind(entries); @@ -3331,9 +3331,9 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 != 0) { s32 cmd_result; - cmd_done[0] = 6; - cmd_done[1] = (s32) arg0; - cmd_result = fn_803AC168(cmd_done); + cmd_done.type = 6; + cmd_done.state = arg0; + cmd_result = fn_803AC168((s32*) &cmd_done); if (cmd_result < 0) { fn_803AE7F8_rewind(entries); return cmd_result; @@ -3468,16 +3468,16 @@ static inline s32 fn_803AF3F0_queue_verify(CardState* state, s32 block, return 0; } { - s32 cmd[9]; + CardCmd cmd; s32 ofs = fn_803ACBE8(state, block); - cmd[0] = 5; - cmd[1] = (s32) state; - cmd[4] = logical; - cmd[5] = seq; - cmd[6] = (s32) data; - cmd[8] = size; - cmd[7] = ofs; - result = fn_803AC168(cmd); + cmd.type = 5; + cmd.state = state; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + result = fn_803AC168((s32*) &cmd); } return result; } @@ -3494,30 +3494,30 @@ static inline s32 fn_803AF3F0_queue_write(CardState* state, s32 block, return -0x101; } { - s32 cmd[9]; + CardCmd cmd; s32 zero = 0; - cmd[0] = 2; - cmd[1] = (s32) state; - cmd[4] = zero; - cmd[5] = zero; - cmd[6] = zero; - cmd[8] = zero; - cmd[7] = ofs; - fn_803AC168(cmd); + cmd.type = 2; + cmd.state = state; + cmd.x10 = zero; + cmd.x14 = zero; + cmd.x18 = (void*) zero; + cmd.x20 = zero; + cmd.x1C = ofs; + fn_803AC168((s32*) &cmd); } } { - s32 cmd[9]; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = block; - cmd[4] = logical; - cmd[5] = seq; - cmd[6] = (s32) data; - cmd[8] = size; - cmd[7] = ofs; - cmd[2] = file_id; - result = fn_803AC168(cmd); + CardCmd cmd; + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + cmd.x8 = file_id; + result = fn_803AC168((s32*) &cmd); } return result; } @@ -3550,9 +3550,9 @@ static inline void fn_803AF3F0_close(CardState* state) } } -static inline s32 fn_803AF3F0_queue_cmd(s32* cmd) +static inline s32 fn_803AF3F0_queue_cmd(CardCmd* cmd) { - return fn_803AC168(cmd); + return fn_803AC168((s32*) cmd); } static inline void fn_803AF3F0_calc_file_blocks(s32 file_idx, CardState* state, @@ -3737,11 +3737,11 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } if (arg3 != 0) { - s32 cmd[9]; + CardCmd cmd; s32 cmd_result; - cmd[0] = 6; - cmd[1] = (s32) state; - cmd_result = fn_803AC168(cmd); + cmd.type = 6; + cmd.state = state; + cmd_result = fn_803AC168((s32*) &cmd); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; @@ -3771,18 +3771,18 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (block == 0) { cmd_result = -257; } else { - s32 cmd[9]; + CardCmd cmd; s32 zero = 0; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = block; - cmd[4] = 0xFFFF; - cmd[5] = zero; - cmd[6] = zero; - cmd[8] = zero; - cmd[7] = ofs; - cmd[2] = arg1; - cmd_result = fn_803AF3F0_queue_cmd(cmd); + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = 0xFFFF; + cmd.x14 = zero; + cmd.x18 = (void*) zero; + cmd.x20 = zero; + cmd.x1C = ofs; + cmd.x8 = arg1; + cmd_result = fn_803AF3F0_queue_cmd(&cmd); } if (cmd_result < 0) { fn_803AF3F0_rewind(entries); @@ -3954,16 +3954,16 @@ static inline s32 fn_803B0120_queue_verify(CardState* state, s32 block, return 0; } { - s32 cmd[9]; + CardCmd cmd; s32 ofs = fn_803ACBE8(state, block); - cmd[0] = 5; - cmd[1] = (s32) state; - cmd[4] = logical; - cmd[5] = seq; - cmd[6] = (s32) data; - cmd[8] = size; - cmd[7] = ofs; - result = fn_803AC168(cmd); + cmd.type = 5; + cmd.state = state; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + result = fn_803AC168((s32*) &cmd); } return result; } @@ -3980,30 +3980,30 @@ static inline s32 fn_803B0120_queue_write(CardState* state, s32 block, return -0x101; } { - s32 cmd[9]; + CardCmd cmd; s32 zero = 0; - cmd[0] = 2; - cmd[1] = (s32) state; - cmd[4] = zero; - cmd[5] = zero; - cmd[6] = zero; - cmd[8] = zero; - cmd[7] = ofs; - fn_803AC168(cmd); + cmd.type = 2; + cmd.state = state; + cmd.x10 = zero; + cmd.x14 = zero; + cmd.x18 = (void*) zero; + cmd.x20 = zero; + cmd.x1C = ofs; + fn_803AC168((s32*) &cmd); } } { - s32 cmd[9]; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = block; - cmd[4] = logical; - cmd[5] = seq; - cmd[6] = (s32) data; - cmd[8] = size; - cmd[7] = ofs; - cmd[2] = file_id; - result = fn_803AC168(cmd); + CardCmd cmd; + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + cmd.x8 = file_id; + result = fn_803AC168((s32*) &cmd); } return result; } @@ -4232,11 +4232,11 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (i >= file_blocks || remaining <= 0) { if (arg3 != 0) { - s32 cmd[9]; + CardCmd cmd; s32 cmd_result; - cmd[0] = 6; - cmd[1] = (s32) state; - cmd_result = fn_803AC168(cmd); + cmd.type = 6; + cmd.state = state; + cmd_result = fn_803AC168((s32*) &cmd); if (cmd_result < 0) { fn_803B0120_rewind(entries); return cmd_result; @@ -4265,18 +4265,18 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (!block) { cmd_result = -257; } else { - s32 cmd[9]; + CardCmd cmd; s32 zero = 0; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = block; - cmd[4] = 0xFFFF; - cmd[5] = zero; - cmd[6] = zero; - cmd[8] = zero; - cmd[7] = ofs; - cmd[2] = arg1; - cmd_result = fn_803AC168(cmd); + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = 0xFFFF; + cmd.x14 = zero; + cmd.x18 = (void*) zero; + cmd.x20 = zero; + cmd.x1C = ofs; + cmd.x8 = arg1; + cmd_result = fn_803AC168((s32*) &cmd); } if (cmd_result < 0) { fn_803B0120_rewind(entries); @@ -4378,7 +4378,7 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return 0; } -static inline s32 fn_803B0E9C_read_icons(struct CardState* arg0, s32* cmd, +static inline s32 fn_803B0E9C_read_icons(struct CardState* arg0, CardCmd* cmd, s32 file_id, s32 seq) { s32 block_idx; @@ -4388,12 +4388,12 @@ static inline s32 fn_803B0E9C_read_icons(struct CardState* arg0, s32* cmd, (u32) block_idx < (0x2F + arg0->x24 + arg0->x8) / arg0->x8; block_idx++) { - cmd[0] = 10; - cmd[1] = (s32) arg0; - cmd[3] = file_id; - cmd[4] = seq; - cmd[2] = block_idx; - result = fn_803AC168(cmd); + cmd->type = 10; + cmd->state = arg0; + cmd->xC = file_id; + cmd->x10 = seq; + cmd->x8 = block_idx; + result = fn_803AC168((s32*) cmd); if (result < 0) { return result; } @@ -4457,14 +4457,14 @@ static inline s32 fn_803B0E9C_read_first(struct CardState* arg0, s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { u8 digest[0x30]; - s32 cmd_done[9]; - s32 cmd_clear[9]; + CardCmd cmd_done; + CardCmd cmd_clear; UNUSED u8 pad_cmd_clear[4]; - s32 cmd_patch[9]; + CardCmd cmd_patch; UNUSED u8 pad_cmd_patch[8]; - s32 cmd_write_icon[9]; + CardCmd cmd_write_icon; UNUSED u8 pad_cmd_write_icon[16]; - s32 cmd_read_icon[9]; + CardCmd cmd_read_icon; UNUSED u8 pad_cmd_read_icon[8]; s32 remaining; s32 payload_pos; @@ -4478,15 +4478,15 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 == 0) { if (arg4 != 0) { - result = fn_803B0E9C_read_icons(arg0, cmd_read_icon, arg1, arg2); + result = fn_803B0E9C_read_icons(arg0, &cmd_read_icon, arg1, arg2); if (result < 0) { return result; } { - cmd_done[0] = 6; - cmd_done[1] = (s32) arg0; - result = fn_803AC168(cmd_done); + cmd_done.type = 6; + cmd_done.state = arg0; + result = fn_803AC168((s32*) &cmd_done); } if (result < 0) { return result; @@ -4513,29 +4513,29 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 != 0) { void* clear_buf = arg0->x0; s32 zero = 0; - cmd_clear[0] = 4; - cmd_clear[1] = (s32) arg0; - cmd_clear[4] = zero; - cmd_clear[5] = zero; - cmd_clear[6] = (s32) clear_buf; - cmd_clear[8] = sector_size; - cmd_clear[7] = zero; - cmd_clear[2] = zero; - result = fn_803AC168(cmd_clear); + cmd_clear.type = 4; + cmd_clear.state = arg0; + cmd_clear.x10 = zero; + cmd_clear.x14 = zero; + cmd_clear.x18 = clear_buf; + cmd_clear.x20 = sector_size; + cmd_clear.x1C = zero; + cmd_clear.x8 = zero; + result = fn_803AC168((s32*) &cmd_clear); } else { if (arg0->x4C[0] > 0) { has_blocks = 1; } else { has_blocks = 0; } - cmd_patch[0] = 2; - cmd_patch[1] = (s32) arg0; - cmd_patch[4] = -1; - cmd_patch[5] = 0; - cmd_patch[6] = 0; - cmd_patch[8] = has_blocks; - cmd_patch[7] = arg4 * sector_size; - result = fn_803AC168(cmd_patch); + cmd_patch.type = 2; + cmd_patch.state = arg0; + cmd_patch.x10 = -1; + cmd_patch.x14 = 0; + cmd_patch.x18 = NULL; + cmd_patch.x20 = has_blocks; + cmd_patch.x1C = arg4 * sector_size; + result = fn_803AC168((s32*) &cmd_patch); } if (result < 0) { return result; @@ -4543,12 +4543,12 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } { - cmd_write_icon[0] = 9; - cmd_write_icon[1] = (s32) arg0; - cmd_write_icon[2] = arg4; - cmd_write_icon[3] = arg1; - cmd_write_icon[4] = arg2; - result = fn_803AC168(cmd_write_icon); + cmd_write_icon.type = 9; + cmd_write_icon.state = arg0; + cmd_write_icon.x8 = arg4; + cmd_write_icon.xC = arg1; + cmd_write_icon.x10 = arg2; + result = fn_803AC168((s32*) &cmd_write_icon); if (result < 0) { return result; } @@ -5000,8 +5000,8 @@ s32 fn_803B1338(CardState* state, s32 arg1) s32 fn_803B1F78(CardState* state, s32 channel, s32 file_id, s32 seq_num, s32 callback) { - s32 buf1[9]; - s32 buf2[9]; + CardCmd buf1; + CardCmd buf2; s32 result; s32 blocks; s32 saved; @@ -5019,11 +5019,11 @@ s32 fn_803B1F78(CardState* state, s32 channel, s32 file_id, s32 seq_num, hsd_804D7998 = hsd_804D7984; blocks = state->x8 * hsd_803B2674(state); - buf1[0] = 7; - buf1[1] = (s32) state; - buf1[2] = channel; - buf1[3] = blocks; - result = fn_803AC168(buf1); + buf1.type = 7; + buf1.state = state; + buf1.x8 = channel; + buf1.xC = blocks; + result = fn_803AC168((s32*) &buf1); if (result < 0) { snap1 = hsd_804D7998; if (snap1 >= 0) { @@ -5065,9 +5065,9 @@ s32 fn_803B1F78(CardState* state, s32 channel, s32 file_id, s32 seq_num, return result; } - buf2[0] = 8; - buf2[1] = (s32) state; - result = fn_803AC168(buf2); + buf2.type = 8; + buf2.state = state; + result = fn_803AC168((s32*) &buf2); if (result < 0) { snap = hsd_804D7998; if (snap >= 0) { @@ -5104,7 +5104,7 @@ s32 fn_803B1F78(CardState* state, s32 channel, s32 file_id, s32 seq_num, s32 fn_803B21E8(s32 card_state, s32 file_id, s32 seq_num, s32 callback) { - s32 buf[9]; + CardCmd buf; s32 result; s32 saved; s32 snap; @@ -5130,9 +5130,9 @@ s32 fn_803B21E8(s32 card_state, s32 file_id, s32 seq_num, s32 callback) return result; } - buf[0] = 8; - buf[1] = card_state; - result = fn_803AC168(buf); + buf.type = 8; + buf.state = (CardState*) card_state; + result = fn_803AC168((s32*) &buf); if (result < 0) { snap2 = hsd_804D7998; if (snap2 >= 0) { From c28d86f73c1f7c5af55cc95cab80e58c4081b117 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 13:57:15 -0700 Subject: [PATCH 13/51] Give fn_803AF3F0's queue-write helper one command buffer per block Declaring both buffers at helper scope puts them in the original's order and lands one on its stack slot. 98.82 -> 98.83. --- src/sysdolphin/baselib/hsd_3A94.c | 49 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 80e057b803..a82980c0b0 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3486,39 +3486,36 @@ static inline s32 fn_803AF3F0_queue_write(CardState* state, s32 block, s32 logical, s32 seq, void* data, s32 size, s32 file_id) { - s32 ofs = fn_803ACBE8(state, block); + CardCmd cmd; + CardCmd init_cmd; s32 result; + s32 ofs = fn_803ACBE8(state, block); if (block == 0) { + s32 zero = 0; + if (logical != 0) { return -0x101; } - { - CardCmd cmd; - s32 zero = 0; - cmd.type = 2; - cmd.state = state; - cmd.x10 = zero; - cmd.x14 = zero; - cmd.x18 = (void*) zero; - cmd.x20 = zero; - cmd.x1C = ofs; - fn_803AC168((s32*) &cmd); - } - } - { - CardCmd cmd; - cmd.type = 1; - cmd.state = state; - cmd.xC = block; - cmd.x10 = logical; - cmd.x14 = seq; - cmd.x18 = data; - cmd.x20 = size; - cmd.x1C = ofs; - cmd.x8 = file_id; - result = fn_803AC168((s32*) &cmd); + init_cmd.type = 2; + init_cmd.state = state; + init_cmd.x10 = zero; + init_cmd.x14 = zero; + init_cmd.x18 = (void*) zero; + init_cmd.x20 = zero; + init_cmd.x1C = ofs; + fn_803AC168((s32*) &init_cmd); } + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + cmd.x8 = file_id; + result = fn_803AC168((s32*) &cmd); return result; } From a59a553253933799a530d66967c26fd1a98cab3a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 13:59:27 -0700 Subject: [PATCH 14/51] Correct fn_803AF3F0's stack padding Block map and frame now land on the original's offsets. 98.83 -> 98.84. --- src/sysdolphin/baselib/hsd_3A94.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index a82980c0b0..f80402b647 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3597,7 +3597,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 total_blocks; u8* data; - PAD_STACK(56); + PAD_STACK(60); seq_match = 0; if (arg3 == 0) { From ad7e05715e56fadbf6d0239efc3a002e31eb15ec Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 14:51:40 -0700 Subject: [PATCH 15/51] Count fn_803B0E9C's icon blocks with an unsigned counter Matches the family idiom in fn_803ACF30 and fn_803ACD58, which the window index picks out as 100%-matched twins of this loop, and drops the cast. Byte-identical. --- src/sysdolphin/baselib/hsd_3A94.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index f80402b647..beb8f4fbfc 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4378,11 +4378,10 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) static inline s32 fn_803B0E9C_read_icons(struct CardState* arg0, CardCmd* cmd, s32 file_id, s32 seq) { - s32 block_idx; + u32 block_idx; s32 result; - for (block_idx = 0; - (u32) block_idx < (0x2F + arg0->x24 + arg0->x8) / arg0->x8; + for (block_idx = 0; block_idx < (0x2F + arg0->x24 + arg0->x8) / arg0->x8; block_idx++) { cmd->type = 10; From 43f5dcd95531700befaaddaabd5dc60b04185779 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 15:23:34 -0700 Subject: [PATCH 16/51] Drop the duplicated blocks-before helper calculateBlocksBefore was a byte-identical second copy of fn_803AC6B8_blocks_before. Byte-identical output; fn_803ADF90 stays matched. --- src/sysdolphin/baselib/hsd_3A94.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index beb8f4fbfc..b797454c9d 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -2550,32 +2550,6 @@ s32 fn_803ADE4C(s32 card_state, s32 channel, s32 callback) return 0; } -static inline s32 calculateBlocksBefore(struct CardState* file_desc, - s32 file_count) -{ - u8* total; - s32 i; - - if (file_count >= 9) { - return 0; - } - if (file_count == 0) { - return 0; - } - - total = (u8*) 1; - if (file_desc->x4C[0] > 0) { - total = (u8*) &((CardState*) fn_803AC634(file_desc, 0))->x8; - total -= 8; - } - - for (i = 1; i < file_count; i++) { - total += fn_803AC634(file_desc, i); - } - - return (s32) total; -} - static inline s32 queueCardCommand2First(CardState* state, s32 block, void* data, s32 length, s32 offset) { @@ -2861,7 +2835,7 @@ s32 fn_803ADF90(struct CardState* arg0, s32 arg1, u8* arg2, s32 arg3, } } - blocks_before = calculateBlocksBefore(arg0, arg1); + blocks_before = fn_803AC6B8_blocks_before(arg0, arg1); file_size = arg0->x4C[arg1]; file_blocks = calculateFileBlockCount(arg0, arg1); From d0d263031bae4c8a610f089f91f2040fa4cfbc52 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 15:38:29 -0700 Subject: [PATCH 17/51] Load fn_803AF3F0's block sequence number once The target keeps state->x270[i] in one register for both the comparison and the assignment. 98.84 -> 98.87. --- src/sysdolphin/baselib/hsd_3A94.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index b797454c9d..74e824bba4 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3627,10 +3627,12 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 arg1x = state->x170[i]; if (arg1x >= 0) { + s32 seq; s32 logical = arg1x - blocks_before; if (logical >= 0 && logical < file_blocks) { - if (fn_803ACB74(current_seq, state->x270[i]) < 0) { - current_seq = state->x270[i]; + seq = state->x270[i]; + if (fn_803ACB74(current_seq, seq) < 0) { + current_seq = seq; } block_map[0][logical] = i; } From ea9ad85922d13c6a6d6d25ffa5d050125a505b2e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 16:00:21 -0700 Subject: [PATCH 18/51] Walk fn_803AE7F8's block scan with an explicit row pointer The original names the induction variable: the target colours it below the scan loop's locals, which a compiler-created IV never is. 99.22 -> 99.42. --- src/sysdolphin/baselib/hsd_3A94.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 74e824bba4..19b7175f66 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3092,6 +3092,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 free_count; s32 i; s32 logical; + CardState* row; s32 repair_result; s32 result; s32 total_blocks; @@ -3128,8 +3129,10 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) free_count = 0; current_seq = -1; - for (i = 1; i <= total_blocks; i++) { - s32 file_idx = state->x170[i]; + for (i = 1, row = (CardState*) ((s32*) state + 1); i <= total_blocks; + i++, row = (CardState*) ((s32*) row + 1)) + { + s32 file_idx = row->x170[0]; if (file_idx < 0) { block_map[2][free_count++] = i; @@ -3139,9 +3142,9 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) logical = file_idx - blocks_before; if (logical >= 0 && logical < file_blocks) { if (current_seq == -1 || - fn_803ACB74(current_seq, state->x270[i]) < 0) + fn_803ACB74(current_seq, row->x270[0]) < 0) { - current_seq = state->x270[i]; + current_seq = row->x270[0]; } if (block_map[0][logical] < 0) { block_map[0][logical] = i; From a2da18ce76b2ac77a2eee425af03649adf5bd1ab Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 16:02:41 -0700 Subject: [PATCH 19/51] Walk fn_803AF3F0's verify pass with an explicit block pointer 98.87 -> 98.93. --- src/sysdolphin/baselib/hsd_3A94.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 19b7175f66..0ddb4de097 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3574,6 +3574,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 total_blocks; u8* data; + s32* vmap; PAD_STACK(60); seq_match = 0; @@ -3663,20 +3664,22 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (seq_match == 0) { remaining = file_size; data = (u8*) arg2; - for (i = 0; i < file_blocks && remaining > 0; i++) { + for (i = 0, vmap = block_map[0]; i < file_blocks && remaining > 0; + i++, vmap++) + { if ((u32) remaining > (u32) fn_803AF3F0_chunk_size(state)) { if (arg3 != 0) { s32 cmd_result = fn_803AF3F0_queue_verify( - state, block_map[0][i], blocks_before + i, current_seq, - data, fn_803AF3F0_chunk_size(state)); + state, *vmap, blocks_before + i, current_seq, data, + fn_803AF3F0_chunk_size(state)); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { s32 verify_result = fn_803ACC0C( - state, block_map[0][i], blocks_before + i, current_seq, - data, fn_803AF3F0_chunk_size(state)); + state, *vmap, blocks_before + i, current_seq, data, + fn_803AF3F0_chunk_size(state)); if (verify_result < 0) { fn_803AF3F0_close(state); return verify_result; @@ -3690,15 +3693,15 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } else { if (arg3 != 0) { s32 cmd_result = fn_803AF3F0_queue_verify( - state, block_map[0][i], blocks_before + i, current_seq, - data, remaining); + state, *vmap, blocks_before + i, current_seq, data, + remaining); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { s32 verify_result = - fn_803ACC0C(state, block_map[0][i], blocks_before + i, + fn_803ACC0C(state, *vmap, blocks_before + i, current_seq, data, remaining); if (verify_result < 0) { fn_803AF3F0_close(state); From 890f4ffcbd6326368705f9675fa4d9ac469b6c61 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 16:06:43 -0700 Subject: [PATCH 20/51] Order fn_803AE7F8's scan increments and fast-open loads like the original The walker steps before the counter, and the open block reads the offset before the file descriptor. 99.42 -> 99.46. --- src/sysdolphin/baselib/hsd_3A94.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 0ddb4de097..751526e459 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3130,7 +3130,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) current_seq = -1; for (i = 1, row = (CardState*) ((s32*) state + 1); i <= total_blocks; - i++, row = (CardState*) ((s32*) row + 1)) + row = (CardState*) ((s32*) row + 1), i++) { s32 file_idx = row->x170[0]; @@ -3190,10 +3190,13 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 != 0) { hsd_804D7998 = hsd_804D7984; } else { - s32 fd = state->x4; - s32 ofs = state->x20; + s32 fd; + s32 ofs; s32 open_result; s32 retries; + + ofs = state->x20; + fd = state->x4; for (retries = 0; retries < 10; retries++) { open_result = CARDFastOpen(fd, ofs, &state->file_info); if (open_result != -1) { From dc3501c2af4ae4547520eb49e27a7ce9199a2789 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 16:27:29 -0700 Subject: [PATCH 21/51] Recompute the data block size in fn_803AE7F8's write pass The original does not name it there, so the value coalesces into its register instead of being copied. 99.46 -> 99.66. --- src/sysdolphin/baselib/hsd_3A94.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 751526e459..6e54cfca37 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3346,23 +3346,22 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) data = (u8*) arg2; for (i = 0; i < file_blocks && remaining > 0; i++) { s32 phys; - s32 chunk = state->x8 - 0x20; - if ((u32) remaining > (u32) chunk) { + if ((u32) remaining > (u32) (state->x8 - 0x20)) { phys = map[i]; if (phys >= 0) { if (arg3 != 0) { s32 cmd_result = fn_803AE7F8_queue_write( arg0, phys, blocks_before + i, current_seq, - data, chunk, arg1); + data, state->x8 - 0x20, arg1); if (cmd_result < 0) { fn_803AE7F8_rewind(entries); return cmd_result; } } else { - result = - fn_803ACFC0(arg0, phys, blocks_before + i, - current_seq, data, chunk, arg1); + result = fn_803ACFC0(arg0, phys, blocks_before + i, + current_seq, data, + state->x8 - 0x20, arg1); if (result < 0) { state->x170[block_map[pass][i]] = -0x7FFF; state->x270[block_map[pass][i]] = 0; From f51c4f8d610750d2b86b2a3ee4abe9fa7ac5949e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 16:48:57 -0700 Subject: [PATCH 22/51] hsd_3A94: fix AE7F8 write-pass command-block stack layout Declaring the block offset before the command buffer puts the buffer at the lower slot, which matches the frame the original emits for both write-pass queue commands. Also spell the sequence wrap as an explicit 0xFF mask. --- src/sysdolphin/baselib/hsd_3A94.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 6e54cfca37..32d723489e 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3232,8 +3232,8 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (chunk == 0) { cmd_result = 0; } else { - CardCmd cmd; s32 ofs = fn_803ACBE8(arg0, phys); + CardCmd cmd; cmd.type = 5; cmd.state = arg0; @@ -3274,8 +3274,8 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (remaining == 0) { cmd_result = 0; } else { - CardCmd cmd; s32 ofs = fn_803ACBE8(arg0, phys); + CardCmd cmd; cmd.type = 5; cmd.state = arg0; @@ -3335,7 +3335,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } after_verify: - current_seq = (u8) (current_seq + 1); + current_seq = (current_seq + 1) & 0xFF; { s32 pass; s32* map = block_map_ptr; From 9656440069ba02209ee7b683f2f91f85f73d5e11 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 17:19:38 -0700 Subject: [PATCH 23/51] hsd_3A94: write out AE7F8's repair-loop card commands directly The repair pass builds its two commands in place, like the write pass, rather than through a shared inline: the original's frame puts all six command buffers in one contiguous run of 40-byte blocks, which only happens for block-scope buffers. With the block chunk size hoisted into the loop condition the whole frame and every branch target now agree. --- src/sysdolphin/baselib/hsd_3A94.c | 81 +++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 32d723489e..f0ddd4e1f8 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3346,22 +3346,53 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) data = (u8*) arg2; for (i = 0; i < file_blocks && remaining > 0; i++) { s32 phys; + s32 chunk; - if ((u32) remaining > (u32) (state->x8 - 0x20)) { + if ((u32) remaining > (u32) (chunk = state->x8 - 0x20)) { phys = map[i]; if (phys >= 0) { if (arg3 != 0) { - s32 cmd_result = fn_803AE7F8_queue_write( - arg0, phys, blocks_before + i, current_seq, - data, state->x8 - 0x20, arg1); + s32 zero; + s32 cmd_result; + CardCmd init_cmd; + s32 ofs; + CardCmd cmd; + + ofs = fn_803ACBE8(arg0, phys); + if (phys == 0) { + zero = 0; + if (blocks_before + i != 0) { + cmd_result = -0x101; + goto repair_full_queued; + } + init_cmd.type = 2; + init_cmd.state = arg0; + init_cmd.x10 = zero; + init_cmd.x14 = zero; + init_cmd.x18 = (void*) zero; + init_cmd.x20 = zero; + init_cmd.x1C = ofs; + fn_803AC168((s32*) &init_cmd); + } + cmd.type = 1; + cmd.state = arg0; + cmd.xC = phys; + cmd.x10 = blocks_before + i; + cmd.x14 = current_seq; + cmd.x18 = data; + cmd.x20 = chunk; + cmd.x1C = ofs; + cmd.x8 = arg1; + cmd_result = fn_803AC168((s32*) &cmd); + repair_full_queued: if (cmd_result < 0) { fn_803AE7F8_rewind(entries); return cmd_result; } } else { - result = fn_803ACFC0(arg0, phys, blocks_before + i, - current_seq, data, - state->x8 - 0x20, arg1); + result = + fn_803ACFC0(arg0, phys, blocks_before + i, + current_seq, data, chunk, arg1); if (result < 0) { state->x170[block_map[pass][i]] = -0x7FFF; state->x270[block_map[pass][i]] = 0; @@ -3380,9 +3411,39 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) phys = map[i]; if (phys >= 0) { if (arg3 != 0) { - s32 cmd_result = fn_803AE7F8_queue_write( - arg0, phys, blocks_before + i, current_seq, - data, remaining, arg1); + s32 zero; + s32 cmd_result; + CardCmd init_cmd; + s32 ofs; + CardCmd cmd; + + ofs = fn_803ACBE8(arg0, phys); + if (phys == 0) { + zero = 0; + if (blocks_before + i != 0) { + cmd_result = -0x101; + goto repair_tail_queued; + } + init_cmd.type = 2; + init_cmd.state = arg0; + init_cmd.x10 = zero; + init_cmd.x14 = zero; + init_cmd.x18 = (void*) zero; + init_cmd.x20 = zero; + init_cmd.x1C = ofs; + fn_803AC168((s32*) &init_cmd); + } + cmd.type = 1; + cmd.state = arg0; + cmd.xC = phys; + cmd.x10 = blocks_before + i; + cmd.x14 = current_seq; + cmd.x18 = data; + cmd.x20 = remaining; + cmd.x1C = ofs; + cmd.x8 = arg1; + cmd_result = fn_803AC168((s32*) &cmd); + repair_tail_queued: if (cmd_result < 0) { fn_803AE7F8_rewind(entries); return cmd_result; From e43b2df27e4f815797139385dc7fe704cd45427a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 17:20:58 -0700 Subject: [PATCH 24/51] hsd_3A94: keep AE7F8's repair-pass block bookkeeping on the synchronous path The queued path's success branch jumps past the x170/x270 updates, so those two stores belong to the CARDWrite branch only. --- src/sysdolphin/baselib/hsd_3A94.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index f0ddd4e1f8..c4a2b99b52 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3399,10 +3399,9 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) fn_803AE7F8_close(state); return pass == 0 ? -260 : -258; } + state->x170[map[i]] = blocks_before + i; + state->x270[map[i]] = current_seq; } - - state->x170[map[i]] = blocks_before + i; - state->x270[map[i]] = current_seq; } remaining -= state->x8 - 0x20; @@ -3458,10 +3457,9 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) fn_803AE7F8_close(state); return pass == 0 ? -260 : -258; } + state->x170[map[i]] = blocks_before + i; + state->x270[map[i]] = current_seq; } - - state->x170[map[i]] = blocks_before + i; - state->x270[map[i]] = current_seq; } remaining = 0; From c56e16f771fc558330e70372423ffa2d33cb04c6 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 17:23:19 -0700 Subject: [PATCH 25/51] hsd_3A94: declare AE7F8's verify-pass block map before the pass counter --- src/sysdolphin/baselib/hsd_3A94.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index c4a2b99b52..16658670cd 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3210,8 +3210,8 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } if (verify_failed == 0) { - s32 pass; s32* map; + s32 pass; pass = 0; map = block_map_ptr; From 06dabfa771405bfa4e9154d704d5319577fe6cfc Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 17:56:16 -0700 Subject: [PATCH 26/51] hsd_3A94: compare AE7F8's logical block index against zero on the left The scan loop's index and the inlined sequence comparison hold each other's registers otherwise. --- src/sysdolphin/baselib/hsd_3A94.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 16658670cd..e39242ac44 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3140,7 +3140,7 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } logical = file_idx - blocks_before; - if (logical >= 0 && logical < file_blocks) { + if (0 <= logical && logical < file_blocks) { if (current_seq == -1 || fn_803ACB74(current_seq, row->x270[0]) < 0) { From 6e08a1b80be023fa31593d565ed9f3f353976ea8 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 17:57:49 -0700 Subject: [PATCH 27/51] hsd_3A94: compare AF3F0's logical block index against zero on the left --- src/sysdolphin/baselib/hsd_3A94.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index e39242ac44..3ccdd88495 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3694,7 +3694,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg1x >= 0) { s32 seq; s32 logical = arg1x - blocks_before; - if (logical >= 0 && logical < file_blocks) { + if (0 <= logical && logical < file_blocks) { seq = state->x270[i]; if (fn_803ACB74(current_seq, seq) < 0) { current_seq = seq; @@ -3703,7 +3703,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } else { s32 logical = -(arg1x + blocks_before); - if (logical >= 0 && logical < file_blocks) { + if (0 <= logical && logical < file_blocks) { block_map[1][secondary_count] = i; secondary_count++; } else { From 7bebd963b68bc46ba906ef6e47e94c057a7a6cc4 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 18:21:04 -0700 Subject: [PATCH 28/51] hsd_3A94: compare AF3F0's spare primary block against zero on the left --- src/sysdolphin/baselib/hsd_3A94.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 3ccdd88495..5ca9f1cc2b 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3847,7 +3847,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) for (i = 0; i < file_blocks && secondary_count < file_blocks; i++) { s32 primary = block_map[0][i]; - if (primary >= 0) { + if (0 <= primary) { block_map[1][secondary_count++] = primary; block_map[0][i] = -1; } From 2f9331309e0a4879966d1d07aa524d98e5777d9e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 18:23:41 -0700 Subject: [PATCH 29/51] hsd_3A94: read AF3F0's scan sequence number straight from the block --- src/sysdolphin/baselib/hsd_3A94.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 5ca9f1cc2b..50f85f648e 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3692,12 +3692,10 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 arg1x = state->x170[i]; if (arg1x >= 0) { - s32 seq; s32 logical = arg1x - blocks_before; if (0 <= logical && logical < file_blocks) { - seq = state->x270[i]; - if (fn_803ACB74(current_seq, seq) < 0) { - current_seq = seq; + if (fn_803ACB74(current_seq, state->x270[i]) < 0) { + current_seq = state->x270[i]; } block_map[0][logical] = i; } From cb57988484714d3dcc66f6a24747f96c252f0a9e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 19:13:11 -0700 Subject: [PATCH 30/51] hsd_3A94: match fn_803AE7F8 The file block count comes from fn_803AC634 itself rather than a duplicate of its body, and the queue entry the completion path fills in is addressed through a pointer. --- src/sysdolphin/baselib/hsd_3A94.c | 42 ++++++------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 50f85f648e..66c653f658 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3048,35 +3048,6 @@ static inline s32 fn_803AE7F8_queue_write(CardState* state, s32 block, return result; } -static inline void fn_803AE7F8_calc_file_blocks(s32 file_idx, CardState* state, - s32* file_blocks, - s32* total_blocks) -{ - if (state->x4C[file_idx] <= 0) { - *file_blocks = 0; - } else if (file_idx == 0) { - u32 sector_size = state->x8; - u32 usable; - s32 rem; - - rem = state->x4C[0]; - usable = sector_size - 0x20; - rem = rem - (s32) (usable - (state->x24 + 0x30) % sector_size); - if (rem <= 0) { - *file_blocks = 1; - } else { - *file_blocks = - (u32) (rem + sector_size - 0x21) / (sector_size - 0x20) + 1; - } - } else { - u32 sector_size = state->x8; - *file_blocks = (u32) (state->x4C[file_idx] + sector_size - 0x21) / - (sector_size - 0x20); - } - - *total_blocks = fn_803AC7DC(state); -} - s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { CardState* state = arg0; @@ -3119,7 +3090,8 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) blocks_before = fn_803AC6B8_blocks_before(arg0, arg1); file_size = state->x4C[arg1]; - fn_803AE7F8_calc_file_blocks(arg1, arg0, &file_blocks, &total_blocks); + file_blocks = fn_803AC634(arg0, arg1); + total_blocks = fn_803AC7DC(arg0); for (i = 0; i < file_blocks; i++) { block_map[0][i] = -1; @@ -3481,10 +3453,12 @@ s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return repair_result == 0 ? -267 : repair_result; } } else { - entries[0].x0 = 2; - entries[0].x4 = (s32) arg0; - entries[0].x8 = arg4; - entries[0].xC = arg1; + CardBufEntry* entry = entries; + + entry->x0 = 2; + entry->x4 = (s32) arg0; + entry->x8 = arg4; + entry->xC = arg1; hsd_804D7998 = -1; } From 77d54219febf8e81fe15b50b0e2e4108fa8ac438 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 19:41:07 -0700 Subject: [PATCH 31/51] hsd_3A94: queue AF3F0's freed blocks through the write command builder The spare-block command is the ordinary write command with a logical index of 0xFFFF, which is what makes it reject block zero. --- src/sysdolphin/baselib/hsd_3A94.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 66c653f658..fee2a3dfe3 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3499,9 +3499,9 @@ static inline s32 fn_803AF3F0_queue_write(CardState* state, s32 block, s32 size, s32 file_id) { CardCmd cmd; + s32 ofs = fn_803ACBE8(state, block); CardCmd init_cmd; s32 result; - s32 ofs = fn_803ACBE8(state, block); if (block == 0) { s32 zero = 0; @@ -3610,7 +3610,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 total_blocks; u8* data; s32* vmap; - PAD_STACK(60); + PAD_STACK(24); seq_match = 0; if (arg3 == 0) { @@ -3777,25 +3777,8 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (secondary_count > file_blocks) { for (i = file_blocks; i < secondary_count; i++) { if (arg3 != 0) { - s32 block = block_map[1][i]; - s32 cmd_result; - s32 ofs = fn_803ACBE8(state, block); - if (block == 0) { - cmd_result = -257; - } else { - CardCmd cmd; - s32 zero = 0; - cmd.type = 1; - cmd.state = state; - cmd.xC = block; - cmd.x10 = 0xFFFF; - cmd.x14 = zero; - cmd.x18 = (void*) zero; - cmd.x20 = zero; - cmd.x1C = ofs; - cmd.x8 = arg1; - cmd_result = fn_803AF3F0_queue_cmd(&cmd); - } + s32 cmd_result = fn_803AF3F0_queue_write( + state, block_map[1][i], 0xFFFF, 0, NULL, 0, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; From dcce87519631913061d9ba12198c8fdcdd5fba19 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 20:19:31 -0700 Subject: [PATCH 32/51] hsd_3A94: match AF3F0's stack frame Pull the card-open retry and the sequence-number check into helpers, name the write pass's block size, and address the completion queue entry through a pointer; every command buffer now lands at its retail slot. --- src/sysdolphin/baselib/hsd_3A94.c | 81 +++++++++++++++++++------------ 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index fee2a3dfe3..700a524888 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3564,6 +3564,34 @@ static inline s32 fn_803AF3F0_queue_cmd(CardCmd* cmd) return fn_803AC168((s32*) cmd); } +static inline s32 fn_803AF3F0_open(s32 fd, s32 ofs, CARDFileInfo* info) +{ + s32 retries; + s32 open_result; + + for (retries = 0; retries < 10; retries++) { + open_result = CARDFastOpen(fd, ofs, info); + if (open_result != -1) { + break; + } + } + return open_result; +} + +static inline void fn_803AF3F0_check_seq(CardState* state, s32* primary, + s32 count, s32 current_seq, + s32* seq_match) +{ + s32 i; + + for (i = 0; i < count; i++) { + if (primary[i] < 0 || state->x270[primary[i]] != current_seq) { + *seq_match = 1; + break; + } + } +} + static inline void fn_803AF3F0_calc_file_blocks(s32 file_idx, CardState* state, s32* file_blocks, s32* total_blocks) @@ -3635,19 +3663,9 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (arg3 != 0) { hsd_804D7998 = hsd_804D7984; } else { - s32 retries; - s32 fd; - s32 ofs; - s32 open_result; + s32 open_result = + fn_803AF3F0_open(state->x4, state->x20, &state->file_info); - ofs = state->x20; - fd = state->x4; - for (retries = 0; retries < 10; retries++) { - open_result = CARDFastOpen(fd, ofs, &state->file_info); - if (open_result != -1) { - break; - } - } if (open_result < 0) { return open_result; } @@ -3686,13 +3704,8 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } - for (i = 0; i < file_blocks; i++) { - if (block_map[0][i] < 0 || state->x270[block_map[0][i]] != current_seq) - { - seq_match = 1; - break; - } - } + fn_803AF3F0_check_seq(state, block_map[0], file_blocks, current_seq, + &seq_match); if (seq_match == 0) { remaining = file_size; @@ -3823,12 +3836,14 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) current_seq = (u8) next; } for (i = 0; i < file_blocks && remaining > 0; i++) { - if ((u32) remaining > (u32) fn_803AF3F0_chunk_size(state)) { + s32 chunk; + + if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { s32 block = block_map[1][i]; - s32 cmd_result = fn_803AF3F0_queue_write( - state, block, blocks_before + i, current_seq, data, - fn_803AF3F0_chunk_size(state), arg1); + s32 cmd_result = + fn_803AF3F0_queue_write(state, block, blocks_before + i, + current_seq, data, chunk, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; @@ -3846,7 +3861,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) state->x170[block_map[1][i]] = blocks_before + i; state->x270[block_map[1][i]] = current_seq; } - remaining -= fn_803AF3F0_chunk_size(state); + remaining -= chunk; data += fn_803AF3F0_chunk_size(state); } else { if (arg3 != 0) { @@ -3883,21 +3898,25 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } { s32 retries; + s32 close_result; + for (retries = 0; retries < 10; retries++) { - result = CARDClose(&state->file_info); - if (result != -1) { + close_result = CARDClose(&state->file_info); + if (close_result != -1) { break; } } - if (result < 0) { + if (close_result < 0) { return -267; } } } else { - entries[0].x0 = 3; - entries[0].x4 = (s32) state; - entries[0].x8 = arg4; - entries[0].xC = arg1; + CardBufEntry* entry = entries; + + entry->x0 = 3; + entry->x4 = (s32) state; + entry->x8 = arg4; + entry->xC = arg1; hsd_804D7998 = -1; } From 805bd58f799034a3cf875e412c3a670d66c00ab4 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 20:20:39 -0700 Subject: [PATCH 33/51] hsd_3A94: recompute AF3F0's write-pass step from the sector size --- src/sysdolphin/baselib/hsd_3A94.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 700a524888..feef9d8c5b 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3861,7 +3861,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) state->x170[block_map[1][i]] = blocks_before + i; state->x270[block_map[1][i]] = current_seq; } - remaining -= chunk; + remaining -= state->x8 - 0x20; data += fn_803AF3F0_chunk_size(state); } else { if (arg3 != 0) { From 330577212fd294721587a003e3ab8c0702bb89f5 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 22:13:51 -0700 Subject: [PATCH 34/51] hsd_3A94: match fn_803B0E9C The digest slice offset is the block index scaled by the digest size, not a second counter; the compiler strength-reduces it, which is why the original never spends a register on it. --- src/sysdolphin/baselib/hsd_3A94.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index feef9d8c5b..5ec704fd4f 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4482,7 +4482,6 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 payload_pos; s32 block_idx; - s32 digest_idx; s32 result; s32 has_blocks; u32 sector_size; @@ -4601,7 +4600,6 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) memset(digest, 0, sizeof(digest)); block_idx = 0; - digest_idx = 0; remaining = arg0->x24 - payload_pos; while (remaining >= 0) { @@ -4612,7 +4610,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) sector_size = arg0->x8; arg2 += sector_size - payload_pos; remaining -= sector_size - payload_pos; - hsd_803B2B20(arg0->x0, sector_size, &digest[digest_idx]); + hsd_803B2B20(arg0->x0, sector_size, &digest[block_idx * 0x10]); result = fn_803B0E9C_write_block(arg0, block_idx); @@ -4622,7 +4620,6 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) payload_pos = 0; block_idx++; - digest_idx += 0x10; if (remaining + 0x30 < (s32) arg0->x24) { if (arg3 != 0) { @@ -4650,7 +4647,7 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) memcpy(arg0->x0 + payload_pos, (void*) arg2, remaining); payload_pos += remaining; - hsd_803B2B20(arg0->x0, payload_pos, &digest[digest_idx]); + hsd_803B2B20(arg0->x0, payload_pos, &digest[block_idx * 0x10]); memcpy(arg0->x0 + payload_pos, digest, 0x30); remaining = -1; @@ -4661,7 +4658,6 @@ s32 fn_803B0E9C(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } block_idx++; - digest_idx += 0x10; } return 0; From 4eb8da3d3044224dd2dd6c90f50866797622e6d1 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 22:30:01 -0700 Subject: [PATCH 35/51] hsd_3A94: give AF3F0's write pass its own block counter --- src/sysdolphin/baselib/hsd_3A94.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 5ec704fd4f..56f62fbcd0 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3634,6 +3634,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 secondary_count; s32 free_count; s32 i; + s32 j; s32 remaining; s32 total_blocks; u8* data; @@ -3835,14 +3836,14 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) data = (u8*) arg2; current_seq = (u8) next; } - for (i = 0; i < file_blocks && remaining > 0; i++) { + for (j = 0; j < file_blocks && remaining > 0; j++) { s32 chunk; if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { - s32 block = block_map[1][i]; + s32 block = block_map[1][j]; s32 cmd_result = - fn_803AF3F0_queue_write(state, block, blocks_before + i, + fn_803AF3F0_queue_write(state, block, blocks_before + j, current_seq, data, chunk, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); @@ -3850,24 +3851,24 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } else { s32 write_result = fn_803ACFC0( - state, block_map[1][i], blocks_before + i, current_seq, + state, block_map[1][j], blocks_before + j, current_seq, data, fn_803AF3F0_chunk_size(state), arg1); if (write_result < 0) { - state->x170[block_map[1][i]] = -0x7FFF; - state->x270[block_map[1][i]] = 0; + state->x170[block_map[1][j]] = -0x7FFF; + state->x270[block_map[1][j]] = 0; fn_803AF3F0_close(state); return write_result; } - state->x170[block_map[1][i]] = blocks_before + i; - state->x270[block_map[1][i]] = current_seq; + state->x170[block_map[1][j]] = blocks_before + j; + state->x270[block_map[1][j]] = current_seq; } remaining -= state->x8 - 0x20; data += fn_803AF3F0_chunk_size(state); } else { if (arg3 != 0) { - s32 block = block_map[1][i]; + s32 block = block_map[1][j]; s32 cmd_result = fn_803AF3F0_queue_write( - state, block, blocks_before + i, current_seq, data, + state, block, blocks_before + j, current_seq, data, remaining, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); @@ -3875,16 +3876,16 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } else { s32 write_result = - fn_803ACFC0(state, block_map[1][i], blocks_before + i, + fn_803ACFC0(state, block_map[1][j], blocks_before + j, current_seq, data, remaining, arg1); if (write_result < 0) { - state->x170[block_map[1][i]] = -0x7FFF; - state->x270[block_map[1][i]] = 0; + state->x170[block_map[1][j]] = -0x7FFF; + state->x270[block_map[1][j]] = 0; fn_803AF3F0_close(state); return write_result; } - state->x170[block_map[1][i]] = blocks_before + i; - state->x270[block_map[1][i]] = current_seq; + state->x170[block_map[1][j]] = blocks_before + j; + state->x270[block_map[1][j]] = current_seq; } remaining = 0; } From 71868043e97556ba336558153364a5d4927ae374 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 22:35:55 -0700 Subject: [PATCH 36/51] hsd_3A94: satisfy clang-tidy on AF3F0's sequence check The block list the check only reads is const, and drop the now-unused command wrapper. --- src/sysdolphin/baselib/hsd_3A94.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 56f62fbcd0..2778f8e9b1 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3559,11 +3559,6 @@ static inline void fn_803AF3F0_close(CardState* state) } } -static inline s32 fn_803AF3F0_queue_cmd(CardCmd* cmd) -{ - return fn_803AC168((s32*) cmd); -} - static inline s32 fn_803AF3F0_open(s32 fd, s32 ofs, CARDFileInfo* info) { s32 retries; @@ -3578,7 +3573,7 @@ static inline s32 fn_803AF3F0_open(s32 fd, s32 ofs, CARDFileInfo* info) return open_result; } -static inline void fn_803AF3F0_check_seq(CardState* state, s32* primary, +static inline void fn_803AF3F0_check_seq(CardState* state, const s32* primary, s32 count, s32 current_seq, s32* seq_match) { From b185e7bead0b32da039e880fe23b453d51fcac06 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Aug 2026 22:46:50 -0700 Subject: [PATCH 37/51] hsd_3A94: drop the queue helper AE7F8 no longer calls --- src/sysdolphin/baselib/hsd_3A94.c | 37 ------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 2778f8e9b1..c0c5419b88 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3011,43 +3011,6 @@ static inline void fn_803AE7F8_close(CardState* state) } } -static inline s32 fn_803AE7F8_queue_write(CardState* state, s32 block, - s32 logical, s32 seq, void* data, - s32 size, s32 file_id) -{ - CardCmd cmd; - s32 ofs = fn_803ACBE8(state, block); - CardCmd init_cmd; - s32 result; - - if (block == 0) { - s32 zero = 0; - - if (logical != 0) { - return -0x101; - } - init_cmd.type = 2; - init_cmd.state = state; - init_cmd.x10 = zero; - init_cmd.x14 = zero; - init_cmd.x18 = (void*) zero; - init_cmd.x20 = zero; - init_cmd.x1C = ofs; - fn_803AC168((s32*) &init_cmd); - } - cmd.type = 1; - cmd.state = state; - cmd.xC = block; - cmd.x10 = logical; - cmd.x14 = seq; - cmd.x18 = data; - cmd.x20 = size; - cmd.x1C = ofs; - cmd.x8 = file_id; - result = fn_803AC168((s32*) &cmd); - return result; -} - s32 fn_803AE7F8(struct CardState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { CardState* state = arg0; From c751d5b790955608e11e53dba9e02d362d31e3df Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 02:09:02 -0700 Subject: [PATCH 38/51] hsd_3A94: nest AF3F0's out-of-blocks bail inside the reclaim guard The retail exit from the free-block reclaim loop branches straight to the write pass; ours re-tested secondary_count first. Nesting the -257 bail inside the same guard as the reclaim loop reproduces the branch target. --- src/sysdolphin/baselib/hsd_3A94.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index c0c5419b88..02ea04fc11 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3779,12 +3779,12 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) block_map[0][i] = -1; } } - } - if (secondary_count < file_blocks) { - if (arg3 == 0) { - fn_803AF3F0_close(state); + if (secondary_count < file_blocks) { + if (arg3 == 0) { + fn_803AF3F0_close(state); + } + return -257; } - return -257; } } From 7f927228f1159f4ff3e4c70c736e58f946b94ecc Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 02:24:32 -0700 Subject: [PATCH 39/51] hsd_3A94: index AF3F0's verify loop and split its queue helper in two The verify walker is compiler-created: retail seeds it from the already materialised block_map base and steps it before the counter, both of which only the block_map[0][i] index form reproduces. With that in place the two queue sites need separate inlines, as elsewhere in this file: the first returns the command result directly, the final keeps a named result. That one extra web is what orders the walker, the counter and the data pointer, and with them file_blocks, free_count and the sequence-match flag. --- src/sysdolphin/baselib/hsd_3A94.c | 50 +++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 02ea04fc11..dea3e91389 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3433,9 +3433,30 @@ static inline s32 fn_803AF3F0_chunk_size(CardState* state) return state->x8 - 0x20; } -static inline s32 fn_803AF3F0_queue_verify(CardState* state, s32 block, - s32 logical, s32 seq, void* data, - s32 size) +static inline s32 fn_803AF3F0_queue_verify_first(CardState* state, s32 block, + s32 logical, s32 seq, + void* data, s32 size) +{ + if (size == 0) { + return 0; + } + { + CardCmd cmd; + s32 ofs = fn_803ACBE8(state, block); + cmd.type = 5; + cmd.state = state; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + return fn_803AC168((s32*) &cmd); + } +} + +static inline s32 fn_803AF3F0_queue_verify_final(CardState* state, s32 block, + s32 logical, s32 seq, + void* data, s32 size) { s32 result; @@ -3596,7 +3617,6 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 total_blocks; u8* data; - s32* vmap; PAD_STACK(24); seq_match = 0; @@ -3669,22 +3689,20 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (seq_match == 0) { remaining = file_size; data = (u8*) arg2; - for (i = 0, vmap = block_map[0]; i < file_blocks && remaining > 0; - i++, vmap++) - { + for (i = 0; i < file_blocks && remaining > 0; i++) { if ((u32) remaining > (u32) fn_803AF3F0_chunk_size(state)) { if (arg3 != 0) { - s32 cmd_result = fn_803AF3F0_queue_verify( - state, *vmap, blocks_before + i, current_seq, data, - fn_803AF3F0_chunk_size(state)); + s32 cmd_result = fn_803AF3F0_queue_verify_first( + state, block_map[0][i], blocks_before + i, current_seq, + data, fn_803AF3F0_chunk_size(state)); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { s32 verify_result = fn_803ACC0C( - state, *vmap, blocks_before + i, current_seq, data, - fn_803AF3F0_chunk_size(state)); + state, block_map[0][i], blocks_before + i, current_seq, + data, fn_803AF3F0_chunk_size(state)); if (verify_result < 0) { fn_803AF3F0_close(state); return verify_result; @@ -3697,16 +3715,16 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) data += fn_803AF3F0_chunk_size(state); } else { if (arg3 != 0) { - s32 cmd_result = fn_803AF3F0_queue_verify( - state, *vmap, blocks_before + i, current_seq, data, - remaining); + s32 cmd_result = fn_803AF3F0_queue_verify_final( + state, block_map[0][i], blocks_before + i, current_seq, + data, remaining); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; } } else { s32 verify_result = - fn_803ACC0C(state, *vmap, blocks_before + i, + fn_803ACC0C(state, block_map[0][i], blocks_before + i, current_seq, data, remaining); if (verify_result < 0) { fn_803AF3F0_close(state); From b36ad79149bd8a98195033d23dfc72dbd4fb6de6 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 02:28:09 -0700 Subject: [PATCH 40/51] hsd_3A94: mask AF3F0's new sequence number instead of casting it Retail keeps the incremented sequence number in a scratch register and masks it into current_seq; the (u8) cast folds both into one web, the & 0xFF spelling (as in fn_803AE7F8) keeps them apart. --- src/sysdolphin/baselib/hsd_3A94.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index dea3e91389..9e6e23e88c 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3810,7 +3810,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 next = current_seq + 1; remaining = file_size; data = (u8*) arg2; - current_seq = (u8) next; + current_seq = next & 0xFF; } for (j = 0; j < file_blocks && remaining > 0; j++) { s32 chunk; From ecad009d8cbd06974789aa513916d20f62040620 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 02:38:46 -0700 Subject: [PATCH 41/51] hsd_3A94: split AF3F0's write command builder in two as well Same shape as the verify pair: the full-chunk site returns the command result directly, the trailing-chunk site (and the free-block site that shares its register web) keeps a named result. That fixes the write pass's walker and its remaining counter. --- src/sysdolphin/baselib/hsd_3A94.c | 53 ++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 9e6e23e88c..929f3be758 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3478,9 +3478,46 @@ static inline s32 fn_803AF3F0_queue_verify_final(CardState* state, s32 block, return result; } -static inline s32 fn_803AF3F0_queue_write(CardState* state, s32 block, - s32 logical, s32 seq, void* data, - s32 size, s32 file_id) +static inline s32 fn_803AF3F0_queue_write_first(CardState* state, s32 block, + s32 logical, s32 seq, + void* data, s32 size, + s32 file_id) +{ + CardCmd cmd; + s32 ofs = fn_803ACBE8(state, block); + CardCmd init_cmd; + + if (block == 0) { + s32 zero = 0; + + if (logical != 0) { + return -0x101; + } + init_cmd.type = 2; + init_cmd.state = state; + init_cmd.x10 = zero; + init_cmd.x14 = zero; + init_cmd.x18 = (void*) zero; + init_cmd.x20 = zero; + init_cmd.x1C = ofs; + fn_803AC168((s32*) &init_cmd); + } + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + cmd.x8 = file_id; + return fn_803AC168((s32*) &cmd); +} + +static inline s32 fn_803AF3F0_queue_write_final(CardState* state, s32 block, + s32 logical, s32 seq, + void* data, s32 size, + s32 file_id) { CardCmd cmd; s32 ofs = fn_803ACBE8(state, block); @@ -3767,7 +3804,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (secondary_count > file_blocks) { for (i = file_blocks; i < secondary_count; i++) { if (arg3 != 0) { - s32 cmd_result = fn_803AF3F0_queue_write( + s32 cmd_result = fn_803AF3F0_queue_write_final( state, block_map[1][i], 0xFFFF, 0, NULL, 0, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); @@ -3818,9 +3855,9 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { s32 block = block_map[1][j]; - s32 cmd_result = - fn_803AF3F0_queue_write(state, block, blocks_before + j, - current_seq, data, chunk, arg1); + s32 cmd_result = fn_803AF3F0_queue_write_first( + state, block, blocks_before + j, current_seq, data, chunk, + arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; @@ -3843,7 +3880,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } else { if (arg3 != 0) { s32 block = block_map[1][j]; - s32 cmd_result = fn_803AF3F0_queue_write( + s32 cmd_result = fn_803AF3F0_queue_write_final( state, block, blocks_before + j, current_seq, data, remaining, arg1); if (cmd_result < 0) { From 97e0a1c2b5228727d8be2c53dcc8f2ec5fbb0fb8 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 02:42:03 -0700 Subject: [PATCH 42/51] hsd_3A94: share AF3F0's loop counter with the write pass again With the two command builders split, the write pass wants the same counter web as the other loops rather than its own; that recovers the data pointer, the sequence number and the counter itself. --- src/sysdolphin/baselib/hsd_3A94.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 929f3be758..dcaa485bc2 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3650,7 +3650,6 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 secondary_count; s32 free_count; s32 i; - s32 j; s32 remaining; s32 total_blocks; u8* data; @@ -3849,14 +3848,14 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) data = (u8*) arg2; current_seq = next & 0xFF; } - for (j = 0; j < file_blocks && remaining > 0; j++) { + for (i = 0; i < file_blocks && remaining > 0; i++) { s32 chunk; if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { - s32 block = block_map[1][j]; + s32 block = block_map[1][i]; s32 cmd_result = fn_803AF3F0_queue_write_first( - state, block, blocks_before + j, current_seq, data, chunk, + state, block, blocks_before + i, current_seq, data, chunk, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); @@ -3864,24 +3863,24 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } else { s32 write_result = fn_803ACFC0( - state, block_map[1][j], blocks_before + j, current_seq, + state, block_map[1][i], blocks_before + i, current_seq, data, fn_803AF3F0_chunk_size(state), arg1); if (write_result < 0) { - state->x170[block_map[1][j]] = -0x7FFF; - state->x270[block_map[1][j]] = 0; + state->x170[block_map[1][i]] = -0x7FFF; + state->x270[block_map[1][i]] = 0; fn_803AF3F0_close(state); return write_result; } - state->x170[block_map[1][j]] = blocks_before + j; - state->x270[block_map[1][j]] = current_seq; + state->x170[block_map[1][i]] = blocks_before + i; + state->x270[block_map[1][i]] = current_seq; } remaining -= state->x8 - 0x20; data += fn_803AF3F0_chunk_size(state); } else { if (arg3 != 0) { - s32 block = block_map[1][j]; + s32 block = block_map[1][i]; s32 cmd_result = fn_803AF3F0_queue_write_final( - state, block, blocks_before + j, current_seq, data, + state, block, blocks_before + i, current_seq, data, remaining, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); @@ -3889,16 +3888,16 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } else { s32 write_result = - fn_803ACFC0(state, block_map[1][j], blocks_before + j, + fn_803ACFC0(state, block_map[1][i], blocks_before + i, current_seq, data, remaining, arg1); if (write_result < 0) { - state->x170[block_map[1][j]] = -0x7FFF; - state->x270[block_map[1][j]] = 0; + state->x170[block_map[1][i]] = -0x7FFF; + state->x270[block_map[1][i]] = 0; fn_803AF3F0_close(state); return write_result; } - state->x170[block_map[1][j]] = blocks_before + j; - state->x270[block_map[1][j]] = current_seq; + state->x170[block_map[1][i]] = blocks_before + i; + state->x270[block_map[1][i]] = current_seq; } remaining = 0; } From 921e8755186ce665be4f6b22085e967e9c32f01a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 02:57:02 -0700 Subject: [PATCH 43/51] hsd_3A94: pass AF3F0's full-chunk block straight to the write builder Only the trailing-chunk site keeps a named block; the full-chunk site reads the map inline, which is what puts the chunk size in its retail register. --- src/sysdolphin/baselib/hsd_3A94.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index dcaa485bc2..0d8fa0a1ca 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3853,10 +3853,9 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { - s32 block = block_map[1][i]; s32 cmd_result = fn_803AF3F0_queue_write_first( - state, block, blocks_before + i, current_seq, data, chunk, - arg1); + state, block_map[1][i], blocks_before + i, current_seq, + data, chunk, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; From 523dbf6694d4a6238f4644fd48685cf380f90c03 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 03:36:33 -0700 Subject: [PATCH 44/51] hsd_3A94: name AF3F0's full-chunk verify block The block value has to outrank the offset helper's own pooled temps in the simplify scan; a named local puts it in the low band, where it survives the first pass and colours before the logical index. --- src/sysdolphin/baselib/hsd_3A94.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 0d8fa0a1ca..0a8f6f9d92 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3728,9 +3728,10 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) for (i = 0; i < file_blocks && remaining > 0; i++) { if ((u32) remaining > (u32) fn_803AF3F0_chunk_size(state)) { if (arg3 != 0) { + s32 block = block_map[0][i]; s32 cmd_result = fn_803AF3F0_queue_verify_first( - state, block_map[0][i], blocks_before + i, current_seq, - data, fn_803AF3F0_chunk_size(state)); + state, block, blocks_before + i, current_seq, data, + fn_803AF3F0_chunk_size(state)); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; From 60af387e11d03a9f67567c841aa7c4ced8be30ba Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 08:11:13 -0700 Subject: [PATCH 45/51] hsd_3A94: carry AF3F0's queued block indices through one-field structs The two block values reach their command builders as inline parameter temps, which are numbered above the offset helper's own temps and so colour after them. A one-field carrier re-ranks each into the loop temp band, which fixes the free-block command and the full-chunk write command; PAD_STACK absorbs the two carriers' frame slots. --- src/sysdolphin/baselib/hsd_3A94.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 0a8f6f9d92..81d2812aa0 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3653,7 +3653,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 total_blocks; u8* data; - PAD_STACK(24); + PAD_STACK(16); seq_match = 0; if (arg3 == 0) { @@ -3804,8 +3804,14 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (secondary_count > file_blocks) { for (i = file_blocks; i < secondary_count; i++) { if (arg3 != 0) { - s32 cmd_result = fn_803AF3F0_queue_write_final( - state, block_map[1][i], 0xFFFF, 0, NULL, 0, arg1); + struct { + s32 v; + } blk; + s32 cmd_result; + + blk.v = block_map[1][i]; + cmd_result = fn_803AF3F0_queue_write_final( + state, blk.v, 0xFFFF, 0, NULL, 0, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; @@ -3854,9 +3860,15 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { - s32 cmd_result = fn_803AF3F0_queue_write_first( - state, block_map[1][i], blocks_before + i, current_seq, - data, chunk, arg1); + struct { + s32 v; + } blk; + s32 cmd_result; + + blk.v = block_map[1][i]; + cmd_result = fn_803AF3F0_queue_write_first( + state, blk.v, blocks_before + i, current_seq, data, chunk, + arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; From 4a4a7ba182ca15937e17e83ab459040bf3730aa9 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 08:13:36 -0700 Subject: [PATCH 46/51] hsd_3A94: hoist AF3F0's block carriers to function scope Block-scope carriers sort below the completion command buffer and push it eight bytes up; at function scope they sort above it, so the whole frame comes back exact. --- src/sysdolphin/baselib/hsd_3A94.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 81d2812aa0..f0bc9bd94a 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3653,6 +3653,12 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 total_blocks; u8* data; + struct { + s32 v; + } free_blk; + struct { + s32 v; + } write_blk; PAD_STACK(16); seq_match = 0; @@ -3804,14 +3810,11 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (secondary_count > file_blocks) { for (i = file_blocks; i < secondary_count; i++) { if (arg3 != 0) { - struct { - s32 v; - } blk; s32 cmd_result; - blk.v = block_map[1][i]; + free_blk.v = block_map[1][i]; cmd_result = fn_803AF3F0_queue_write_final( - state, blk.v, 0xFFFF, 0, NULL, 0, arg1); + state, free_blk.v, 0xFFFF, 0, NULL, 0, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; @@ -3860,15 +3863,12 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { - struct { - s32 v; - } blk; s32 cmd_result; - blk.v = block_map[1][i]; + write_blk.v = block_map[1][i]; cmd_result = fn_803AF3F0_queue_write_first( - state, blk.v, blocks_before + i, current_seq, data, chunk, - arg1); + state, write_blk.v, blocks_before + i, current_seq, data, + chunk, arg1); if (cmd_result < 0) { fn_803AF3F0_rewind(entries); return cmd_result; From 317cbb59ee42f7ff2411b3bf40867c6c5d1cff6e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 09:08:23 -0700 Subject: [PATCH 47/51] hsd_3A94: note AF3F0's remaining register residual --- src/sysdolphin/baselib/hsd_3A94.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index f0bc9bd94a..916c48bb6c 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3637,6 +3637,7 @@ static inline void fn_803AF3F0_calc_file_blocks(s32 file_idx, CardState* state, *total_blocks = fn_803AC7DC(state); } +/// @todo Only the write loop's sector/offset registers differ. s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { CardBufEntry* entries = (CardBufEntry*) hsd_804D1138; From ee093e567a038c7a8ba987cb78e5eec150e001a9 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 09:38:31 -0700 Subject: [PATCH 48/51] hsd_3A94: match fn_803AF3F0 The write loop's sector load and the two block-offset sums were swapped between r3 and r4. The sector web is removed from the interference graph with degree 27 against K=29, just under the threshold, because four earlier pool temps decrement it first; it is therefore coloured after the sums instead of before. Reading remaining at the top of the loop body extends its live range across the loop head, which restores the two edges that defer the sector web to a later simplify pass. --- src/sysdolphin/baselib/hsd_3A94.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 916c48bb6c..c1bd5c9baa 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3637,7 +3637,6 @@ static inline void fn_803AF3F0_calc_file_blocks(s32 file_idx, CardState* state, *total_blocks = fn_803AC7DC(state); } -/// @todo Only the write loop's sector/offset registers differ. s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { CardBufEntry* entries = (CardBufEntry*) hsd_804D1138; @@ -3861,6 +3860,7 @@ s32 fn_803AF3F0(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } for (i = 0; i < file_blocks && remaining > 0; i++) { s32 chunk; + (void) (remaining != 0); if ((u32) remaining > (u32) (chunk = fn_803AF3F0_chunk_size(state))) { if (arg3 != 0) { From 9a89452ecef766c141fb2f158767ef5431280d37 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 10:53:46 -0700 Subject: [PATCH 49/51] hsd_3A94: fix fn_803B0120's frame size and verify-loop exit The frame was 0x18 short of the target's 0x548; PAD_STACK(80) recovers it exactly. The verify loop left through a break and then re-tested i >= file_blocks || remaining <= 0, which emitted the loop's bottom test a second time. The target reaches the close block by fallthrough, so the early exits become a goto past it and the guard goes away. 97.2688 -> 97.7601. --- src/sysdolphin/baselib/hsd_3A94.c | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index c1bd5c9baa..d50584b38e 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4064,7 +4064,7 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 result; u8* data; - PAD_STACK(56); + PAD_STACK(80); needs_rewrite = 0; if (arg3 == 0) { @@ -4240,7 +4240,7 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return result; } if (result > 0) { - break; + goto verify_done; } } remaining -= chunk; @@ -4263,38 +4263,38 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) return result; } if (result > 0) { - break; + goto verify_done; } } remaining = 0; } } - if (i >= file_blocks || remaining <= 0) { - if (arg3 != 0) { - CardCmd cmd; - s32 cmd_result; - cmd.type = 6; - cmd.state = state; - cmd_result = fn_803AC168((s32*) &cmd); - if (cmd_result < 0) { - fn_803B0120_rewind(entries); - return cmd_result; - } - } else { - s32 retries; - for (retries = 0; retries < 10; retries++) { - result = CARDClose(&state->file_info); - if (result != -1) { - break; - } - } - if (result < 0) { - return -267; + if (arg3 != 0) { + CardCmd cmd; + s32 cmd_result; + cmd.type = 6; + cmd.state = state; + cmd_result = fn_803AC168((s32*) &cmd); + if (cmd_result < 0) { + fn_803B0120_rewind(entries); + return cmd_result; + } + } else { + s32 retries; + for (retries = 0; retries < 10; retries++) { + result = CARDClose(&state->file_info); + if (result != -1) { + break; } - return 1; } + if (result < 0) { + return -267; + } + return 1; } + + verify_done:; } while (secondary_count > 0) { secondary_count--; From 8ebe43b49016ff0a4ccbdbeb74eacabcd98211fc Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 11:25:24 -0700 Subject: [PATCH 50/51] hsd_3A94: recover fn_803B0120's command-buffer frame layout Transplants the matched fn_803AF3F0 shapes: the write helper declares its main CardCmd before the offset temp so the type-1 buffer lands below the type-2 one; the secondary-block site becomes a real queue_write call, whose unreachable type-2 buffer is still allocated and produces the target's 0x50 buffer gap; state->x8 - 0x20 moves into a chunk_size helper whose local bills the 16 bytes missing below the first buffer; and the write preheader takes AF3F0's named-temp form. Six of the eight command buffers and block_map now sit at the target's offsets. 97.7601 -> 97.8169. --- src/sysdolphin/baselib/hsd_3A94.c | 89 ++++++++++++++----------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index d50584b38e..126d44e4b5 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -3984,6 +3984,13 @@ static inline void fn_803B0120_close(CardState* state) } } +static inline s32 fn_803B0120_chunk_size(CardState* state) +{ + s32 size = state->x8; + + return size - 0x20; +} + static inline s32 fn_803B0120_queue_verify(CardState* state, s32 block, s32 logical, s32 seq, void* data, s32 size) @@ -4012,39 +4019,36 @@ static inline s32 fn_803B0120_queue_write(CardState* state, s32 block, s32 logical, s32 seq, void* data, s32 size, s32 file_id) { + CardCmd cmd; s32 ofs = fn_803ACBE8(state, block); + CardCmd init_cmd; s32 result; if (block == 0) { + s32 zero = 0; + if (logical != 0) { return -0x101; } - { - CardCmd cmd; - s32 zero = 0; - cmd.type = 2; - cmd.state = state; - cmd.x10 = zero; - cmd.x14 = zero; - cmd.x18 = (void*) zero; - cmd.x20 = zero; - cmd.x1C = ofs; - fn_803AC168((s32*) &cmd); - } - } - { - CardCmd cmd; - cmd.type = 1; - cmd.state = state; - cmd.xC = block; - cmd.x10 = logical; - cmd.x14 = seq; - cmd.x18 = data; - cmd.x20 = size; - cmd.x1C = ofs; - cmd.x8 = file_id; - result = fn_803AC168((s32*) &cmd); + init_cmd.type = 2; + init_cmd.state = state; + init_cmd.x10 = zero; + init_cmd.x14 = zero; + init_cmd.x18 = (void*) zero; + init_cmd.x20 = zero; + init_cmd.x1C = ofs; + fn_803AC168((s32*) &init_cmd); } + cmd.type = 1; + cmd.state = state; + cmd.xC = block; + cmd.x10 = logical; + cmd.x14 = seq; + cmd.x18 = data; + cmd.x20 = size; + cmd.x1C = ofs; + cmd.x8 = file_id; + result = fn_803AC168((s32*) &cmd); return result; } @@ -4064,7 +4068,7 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) s32 remaining; s32 result; u8* data; - PAD_STACK(80); + PAD_STACK(16); needs_rewrite = 0; if (arg3 == 0) { @@ -4219,7 +4223,7 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) if (logical == 0) { chunk = fn_803B0120_first_chunk(state); } else { - chunk = state->x8 - 0x20; + chunk = fn_803B0120_chunk_size(state); } if (remaining > chunk) { @@ -4300,24 +4304,8 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) secondary_count--; if (arg3 != 0) { s32 block = block_map[1][secondary_count]; - s32 cmd_result; - s32 ofs = fn_803ACBE8(state, block); - if (!block) { - cmd_result = -257; - } else { - CardCmd cmd; - s32 zero = 0; - cmd.type = 1; - cmd.state = state; - cmd.xC = block; - cmd.x10 = 0xFFFF; - cmd.x14 = zero; - cmd.x18 = (void*) zero; - cmd.x20 = zero; - cmd.x1C = ofs; - cmd.x8 = arg1; - cmd_result = fn_803AC168((s32*) &cmd); - } + s32 cmd_result = fn_803B0120_queue_write(state, block, 0xFFFF, 0, + NULL, 0, arg1); if (cmd_result < 0) { fn_803B0120_rewind(entries); return cmd_result; @@ -4334,16 +4322,19 @@ s32 fn_803B0120(CardState* state, s32 arg1, s32 arg2, s32 arg3, s32 arg4) } } - remaining = file_size; - data = (u8*) arg2; - current_seq = (u8) (current_seq + 1); + { + s32 next = current_seq + 1; + remaining = file_size; + data = (u8*) arg2; + current_seq = next & 0xFF; + } for (i = 0; i < file_blocks && remaining > 0; i++) { s32 logical = blocks_before + i; s32 chunk; if (logical == 0) { chunk = fn_803B0120_first_chunk(state); } else { - chunk = state->x8 - 0x20; + chunk = fn_803B0120_chunk_size(state); } if (remaining > chunk) { From 07dea0996efd8ecabcc99de2fe97a343accd6baf Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 11:43:13 -0700 Subject: [PATCH 51/51] hsd_3A94: correct fn_803B1338's command-buffer ordering Two structural facts, both read off the frame rather than the score. The write helper declared its type-2 buffer before the type-1 one, so every expansion's pair was allocated in the wrong order. And the redundancy loop hand-expanded queue_write with phys = 0xFFFF, which allocated one named buffer at the top of the frame where the target has it fifth, followed by the unreachable type-2 buffer that produces the 0x50 gap. Calling the helper also removes the duplicated command block. Score is unchanged at 98.7003; the buffer order and the misplaced buffer are now right, which the score cannot see. --- src/sysdolphin/baselib/hsd_3A94.c | 64 +++++++++++-------------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/src/sysdolphin/baselib/hsd_3A94.c b/src/sysdolphin/baselib/hsd_3A94.c index 126d44e4b5..6b19af58a0 100644 --- a/src/sysdolphin/baselib/hsd_3A94.c +++ b/src/sysdolphin/baselib/hsd_3A94.c @@ -4684,38 +4684,34 @@ static inline s32 fn_803B1338_queue_write(CardState* state, s32 logical, s32 phys, void* data, s32 size, s32 file_id) { + s32 cmd[10]; s32 ofs = fn_803ACBE8(state, logical); + s32 init_cmd[10]; s32 result; if (logical == 0) { if (phys != 0) { return -0x101; } - { - s32 cmd[10]; - cmd[0] = 2; - cmd[1] = (s32) state; - cmd[4] = 0; - cmd[5] = 0; - cmd[6] = 0; - cmd[8] = 0; - cmd[7] = ofs; - fn_803AC168(cmd); - } - } - { - s32 cmd[10]; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = logical; - cmd[4] = phys; - cmd[5] = 0; - cmd[6] = (s32) data; - cmd[8] = size; - cmd[7] = ofs; - cmd[2] = file_id; - result = fn_803AC168(cmd); + init_cmd[0] = 2; + init_cmd[1] = (s32) state; + init_cmd[4] = 0; + init_cmd[5] = 0; + init_cmd[6] = 0; + init_cmd[8] = 0; + init_cmd[7] = ofs; + fn_803AC168(init_cmd); } + cmd[0] = 1; + cmd[1] = (s32) state; + cmd[3] = logical; + cmd[4] = phys; + cmd[5] = 0; + cmd[6] = (s32) data; + cmd[8] = size; + cmd[7] = ofs; + cmd[2] = file_id; + result = fn_803AC168(cmd); return result; } @@ -4730,7 +4726,7 @@ s32 fn_803B1338(CardState* state, s32 arg1) s32 i; s32 phys; u8* fdata; - PAD_STACK(168); + PAD_STACK(128); max_redun = 0; phys = 1; @@ -4926,22 +4922,8 @@ s32 fn_803B1338(CardState* state, s32 arg1) for (i = 0; i < max_redun; i++) { if (arg1 != 0) { - s32 ofs = fn_803ACBE8(state, logical); - if (logical == 0) { - result = -257; - } else { - s32 cmd[10]; - cmd[0] = 1; - cmd[1] = (s32) state; - cmd[3] = logical; - cmd[4] = 0xFFFF; - cmd[5] = 0; - cmd[6] = 0; - cmd[8] = 0; - cmd[7] = ofs; - cmd[2] = file_id; - result = fn_803AC168(cmd); - } + result = fn_803B1338_queue_write(state, logical, 0xFFFF, NULL, 0, + file_id); if (result < 0) { return result; }