From d3cd09a4f40c4da0d3a085bcc627c7727f7e4d53 Mon Sep 17 00:00:00 2001 From: Mike-Goutokuji <83477269+mike-goutokuji@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:19:54 -0400 Subject: [PATCH] Mark Revision 0xA changes that are actually bug fixes and tested Things that address UBFixes are marked as such. What addresses BUGFIXES are marked as such. --- include/overworld.h | 3 +- src/battle_controller_player.c | 7 +- src/battle_main.c | 4 +- src/cable_club.c | 6 +- src/field_fadetransition.c | 3 +- src/librfu_rfu.c | 2 + src/link.c | 4 +- src/main.c | 4 + src/menu.c | 5 +- src/overworld.c | 2 +- src/save.c | 9 +- src/task.c | 4 +- src/trade.c | 3 +- src/union_room.c | 23 +++-- src/union_room_battle.c | 7 +- src/wireless_communication_status_screen.c | 114 +++++++++------------ 16 files changed, 108 insertions(+), 92 deletions(-) diff --git a/include/overworld.h b/include/overworld.h index b4f56bee63c..2071ae98056 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -180,7 +180,8 @@ void UpdateEscapeWarp(s16 x, s16 y); bool8 SetDiveWarpEmerge(u16 x, u16 y); bool8 SetDiveWarpDive(u16 x, u16 y); -#if REVISION >= 0xA +#if defined(BUGFIX) || REVISION >= 0xA +// Clears stale post-link callback after CB2_LinkError (see link.c). void ClearFieldCallback(void); #endif diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index d8e1fe71e4e..b3f9d6cc20a 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -819,14 +819,15 @@ static void SetLinkBattleEndCallbacks(void) void SetBattleEndCallbacks(void) { -#if REVISION >= 0xA -#else + // BUGFIX: Link battles wait for RFU/cable idle and fade end before standby/close. + // SetLinkStandbyCallback no-ops while gRfu.callback is set, which could skip teardown. +#if !(defined(BUGFIX) || REVISION >= 0xA) if (!gPaletteFade.active) #endif { if (gBattleTypeFlags & BATTLE_TYPE_LINK) { -#if REVISION >= 0xA +#if REVISION >= 0xA || defined(BUGFIX) if (!IsLinkTaskFinished() || gPaletteFade.active) return; #endif if (gWirelessCommType == 0) diff --git a/src/battle_main.c b/src/battle_main.c index 64c9e59b7ef..8ba6fe071e6 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1160,7 +1160,8 @@ static void CB2_PreInitMultiBattle(void) } break; case 2: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before closing link after multi battle party showcase. +#if defined(BUGFIX) || REVISION >= 0xA if (IsLinkTaskFinished() && !gPaletteFade.active) #else if (!gPaletteFade.active) @@ -3927,6 +3928,7 @@ static void ReturnFromBattleToOverworld(void) if (gBattleTypeFlags & BATTLE_TYPE_ROAMER) { UpdateRoamerHPStatus(&gEnemyParty[0]); + // BUGFIX: Use == B_OUTCOME_WON, not &. Roar sets outcome to PLAYER_TELEPORTED (5); 5 & 1 is nonzero. #if defined(BUGFIX) || REVISION >= 0xA if ((gBattleOutcome == B_OUTCOME_WON) || gBattleOutcome == B_OUTCOME_CAUGHT || gBattleOutcome == B_OUTCOME_DREW) #else diff --git a/src/cable_club.c b/src/cable_club.c index d2f61cdcf61..e4a9f05ac0b 100644 --- a/src/cable_club.c +++ b/src/cable_club.c @@ -718,7 +718,8 @@ static void Task_StartWirelessCableClubBattle(u8 taskId) tState = 5; break; case 5: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before starting cable club battle standby. +#if defined(BUGFIX) || REVISION >= 0xA if (!IsLinkTaskFinished()) break; #endif SetLinkStandbyCallback(); @@ -923,7 +924,8 @@ static void Task_StartWirelessTrade(u8 taskId) tState++; break; case 2: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before trade setup standby. +#if defined(BUGFIX) || REVISION >= 0xA if (!IsLinkTaskFinished()) break; #endif gSelectedTradeMonPositions[TRADE_PLAYER] = 0; diff --git a/src/field_fadetransition.c b/src/field_fadetransition.c index f392a06d5c0..277cd3c5fcf 100644 --- a/src/field_fadetransition.c +++ b/src/field_fadetransition.c @@ -207,7 +207,8 @@ static void Task_ReturnToFieldRecordMixing(u8 taskId) switch (task->data[0]) { case 0: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before wireless return-to-field standby. +#if defined(BUGFIX) || REVISION >= 0xA if (!IsLinkTaskFinished()) break; #endif SetLinkStandbyCallback(); diff --git a/src/librfu_rfu.c b/src/librfu_rfu.c index 76e2158e684..2f7c54f8350 100644 --- a/src/librfu_rfu.c +++ b/src/librfu_rfu.c @@ -784,6 +784,7 @@ static void rfu_CB_pollConnectParent(u8 reqCommand, u16 reqResult) u16 id; u8 slot; u8 bm_slot_flag, i; + // UBFIX: Initialize target_p; some failure paths never assign it before use. #if REVISION >= 0xA || defined(UBFIX) struct RfuTgtData *target_p = NULL; #else @@ -1424,6 +1425,7 @@ static u16 rfu_STC_setSendData_org(u8 ni_or_uni, u8 bmSendSlot, u8 subFrameSize, { u8 bm_slot_id, sendSlotFlag; u8 frameSize; + // UBFIX: llFrameSize_p may be unset if parentChild is neither parent nor child. #if REVISION >= 0xA || defined(UBFIX) u8 *llFrameSize_p = NULL; #else diff --git a/src/link.c b/src/link.c index 84506bf0e9a..68105f2d0c7 100644 --- a/src/link.c +++ b/src/link.c @@ -1425,7 +1425,9 @@ void CB2_LinkError(void) { u8 *tilemapBuffer; -#if REVISION >= 0xA + // BUGFIX: Link error calls ResetTasks() but leaves gFieldCallback set; overworld may + // then run a stale post-link callback on a dead connection (softlock / erratic link state). +#if defined(BUGFIX) || REVISION >= 0xA ClearFieldCallback(); #endif SetGpuReg(REG_OFFSET_DISPCNT, 0); diff --git a/src/main.c b/src/main.c index 542b0f5d16d..ec83c1f0486 100644 --- a/src/main.c +++ b/src/main.c @@ -134,6 +134,8 @@ void AgbMain() RegisterRamReset(RESET_ALL); #endif //MODERN +// It is white because the GBA Boot screen is white. +// Sloop does not display this screen, so fade from black instead. #if REVISION >= 0xA *(vu16 *)BG_PLTT = RGB_BLACK; #else @@ -184,6 +186,8 @@ void AgbMain() && (gMain.heldKeysRaw & B_START_SELECT) == B_START_SELECT) { rfu_REQ_stopMode(); + + // Note: Rev10 skips rfu_waitREQComplete for faster Sloop reset; keep the wait on real hardware. #if REVISION < 0xA rfu_waitREQComplete(); #endif diff --git a/src/menu.c b/src/menu.c index 9ee9968cbce..24a6e649dcf 100644 --- a/src/menu.c +++ b/src/menu.c @@ -567,12 +567,13 @@ s8 Menu_ProcessInputNoWrapClearOnChoose(void) void DestroyYesNoMenu(void) { -#if REVISION >= 0xA + // UBFIX: Guard double-destroy when sYesNoWindowId is already invalid. +#if REVISION >= 0xA || defined(UBFIX) if (sYesNoWindowId == 0xFF) return; #endif ClearStdWindowAndFrameToTransparent(sYesNoWindowId, TRUE); RemoveWindow(sYesNoWindowId); -#if REVISION >= 0xA +#if REVISION >= 0xA || defined(UBFIX) sYesNoWindowId = 0xFF; #endif } diff --git a/src/overworld.c b/src/overworld.c index 339014da46a..deb7f515db8 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1517,7 +1517,7 @@ static bool8 RunFieldCallback(void) return TRUE; } -#if REVISION >= 0xA +#if defined(BUGFIX) || REVISION >= 0xA void ClearFieldCallback(void) { gFieldCallback = NULL; diff --git a/src/save.c b/src/save.c index 36b960f375c..9910da4e9e8 100644 --- a/src/save.c +++ b/src/save.c @@ -888,7 +888,8 @@ void Task_LinkFullSave(u8 taskId) gTasks[taskId].data[0] = 1; break; case 1: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before link full-save standby (see also cases 7 and 9). +#if defined(BUGFIX) || REVISION >= 0xA if (!IsLinkTaskFinished()) break; #endif SetLinkStandbyCallback(); @@ -924,7 +925,8 @@ void Task_LinkFullSave(u8 taskId) gTasks[taskId].data[0] = 7; break; case 7: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle between save sectors. +#if defined(BUGFIX) || REVISION >= 0xA if (!IsLinkTaskFinished()) break; #endif ClearContinueGameWarpStatus2(); @@ -942,7 +944,8 @@ void Task_LinkFullSave(u8 taskId) } break; case 9: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before final post-save standby. +#if defined(BUGFIX) || REVISION >= 0xA if (!IsLinkTaskFinished()) break; #endif SetLinkStandbyCallback(); diff --git a/src/task.c b/src/task.c index 01503dc7ae7..0327c79a57a 100644 --- a/src/task.c +++ b/src/task.c @@ -86,8 +86,8 @@ static void InsertTask(u8 newTaskId) void DestroyTask(u8 taskId) { -#if REVISION >= 0xA - // Bounds check on the task ID. +#if REVISION >= 0xA || defined(UBFIX) + // UBFIX: Invalid taskId would read past gTasks[]. if (taskId >= NUM_TASKS) return; #endif if (gTasks[taskId].isActive) diff --git a/src/trade.c b/src/trade.c index c1936096350..f35dbc81780 100644 --- a/src/trade.c +++ b/src/trade.c @@ -2116,7 +2116,8 @@ static void CB_HandleTradeCanceled(void) static void CB_InitExitCanceledTrade(void) { -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before exiting a canceled trade. +#if defined(BUGFIX) || REVISION >= 0xA if (IsLinkTaskFinished() && !gPaletteFade.active) #else if (!gPaletteFade.active) diff --git a/src/union_room.c b/src/union_room.c index 91bd92c99a9..bda795121ea 100644 --- a/src/union_room.c +++ b/src/union_room.c @@ -525,7 +525,8 @@ static void Task_TryBecomeLinkLeader(u8 taskId) } break; case LL_STATE_MEMBER_LEFT: -#if REVISION >= 0xA + // BUGFIX: Use group size, not activity id, to pick the unavailable-player message. +#if defined(BUGFIX) || REVISION >= 0xA id = (GROUP_MAX(sPlayerActivityGroupSize) != 2) ? 1 : 0; #else id = (GROUP_MAX(sPlayerCurrActivity) == 2) ? 1 : 0; @@ -619,7 +620,8 @@ static void Task_TryBecomeLinkLeader(u8 taskId) { RequestDisconnectSlotByTrainerNameAndId(data->playerList->players[data->playerCount].rfu.name, ReadAsU16(data->playerList->players[data->playerCount].rfu.data.compatibility.playerTrainerId)); data->playerList->players[data->playerCount].groupScheduledAnim = UNION_ROOM_SPAWN_NONE; -#if REVISION >= 0xA + // BUGFIX: Use LeaderPrunePlayerList return value to update playerCount. +#if defined(BUGFIX) || REVISION >= 0xA data->playerCount = LeaderPrunePlayerList(data->playerList); #else LeaderPrunePlayerList(data->playerList); @@ -966,11 +968,13 @@ static bool8 Leader_SetStateIfMemberListChanged(struct WirelessLink_Leader * dat #endif break; case UNION_ROOM_SPAWN_OUT: -#if REVISION >= 0xA + // BUGFIX: Handle pending disconnect (countdown != 0) before player is fully removed. +#if defined(BUGFIX) || REVISION >= 0xA case UNION_ROOM_SPAWN_OUT_SOON: #endif RfuSetStatus(RFU_STATUS_OK, 0); -#if REVISION >= 0xA + // BUGFIX: Prune list when member leaves (or is about to). +#if defined(BUGFIX) || REVISION >= 0xA data->playerCount = LeaderPrunePlayerList(data->playerList); #endif RedrawListMenu(data->listTaskId); @@ -1006,7 +1010,8 @@ static void ItemPrintFunc_PossibleGroupMembers(u8 windowId, u32 id, u8 y) static u8 LeaderUpdateGroupMembership(struct RfuPlayerList * list) { struct WirelessLink_Leader * data = sWirelessLinkMain.leader; -#if REVISION >= 0xA + // BUGFIX: Classify spawn in/out by countdown so SPAWN_OUT_SOON can be handled. +#if defined(BUGFIX) || REVISION >= 0xA s32 id; #else u8 ret = UNION_ROOM_SPAWN_NONE; @@ -1030,7 +1035,7 @@ static u8 LeaderUpdateGroupMembership(struct RfuPlayerList * list) { // No new incoming player data->playerList->players[i].groupScheduledAnim = UNION_ROOM_SPAWN_OUT; -#if REVISION >= 0xA +#if defined(BUGFIX) || REVISION >= 0xA #else ret = UNION_ROOM_SPAWN_OUT; #endif @@ -1041,7 +1046,7 @@ static u8 LeaderUpdateGroupMembership(struct RfuPlayerList * list) for (id = 0; id < RFU_CHILD_MAX; id++) TryAddIncomingPlayerToList(data->playerList->players, &data->incomingPlayerList->players[id], MAX_RFU_PLAYERS); -#if REVISION >= 0xA +#if defined(BUGFIX) || REVISION >= 0xA id = 1; { struct RfuPlayerList* playerList = data->playerList; @@ -1593,6 +1598,7 @@ static bool32 IsPartnerActivityAcceptable(u32 activity, u32 group) return TRUE; #ifdef UBFIX + // UBFIX: group <= ARRAY_COUNT may read one past sAcceptedActivityIds. if (group < ARRAY_COUNT(sAcceptedActivityIds)) #else if (group <= ARRAY_COUNT(sAcceptedActivityIds)) // UB: <= may access data outside the array @@ -1992,7 +1998,8 @@ static void Task_RunScriptAndFadeToActivity(u8 taskId) } break; case 2: -#if REVISION >= 0xA + // BUGFIX: Wait for link idle before activity start standby. +#if defined(BUGFIX) || REVISION >= 0xA if (IsLinkTaskFinished() && !gPaletteFade.active) #else if (!gPaletteFade.active) diff --git a/src/union_room_battle.c b/src/union_room_battle.c index f680c33ec40..f0ff0a312d6 100644 --- a/src/union_room_battle.c +++ b/src/union_room_battle.c @@ -179,7 +179,8 @@ void CB2_UnionRoomBattle(void) case 50: if (!UpdatePaletteFade()) { -#if REVISION >= 0xA + // BUGFIX: Defer standby until after fade; extra round syncs link before battle start. +#if REVISION >= 0xA || defined(BUGFIX) #else SetLinkStandbyCallback(); #endif @@ -189,7 +190,7 @@ void CB2_UnionRoomBattle(void) case 51: if (IsLinkTaskFinished()) { -#if REVISION >= 0xA +#if REVISION >= 0xA || defined(BUGFIX) SetLinkStandbyCallback(); gMain.state++; #else @@ -197,7 +198,7 @@ void CB2_UnionRoomBattle(void) #endif } break; -#if REVISION >= 0xA +#if REVISION >= 0xA || defined(BUGFIX) case 52: if (IsLinkTaskFinished()) { diff --git a/src/wireless_communication_status_screen.c b/src/wireless_communication_status_screen.c index 28b0993bc58..d23389d04c9 100644 --- a/src/wireless_communication_status_screen.c +++ b/src/wireless_communication_status_screen.c @@ -144,7 +144,8 @@ static const u8 sActivityGroupInfo[][3] = { {ACTIVITY_TRADE, GROUPTYPE_TRADE, 2}, {ACTIVITY_WONDER_CARD, GROUPTYPE_TOTAL, 2}, {ACTIVITY_WONDER_NEWS, GROUPTYPE_TOTAL, 2}, -#if REVISION >= 0xA + // UBFIX: NUM_GROUPTYPES is the array length, not a valid group_type / groupCounts index. +#if defined(UBFIX) || REVISION >= 0xA {ACTIVITY_POKEMON_JUMP, GROUPTYPE_TOTAL, 0}, {ACTIVITY_BERRY_CRUSH, GROUPTYPE_TOTAL, 0}, {ACTIVITY_BERRY_PICK, GROUPTYPE_TOTAL, 0}, @@ -156,7 +157,8 @@ static const u8 sActivityGroupInfo[][3] = { {ACTIVITY_SEARCH, GROUPTYPE_NONE, 0}, {ACTIVITY_SPIN_TRADE, GROUPTYPE_TRADE, 0}, {ACTIVITY_ITEM_TRADE, GROUPTYPE_NONE, 0}, -#if REVISION >= 0xA + // UBFIX: Same invalid index as Jump/Crush/Pick above. +#if defined(UBFIX) || REVISION >= 0xA {ACTIVITY_RECORD_CORNER, GROUPTYPE_TOTAL, 0}, #else {ACTIVITY_RECORD_CORNER, NUM_GROUPTYPES, 0}, @@ -385,58 +387,50 @@ static void WCSS_AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 * AddTextPrinterParameterized4(windowId, fontId, x, y, fontId == FONT_SMALL ? 0 : 1, 0, textColor, TEXT_SKIP_DRAW, str); } -static u32 CountPlayersInGroupAndGetActivity(struct RfuPlayer * player, u32 * groupCounts) +static u32 CountPlayersInGroupAndGetActivity(struct RfuPlayer *player, u32 *groupCounts) { -#if REVISION >= 0xA u32 activity = player->rfu.data.activity; + s32 i, j; + + #define group_activity(i) (sActivityGroupInfo[(i)][0]) + #define group_type(i) (sActivityGroupInfo[(i)][1]) + #define group_players(i) (sActivityGroupInfo[(i)][2]) + + // UBFIX: Bounds-check group_type before indexing groupCounts. +#if defined(UBFIX) || REVISION >= 0xA if (player->groupScheduledAnim == UNION_ROOM_SPAWN_IN) { - - u32 i = 0; - const u8 * group_info = &sActivityGroupInfo[0][0]; - const u8 * group_players = &group_info[2]; - const u8 * group_activity = group_info; - s32 offset = 0; - for (; i < ARRAY_COUNT(sActivityGroupInfo); i++) + for (i = 0; i < ARRAY_COUNT(sActivityGroupInfo); i++) { - const u8 * group_type = &group_info[1]; - u8 type = ((u8*)offset)[(u32)group_type]; // needed to match, but nobody would write this??? - if (type < MAX_LINK_PLAYERS && activity == *group_activity) + u8 type = group_type(i); + + if (type < NUM_GROUPTYPES && activity == group_activity(i)) { - u8 k = *group_players; - if (k == 0) - { - s32 j; - for (j = 0; j < RFU_CHILD_MAX; j++) - if (player->rfu.data.partnerInfo[j] != 0) k++; - k++; - } - groupCounts[type] += k; - break; + u8 k = group_players(i); + if (k == 0) + { + for (j = 0; j < RFU_CHILD_MAX; j++) + if (player->rfu.data.partnerInfo[j] != 0) + k++; + k++; + } + + groupCounts[type] += k; + break; } - group_players += sizeof(sActivityGroupInfo[0]); - group_activity += sizeof(sActivityGroupInfo[0]); - offset += (u8)sizeof(sActivityGroupInfo[0]); } - } #else - u32 activity = player->rfu.data.activity; - s32 i, j, k; - - #define group_activity(i) (sActivityGroupInfo[(i)][0]) - #define group_type(i) (sActivityGroupInfo[(i)][1]) - #define group_players(i) (sActivityGroupInfo[(i)][2]) - for (i = 0; i < ARRAY_COUNT(sActivityGroupInfo); i++) { if (activity == group_activity(i) && player->groupScheduledAnim == UNION_ROOM_SPAWN_IN) { if (group_players(i) == 0) { - k = 0; - for (j = 0; j < RFU_CHILD_MAX; j++) - if (player->rfu.data.partnerInfo[j] != 0) k++; + s32 k; + for (k = 0, j = 0; j < RFU_CHILD_MAX; j++) + if (player->rfu.data.partnerInfo[j] != 0) + k++; k++; groupCounts[group_type(i)] += k; } @@ -446,14 +440,13 @@ static u32 CountPlayersInGroupAndGetActivity(struct RfuPlayer * player, u32 * gr } } } +#endif #undef group_activity #undef group_type #undef group_players -#endif return activity; - } static bool32 HaveCountsChanged(const u32 * curCounts, const u32 * prevCounts) @@ -471,52 +464,47 @@ static bool32 HaveCountsChanged(const u32 * curCounts, const u32 * prevCounts) static bool32 UpdateCommunicationCounts(u32 * groupCounts, u32 * prevGroupCounts, u32 * activities, u8 taskId) { - bool32 activitiesUpdated = FALSE; + bool32 activitiesChanged = FALSE; u32 groupCountBuffer[NUM_GROUPTYPES] = {0, 0, 0, 0}; - struct WirelessLink_Group * group = (void *)gTasks[taskId].data; - s32 i; + struct WirelessLink_Group *group = (void *)gTasks[taskId].data; + s32 i, activity; for (i = 0; i < NUM_TASK_DATA; i++) { - u32 activity = CountPlayersInGroupAndGetActivity(&group->playerList->players[i], groupCountBuffer); + activity = CountPlayersInGroupAndGetActivity(&group->playerList->players[i], groupCountBuffer); if (activity != activities[i]) { activities[i] = activity; - activitiesUpdated = TRUE; + activitiesChanged = TRUE; } } -#if REVISION >= 0xA if (HaveCountsChanged(groupCountBuffer, prevGroupCounts)) -#else - if (!HaveCountsChanged(groupCountBuffer, prevGroupCounts)) - { - if (activitiesUpdated == TRUE) - return TRUE; - else - return FALSE; - } -#endif { - memcpy(groupCounts, groupCountBuffer, sizeof(groupCountBuffer)); + memcpy(groupCounts, groupCountBuffer, sizeof(groupCountBuffer)); memcpy(prevGroupCounts, groupCountBuffer, sizeof(groupCountBuffer)); groupCounts[GROUPTYPE_TOTAL] = groupCounts[GROUPTYPE_TRADE] + groupCounts[GROUPTYPE_BATTLE] + groupCounts[GROUPTYPE_UNION] - #if defined(BUGFIX) || REVISION >= 0xA - + groupCounts[GROUPTYPE_TOTAL] // Missing count for activities not in above groups - #endif - ; + // BUGFIX: Include activities counted in GROUPTYPE_TOTAL bucket. +#if defined(BUGFIX) || REVISION >= 0xA + + groupCounts[GROUPTYPE_TOTAL] +#endif + ; #if REVISION >= 0xA - activitiesUpdated = TRUE; + activitiesChanged = TRUE; +#else + return TRUE; #endif } #if REVISION >= 0xA - return activitiesUpdated; + return activitiesChanged; #else - return TRUE; + if (activitiesChanged == TRUE) + return TRUE; + return FALSE; #endif }