Skip to content

pokeathlon part 3#418

Open
nathanfallet wants to merge 19 commits intopret:masterfrom
nathanfallet:pokeathlon-part-3
Open

pokeathlon part 3#418
nathanfallet wants to merge 19 commits intopret:masterfrom
nathanfallet:pokeathlon-part-3

Conversation

@nathanfallet
Copy link
Contributor

Same idea as #416 but I'm doing the next functions.

@nathanfallet nathanfallet marked this pull request as ready for review November 14, 2025 00:03
@nathanfallet nathanfallet changed the title wip: pokeathlon part 3 pokeathlon part 3 Nov 14, 2025
Comment on lines 282 to 285
struct {
void *ptr;
u8 bytes[4];
} *stateInfo = dest;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have identified the structure stored at the memory address dest, therefore you know the dest argument points to an instance of this struct, instead of an ambiguous void *. The function prototype should make this clear. :)

Try backtracking the function call, see where this dest takes its value from. It's the pointer to a member of PokeathlonCourseData. Can you somehow fit a StateInfo object inside PokeathlonCourseData so that the code still matches? As a hint, this will give you insight into what u8 bytes[4] actually contains.

Comment on lines 304 to 306
void *dest = PokeathlonCourse_GetPlayerProfile(data->heapAllocPtr1, 0);
void *src = Save_PlayerData_GetProfile(data->args->saveData);
PlayerProfile_Copy(src, dest);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like you're relying on void pointers too much. Take a look at this for example. You have a call to Save_PlayerData_GetProfile. Looking at the function prototype, you know the parameter is of type SaveData*, and the return value is of type PlayerProfile*. This should be enough for you to

  • define saveData in PokeathlonCourseArgs as a SaveData* instead of void*,
  • define the src variable you're initialising here as PlayerProfile* instead of void*,
  • modify PokeathlonCourse_GetSaveData function declaration so that it returns a SaveData* instead of void*...

By modifying the type of PokeathlonCourseArgs::saveData first, you should get a bunch of compile warnings about implicit conversions, which will help you find all spots in the code you need to update to the new type.

Likewise, PlayerProfile_Copy takes a PlayerProfile* as the second argument, so that should be the type of dest.

Comment on lines 316 to 318
void *PokeathlonCourse_GetPlayerProfile(void *profiles, int index) {
return (u8 *)profiles + (PlayerProfile_sizeof() * index);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you apply the changes suggested above, you'll see that you've initialised a PlayerProfile* by calling PokeathlonCourse_GetPlayerProfile. This lets you know that PokeathlonCourse_GetPlayerProfile returns a PlayerProfile*, and you should update the function prototype to reflect that. It also suggests that void *profiles is actually an array of PlayerProfile objects.

On a side note, is the cast to (u8 *) really necessary? It matches for me without it.

Copy link
Collaborator

@adrienntindall adrienntindall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with what Laquinh is saying.

Most of the void* calls are actually pointers to structs or function pointers that you haven't defined yet and that need to be defined, even if they're left as unknowns for now.

}

BOOL ov96_021E5C2C(PokeathlonCourseData *data) {
typedef BOOL (*StateFunc)(PokeathlonCourseData *, void **);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't typedef in a local scope

typedef BOOL (*StateFunc)(PokeathlonCourseData *, void **);
void **stateDataPtr = (void **)&data->stateArgsBase;
u8 index = data->stateIndex;
void **functionTable = data->stateData;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a void** instead of a StateFunc* data type?

BOOL result;

do {
statePtr = (void **)data->state;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be a StateHandlerFunc* data type

}

void *PokeathlonCourse_GetParticipantData1(PokeathlonCourseData *data, int index) {
return (u8 *)&data->participantData1Base + (0x7C * index);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define participantData1Base struct, this is obvious struct access to it.

}

void *PokeathlonCourse_GetParticipantData2(PokeathlonCourseData *data, int index) {
return &data->participantData2[0x7C * index];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a struct array where participantData2 is a struct of size 0x7C

}

void *ov96_021E5D78(PokeathlonCourseData *data, int index) {
return &data->field_72C[0x60 * index];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a struct array where field_72C is a struct of size 0x60

Copy link
Collaborator

@adrienntindall adrienntindall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to request changes on previous review

@nathanfallet
Copy link
Contributor Author

Thanks for all your feedbacks. I'll try to have a look and fix that soon!

@nathanfallet
Copy link
Contributor Author

I tried to make it better based on your reviews, but I cannot get all of it yet. So maybe we keep it close to what we currently have and you can have a look later if you want to improve it. Let me know what you think.

Comment on lines +329 to +331
PokeathlonFieldData *ov96_021E5D6C(PokeathlonCourseData *data) {
return (PokeathlonFieldData *)data->field_72C;
}
Copy link
Contributor

@Laquinh Laquinh Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function makes me believe that field_72C is, in fact, an array of PokeathlonFieldData instead of an array of u8. That would allow us to get rid of the casting.
And PokeathlonFieldData takes up 0x60 bytes! Maybe that has something to do with field_72C's 0x60 stride? :)

Comment on lines +317 to +323
PokeathlonParticipantData *PokeathlonCourse_GetParticipantData1(PokeathlonCourseData *data, int index) {
return (PokeathlonParticipantData *)((u8 *)&data->participantData1Base + (0x7C * index));
}

PokeathlonParticipantData *PokeathlonCourse_GetParticipantData2(PokeathlonCourseData *data, int index) {
return (PokeathlonParticipantData *)&data->participantData2[0x7C * index];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a similar case. It smells a lot like there's gotta be a PokeathlonParticipantData array somewhere inside PokeathlonCourseData, especially given that we're using a 0x7C stride here, and PokeathlonParticipantData takes up 0x7C bytes.

May I ask where you got the idea that the fields are divided in participantData1Base and participantData2? a 4-element array of PokeathlonParticipantData in participantData1Base's place would fit perfectly, and would clean the PokeathlonCourse_GetParticipantData1 function a lot. As for PokeathlonCourse_GetParticipantData2, maybe it's accessing a member of PokeathlonParticipantData? And that would explain the +0x04 offset you were probably seeing in the assembly. From what i know, you don't really have any proof that PokeathlonCourse_GetParticipantData2 returns a PokeathlonParticipantData*, after all.

Afterwards, the functions should be renamed, so the one above is something like PokeathlonCourse_GetParticipantData (not 1), and the one below is something like PokeathlonCourse_GetParticipantUnk04 (this is a suggestion, idk if others may have a different opinion on how to name such a function).

Comment on lines +279 to +285
void PokeathlonCourse_InitStateInfo(const void *src, PokeathlonStateInfo *dest) {
dest->ptr = (void *)src;
dest->field_04 = 1;
dest->stateArgsBase = 0;
dest->stateIndex = 0;
dest->field_07 = 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of naming the parameters src and dest. It suggests that you're going to copy the contents of src onto dest or something like that, which you're not doing.

}

PlayerProfile *PokeathlonCourse_GetPlayerProfile(PlayerProfile *profiles, int index) {
return (PlayerProfile *)((u8 *)profiles + (PlayerProfile_sizeof() * index));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have too much spare time right now and I haven't managed to match changing any of this, but I'm pretty confident there should be a cleaner way to write this function.


// Participant data structure (0x7C = 124 bytes each)
typedef struct PokeathlonParticipantData {
u8 data[0x7C]; // Opaque data for now
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As explained in the other comment, there appears to be a function (PokeathlonCourse_GetParticipantData2) that returns the 4th element of this array. May be worth to un-opaque that one element (?), like

Suggested change
u8 data[0x7C]; // Opaque data for now
u8 field_00[0x03];
u8 field_04;
u8 field_05[0x78];

Not sure if this is something we usually do, though.

Copy link
Contributor

@Laquinh Laquinh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely an improvement :)

@github-actions
Copy link

github-actions bot commented Feb 2, 2026

This pull request has had no activity for 60 days and will be marked stale. If there is no further activity, it will be closed in 30 days.

@github-actions github-actions bot added the stale label Feb 2, 2026
@nathanfallet
Copy link
Contributor Author

nathanfallet commented Feb 2, 2026

Hey @Laquinh @adrienntindall I don't think I'll have more time (in upcoming days) to improve the code here. So either we merge that and you're free to improve it later, or we let the PR close and we loose all of that (would be sad right?) What do you think?

@adrienntindall
Copy link
Collaborator

Hey @Laquinh @adrienntindall I don't think I'll have more time (in upcoming days) to improve the code here. So either we merge that and you're free to improve it later, or we let the PR close and we loose all of that (would be sad right?) What do you think?

I think that the current state of the PR isn't up to the repository's standards to merge. Asking maintainers to clean up code in your stead when we have already asked for changes isn't fair to anyone actively working on the project.

It is ok if you take a while to get things done, but if the PR remains inactive then it will be closed. If you want to finish it at a later date and reopen it after that then that's fine, but we will still be asking for all the requested changes to me made.

@github-actions github-actions bot removed the stale label Feb 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants