Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

itemEntity struct and 2 functions #267

Closed
DenSinH opened this issue Apr 18, 2021 · 4 comments
Closed

itemEntity struct and 2 functions #267

DenSinH opened this issue Apr 18, 2021 · 4 comments

Comments

@DenSinH
Copy link

DenSinH commented Apr 18, 2021

While helping my friend out with his emulator, I found some info on the itemEntity struct and 2 functions. We used a dump of the RAM in his emulator to see what was going on.

The field currently named unk_34 seems to hold only 4 byte sized values. There is a function called update_item_entities, that should update item entities. In the ROM this is located at 900c85ec. This function is copied to RAM location 80131eec. In a for loop, this function checks if the itemID of some itemEntity structs is equal to 0x157, which from https://tcrf.net/Notes:Paper_Mario I gathered were coins. If they are coins, there is a 10% chance some values get updated in the struct, which I believe are values related to a (sparkling) animation sequence. The more interesting function call is that to FUN_90130acc, which is nothing, but after being copied to RAM this is a call to 80130acc corresponding to a function at 900c71cc. The decompilation for this function should be something like

void do_animation(itemEntity* item_entity) {
    if (--item_entity->frames_left < 1)  {  // Ghidra says < 1, presumably this is just == 0 and the field is an unsigned integer
        do { } while (next_step(item_entity));
    }
}

The frames_left field is the field at offset 0x3c in the itemEntity struct. Essentially, every animation sequence step (I am just guessing it is an animation sequence, it might be some other sequence) lasts for a certain amount of frames.
The next_step function in ROM is a call to 90130a04, which again points to nothing, but in RAM this is a call to 80130a04, corresponding to 900c7104 in the ROM. This function essentially does one step in the animation and returns whether a next step should be taken. It should look something like this:

int next_sequence_step(ItemEntity *item_entity)
{
  undefined4 uVar1;
  int *current_state_ptr;
  uint *next_ptr;
  
  current_state_ptr = item_entity->current_state_ptr;
  next_ptr = (uint *)(current_state_ptr + 1);  // this pointer has a different meaning depending on the state
  switch(*current_state_ptr) {
  case 0:  // this is an error state it seems, and will hang the above function
    return 1;
  case 1:
    item_entity->frames_left = *next_ptr;
    uVar1 = current_state_ptr[2];
    item_entity->current_state_ptr = current_state_ptr + 3;
    item_entity->field_0x44 = uVar1;
    break;
  case 2:
    item_entity->current_state_ptr = item_entity->sequence_start;
    return 1;
  case 3:
    item_entity->sequence_start = (int *)next_ptr;
    item_entity->current_state_ptr = (int *)next_ptr;
    return 1;
  case 4:
    item_entity->current_state_ptr = current_state_ptr + 2;
    return 1;
  case 5:
  case 6:
    break;
  case 7:
    item_entity->frames_left = *next_ptr;
    item_entity->field_0x4c = (int *)current_state_ptr[2];
    item_entity->field_0x50 = (int *)current_state_ptr[3];
    item_entity->field_0x54 = current_state_ptr[4];
    uVar1 = current_state_ptr[5];
    item_entity->current_state_ptr = current_state_ptr + 6;
    item_entity->field_0x58 = uVar1;
    break;
  default:
    return 0;
  }
  return 0;
}

essentially, the current_state_ptr (field offset 0x40) points to a struct, which varies on what state it is. In all cases it starts with an int showing what state it actually is. In the case of 0 it is just an int, and the do_animation function will hang. In case of 1, it looks like

struct sequence_state_1 {
    int state; // is always 1
    uint frames_left; // amount of frames it stays in this state
    int field_0x44; // struct field 0x44 is set to this value
}

case 2 resets the animation. The struct is also just an int then. case 3 switches to a new animation, and the struct looks like

struct {
    int state;  // is always 3
    int* next_animation; 
}

case 4 simply advances the state. The struct does hold an extra 32 bit piece of information, but this is not really used here it seems. case 5 or 6 do nothing, but will not hang the do_animation function. case 7 is the most interesting one, it holds a struct that looks like

struct {
    int state;  // always 7
    uint frames_left;  // amount of frames it stays in this state
    void* pointer_0x4c;  // pointer to 0x20 bytes of data
    void* pointer_0x50;  // pointer to at least 0x20 bytes of data
    int field_0x54;
    int field_0x58;
}

Of course, the integers showing the state are likely some enum. The struct sizes can be checked by the amount that item_entity->current_state_ptr is advanced (in strides of 4 bytes). An example of such a sequence can be found in ROM at 9009df70, copied to RAM at 80104ac0. This is a sequence that starts at state 4, then state 7 a few times and then state 0 (which presumably the N64 never reaches, have not figured this out yet). Looking at the values for the pointers of field 0x4c and 0x50, 4c seems to point at 0x20 bytes of data, and 0x50 at some multiple of 0x20.

Because of this, I also think that the itemEntity struct will look something like

typedef struct ItemEntity {
    /* 0x00 */ s32 flags;
    /* 0x04 */ s16 boundVar; /* see make_item_entity */
    /* 0x06 */ char unk_06[2];
    /* 0x08 */ Vec3f position;
    /* 0x14 */ struct ItemEntityPhysicsData* physicsData;
    /* 0x18 */ s16 itemID; /* into item table, also worldIconID */
    /* 0x1A */ u8 state;
    /* 0x1B */ u8 type;
    /* 0x1C */ u8 pickupDelay; /* num frames before item can be picked up */
    /* 0x1D */ char unk_1D;
    /* 0x1E */ s16 wsFaceAngle; /* < 0 means none */
    /* 0x20 */ s16 shadowIndex;
    /* 0x22 */ char unk_22[2];
    /* 0x24 */ u32* readPos;
    /* 0x28 */ u32* savedReadPos;
    /* 0x2C */ char unk_2C[2];
    /* 0x2E */ u8 unkCounter;
    /* 0x2F */ s8 unk_2F;
    /* 0x30 */ f32 scale;
    /* 0x34 */ u32 unk_34;
    /* 0x38 */ u32 unk_38;
    /* 0x3c */ u32 frames_left;
    /* 0x40 */ u32* current_state_ptr;
    /* 0x44 */ u32 unk_44;
    /* 0x48 */ u32* sequence_start_ptr;
    /* 0x4c */ u32* unk_4c;  // 32 bytes of data
    /* 0x50 */ u32* unk_50;  // 32 bytes or multiple of 32 bytes of data
    /* 0x54 */ u32 unk_54;
    /* 0x58 */ u32 unk_58;
} ItemEntity; // size = 0x5C
@ethteck
Copy link
Member

ethteck commented Apr 18, 2021

Thank you @DenSinH ! Please feel free to let us know if you make any further discoveries / want to get more involved with decomp in the future

@ethteck ethteck closed this as completed Apr 18, 2021
@Kelebek1
Copy link
Contributor

I didn't include the 2 structs in my PR since it seems you weren't too sure on them and they weren't used in these 2 functions. Let us know if you can flesh them out and get some functions which interact with them.

@DenSinH
Copy link
Author

DenSinH commented Apr 18, 2021

@ethteck apparently the real N64 might theoretically be able to reach the 0 (hanging) state but the probabiliy is very low due to the 10% chance of the animation being reset before that. So it seems that this theory is correct.

@Kelebek1 the thing is that the structs look different depending on the state, and all fields correspond to fields in the itemEntity struct, so I'm pretty sure that those are correct (except I don't know why the one for the 4 state is 2 32 bit values). The structs pointed at by field 4c and 50 (though 50 might point at some variable sized data), I don't know much about.

I would like to maybe help with the decomp some time, but I'll have to find some time to set it all up, since I gathered the goal is a byte-level accurate decompilation.

@bates64
Copy link
Member

bates64 commented Apr 18, 2021

I would like to maybe help with the decomp some time, but I'll have to find some time to set it all up, since I gathered the goal is a byte-level accurate decompilation.

Yep! IMO it's not especially difficult to set up if you have a Unix shell (Linux, macOS, WSL 2) already - there's no fancy stuff like manually setting up a N64 whole toolchain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants