Merge branch '_RHH/master' into _RHH/upcoming

This commit is contained in:
Eduardo Quezada 2024-10-24 07:47:52 -03:00
commit afa7ab9b2e
14 changed files with 3451 additions and 45 deletions

View file

@ -9280,8 +9280,8 @@ gBattleAnimMove_ThousandArrows::
playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER
waitforvisualfinish
delay 0x30
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
call ThousandArrowsDown
call ThousandArrowsDown
call ThousandArrowsDown
@ -9481,8 +9481,8 @@ gBattleAnimMove_LandsWrath::
fadetobg BG_FISSURE
waitbgfadeout
playsewithpan SE_M_EARTHQUAKE, 0x0
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
createvisualtask AnimTask_PositionFissureBgOnBattler, 5, ANIM_TARGET, 5, -1
waitbgfadein
waitforvisualfinish
@ -9649,8 +9649,8 @@ gBattleAnimMove_PrecipiceBlades::
fadetobg 0x15
waitbgfadeout
createvisualtask AnimTask_PositionFissureBgOnBattler, 5, ANIM_TARGET, 5, -1
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x5
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x5
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x5
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x5
playsewithpan SE_M_EARTHQUAKE, 0x0
createvisualtask AnimTask_IsTargetPlayerSide, 0x2
jumpargeq 0x7, ANIM_TARGET, PrecipiceBladesOpponent
@ -11761,8 +11761,8 @@ gBattleAnimMove_StompingTantrum::
monbg ANIM_TARGET
createvisualtask AnimTask_Splash, 2, ANIM_ATTACKER, 3
delay 0x5
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x5
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x5
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x5
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x5
createsprite gStompingTantrumRockTemplate, ANIM_ATTACKER, 2, 0x0, 0x0
createsprite gStompingTantrumRockTemplate, ANIM_ATTACKER, 2, 0x0, 0x1
createsprite gStompingTantrumRockTemplate, ANIM_ATTACKER, 2, 0x0, 0x2
@ -11897,8 +11897,8 @@ gBattleAnimMove_PrismaticLaser::
unloadspritegfx ANIM_TAG_ICE_CHUNK
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
delay 0x1E
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
playsewithpan SE_M_SOLAR_BEAM, SOUND_PAN_ATTACKER
call PrismaticLaserRain
call PrismaticLaserRain
@ -16579,8 +16579,8 @@ gBattleAnimMove_GigatonHammer::
delay 18
createvisualtask AnimTask_SquishTarget, 0x2
delay 6
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 50
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 50
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 50
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 50
call GigatonHammerImpact
waitforvisualfinish
end
@ -20282,8 +20282,8 @@ gBattleAnimMove_LowKick::
end
gBattleAnimMove_Earthquake::
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 50
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 50
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 50
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 50
playsewithpan SE_M_EARTHQUAKE, 0
delay 10
createsprite gComplexPaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 3, 1, RGB_BLACK, 14, RGB_WHITE, 14
@ -20293,8 +20293,8 @@ gBattleAnimMove_Earthquake::
gBattleAnimMove_Fissure::
loadspritegfx ANIM_TAG_MUD_SAND
createvisualtask AnimTask_HorizontalShake, 3, ANIM_PLAYER_RIGHT, 10, 50
createvisualtask AnimTask_HorizontalShake, 3, ANIM_TARGET, 10, 50
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 50
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 50
playsewithpan SE_M_EARTHQUAKE, SOUND_PAN_TARGET
delay 8
call FissureDirtPlumeFar
@ -21020,13 +21020,13 @@ gBattleAnimMove_Magnitude::
MagnitudeEnd:
end
MagnitudeRegular:
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 0, 50
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 0, 50
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 0, 50
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 0, 50
loopsewithpan SE_M_STRENGTH, SOUND_PAN_TARGET, 8, 10
goto MagnitudeEnd
MagnitudeIntense:
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 0, 50
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 0, 50
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 0, 50
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 0, 50
loopsewithpan SE_M_STRENGTH, SOUND_PAN_TARGET, 8, 10
delay 10
createsprite gComplexPaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 3, 1, RGB_BLACK, 14, RGB_WHITE, 14
@ -21499,8 +21499,8 @@ gBattleAnimMove_Eruption::
createsprite gEruptionFallingRockSpriteTemplate, ANIM_ATTACKER, 40, 110, -32, 64, 50, 0
createsprite gEruptionFallingRockSpriteTemplate, ANIM_ATTACKER, 40, 60, -32, 80, 70, 1
delay 22
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 8, 60
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 8, 60
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 8, 60
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 8, 60
loopsewithpan SE_M_ROCK_THROW, SOUND_PAN_TARGET, 16, 12
delay 80
createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 40, F_PAL_BG | F_PAL_BATTLERS, 4, 4, 0, RGB_RED
@ -21530,7 +21530,7 @@ gBattleAnimMove_Imprison::
waitforvisualfinish
delay 4
createsprite gRedXSpriteTemplate, ANIM_ATTACKER, 5, ANIM_ATTACKER, 40
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 1, 10
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 1, 10
playsewithpan SE_M_HYPER_BEAM, SOUND_PAN_ATTACKER
clearmonbg ANIM_DEF_PARTNER
call UnsetPsychicBg
@ -29004,8 +29004,8 @@ gBattleAnimMove_BreakneckBlitz::
loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
loadspritegfx ANIM_TAG_METEOR
loadspritegfx ANIM_TAG_FLAT_ROCK
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 5, 32 @ shake screen
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 5, 32 @ shake banks
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 5, 32 @ shake screen
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 5, 32 @ shake banks
playsewithpan SE_M_EARTHQUAKE, 0x0
monbg ANIM_ATTACKER
splitbgprio ANIM_ATTACKER

View file

@ -45,5 +45,6 @@ void SE12PanpotControl(s8 pan);
bool8 IsSEPlaying(void);
bool8 IsBGMPlaying(void);
bool8 IsSpecialSEPlaying(void);
void Task_DuckBGMForPokemonCry(u8 taskId);
#endif // GUARD_SOUND_H

View file

@ -356,13 +356,14 @@ const struct SpriteTemplate gSkyAttackBirdSpriteTemplate =
.callback = AnimSkyAttackBird,
};
// same as AnimEllipticalGust but centered on targets
// same as AnimEllipticalGust but centered on targets in a double battle
static void AnimEllipticalGustCentered(struct Sprite *sprite)
{
if (IsDoubleBattle())
InitSpritePosToAnimTargetsCentre(sprite, FALSE);
else
InitSpritePosToAnimTarget(sprite, FALSE);
sprite->y += 20;
sprite->data[1] = 191;
sprite->callback = AnimEllipticalGust_Step;

View file

@ -1772,7 +1772,20 @@ void CB2_QuitRecordedBattle(void)
m4aMPlayStop(&gMPlayInfo_SE1);
m4aMPlayStop(&gMPlayInfo_SE2);
if (gTestRunnerEnabled)
{
// Clean up potentially-leaking tasks.
// I think these leak when the battle ends soon after a
// battler is fainted.
u8 taskId;
taskId = FindTaskIdByFunc(Task_PlayerController_RestoreBgmAfterCry);
if (taskId != TASK_NONE)
DestroyTask(taskId);
taskId = FindTaskIdByFunc(Task_DuckBGMForPokemonCry);
if (taskId != TASK_NONE)
DestroyTask(taskId);
TestRunner_Battle_AfterLastTurn();
}
FreeRestoreBattleData();
FreeAllWindowBuffers();
SetMainCallback2(gMain.savedCallback);

View file

@ -5132,7 +5132,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
break;
case ABILITY_SHED_SKIN:
if ((gBattleMons[battler].status1 & STATUS1_ANY)
&& (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_SHED_SKIN, 30) : RandomChance(RNG_SHED_SKIN, 1, 3)))
&& (B_ABILITY_TRIGGER_CHANCE == GEN_4 ? RandomPercentage(RNG_SHED_SKIN, 30) : RandomChance(RNG_SHED_SKIN, 1, 3)))
{
ABILITY_HEAL_MON_STATUS:
if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON))

View file

@ -18792,7 +18792,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.type = TYPE_FAIRY,
.accuracy = 80,
.pp = 5,
.target = MOVE_TARGET_SELECTED,
.target = MOVE_TARGET_BOTH,
.priority = 0,
.category = DAMAGE_CATEGORY_SPECIAL,
.windMove = TRUE,

View file

@ -460,7 +460,8 @@ struct PokedexView
u16 maxScrollTimer;
u16 scrollSpeed;
u16 unkArr1[4]; // Cleared, never read
u8 filler[8];
u16 originalSearchSelectionNum;
u8 filler[6];
u8 currentPage;
u8 currentPageBackup;
bool8 isSearchResults:1;
@ -2139,6 +2140,7 @@ static void ResetPokedexView(struct PokedexView *pokedexView)
pokedexView->unkArr2[i] = 0;
for (i = 0; i < ARRAY_COUNT(pokedexView->unkArr3); i++)
pokedexView->unkArr3[i] = 0;
pokedexView->originalSearchSelectionNum = 0;
}
static void VBlankCB_Pokedex(void)
@ -2438,6 +2440,13 @@ static bool8 LoadPokedexListPage(u8 page)
case 3:
if (page == PAGE_MAIN)
CreatePokedexList(sPokedexView->dexMode, sPokedexView->dexOrder);
if (sPokedexView->originalSearchSelectionNum != 0)
{
// when returning to search results after selecting an evo, we have to restore
// the original dexNum because the search results page doesn't rebuild the list
sPokedexListItem->dexNum = sPokedexView->originalSearchSelectionNum;
sPokedexView->originalSearchSelectionNum = 0;
}
CreateMonSpritesAtPos(sPokedexView->selectedPokemon, 0xE);
sPokedexView->statBarsSpriteId = 0xFF; //stat bars
CreateStatBars(&sPokedexView->pokedexList[sPokedexView->selectedPokemon]); //stat bars
@ -6146,6 +6155,9 @@ static void Task_HandleEvolutionScreenInput(u8 taskId)
{
u16 targetSpecies = sPokedexView->sEvoScreenData.targetSpecies[sPokedexView->sEvoScreenData.menuPos];
u16 dexNum = SpeciesToNationalPokedexNum(targetSpecies);
if (sPokedexView->isSearchResults && sPokedexView->originalSearchSelectionNum == 0)
sPokedexView->originalSearchSelectionNum = sPokedexListItem->dexNum;
sPokedexListItem->dexNum = dexNum;
sPokedexListItem->seen = GetSetPokedexFlag(dexNum, FLAG_GET_SEEN);
sPokedexListItem->owned = GetSetPokedexFlag(dexNum, FLAG_GET_CAUGHT);
@ -6881,6 +6893,9 @@ static void Task_HandleFormsScreenInput(u8 taskId)
{
u8 formId = sPokedexView->sFormScreenData.formIds[menuPos];
u16 formSpecies = GetFormSpeciesId(NationalPokedexNumToSpecies(sPokedexListItem->dexNum), formId);
if (sPokedexView->isSearchResults && sPokedexView->originalSearchSelectionNum == 0)
sPokedexView->originalSearchSelectionNum = sPokedexListItem->dexNum;
if (formSpecies == GetFormSpeciesId(formSpecies, 0))
sPokedexView->formSpecies = 0;
else

View file

@ -31,7 +31,6 @@ extern struct ToneData gCryTable_Reverse[];
static void Task_Fanfare(u8 taskId);
static void CreateFanfareTask(void);
static void Task_DuckBGMForPokemonCry(u8 taskId);
static void RestoreBGMVolumeAfterPokemonCry(void);
static const struct Fanfare sFanfares[] = {
@ -513,7 +512,7 @@ bool8 IsCryPlaying(void)
return FALSE;
}
static void Task_DuckBGMForPokemonCry(u8 taskId)
void Task_DuckBGMForPokemonCry(u8 taskId)
{
if (gPokemonCryBGMDuckingCounter)
{

View file

@ -0,0 +1,28 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Guard Dog raises Attack when intimidated", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_INTIMIDATE; }
PARAMETRIZE { ability = ABILITY_SHED_SKIN; }
GIVEN {
PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_GUARD_DOG); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARBOK) { Ability(ability); }
} WHEN {
TURN { SWITCH(opponent, 1); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
if (ability == ABILITY_INTIMIDATE)
{
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
ABILITY_POPUP(player, ABILITY_GUARD_DOG);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Okidogi's Attack rose!");
}
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[0].damage);
}
}

View file

@ -1,11 +1,13 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Shed Skin triggers 30% of the time")
SINGLE_BATTLE_TEST("Shed Skin triggers 33% of the time")
{
PASSES_RANDOMLY(3, 10, RNG_SHED_SKIN);
if (B_ABILITY_TRIGGER_CHANCE == GEN_4)
PASSES_RANDOMLY(30, 100, RNG_SHED_SKIN);
else
PASSES_RANDOMLY(33, 100, RNG_SHED_SKIN);
GIVEN {
ASSUME(B_ABILITY_TRIGGER_CHANCE >= GEN_4);
ASSUME(gMovesInfo[MOVE_TACKLE].makesContact);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARBOK) { Status1(STATUS1_POISON); Ability(ABILITY_SHED_SKIN); }

View file

@ -0,0 +1,23 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Steam Engine raises speed when hit by a Fire or Water move")
{
u16 move;
PARAMETRIZE { move = MOVE_EMBER; }
PARAMETRIZE { move = MOVE_WATER_GUN; }
GIVEN {
PLAYER(SPECIES_COALOSSAL) { Ability(ABILITY_STEAM_ENGINE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ABILITY_POPUP(player, ABILITY_STEAM_ENGINE);
MESSAGE("Coalossal's Speed drastically rose!");
} THEN {
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 6);
}
}

View file

@ -5,6 +5,7 @@
#include "main.h"
#include "malloc.h"
#include "random.h"
#include "task.h"
#include "constants/characters.h"
#include "test_runner.h"
#include "test/test.h"
@ -190,6 +191,7 @@ top:
else
gTestRunnerState.timeoutSeconds = UINT_MAX;
InitHeap(gHeap, HEAP_SIZE);
ResetTasks();
EnableInterrupts(INTR_FLAG_TIMER2);
REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second.
REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK;
@ -243,6 +245,7 @@ top:
if (gTestRunnerState.result == TEST_RESULT_PASS
&& !gTestRunnerState.expectLeaks)
{
int i;
const struct MemBlock *head = HeapHead();
const struct MemBlock *block = head;
do
@ -251,7 +254,7 @@ top:
|| !(EWRAM_START <= (uintptr_t)block->next && (uintptr_t)block->next < EWRAM_END)
|| (block->next <= block && block->next != head))
{
Test_MgbaPrintf("gHeap corrupted block at 0x%p", block);
Test_MgbaPrintf("gHeap corrupted block at %p", block);
gTestRunnerState.result = TEST_RESULT_ERROR;
break;
}
@ -268,6 +271,15 @@ top:
block = block->next;
}
while (block != head);
for (i = 0; i < NUM_TASKS; i++)
{
if (gTasks[i].isActive)
{
Test_MgbaPrintf("%p: task not freed", gTasks[i].func);
gTestRunnerState.result = TEST_RESULT_FAIL;
}
}
}
if (gTestRunnerState.test->runner == &gAssumptionsRunner)
@ -585,6 +597,9 @@ static s32 MgbaVPrintf_(const char *fmt, va_list va)
p = va_arg(va, unsigned);
{
s32 n;
i = MgbaPutchar_(i, '<');
i = MgbaPutchar_(i, '0');
i = MgbaPutchar_(i, 'x');
for (n = 0; n < 7; n++)
{
unsigned nybble = (p >> (24 - (4*n))) & 0xF;
@ -593,6 +608,7 @@ static s32 MgbaVPrintf_(const char *fmt, va_list va)
else
i = MgbaPutchar_(i, 'a' + nybble - 10);
}
i = MgbaPutchar_(i, '>');
}
break;
case 'q':

File diff suppressed because it is too large Load diff

View file

@ -21,6 +21,7 @@
#include <regex.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -32,6 +33,7 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "elf.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
@ -69,10 +71,103 @@ struct Runner
char assumeFailed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
};
struct Symbol {
const char *name;
uint32_t address;
size_t size;
};
struct SymbolTable {
struct Symbol *symbols;
size_t symbols_n;
};
static unsigned nrunners = 0;
static unsigned runners_digits = 0;
static struct Runner *runners = NULL;
// TODO: Build the symbol table on demand.
static struct SymbolTable symbol_table = { NULL, 0 };
static const struct Symbol *lookup_address(uint32_t address)
{
int lo = 0, hi = symbol_table.symbols_n;
while (lo < hi)
{
int mi = lo + (hi - lo) / 2;
const struct Symbol *symbol = &symbol_table.symbols[mi];
if (address < symbol->address)
hi = mi;
else if (address >= symbol->address + symbol->size)
lo = mi + 1;
else
return symbol;
}
return NULL;
}
// Similar to 'fwrite(buffer, 1, size, f)' except that anything which
// looks like the output of '%p' (i.e. '<0x\d{7}>') is translated into
// the name of a symbol (if it represents one).
static void fprint_buffer(FILE *f, const char *buffer, size_t size)
{
const char *buffer_end = buffer + size;
while (buffer < buffer_end)
{
// Find the next '<0x'.
char *buffer_ = memmem(buffer, buffer_end - buffer, "<0x", 3);
// No '<0x' or could not possibly match, print everything.
if (buffer_ == NULL || buffer_end - buffer_ < 11 || buffer_[10] != '>')
{
fwrite(buffer, 1, buffer_end - buffer, f);
break;
}
// Print everything before the '<0x'.
fwrite(buffer, 1, buffer_ - buffer, f);
buffer = buffer_;
unsigned long address = strtoul(buffer + 3, &buffer_, 16);
// Un-mirror EWRAM/IWRAM/ROM addresses.
switch (address & 0xF000000)
{
case 0x2000000: address = address & 0x203FFFF; break;
case 0x3000000: address = address & 0x3007FFF; break;
case 0x7000000: address = address & 0x70003FF; break;
case 0xA000000: address = address & 0x9FFFFFF; break;
case 0xB000000: address = address & 0x9FFFFFF; break;
case 0xC000000: address = address & 0x9FFFFFF; break;
case 0xD000000: address = address & 0x9FFFFFF; break;
}
// Not a 7-digit address, print the '<0x' part and loop.
if (buffer_ != buffer + 10)
{
fwrite(buffer, 1, 3, f);
buffer += 3;
continue;
}
const struct Symbol *symbol = lookup_address(address);
// Not a symbol, print the parsed part and loop.
if (symbol == NULL)
{
fwrite(buffer, 1, 11, f);
buffer += 11;
continue;
}
if (symbol->address == address)
fprintf(f, "<%s>", symbol->name);
else
fprintf(f, "<%s+0x%lx>", symbol->name, address - symbol->address);
buffer += 11;
}
}
static void handle_read(int i, struct Runner *runner)
{
char *sol = runner->input_buffer;
@ -156,7 +251,7 @@ add_to_results:
soc += 2;
fprintf(stdout, "[%0*d] %s: ", runners_digits, i, runner->test_name);
fwrite(soc, 1, eol - soc, stdout);
fwrite(runner->output_buffer, 1, runner->output_buffer_size, stdout);
fprint_buffer(stdout, runner->output_buffer, runner->output_buffer_size);
strcpy(runner->test_name, "WAITING...");
runner->output_buffer_size = 0;
break;
@ -186,11 +281,7 @@ buffer_output:
}
else
{
if (write(STDOUT_FILENO, sol, eol - sol) == -1)
{
perror("write failed");
exit(2);
}
fwrite(sol, 1, eol - sol, stdout);
}
sol += n;
consumed += n;
@ -233,12 +324,80 @@ static void exit2(int _)
exit(2);
}
int compare_strings(const void * a, const void * b)
static int compare_addresses(const void *a, const void *b)
{
const char *arg1 = (const char *) a;
const char *arg2 = (const char *) b;
const struct Symbol *sa = a, *sb = b;
if (sa->address < sb->address)
return -1;
else if (sa->address == sb->address)
return 0;
else
return 1;
}
return strcmp(arg1, arg2);
static void build_symbol_table(void *elf)
{
if (memcmp(elf, ELFMAG, 4) != 0)
goto error;
size_t symbol_table_symbols_c = 1024;
symbol_table.symbols = malloc(symbol_table_symbols_c * sizeof(*symbol_table.symbols));
if (symbol_table.symbols == NULL)
goto error;
const Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elf;
const Elf32_Shdr *shdrs = (Elf32_Shdr *)(elf + ehdr->e_shoff);
if (ehdr->e_shstrndx == SHN_UNDEF)
goto error;
const Elf32_Shdr *shdr_shstr = &shdrs[ehdr->e_shstrndx];
const char *shstr = (const char *)(elf + shdr_shstr->sh_offset);
const Elf32_Shdr *shdr_symtab = NULL;
const Elf32_Shdr *shdr_strtab = NULL;
for (int i = 0; i < ehdr->e_shnum; i++)
{
const char *sh_name = shstr + shdrs[i].sh_name;
if (strcmp(sh_name, ".symtab") == 0)
shdr_symtab = &shdrs[i];
else if (strcmp(sh_name, ".strtab") == 0)
shdr_strtab = &shdrs[i];
}
if (!shdr_symtab)
goto error;
if (!shdr_strtab)
goto error;
const Elf32_Sym *symtab = (Elf32_Sym *)(elf + shdr_symtab->sh_offset);
const char *strtab = (const char *)(elf + shdr_strtab->sh_offset);
for (int i = 0; i < shdr_symtab->sh_size / shdr_symtab->sh_entsize; i++)
{
if (symtab[i].st_name == 0) continue;
if (symtab[i].st_shndx > ehdr->e_shnum) continue;
if (symtab[i].st_value < 0x2000000 || symtab[i].st_size == 0) continue;
struct Symbol symbol =
{
.name = strtab + symtab[i].st_name,
.address = symtab[i].st_value,
.size = symtab[i].st_size,
};
if (symbol_table.symbols_n == symbol_table_symbols_c)
{
symbol_table_symbols_c *= 2;
void *symbols = realloc(symbol_table.symbols, symbol_table_symbols_c * sizeof(*symbol_table.symbols));
if (symbols == NULL)
goto error;
symbol_table.symbols = symbols;
}
symbol_table.symbols[symbol_table.symbols_n++] = symbol;
}
qsort(symbol_table.symbols, symbol_table.symbols_n, sizeof(*symbol_table.symbols), compare_addresses);
return;
error:
free(symbol_table.symbols);
symbol_table.symbols = NULL;
symbol_table.symbols_n = 0;
}
int main(int argc, char *argv[])
@ -292,6 +451,8 @@ int main(int argc, char *argv[])
exit(2);
}
build_symbol_table(elf);
nrunners = 1;
const char *makeflags = getenv("MAKEFLAGS");
if (makeflags)