Merge branch 'RHH/master' into RHH/upcoming

# Conflicts:
#	asm/macros/battle_script.inc
#	src/battle_script_commands.c
This commit is contained in:
Eduardo Quezada 2023-10-23 17:36:42 -03:00
commit 1137d54b61
14 changed files with 302 additions and 54 deletions

View File

@ -85,7 +85,11 @@ ELF = $(ROM:.gba=.elf)
MAP = $(ROM:.gba=.map)
SYM = $(ROM:.gba=.sym)
ifeq ($(MODERN),0)
TEST_OBJ_DIR_NAME := build/test
else
TEST_OBJ_DIR_NAME := build/modern-test
endif
TESTELF = $(ROM:.gba=-test.elf)
HEADLESSELF = $(ROM:.gba=-test-headless.elf)

View File

@ -1456,6 +1456,11 @@
.4byte \jumpInstr
.endm
.macro tryreflecttype failInstr:req
callnative BS_TryReflectType
.4byte \failInstr
.endm
@ Used to active a different Max Move effects.
.macro setmaxmoveeffect
callnative BS_SetMaxMoveEffect
@ -1729,11 +1734,6 @@
.4byte \failInstr
.endm
.macro tryreflecttype failInstr:req
various BS_ATTACKER, VARIOUS_TRY_REFLECT_TYPE
.4byte \failInstr
.endm
.macro trysoak failInstr:req
various BS_ATTACKER, VARIOUS_TRY_SOAK
.4byte \failInstr

View File

@ -753,12 +753,16 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER
#define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0))
#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type)))
#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY)
#define SET_BATTLER_TYPE(battlerId, type) \
{ \
gBattleMons[battlerId].type1 = type; \
gBattleMons[battlerId].type2 = type; \
gBattleMons[battlerId].type3 = TYPE_MYSTERY; \
}
#define RESTORE_BATTLER_TYPE(battlerId) \
{ \
gBattleMons[battlerId].type1 = gSpeciesInfo[gBattleMons[battlerId].species].types[0]; \

View File

@ -117,7 +117,7 @@
#define B_SHADOW_TAG_ESCAPE GEN_LATEST // In Gen4+, if both sides have a Pokémon with Shadow Tag, all battlers can escape. Before, neither side could escape this situation.
#define B_MOODY_ACC_EVASION GEN_LATEST // In Gen8, Moody CANNOT raise Accuracy and Evasion anymore.
#define B_FLASH_FIRE_FROZEN GEN_LATEST // In Gen5+, Flash Fire can trigger even when frozen, when it couldn't before.
#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same ability, as opposed to 50% previously.
#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same Nature, as opposed to 50% previously. In Gen9, it has no out-of-battle effect.
#define B_SYNCHRONIZE_TOXIC GEN_LATEST // In Gen5+, if a Pokémon with Synchronize is badly poisoned, the opponent will also become badly poisoned. Previously, the opponent would become regular poisoned.
#define B_UPDATED_INTIMIDATE GEN_LATEST // In Gen8, Intimidate doesn't work on opponents with the Inner Focus, Scrappy, Own Tempo or Oblivious abilities. It also activates Rattled.
#define B_OBLIVIOUS_TAUNT GEN_LATEST // In Gen6+, Pokémon with Oblivious can't be taunted.

View File

@ -47,6 +47,7 @@ struct TestRunnerState
u8 expectedResult;
bool8 expectLeaks:1;
bool8 inBenchmark:1;
bool8 tearDown:1;
u32 timeoutSeconds;
};

View File

@ -1883,7 +1883,7 @@ static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i)
void ModifyPersonalityForNature(u32 *personality, u32 newNature)
{
u32 nature = GetNatureFromPersonality(*personality);
s32 diff = abs(nature - newNature);
s32 diff = abs((s32)nature - (s32)newNature);
s32 sign = (nature > newNature) ? 1 : -1;
if (diff > NUM_NATURES / 2)
{

View File

@ -9398,38 +9398,6 @@ static void Cmd_various(void)
}
return;
}
case VARIOUS_TRY_REFLECT_TYPE:
{
VARIOUS_ARGS(const u8 *failInstr);
if (GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species) == SPECIES_ARCEUS
|| GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species) == SPECIES_SILVALLY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) != TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 1);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (GetBattlerType(gBattlerTarget, 0) != TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 0);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1);
gBattlescriptCurrInstr = cmd->nextInstr;
}
return;
}
case VARIOUS_TRY_SOAK:
{
VARIOUS_ARGS(const u8 *failInstr);
@ -16250,3 +16218,49 @@ void BS_JumpIfTerrainAffected(void)
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryReflectType(void)
{
NATIVE_ARGS(const u8 *failInstr);
u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species);
u8 targetType1 = GetBattlerType(gBattlerTarget, 0);
u8 targetType2 = GetBattlerType(gBattlerTarget, 1);
u8 targetType3 = GetBattlerType(gBattlerTarget, 2);
if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (IS_BATTLER_TYPELESS(gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (targetType1 == TYPE_MYSTERY && targetType2 == TYPE_MYSTERY && targetType3 != TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = TYPE_NORMAL;
gBattleMons[gBattlerAttacker].type2 = TYPE_NORMAL;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (targetType1 == TYPE_MYSTERY && targetType2 != TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = targetType2;
gBattleMons[gBattlerAttacker].type2 = targetType2;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (targetType1 != TYPE_MYSTERY && targetType2 == TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = targetType1;
gBattleMons[gBattlerAttacker].type2 = targetType1;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattleMons[gBattlerAttacker].type1 = targetType1;
gBattleMons[gBattlerAttacker].type2 = targetType2;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}

View File

@ -6041,8 +6041,8 @@ const struct Item gItems[] =
.holdEffectParam = 10,
.description = sRazorFangDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.type = EVO_HELD_ITEM_TYPE,
.fieldUseFunc = EVO_HELD_ITEM_FIELD_FUNC,
.flingPower = 30,
},

View File

@ -618,6 +618,7 @@ const u8 *const gItemEffectTable[ITEMS_COUNT] =
[ITEM_METAL_COAT] = gItemEffect_EvoItem,
[ITEM_KINGS_ROCK] = gItemEffect_EvoItem,
[ITEM_RAZOR_CLAW] = gItemEffect_EvoItem,
[ITEM_RAZOR_FANG] = gItemEffect_EvoItem,
[ITEM_AUSPICIOUS_ARMOR] = gItemEffect_EvoItem,
[ITEM_MALICIOUS_ARMOR] = gItemEffect_EvoItem,
[ITEM_SCROLL_OF_DARKNESS] = gItemEffect_EvoItem,

View File

@ -415,6 +415,7 @@ static u8 PickWildMonNature(void)
}
}
}
#if B_SYNCHRONIZE_NATURE < GEN_9
// check synchronize for a pokemon with the same ability
if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG)
&& GetMonAbility(&gPlayerParty[0]) == ABILITY_SYNCHRONIZE
@ -422,6 +423,7 @@ static u8 PickWildMonNature(void)
{
return GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY) % NUM_NATURES;
}
#endif
// random nature
return Random() % NUM_NATURES;

View File

@ -0,0 +1,186 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Reflect Type fails if the user is Terastallized");
TO_DO_BATTLE_TEST("Reflect Type succeeds against a Terastallized target and copies its Tera type");
SINGLE_BATTLE_TEST("Reflect Type does not affect any of Arceus' forms")
{
u32 j;
static const u16 sArceusFormSpeciesIdTable[] = {
SPECIES_ARCEUS,
SPECIES_ARCEUS_FIGHTING,
SPECIES_ARCEUS_FLYING,
SPECIES_ARCEUS_POISON,
SPECIES_ARCEUS_GROUND,
SPECIES_ARCEUS_ROCK,
SPECIES_ARCEUS_BUG,
SPECIES_ARCEUS_GHOST,
SPECIES_ARCEUS_STEEL,
SPECIES_ARCEUS_FIRE,
SPECIES_ARCEUS_WATER,
SPECIES_ARCEUS_GRASS,
SPECIES_ARCEUS_ELECTRIC,
SPECIES_ARCEUS_PSYCHIC,
SPECIES_ARCEUS_ICE,
SPECIES_ARCEUS_DRAGON,
SPECIES_ARCEUS_DARK,
SPECIES_ARCEUS_FAIRY,
};
u16 species = SPECIES_NONE;
for (j = 0; j < ARRAY_COUNT(sArceusFormSpeciesIdTable); j++)
{
PARAMETRIZE { species = sArceusFormSpeciesIdTable[j]; }
}
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Wobbuffet used Reflect Type!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Reflect Type does not affect any of Silvally's forms")
{
u32 j;
static const u16 sSilvallyFormSpeciesIdTable[] = {
SPECIES_SILVALLY,
SPECIES_SILVALLY_FIGHTING,
SPECIES_SILVALLY_FLYING,
SPECIES_SILVALLY_POISON,
SPECIES_SILVALLY_GROUND,
SPECIES_SILVALLY_ROCK,
SPECIES_SILVALLY_BUG,
SPECIES_SILVALLY_GHOST,
SPECIES_SILVALLY_STEEL,
SPECIES_SILVALLY_FIRE,
SPECIES_SILVALLY_WATER,
SPECIES_SILVALLY_GRASS,
SPECIES_SILVALLY_ELECTRIC,
SPECIES_SILVALLY_PSYCHIC,
SPECIES_SILVALLY_ICE,
SPECIES_SILVALLY_DRAGON,
SPECIES_SILVALLY_DARK,
SPECIES_SILVALLY_FAIRY,
};
u16 species = SPECIES_NONE;
for (j = 0; j < ARRAY_COUNT(sSilvallyFormSpeciesIdTable); j++)
{
PARAMETRIZE { species = sSilvallyFormSpeciesIdTable[j]; }
}
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Wobbuffet used Reflect Type!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Reflect Type does not affect Pokémon with no types")
{
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING);
GIVEN {
PLAYER(SPECIES_ARCANINE);
OPPONENT(SPECIES_POLIWRATH);
} WHEN {
TURN { MOVE(player, MOVE_BURN_UP); MOVE(opponent, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Arcanine used Burn Up!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player);
HP_BAR(opponent);
MESSAGE("Arcanine burned itself out!");
MESSAGE("Foe Poliwrath used Reflect Type!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Reflect Type copies a target's dual types")
{
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING);
GIVEN {
PLAYER(SPECIES_ARCANINE);
OPPONENT(SPECIES_POLIWRATH);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Arcanine used Reflect Type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player);
MESSAGE("Arcanine's type changed to match the Foe Poliwrath's!");
} THEN {
EXPECT_EQ(player->type1, TYPE_WATER);
EXPECT_EQ(player->type2, TYPE_FIGHTING);
EXPECT_EQ(player->type3, TYPE_MYSTERY);
}
}
SINGLE_BATTLE_TEST("Reflect Type copies a target's pure type")
{
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[0] == TYPE_ROCK);
ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[1] == TYPE_ROCK);
GIVEN {
PLAYER(SPECIES_ARCANINE);
OPPONENT(SPECIES_SUDOWOODO);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Arcanine used Reflect Type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player);
MESSAGE("Arcanine's type changed to match the Foe Sudowoodo's!");
} THEN {
EXPECT_EQ(player->type1, TYPE_ROCK);
EXPECT_EQ(player->type2, TYPE_ROCK);
EXPECT_EQ(player->type3, TYPE_MYSTERY);
}
}
SINGLE_BATTLE_TEST("Reflect Type defaults to Normal type for the user's type1 and type2 if the target only has a 3rd type")
{
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARCANINE);
} WHEN {
TURN { MOVE(opponent, MOVE_BURN_UP); }
TURN { MOVE(player, MOVE_FORESTS_CURSE); }
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
// Turn 1
MESSAGE("Foe Arcanine used Burn Up!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, opponent);
HP_BAR(player);
MESSAGE("Foe Arcanine burned itself out!");
// Turn 2
MESSAGE("Wobbuffet used Forest'sCurs!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESTS_CURSE, player);
MESSAGE("Grass type was added to Foe Arcanine!");
// Turn 3
MESSAGE("Wobbuffet used Reflect Type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player);
MESSAGE("Wobbuffet's type changed to match the Foe Arcanine's!");
} THEN {
EXPECT_EQ(player->type1, TYPE_NORMAL);
EXPECT_EQ(player->type2, TYPE_NORMAL);
EXPECT_EQ(player->type3, TYPE_GRASS);
}
}

View File

@ -4,6 +4,7 @@
#include "battle_main.h"
#include "data.h"
#include "malloc.h"
#include "random.h"
#include "string_util.h"
#include "constants/item.h"
#include "constants/abilities.h"
@ -119,3 +120,17 @@ TEST("CreateNPCTrainerPartyForTrainer generates different personalities for diff
EXPECT(testParty[0].box.personality != testParty[1].box.personality);
Free(testParty);
}
TEST("ModifyPersonalityForNature can set any nature")
{
u32 personality, nature, j, k;
for (j = 0; j < 64; j++)
{
for (k = 0; k < NUM_NATURES; k++)
{
PARAMETRIZE { personality = Random32(); nature = k; }
}
}
ModifyPersonalityForNature(&personality, nature);
EXPECT_EQ(GetNatureFromPersonality(personality), nature);
}

View File

@ -56,9 +56,10 @@ static bool32 PrefixMatch(const char *pattern, const char *string)
enum
{
STATE_INIT,
STATE_NEXT_TEST,
STATE_ASSIGN_TEST,
STATE_RUN_TEST,
STATE_REPORT_RESULT,
STATE_NEXT_TEST,
STATE_EXIT,
};
@ -152,17 +153,15 @@ void CB2_TestRunner(void)
}
else
{
gTestRunnerState.state = STATE_NEXT_TEST;
gTestRunnerState.test = __start_tests - 1;
gTestRunnerState.state = STATE_ASSIGN_TEST;
gTestRunnerState.test = __start_tests;
}
gTestRunnerState.exitCode = 0;
gTestRunnerState.skipFilename = NULL;
break;
case STATE_NEXT_TEST:
gTestRunnerState.test++;
case STATE_ASSIGN_TEST:
if (gTestRunnerState.test == __stop_tests)
{
gTestRunnerState.state = STATE_EXIT;
@ -172,6 +171,7 @@ void CB2_TestRunner(void)
if (gTestRunnerState.test->runner != &gAssumptionsRunner
&& !PrefixMatch(gTestRunnerArgv, gTestRunnerState.test->name))
{
gTestRunnerState.state = STATE_NEXT_TEST;
return;
}
@ -191,6 +191,8 @@ void CB2_TestRunner(void)
sCurrentTest.address = (uintptr_t)gTestRunnerState.test;
sCurrentTest.state = CURRENT_TEST_STATE_ESTIMATE;
// If AssignCostToRunner fails, we want to report the failure.
gTestRunnerState.state = STATE_REPORT_RESULT;
if (AssignCostToRunner() == gTestRunnerI)
gTestRunnerState.state = STATE_RUN_TEST;
else
@ -204,7 +206,10 @@ void CB2_TestRunner(void)
SeedRng(0);
SeedRng2(0);
if (gTestRunnerState.test->runner->setUp)
{
gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data);
gTestRunnerState.tearDown = TRUE;
}
// NOTE: Assumes that the compiler interns __FILE__.
if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) // Assumption fails for tests in this file.
{
@ -216,13 +221,17 @@ void CB2_TestRunner(void)
gTestRunnerState.test->runner->run(gTestRunnerState.test->data);
}
break;
case STATE_REPORT_RESULT:
REG_TM2CNT_H = 0;
gTestRunnerState.state = STATE_NEXT_TEST;
if (gTestRunnerState.test->runner->tearDown)
if (gTestRunnerState.tearDown && gTestRunnerState.test->runner->tearDown)
{
gTestRunnerState.test->runner->tearDown(gTestRunnerState.test->data);
gTestRunnerState.tearDown = FALSE;
}
if (gTestRunnerState.result == TEST_RESULT_PASS
&& !gTestRunnerState.expectLeaks)
@ -342,6 +351,11 @@ void CB2_TestRunner(void)
break;
case STATE_NEXT_TEST:
gTestRunnerState.state = STATE_ASSIGN_TEST;
gTestRunnerState.test++;
break;
case STATE_EXIT:
MgbaExit_(gTestRunnerState.exitCode);
break;

View File

@ -2,9 +2,9 @@
* parses the output to display human-readable progress.
*
* Output lines starting with "GBA Debug: :" are parsed as commands to
* Hydra, other output lines starting with "GBA Debug: " are parsed as
* output from the current test, and any other lines are parsed as
* output from the mgba-rom-test process itself.
* Hydra, other output lines starting with "GBA Debug: " or with "GBA: "
* are parsed as output from the current test, and any other lines are
* parsed as output from the mgba-rom-test process itself.
*
* COMMANDS
* N: Sets the test name to the remainder of the line.
@ -75,10 +75,17 @@ static void handle_read(int i, struct Runner *runner)
{
eol++;
size_t n = eol - sol;
if (runner->input_buffer_size >= strlen("GBA Debug: ")
&& !strncmp(sol, "GBA Debug: ", strlen("GBA Debug: ")))
char *soc;
if (runner->input_buffer_size >= strlen("GBA: ")
&& !strncmp(sol, "GBA: ", strlen("GBA: ")))
{
char *soc = sol + strlen("GBA Debug: ");
soc = sol + strlen("GBA: ");
goto buffer_output;
}
else if (runner->input_buffer_size >= strlen("GBA Debug: ")
&& !strncmp(sol, "GBA Debug: ", strlen("GBA Debug: ")))
{
soc = sol + strlen("GBA Debug: ");
if (soc[0] == ':')
{
switch (soc[1])