code:
#include "global.h"
#include "contest.h"
#include "decompress.h"
#include "event_data.h"
#include "link.h"
#include "pokemon.h"
#include "random.h"
#include "task.h"
#include "contest_link.h"

static void Task_LinkContest_StartInitFlags(u8);
static void Task_LinkContest_InitFlags(u8);

#define tState         data[0]
#define tDelayTimer    data[1]
#define tCategories(i) data[(i) + 1]
#define tLeaderIds(i)  data[(i) + 5]
#define tCategory      data[9]
#define tTimer         data[11]
#define tStandbyState  data[12]

bool32 LinkContest_SendBlock(void *src, u16 size)
{
    memcpy(gDecompressionBuffer, src, size);
    if (SendBlock(bitmask_all_link_players_but_self(), gDecompressionBuffer, size))
        return TRUE;
    else
        return FALSE;
}

bool8 LinkContest_GetBlockReceived(u8 flag)
{
    u8 mask = (1 << flag);
    if (!(GetBlockReceivedStatus() & mask))
    {
        return FALSE;
    }
    else
    {
        ResetBlockReceivedFlag(flag);
        return TRUE;
    }
}

bool8 LinkContest_GetBlockReceivedFromAllPlayers(void)
{
    if (GetBlockReceivedStatus() == GetLinkPlayerCountAsBitFlags())
    {
        ResetBlockReceivedFlags();
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

void Task_LinkContest_Init(u8 taskId)
{
    u8 i;

    for (i = 0; i < CONTESTANT_COUNT; i++)
        gBlockRecvBuffer[i][0] = 0xFF;

    gTasks[taskId].tState = 0;
    gTasks[taskId].func = Task_LinkContest_StartInitFlags;
}

static void Task_LinkContest_StartInitFlags(u8 taskId)
{
    gTasks[taskId].func = Task_LinkContest_InitFlags;
}

static void Task_LinkContest_InitFlags(u8 taskId)
{
    int i;

    if (!gReceivedRemoteLinkPlayers)
        return;

    gContestPlayerMonIndex = GetMultiplayerId();
    gNumLinkContestPlayers = GetLinkPlayerCount();
    gLinkContestFlags = LINK_CONTEST_FLAG_IS_LINK;
    if (gWirelessCommType == 1)
        gLinkContestFlags = LINK_CONTEST_FLAG_IS_LINK | LINK_CONTEST_FLAG_IS_WIRELESS;

    // Get number of players using Emerald/FRLG
    for (i = 0; i < gNumLinkContestPlayers && (u32)(gLinkPlayers[i].version & 0xFF) - 1 > VERSION_RUBY - 1; i++)
        ;

    if (i < gNumLinkContestPlayers)
        gLinkContestFlags |= LINK_CONTEST_FLAG_HAS_RS_PLAYER;

    SwitchTaskToFollowupFunc(taskId);
}

bool32 LinkContest_TryLinkStandby(s16 *state)
{
    // Skip standby for RS cabled links
    if (gLinkContestFlags & LINK_CONTEST_FLAG_HAS_RS_PLAYER)
        return TRUE;

    switch (*state)
    {
    case 0:
        if (IsLinkTaskFinished())
        {
            SetLinkStandbyCallback();
            (*state)++;
        }
        return FALSE;
    case 1:
        (*state)++;
        return FALSE;
    default:
        if (IsLinkTaskFinished() != TRUE)
            return FALSE;
        else
            return TRUE;
    }
}

void Task_LinkContest_CommunicateMonsRS(u8 taskId)
{
    int i;

    if (!LinkContest_TryLinkStandby(&gTasks[taskId].tStandbyState))
        return;

    switch (gTasks[taskId].tState)
    {
    case 0:
        // Send players mon
        if (GetMultiplayerId() == 0)
        {
            if (IsLinkTaskFinished())
            {
                memcpy(gBlockSendBuffer, &gContestMons[gContestPlayerMonIndex], sizeof(struct ContestPokemon));
                gTasks[taskId].tState = 10;
            }
        }
        else
        {
            memcpy(gBlockSendBuffer, &gContestMons[gContestPlayerMonIndex], sizeof(struct ContestPokemon));
            gTasks[taskId].tState = 1;
        }
        break;
    case 1:
        // Wait for other players data
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            for (i = 0; i < gNumLinkContestPlayers; i++)
            {
                memcpy(&gContestMons[i], gBlockRecvBuffer[i], sizeof(struct ContestPokemon));
                StripPlayerAndMonNamesForLinkContest(&gContestMons[i], gLinkPlayers[i].language);
            }

            gTasks[taskId].tState++;
        }
        break;
    case 10:
        // Only if leader. Request other players data
        if (++gTasks[taskId].tTimer > 300)
        {
            SendBlockRequest(2);
            gTasks[taskId].tState = 1;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        gTasks[taskId].tTimer = 0;
        gTasks[taskId].tStandbyState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateRngRS(u8 taskId)
{
    switch (gTasks[taskId].tState)
    {
    case 0:
        if (GetMultiplayerId() == 0)
        {
            if (IsLinkTaskFinished() && LinkContest_SendBlock(&gRngValue, sizeof(gRngValue)) == TRUE)
                gTasks[taskId].tState++;
        }
        else
        {
            gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceived(0))
        {
            memcpy(&gRngValue, gBlockRecvBuffer[0], sizeof(gRngValue));
            memcpy(&gContestRngValue, gBlockRecvBuffer[0], sizeof(gContestRngValue));
            gTasks[taskId].tState++;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateCategoryRS(u8 taskId)
{
    int i;

    if (!LinkContest_TryLinkStandby(&gTasks[taskId].tStandbyState))
        return;

    switch (gTasks[taskId].tState)
    {
    case 0:
        gBlockSendBuffer[0] = gTasks[taskId].tCategory;
        if (GetMultiplayerId() == 0)
        {
            if (IsLinkTaskFinished())
                gTasks[taskId].tState = 10;
        }
        else
        {
            gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            for (i = 0; i < gNumLinkContestPlayers; i++)
                gTasks[taskId].tCategories(i) = gBlockRecvBuffer[i][0];

            gTasks[taskId].tState++;
        }
        break;
    case 10:
        if (++gTasks[taskId].tTimer > 10)
        {
            SendBlockRequest(2);
            gTasks[taskId].tState = 1;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        gTasks[taskId].tTimer = 0;
        gTasks[taskId].tStandbyState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateMonIdxs(u8 taskId)
{
    switch (gTasks[taskId].tState)
    {
    case 0:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(&gContestPlayerMonIndex, sizeof(gContestPlayerMonIndex)) == TRUE)
                gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
            gTasks[taskId].tState++;
        break;
    default:
        gTasks[taskId].tState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateMoveSelections(u8 taskId)
{
    int i;

    switch (gTasks[taskId].tState)
    {
    case 0:
        if (IsLinkTaskFinished())
        {
            // Send player's move selection
            if (LinkContest_SendBlock(&eContestantStatus[gContestPlayerMonIndex].currMove, sizeof(eContestantStatus[gContestPlayerMonIndex].currMove)) == TRUE)
                gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            // Receive partners' move selections
            for (i = 0; i < gNumLinkContestPlayers; i++)
                eContestantStatus[i].currMove = gBlockRecvBuffer[i][0];

            gTasks[taskId].tState++;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateFinalStandings(u8 taskId)
{
    switch (gTasks[taskId].tState)
    {
    case 0:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestMonTotalPoints, sizeof(gContestMonTotalPoints)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestMonTotalPoints, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(gContestMonTotalPoints));
            gTasks[taskId].tState++;
        }
        break;
    case 2:
    case 5:
    case 8:
    case 11:
        if (gTasks[taskId].tDelayTimer++ > 10)
        {
            gTasks[taskId].tDelayTimer = 0;
            gTasks[taskId].tState++;
        }
        break;
    case 3:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestMonAppealPointTotals, sizeof(gContestMonAppealPointTotals)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 4:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestMonAppealPointTotals, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(gContestMonAppealPointTotals));
            gTasks[taskId].tState++;
        }
        break;
    case 6:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestMonRound2Points, sizeof(gContestMonRound2Points)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 7:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestMonRound2Points, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(gContestMonRound2Points));
            gTasks[taskId].tState++;
        }
        break;
    case 9:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestFinalStandings, sizeof(gContestFinalStandings)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 10:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestFinalStandings, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(gContestFinalStandings));
            gTasks[taskId].tState++;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateAppealsState(u8 taskId)
{
    switch (gTasks[taskId].tState)
    {
    case 0:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(eContestantStatus, CONTESTANT_COUNT * sizeof(struct ContestantStatus)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(eContestantStatus, gBlockRecvBuffer[gContestLinkLeaderIndex], CONTESTANT_COUNT * sizeof(struct ContestantStatus));
            gTasks[taskId].tState++;
        }
        break;
    case 2:
    case 5:
    case 8:
    case 11:
        if (gTasks[taskId].tDelayTimer++ > 10)
        {
            gTasks[taskId].tDelayTimer = 0;
            gTasks[taskId].tState++;
        }
        break;
    case 3:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestResources->appealResults, sizeof(struct ContestAppealMoveResults)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 4:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestResources->appealResults, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(struct ContestAppealMoveResults));
            gTasks[taskId].tState++;
        }
        break;
    case 6:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestResources->excitement, sizeof(struct ContestExcitement)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 7:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestResources->excitement, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(struct ContestExcitement));
            gTasks[taskId].tState++;
        }
        break;
    case 9:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestantTurnOrder, sizeof(gContestantTurnOrder)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 10:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestantTurnOrder, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(gContestantTurnOrder));
            gTasks[taskId].tState++;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateLeaderIdsRS(u8 taskId)
{
    int i;

    if (!LinkContest_TryLinkStandby(&gTasks[taskId].tStandbyState))
        return;

    switch (gTasks[taskId].tState)
    {
    case 0:
        gBlockSendBuffer[0] = 0x6E;
        if (GetMultiplayerId() == 0)
        {
            if (IsLinkTaskFinished())
                gTasks[taskId].tState = 10;
        }
        else
        {
            gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            for (i = 0; i < CONTESTANT_COUNT; i++)
                gTasks[taskId].tLeaderIds(i) = gBlockRecvBuffer[i][0];

            gTasks[taskId].tState++;
        }
        break;
    case 10:
        if (++gTasks[taskId].tTimer > 10)
        {
            SendBlockRequest(2);
            gTasks[taskId].tState = 1;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        gTasks[taskId].tTimer = 0;
        gTasks[taskId].tStandbyState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateRound1Points(u8 taskId)
{
    if (!LinkContest_TryLinkStandby(&gTasks[taskId].tStandbyState))
        return;

    switch (gTasks[taskId].tState)
    {
    case 0:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestMonRound1Points, sizeof(gContestMonRound1Points)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestMonRound1Points, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(gContestMonRound1Points));
            gTasks[taskId].tState++;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        gTasks[taskId].tStandbyState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}

void Task_LinkContest_CommunicateTurnOrder(u8 taskId)
{
    if (!LinkContest_TryLinkStandby(&gTasks[taskId].tStandbyState))
        return;

    switch (gTasks[taskId].tState)
    {
    case 0:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(gContestantTurnOrder, sizeof(gContestantTurnOrder)) == 1)
                gTasks[taskId].tState++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            memcpy(gContestantTurnOrder, gBlockRecvBuffer[gContestLinkLeaderIndex], sizeof(gContestantTurnOrder));
            gTasks[taskId].tState++;
        }
        break;
    default:
        gTasks[taskId].tState = 0;
        gTasks[taskId].tStandbyState = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    }
}


code:
#include "global.h"
#include "contest.h"
#include "contest_link.h"
#include "event_data.h"
#include "link.h"
#include "random.h"
#include "task.h"

/*
    The functions in this file handle preliminary communication
    for Emerald-only link contests. If the link contest has an RS
    player linked, none of these functions are used.

    The equivalent functions for RS-linked contests are spread
    between contest_link.c and contest_util.c, and are suffixed RS
    instead of Em
*/

static void Task_LinkContest_CommunicateMonsEm(u8);
static void Task_LinkContest_StartCommunicateRngEm(u8);
static void Task_LinkContest_CommunicateRngEm(u8);
static void Task_LinkContest_StartCommunicateLeaderIdsEm(u8);
static void Task_LinkContest_CommunicateLeaderIdsEm(u8);
static void Task_LinkContest_StartCommunicateCategoryEm(u8);
static void Task_LinkContest_CommunicateCategoryEm(u8);
static void Task_LinkContest_SetUpContestEm(u8);
static void Task_LinkContest_CommunicateAIMonsEm(u8);
static void Task_LinkContest_CalculateRound1Em(u8);
static void Task_LinkContest_CalculateTurnOrderEm(u8);

#define tCategory data[9]

void Task_LinkContest_StartCommunicationEm(u8 taskId)
{
    int gameCleared;

    switch (gTasks[taskId].tCategory)
    {
    case CONTEST_CATEGORY_COOL:
        gHighestRibbonRank = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_COOL_RIBBON);
        break;
    case CONTEST_CATEGORY_BEAUTY:
        gHighestRibbonRank = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_BEAUTY_RIBBON);
        break;
    case CONTEST_CATEGORY_CUTE:
        gHighestRibbonRank = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_CUTE_RIBBON);
        break;
    case CONTEST_CATEGORY_SMART:
        gHighestRibbonRank = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_SMART_RIBBON);
        break;
    case CONTEST_CATEGORY_TOUGH:
    default:
        gHighestRibbonRank = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_TOUGH_RIBBON);
        break;
    }

    gContestMons[gContestPlayerMonIndex].highestRank = gHighestRibbonRank;
    gameCleared = FlagGet(FLAG_SYS_GAME_CLEAR) > 0;
    gContestMons[gContestPlayerMonIndex].gameCleared = gameCleared;
    SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateMonsEm, Task_LinkContest_StartCommunicateRngEm);
}

static void Task_LinkContest_StartCommunicateRngEm(u8 taskId)
{
    SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateRngEm, Task_LinkContest_StartCommunicateLeaderIdsEm);
}

static void Task_LinkContest_StartCommunicateLeaderIdsEm(u8 taskId)
{
    SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateLeaderIdsEm, Task_LinkContest_StartCommunicateCategoryEm);
}

static void Task_LinkContest_StartCommunicateCategoryEm(u8 taskId)
{
    SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateCategoryEm, Task_LinkContest_SetUpContestEm);
}

static void Task_LinkContest_SetUpContestEm(u8 taskId)
{
    u8 i;
    u8 rank;
    int gameCleared;
    u8 categories[CONTESTANT_COUNT];
    u8 leaderIds[CONTESTANT_COUNT];

    memset(categories, 0, sizeof(categories));
    memset(leaderIds, 0, sizeof(leaderIds));

    for (i = 0; i < gNumLinkContestPlayers; i++)
        categories[i] = gTasks[taskId].data[i + 1];

    // Ensure all players are doing the same category
    for (i = 0; i < gNumLinkContestPlayers && categories[0] == categories[i]; i++)
        ;

    if (i == gNumLinkContestPlayers)
        gSpecialVar_0x8004 = FALSE; // Category choices the same
    else
        gSpecialVar_0x8004 = TRUE; // Category choices differ

    for (i = 0; i < gNumLinkContestPlayers; i++)
        leaderIds[i] = gTasks[taskId].data[i + 5];

    // If < 4 players and player is leader, set AI contestants based on rank and game clear
    if (gNumLinkContestPlayers != CONTESTANT_COUNT && GetMultiplayerId() == 0)
    {
        rank = gContestMons[0].highestRank;
        for (i = 1; i < gNumLinkContestPlayers; i++)
        {
            if (rank < gContestMons[i].highestRank)
                rank = gContestMons[i].highestRank;
        }

        if (rank)
            rank--;

        gameCleared = TRUE;
        for (i = 0; i < gNumLinkContestPlayers; i++)
        {
            if (!gContestMons[i].gameCleared)
            {
                gameCleared = FALSE;
                break;
            }
        }

        SetLinkAIContestants(categories[0], rank, gameCleared);
    }

    // Assign link leader. After initial communication all players will read data only from them
    gContestLinkLeaderIndex = LinkContest_GetLeaderIndex(leaderIds);

    if (gNumLinkContestPlayers < CONTESTANT_COUNT)
        SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateAIMonsEm, Task_LinkContest_CalculateRound1Em);
    else
        gTasks[taskId].func = Task_LinkContest_CalculateRound1Em;
}

static void Task_LinkContest_CalculateRound1Em(u8 taskId)
{
    CalculateRound1Points(gSpecialVar_ContestCategory);
    SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateRound1Points, Task_LinkContest_CalculateTurnOrderEm);
}

static void Task_LinkContest_CalculateTurnOrderEm(u8 taskId)
{
    SortContestants(FALSE);
    SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateTurnOrder, Task_LinkContest_FinalizeConnection);
}

static void Task_LinkContest_CommunicateMonsEm(u8 taskId)
{
    int i;

    if (!LinkContest_TryLinkStandby(&gTasks[taskId].data[12]))
        return;

    switch (gTasks[taskId].data[0])
    {
    default:
        gTasks[taskId].data[0] = 0;
        gTasks[taskId].data[12] = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    case 0:
        if (IsLinkTaskFinished())
        {
            if (LinkContest_SendBlock(&gContestMons[gContestPlayerMonIndex], sizeof(struct ContestPokemon)) == 1)
                gTasks[taskId].data[0]++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            for (i = 0; i < gNumLinkContestPlayers; i++)
            {
                memcpy(&gContestMons[i], gBlockRecvBuffer[i], sizeof(struct ContestPokemon));
                StripPlayerAndMonNamesForLinkContest(&gContestMons[i], gLinkPlayers[i].language);
            }

            gTasks[taskId].data[0]++;
        }
        break;
    }
}

static void Task_LinkContest_CommunicateRngEm(u8 taskId)
{
    if (!LinkContest_TryLinkStandby(&gTasks[taskId].data[12]))
        return;

    switch (gTasks[taskId].data[0])
    {
    default:
        gTasks[taskId].data[0] = 0;
        gTasks[taskId].data[12] = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    case 0:
        if (GetMultiplayerId() == 0)
        {
            // Only the leader sends the RNG seed
            if (!IsLinkTaskFinished())
                return;

            if (LinkContest_SendBlock(&gRngValue, sizeof(gRngValue)) == 1)
                gTasks[taskId].data[0]++;
        }
        else
        {
            // Other link members skip to waiting
            gTasks[taskId].data[0]++;
        }
        break;
    case 1:
        // Wait to receive RNG data
        if (LinkContest_GetBlockReceived(0))
        {
            memcpy(&gRngValue, gBlockRecvBuffer[0], sizeof(gRngValue));
            memcpy(&gContestRngValue, gBlockRecvBuffer[0], sizeof(gContestRngValue));
            gTasks[taskId].data[0]++;
        }
        break;
    }
}

static void Task_LinkContest_CommunicateLeaderIdsEm(u8 taskId)
{
    int i;
    u16 data[CONTESTANT_COUNT];
    u16 leaderId;

    if (!LinkContest_TryLinkStandby(&gTasks[taskId].data[12]))
        return;

    switch (gTasks[taskId].data[0])
    {
    default:
        gTasks[taskId].data[0] = 0;
        gTasks[taskId].data[12] = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    case 0:
        if (IsLinkTaskFinished())
        {
            leaderId = 0x6E;
            if (LinkContest_SendBlock(&leaderId, sizeof(leaderId)) == 1)
                gTasks[taskId].data[0]++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            for (i = 0; i < gNumLinkContestPlayers; i++)
            {
                data[i] = gBlockRecvBuffer[i][0];
                gTasks[taskId].data[i + 5] = data[i];
            }

            gTasks[taskId].data[0]++;
        }
        break;
    }
}

static void Task_LinkContest_CommunicateCategoryEm(u8 taskId)
{
    int i;
    u16 data[CONTESTANT_COUNT];
    u16 category;

    if (!LinkContest_TryLinkStandby(&gTasks[taskId].data[12]))
        return;

    switch (gTasks[taskId].data[0])
    {
    default:
        gTasks[taskId].data[0] = 0;
        gTasks[taskId].data[12] = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    case 0:
        if (IsLinkTaskFinished())
        {
            category = gTasks[taskId].tCategory;
            if (LinkContest_SendBlock(&category, sizeof(category)) == 1)
                gTasks[taskId].data[0]++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceivedFromAllPlayers())
        {
            for (i = 0; i < gNumLinkContestPlayers; i++)
            {
                data[i] = gBlockRecvBuffer[i][0];
                gTasks[taskId].data[i + 1] = data[i];
            }

            gTasks[taskId].data[0]++;
        }
        break;
    }
}

static void Task_LinkContest_CommunicateAIMonsEm(u8 taskId)
{
    int i;

    if (!LinkContest_TryLinkStandby(&gTasks[taskId].data[12]))
        return;

    switch (gTasks[taskId].data[0])
    {
    default:
        gTasks[taskId].data[0] = 0;
        gTasks[taskId].data[12] = 0;
        SwitchTaskToFollowupFunc(taskId);
        break;
    case 0:
        if (GetMultiplayerId() == 0)
        {
            if (!IsLinkTaskFinished())
                return;

            if (LinkContest_SendBlock(&gContestMons[gNumLinkContestPlayers], (CONTESTANT_COUNT - gNumLinkContestPlayers) * sizeof(struct ContestPokemon)) == 1)
                gTasks[taskId].data[0]++;
        }
        else
        {
            gTasks[taskId].data[0]++;
        }
        break;
    case 1:
        if (LinkContest_GetBlockReceived(0))
        {
            memcpy(&gContestMons[gNumLinkContestPlayers], gBlockRecvBuffer[0], (CONTESTANT_COUNT - gNumLinkContestPlayers) * sizeof(struct ContestPokemon));
            for (i = gNumLinkContestPlayers; i < CONTESTANT_COUNT; i++)
                StripPlayerAndMonNamesForLinkContest(&gContestMons[i], gLinkPlayers[0].language);

            gTasks[taskId].data[0]++;
        }
        break;
    }
}

repo_name: Lunos/pokeemerald

license: mit


code:
#include "global.h"
#include "malloc.h"
#include "battle.h"
#include "battle_gfx_sfx_util.h"
#include "bg.h"
#include "contest.h"
#include "contest_painting.h"
#include "data.h"
#include "decompress.h"
#include "gpu_regs.h"
#include "image_processing_effects.h"
#include "international_string_util.h"
#include "main.h"
#include "lilycove_lady.h"
#include "palette.h"
#include "random.h"
#include "scanline_effect.h"
#include "string_util.h"
#include "strings.h"
#include "text.h"
#include "window.h"
#include "constants/rgb.h"

// IWRAM common
u16 (*gContestMonPixels)[][32];
struct ImageProcessingContext gImageProcessingContext;
struct ContestWinner *gContestPaintingWinner;
u16 *gContestPaintingMonPalette;

// IWRAM bss
static u8 gContestPaintingState;
static u16 gContestPaintingMosaicVal;
static u16 gContestPaintingFadeCounter;
static bool8 gUnknown_030011F6;
static u8 gContestPaintingWindowId;

static void ShowContestPainting(void);
static void HoldContestPainting(void);
static void InitContestPaintingWindow(void);
static void InitContestPaintingBg(void);
static void InitContestPaintingVars(bool8);
static void CreateContestPaintingPicture(u8, u8);
static void PrintContestPaintingCaption(u8, u8);
static void VBlankCB_ContestPainting(void);
static void _InitContestMonPixels(u8 *spriteGfx, u16 *palette, u16 (*destPixels)[64][64]);

extern const u8 gUnknown_0827EA0C[];
extern const u8 gContestCoolness[];
extern const u8 gContestBeauty[];
extern const u8 gContestCuteness[];
extern const u8 gContestSmartness[];
extern const u8 gContestToughness[];
extern const u8 gContestRankNormal[];
extern const u8 gContestRankSuper[];
extern const u8 gContestRankHyper[];
extern const u8 gContestRankMaster[];
extern const u8 gContestLink[];
extern const u8 gContestPaintingCool1[];
extern const u8 gContestPaintingCool2[];
extern const u8 gContestPaintingCool3[];
extern const u8 gContestPaintingBeauty1[];
extern const u8 gContestPaintingBeauty2[];
extern const u8 gContestPaintingBeauty3[];
extern const u8 gContestPaintingCute1[];
extern const u8 gContestPaintingCute2[];
extern const u8 gContestPaintingCute3[];
extern const u8 gContestPaintingSmart1[];
extern const u8 gContestPaintingSmart2[];
extern const u8 gContestPaintingSmart3[];
extern const u8 gContestPaintingTough1[];
extern const u8 gContestPaintingTough2[];
extern const u8 gContestPaintingTough3[];

const u16 gPictureFramePalettes[] = INCBIN_U16("graphics/picture_frame/bg.gbapal");
const u8 gPictureFrameTiles_0[] = INCBIN_U8("graphics/picture_frame/frame0.4bpp.rl");
const u8 gPictureFrameTiles_1[] = INCBIN_U8("graphics/picture_frame/frame1.4bpp.rl");
const u8 gPictureFrameTiles_2[] = INCBIN_U8("graphics/picture_frame/frame2.4bpp.rl");
const u8 gPictureFrameTiles_3[] = INCBIN_U8("graphics/picture_frame/frame3.4bpp.rl");
const u8 gPictureFrameTiles_4[] = INCBIN_U8("graphics/picture_frame/frame4.4bpp.rl");
const u8 gPictureFrameTiles_5[] = INCBIN_U8("graphics/picture_frame/frame5.4bpp.rl");
const u8 gPictureFrameTilemap_0[] = INCBIN_U8("graphics/picture_frame/frame0_map.bin.rl");
const u8 gPictureFrameTilemap_1[] = INCBIN_U8("graphics/picture_frame/frame1_map.bin.rl");
const u8 gPictureFrameTilemap_2[] = INCBIN_U8("graphics/picture_frame/frame2_map.bin.rl");
const u8 gPictureFrameTilemap_3[] = INCBIN_U8("graphics/picture_frame/frame3_map.bin.rl");
const u8 gPictureFrameTilemap_4[] = INCBIN_U8("graphics/picture_frame/frame4_map.bin.rl");
const u8 gPictureFrameTilemap_5[] = INCBIN_U8("graphics/picture_frame/frame5_map.bin.rl");

static const u8 *const sContestCategoryNames_Unused[] =
{
    gContestCoolness,
    gContestBeauty,
    gContestCuteness,
    gContestSmartness,
    gContestToughness,
};

static const u8 *const sContestRankNames[] =
{
    gContestRankNormal,
    gContestRankSuper,
    gContestRankHyper,
    gContestRankMaster,
    gContestLink,
};

static const struct BgTemplate sContestPaintingBgTemplates[] =
{
    {
        .bg = 1,
        .charBaseIndex = 1,
        .mapBaseIndex = 10,
        .screenSize = 0,
        .paletteMode = 0,
        .priority = 1,
        .baseTile = 0,
    },
};

static const struct WindowTemplate sContestPaintingWindowTemplate =
{
    .bg = 1,
    .tilemapLeft = 2,
    .tilemapTop = 14,
    .width = 26,
    .height = 4,
    .paletteNum = 15,
    .baseBlock = 1,
};

static const u8 *const sContestPaintingDescriptionPointers[] =
{
    gContestPaintingCool1,
    gContestPaintingCool2,
    gContestPaintingCool3,
    gContestPaintingBeauty1,
    gContestPaintingBeauty2,
    gContestPaintingBeauty3,
    gContestPaintingCute1,
    gContestPaintingCute2,
    gContestPaintingCute3,
    gContestPaintingSmart1,
    gContestPaintingSmart2,
    gContestPaintingSmart3,
    gContestPaintingTough1,
    gContestPaintingTough2,
    gContestPaintingTough3,
};

static const struct OamData sContestPaintingMonOamData =
{
    .y = 0,
    .affineMode = ST_OAM_AFFINE_OFF,
    .objMode = ST_OAM_OBJ_NORMAL,
    .mosaic = TRUE,
    .bpp = ST_OAM_8BPP,
    .shape = SPRITE_SHAPE(64x64),
    .x = 0,
    .size = SPRITE_SIZE(64x64),
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
};

const u16 gUnknown_085B0838[] = {RGB(0, 0, 0), RGB(0, 0, 0)};

void SetContestWinnerForPainting(int contestWinnerId)
{
    // probably fakematching
    u8 *ptr1 = &gUnknown_02039F5D;
    u8 *ptr2 = &gUnknown_02039F5C;
	gCurContestWinner = gSaveBlock1Ptr->contestWinners[contestWinnerId - 1];
	*ptr1 = contestWinnerId - 1;
	*ptr2 = FALSE;
}

void CB2_ContestPainting(void)
{
    ShowContestPainting();
}

static void CB2_HoldContestPainting(void)
{
    HoldContestPainting();
    RunTextPrinters();
    UpdatePaletteFade();
}

static void CB2_QuitContestPainting(void)
{
    SetMainCallback2(gMain.savedCallback);
    FREE_AND_SET_NULL(gContestPaintingMonPalette);
    FREE_AND_SET_NULL(gContestMonPixels);
    RemoveWindow(gContestPaintingWindowId);
    Free(GetBgTilemapBuffer(1));
    FreeMonSpritesGfx();
}

static void ShowContestPainting(void)
{
    switch (gMain.state)
    {
    case 0:
        ScanlineEffect_Stop();
        SetVBlankCallback(NULL);
        AllocateMonSpritesGfx();
        gContestPaintingWinner = &gCurContestWinner;
        InitContestPaintingVars(1);
        InitContestPaintingBg();
        gMain.state++;
        break;
    case 1:
        ResetPaletteFade();
        DmaFillLarge32(3, 0, (void *)BG_VRAM, 0x18000, 0x1000);
        ResetSpriteData();
        gMain.state++;
        break;
    case 2:
        SeedRng(gMain.vblankCounter1);
        InitKeys();
        InitContestPaintingWindow();
        gMain.state++;
        break;
    case 3:
        CreateContestPaintingPicture(gUnknown_02039F5D, gUnknown_02039F5C);
        gMain.state++;
        break;
    case 4:
        PrintContestPaintingCaption(gUnknown_02039F5D, gUnknown_02039F5C);
        LoadPalette(gUnknown_085B0838, 0, 1 * 2);
        DmaClear32(3, PLTT, PLTT_SIZE);
        BeginFastPaletteFade(2);
        SetVBlankCallback(VBlankCB_ContestPainting);
        gContestPaintingState = 0;
        SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON | DISPCNT_BG1_ON | DISPCNT_OBJ_ON);
        SetMainCallback2(CB2_HoldContestPainting);
        break;
    }
}

static void HoldContestPainting(void)
{
    switch (gContestPaintingState)
    {
    case 0:
        if (!gPaletteFade.active)
            gContestPaintingState = 1;
        if (gUnknown_030011F6 && gContestPaintingFadeCounter)
            gContestPaintingFadeCounter--;
        break;
    case 1:
        if ((JOY_NEW(A_BUTTON)) || (JOY_NEW(B_BUTTON)))
        {
            gContestPaintingState++;
            BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
        }

        if (gUnknown_030011F6)
            gContestPaintingFadeCounter = 0;
        break;
    case 2:
        if (!gPaletteFade.active)
            SetMainCallback2(CB2_QuitContestPainting);
        if (gUnknown_030011F6 && gContestPaintingFadeCounter < 30)
            gContestPaintingFadeCounter++;
        break;
    }
}

static void InitContestPaintingWindow(void)
{
    ResetBgsAndClearDma3BusyFlags(0);
    InitBgsFromTemplates(0, sContestPaintingBgTemplates, ARRAY_COUNT(sContestPaintingBgTemplates));
    ChangeBgX(1, 0, 0);
    ChangeBgY(1, 0, 0);
    SetBgTilemapBuffer(1, AllocZeroed(BG_SCREEN_SIZE));
    gContestPaintingWindowId = AddWindow(&sContestPaintingWindowTemplate);
    DeactivateAllTextPrinters();
    FillWindowPixelBuffer(gContestPaintingWindowId, PIXEL_FILL(0));
    PutWindowTilemap(gContestPaintingWindowId);
    CopyWindowToVram(gContestPaintingWindowId, 3);
    ShowBg(1);
}

static void PrintContestPaintingCaption(u8 contestType, bool8 arg1)
{
    int x;
    u8 category;

    if (arg1 == TRUE)
        return;

    category = gContestPaintingWinner->contestCategory;
    if (contestType < 8)
    {
        BufferContestName(gStringVar1, category);
        StringAppend(gStringVar1, gText_Space);
        StringAppend(gStringVar1, sContestRankNames[gContestPaintingWinner->contestRank]);
        StringCopy(gStringVar2, gContestPaintingWinner->trainerName);
        sub_81DB5AC(gStringVar2);
        StringCopy(gStringVar3, gContestPaintingWinner->monName);
        StringExpandPlaceholders(gStringVar4, gUnknown_0827EA0C);
    }
    else
    {
        StringCopy(gStringVar1, gContestPaintingWinner->monName);
        StringExpandPlaceholders(gStringVar4, sContestPaintingDescriptionPointers[category]);
    }

    x = GetStringCenterAlignXOffset(1, gStringVar4, 208);
    AddTextPrinterParameterized(gContestPaintingWindowId, 1, gStringVar4, x, 1, 0, 0);
    CopyBgTilemapBufferToVram(1);
}

static void InitContestPaintingBg(void)
{
    SetGpuReg(REG_OFFSET_DISPCNT, 0);
    REG_IE |= INTR_FLAG_VBLANK;
    SetGpuReg(REG_OFFSET_BG0CNT, BGCNT_PRIORITY(2) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(12) | BGCNT_MOSAIC | BGCNT_16COLOR | BGCNT_TXT256x256);
    SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(1) | BGCNT_CHARBASE(1) | BGCNT_SCREENBASE(10) | BGCNT_MOSAIC | BGCNT_16COLOR | BGCNT_TXT256x256);
    SetGpuReg(REG_OFFSET_BLDCNT, 0);
    SetGpuReg(REG_OFFSET_BLDALPHA, 0);
    SetGpuReg(REG_OFFSET_BLDY, 0);
}

static void InitContestPaintingVars(bool8 arg0)
{
    if (arg0 == FALSE)
    {
        gUnknown_030011F6 = FALSE;
        gContestPaintingMosaicVal = 0;
        gContestPaintingFadeCounter = 0;
    }
    else
    {
        gUnknown_030011F6 = TRUE;
        gContestPaintingMosaicVal = 15;
        gContestPaintingFadeCounter = 30;
    }
}

static void UpdateContestPaintingMosaicEffect(void)
{
    if (!gUnknown_030011F6)
    {
        SetGpuReg(REG_OFFSET_MOSAIC, 0);
    }
    else
    {
        SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(1) | BGCNT_CHARBASE(1) | BGCNT_SCREENBASE(10) | BGCNT_MOSAIC | BGCNT_16COLOR | BGCNT_TXT256x256);
        gContestPaintingMosaicVal = gContestPaintingFadeCounter / 2;
        SetGpuReg(REG_OFFSET_MOSAIC, (gContestPaintingMosaicVal << 12) | (gContestPaintingMosaicVal << 8) | (gContestPaintingMosaicVal << 4) | (gContestPaintingMosaicVal << 0));
    }
}

static void VBlankCB_ContestPainting(void)
{
    UpdateContestPaintingMosaicEffect();
    LoadOam();
    ProcessSpriteCopyRequests();
    TransferPlttBuffer();
}

static void InitContestMonPixels(u16 species, u8 whichSprite)
{
    const void *pal = GetMonSpritePalFromSpeciesAndPersonality(species, gContestPaintingWinner->trainerId, gContestPaintingWinner->personality);
    LZDecompressVram(pal, gContestPaintingMonPalette);
    if (whichSprite == 0)
    {
        HandleLoadSpecialPokePic_DontHandleDeoxys(
            &gMonFrontPicTable[species],
            gMonSpritesGfxPtr->sprites.ptr[1],
            species,
            gContestPaintingWinner->personality);
        _InitContestMonPixels(gMonSpritesGfxPtr->sprites.ptr[1], gContestPaintingMonPalette, (void *)gContestMonPixels);
    }
    else
    {
        HandleLoadSpecialPokePic_DontHandleDeoxys(
            &gMonBackPicTable[species],
            gMonSpritesGfxPtr->sprites.ptr[0],
            species,
            gContestPaintingWinner->personality);
        _InitContestMonPixels(gMonSpritesGfxPtr->sprites.ptr[0], gContestPaintingMonPalette, (void *)gContestMonPixels);
    }
}

static void _InitContestMonPixels(u8 *spriteGfx, u16 *palette, u16 (*destPixels)[64][64])
{
    u16 tileY, tileX, pixelY, pixelX;
    u8 colorIndex;

    for (tileY = 0; tileY < 8; tileY++)
    {
        for (tileX = 0; tileX < 8; tileX++)
        {
            for (pixelY = 0; pixelY < 8; pixelY++)
            {
                for (pixelX = 0; pixelX < 8; pixelX++)
                {
                    colorIndex = spriteGfx[32 * (tileY * 8 + tileX) + (pixelY << 2) + (pixelX >> 1)];
                    if (pixelX & 1)
                        colorIndex >>= 4;
                    else
                        colorIndex &= 0xF;

                    if (colorIndex == 0) // transparent pixel
                        (*destPixels)[8 * tileY + pixelY][tileX * 8 + pixelX] = 0x8000;
                    else
                        (*destPixels)[8 * tileY + pixelY][tileX * 8 + pixelX] = palette[colorIndex];
                }
            }
        }
    }
}

#define VRAM_PICTURE_DATA(x, y) (((u16 *)(BG_SCREEN_ADDR(12)))[(y) * 32 + (x)])

static void LoadContestPaintingFrame(u8 contestWinnerId, bool8 arg1)
{
    u8 x, y;

    LoadPalette(gPictureFramePalettes, 0, 0x100);
    if (arg1 == TRUE)
    {
        switch (gContestPaintingWinner->contestCategory / 3)
        {
        case CONTEST_CATEGORY_COOL:
            RLUnCompVram(gPictureFrameTiles_0, (void *)VRAM);
            RLUnCompWram(gPictureFrameTilemap_0, gContestMonPixels);
            break;
        case CONTEST_CATEGORY_BEAUTY:
            RLUnCompVram(gPictureFrameTiles_1, (void *)VRAM);
            RLUnCompWram(gPictureFrameTilemap_1, gContestMonPixels);
            break;
        case CONTEST_CATEGORY_CUTE:
            RLUnCompVram(gPictureFrameTiles_2, (void *)VRAM);
            RLUnCompWram(gPictureFrameTilemap_2, gContestMonPixels);
            break;
        case CONTEST_CATEGORY_SMART:
            RLUnCompVram(gPictureFrameTiles_3, (void *)VRAM);
            RLUnCompWram(gPictureFrameTilemap_3, gContestMonPixels);
            break;
        case CONTEST_CATEGORY_TOUGH:
            RLUnCompVram(gPictureFrameTiles_4, (void *)VRAM);
            RLUnCompWram(gPictureFrameTilemap_4, gContestMonPixels);
            break;
        }

        // Set the background
        for (y = 0; y < 20; y++)
        {
            for (x = 0; x < 32; x++)
                VRAM_PICTURE_DATA(x, y) = 0x1015;
        }

        // Copy the image frame
        for (y = 0; y < 10; y++)
        {
            for (x = 0; x < 18; x++)
                VRAM_PICTURE_DATA(x + 6, y + 2) = (*gContestMonPixels)[y + 2][x + 6];
        }

        // Re-set the entire top row to the first top frame part
        for (x = 0; x < 16; x++)
            VRAM_PICTURE_DATA(x + 7, 2) = (*gContestMonPixels)[2][7];
    }
    else if (contestWinnerId < 8)
    {
        RLUnCompVram(gPictureFrameTiles_5, (void *)VRAM);
        RLUnCompVram(gPictureFrameTilemap_5, (void *)(BG_SCREEN_ADDR(12)));
    }
    else
    {
        switch (gContestPaintingWinner->contestCategory / 3)
        {
        case CONTEST_CATEGORY_COOL:
            RLUnCompVram(gPictureFrameTiles_0, (void *)VRAM);
            RLUnCompVram(gPictureFrameTilemap_0, (void *)(BG_SCREEN_ADDR(12)));
            break;
        case CONTEST_CATEGORY_BEAUTY:
            RLUnCompVram(gPictureFrameTiles_1, (void *)VRAM);
            RLUnCompVram(gPictureFrameTilemap_1, (void *)(BG_SCREEN_ADDR(12)));
            break;
        case CONTEST_CATEGORY_CUTE:
            RLUnCompVram(gPictureFrameTiles_2, (void *)VRAM);
            RLUnCompVram(gPictureFrameTilemap_2, (void *)(BG_SCREEN_ADDR(12)));
            break;
        case CONTEST_CATEGORY_SMART:
            RLUnCompVram(gPictureFrameTiles_3, (void *)VRAM);
            RLUnCompVram(gPictureFrameTilemap_3, (void *)(BG_SCREEN_ADDR(12)));
            break;
        case CONTEST_CATEGORY_TOUGH:
            RLUnCompVram(gPictureFrameTiles_4, (void *)VRAM);
            RLUnCompVram(gPictureFrameTilemap_4, (void *)(BG_SCREEN_ADDR(12)));
            break;
        }
    }
}

#undef VRAM_PICTURE_DATA

static void InitPaintingMonOamData(u8 contestWinnerId)
{
    gMain.oamBuffer[0] = sContestPaintingMonOamData;
    gMain.oamBuffer[0].tileNum = 0;

    if (contestWinnerId > 1)
    {
        gMain.oamBuffer[0].x = 88;
        gMain.oamBuffer[0].y = 24;
    }
    else
    {
        gMain.oamBuffer[0].x = 88; // Duplicated code
        gMain.oamBuffer[0].y = 24;
    }
}

static u8 GetImageEffectForContestWinner(u8 contestWinnerId)
{
    u8 contestCategory;

    if (contestWinnerId < 8)
        contestCategory = gContestPaintingWinner->contestCategory;
    else
        contestCategory = gContestPaintingWinner->contestCategory / 3;

    switch (contestCategory)
    {
    case CONTEST_CATEGORY_COOL:
        return IMAGE_EFFECT_OUTLINE_COLORED;
    case CONTEST_CATEGORY_BEAUTY:
        return IMAGE_EFFECT_SHIMMER;
    case CONTEST_CATEGORY_CUTE:
        return IMAGE_EFFECT_POINTILLISM;
    case CONTEST_CATEGORY_SMART:
        return IMAGE_EFFECT_CHARCOAL;
    case CONTEST_CATEGORY_TOUGH:
        return IMAGE_EFFECT_GRAYSCALE_LIGHT;
    }

    return contestCategory;
}

static void AllocPaintingResources(void)
{
    gContestPaintingMonPalette = AllocZeroed(OBJ_PLTT_SIZE);
    gContestMonPixels = AllocZeroed(0x2000);
}

static void DoContestPaintingImageProcessing(u8 imageEffect)
{
    gImageProcessingContext.canvasPixels = gContestMonPixels;
    gImageProcessingContext.canvasPalette = gContestPaintingMonPalette;
    gImageProcessingContext.paletteStart = 0;
    gImageProcessingContext.personality = gContestPaintingWinner->personality % 256;
    gImageProcessingContext.columnStart = 0;
    gImageProcessingContext.rowStart = 0;
    gImageProcessingContext.columnEnd = 64;
    gImageProcessingContext.rowEnd = 64;
    gImageProcessingContext.canvasWidth = 64;
    gImageProcessingContext.canvasHeight = 64;

    switch (imageEffect)
    {
    case IMAGE_EFFECT_CHARCOAL:
    case IMAGE_EFFECT_GRAYSCALE_LIGHT:
        gImageProcessingContext.quantizeEffect = QUANTIZE_EFFECT_GRAYSCALE;
        break;
    case IMAGE_EFFECT_OUTLINE_COLORED:
    case IMAGE_EFFECT_SHIMMER:
    case IMAGE_EFFECT_POINTILLISM:
    default:
        gImageProcessingContext.quantizeEffect = QUANTIZE_EFFECT_STANDARD_LIMITED_COLORS;
        break;
    }

    gImageProcessingContext.var_16 = 2;
    gImageProcessingContext.effect = imageEffect;
    gImageProcessingContext.dest = (void *)OBJ_VRAM0;

    ApplyImageProcessingEffects(&gImageProcessingContext);
    ApplyImageProcessingQuantization(&gImageProcessingContext);
    ConvertImageProcessingToGBA(&gImageProcessingContext);
    LoadPalette(gContestPaintingMonPalette, 0x100, 0x200);
}

static void CreateContestPaintingPicture(u8 contestWinnerId, bool8 arg1)
{
    AllocPaintingResources();
    InitContestMonPixels(gContestPaintingWinner->species, 0);
    DoContestPaintingImageProcessing(GetImageEffectForContestWinner(contestWinnerId));
    InitPaintingMonOamData(contestWinnerId);
    LoadContestPaintingFrame(contestWinnerId, arg1);
}