User Tools

Site Tools


yoshis_story

This is an old revision of the document!


Yoshi's Story Hacking


ROM "bank-offset" addressing

Yoshi's Story uses 32-bit “bank-offsets” to point to resources in ROM. The bank table is located at 0x000A79D4 in ROM (0x800A6DD4 in RAM) as an array of bank base addresses. There are 16 available slots for bank addresses, but only bank numbers 0x00, 0x03, and 0x04 seem to be used by the game.

Bank #ROM address
0x000x00001060
0x010x00000000
0x020x00000000
0x030x00526EC0
0x040x00B47A10
0x050x00000000
0x060x00000000
0x070x00000000
0x080x00000000
0x090x00000000
0x0A0x00000000
0x0B0x00000000
0x0C0x00000000
0x0D0x00000000
0x0E0x00000000
0x0F0x00000000

Bank-offsets are 32 bits, containing a 4-bit bank number and a 24-bit offset:

---- bbbb oooooooooooooooooooooooo
BitfieldDescription
-unused bits
b4-bit bank number
o24-bit offset

So a bank-offset to ROM address conversion formula could look something like this:

u32 boToAddress(u32 bankOffset)
{
    u8  bankNum = (bankOffset >> 24) & 0x0F;
    u32 offset = bankOffset & 0x00FFFFFF;
    u32 romAddress = gRomBanks[bankNum] + offset;
    return romAddress;
}

Levels

The main level list is at 0x000A84A0 in ROM as an array of 177 “LevelEntry” structs.

LevelEntry struct

This struct simply points to a LevelInfo struct.

struct LevelEntry
{
    /*00*/ u32 unk00;
    /*04*/ u32 boLevelInfo; // bank-offset of LevelInfo struct
};

LevelInfo struct

This struct contains a pointer to an ObjectPlacementListInfo struct. The rest of the data hasn't been studied yet.

struct LevelInfo
{
    /*00*/ u8    unk00[8];
    /*08*/ f32   unk08;
    /*0C*/ u8    unk0C[24];
    /*24*/ void *unk24;
    /*28*/ u8    unk28[4];
    /*2C*/ f32   unk2C;
    /*30*/ f32   unk30;
    /*34*/ u8    unk34[4];
    /*38*/ f32   unk38;
    /*3C*/ f32   unk3C;
    /*40*/ u8    unk40[4];
    /*44*/ f32   unk44;
    /*48*/ f32   unk48;
    /*4C*/ f32   unk4C;
    /*50*/ u8    unk50[4];
    /*54*/ f32   unk54;
    /*58*/ u8    unk58[4];
    /*5C*/ u32   boObjectPlacementListInfoPtr; // bank-offset of bank-offset of ObjectPlacementListInfo
    /*60*/ u16   unk60[0x98];                  // seems to be a list of objectId's to preload? terminated by 0xFFFF? unknown size
    /*F8*/ u8    unkF8[28];
};

ObjectPlacementListInfo struct

This struct simply references an array of ObjectPlacement structs.

struct ObjectPlacementListInfo
{
    /*00*/ u16 numObjects;         // number of ObjectPlacement structs in the array
    /*02*/ u16 unk02;              // probably padding
    /*04*/ u32 boObjectPlacements; // bank-offset of ObjectPlacement array
};

ObjectPlacement struct

This struct contains the object ID, tag(?), and position of an object to be placed in a level.

struct ObjectPlacement
{
    u16 objectId; // object ID
    u16 tag;      // purpose seems to depend on the type of object
    f32 x;        // x coordinate in level
    f32 y;        // y coordinate in level
};

Objects

The object lookup table is at 0x000A1C90 in ROM as an array of 1582 ObjectEntry structs.

ObjectEntry struct

struct ObjectEntry
{
    /*00*/ u16 objectId;  // unique identifier
    /*02*/ u16 unk02;     // probably padding
    /*04*/ u32 boObjectDefA; // bank-offset of ObjectDefA struct
    /*08*/ u32 boObjectDefB; // bank-offset of ObjectDefB struct
};

Object IDs

ObjectDefA_00 struct

This structure defines the properties of an object.

struct ObjectDefA_00
{
    /*00*/ u16     type;                 // structure type (0x0001)
           u8      _pad02[2];
    /*04*/ CodeInfo codeInfo;
    /*18*/ u16   objectId;             // unique identifier
    /*1A*/ u16   unk1A;
    /*1C*/ u32   tileDimensionsSize;   // sizeof(TileDimensions)
    /*20*/ u32   boTileDimensions;     // bank-offset of TileDimensions struct
    /*24*/ u32   tileDataInfoSize;     // sizeof(BlockInfo)
    /*28*/ u32   boTileDataInfo;       // bank-offset of BlockInfo struct for the tiles' 8bit color-index data
    /*2C*/ u32   tilePaletteInfoSize;  // sizeof(BlockInfo)
    /*30*/ u32   boTilePaletteInfo;    /* bank-offset of BlockInfo struct for the tiles' palette data
                                          palette data is a 0x200 byte array of rgba5551 (u16) colors */
    /*34*/ u32   tileMapInfoSize;      // sizeof(BlockInfo)
    /*38*/ u32   boTileMapInfo;        /* bank-offset of BlockInfo struct for the tile map
                                          the tile map is a u16 array tile indeces */
    /*3C*/ u32   unkData1InfoSize;     // sizeof(BlockInfo)
    /*40*/ u32   boUnkData1;           // bank-offset of BlockInfo struct for ??
    /*44*/ u32   unkData2InfoSize;     // sizeof(BlockInfo)
    /*48*/ u32   boUnkData2;           // bank-offset of BlockInfo struct for ??
    /*4C*/ u32   collisionMapInfoSize; // sizeof(BlockInfo)
    /*50*/ u32   boCollisionMapInfo;   /* bank-offset of BlockInfo struct for collision data
                                          the collision map is a u16 array, parallel to the tile map array */
    /*54*/ u8    unk54[16];            // unknown data
};

ObjectDefA_01 struct

This structure defines the properties of the Yoshi player object.

struct ObjectDefA_01   // only used for yoshi
{
    /*0x00*/ u16 type;   // structure type (0x0001)
    /*0x04*/ u8  unk04;
    /*0x08*/ u32 unk08;
    /*0x18*/ u16 unk18;
    /*0x1C*/ u8  unk1C;
    /*0x1D*/ u8  unk1D;
    /*0x20*/ u32 boUnk20;
    /*0x24*/ u32 boUnk24;
    /*0x28*/ u32 boUnk28;
    /*0x2C*/ u32 unk2C;
    /*0x30*/ u32 boUnk30;
    /*0x34*/ u32 unk34;
    /*0x38*/ u32 unk38;
    /*0x3C*/ u32 unk3C;
    /*0x40*/ u32 boUnk40;
};

ObjectDefA_02 struct

struct ObjectDefA_02
{
    /*00*/ u16 type;      // structure type (0x0002)
           u8 _pad02[2];
           CodeInfo codeInfo;
};

ObjectDefB struct

struct ObjectDefB
{
    // todo
};

TileDimensions struct

struct TileDimensions
{
    /*0x00*/ u16 unk00;      // ?? related to width
    /*0x02*/ u16 unk02;      // ?? related to height
    /*0x04*/ u16 tileWidth;  // width of an individual tile
    /*0x06*/ u16 tileHeight; // height of an individual tile
    /*0x08*/ u16 mapWidth;   // width of the whole map (num tiles X * tileWidth)
    /*0x0A*/ u16 mapHeight   // height of the whole map (num tiles Y * tileHeight)
};

BlockInfo struct

This structure references a block of data in ROM. The block of data may either be raw or SMSR00 compressed depending on the size and sizeEnc fields.

struct BlockInfo
{
    /*0x00*/ u32 size;    // decoded block size
    /*0x04*/ u32 sizeEnc; // encoded block size
    /*0x08*/ u32 boData;  /* bank-offset of the data (may be a CMPR or raw)
                             if the data is raw, both size and sizeEnc will be the same value */
};

CodeInfo struct

This struct references code that an object should use. It can either reference an overlay file or a single function already in memory.

struct CodeInfo
{
    u8 useOverlay;          // 1 = use overlay, 0 = use nativeFunc
    u8 _pad[3];             // padding
    union {
        void *nativeFunc;   // pointer to native function
        struct
        {
            u32 romStart;       // overlay rom start address
            u32 romEnd;         // overlay rom end address
            void *fakeMemStart; // fake ram start address 
            void *fakeMemEnd;   // fake ram end address
        } overlay;
    };
};

Level/Object Data Diagram

Here is a diagram of this game's pointer spaghetti.


Overlays

Yoshi's Story uses overlays to save memory. The structure for overlay files is as follows:

u8  text[textSize]
u8  data[dataSize]
u8  rodata[rodataSize]
meta {
    u32 textSize
    u32 dataSize
    u32 rodataSize
    u32 bssSize
    u32 numRelocations
    u32 relocations[numRelocations]
    u32 metaSize
}
  • The position of the meta section is calculated by subtracting metaSize from the total size of the overlay file.
  • The .bss section is allocated immediately after the overlay file in memory.

Relocation bitfields:

ss -- rrrr oooooooooooooooooooooooo
BitfieldDescription
sSection (0 = invalid?, 1 = .text, 2 = .data, 3 = .rodata)
-Unused?
rRelocation type (4 = R_MIPS_26, 5 = R_MIPS_HI16, 6 = R_MIPS_LO16)
oOffset

The dynamic linker routine is located at 0x80081430 in RAM.

func_80081430(void *overlay_block, void *meta_section);

The game keeps track of active overlays using an array of the following struct at 0x800DA840 in RAM.

struct OverlayEntry
{
    /*00*/ void *location;     // actual location in memory
    /*04*/ u16   objectId;     // id of the object that this overlay belongs to
           u8    _pad[2];
    /*08*/ void *fakeMemStart; // fake start address
    /*0C*/ void *fakeMemEnd;   // fake end address
};

Compression

Yoshi's Story uses an LZ77-type compression format called SMSR00 which is very similar to SM64's MIO0 format. Each SMSR00 block in ROM is preceded by a 16-byte 'CMPR' header which contains the size of the encoded block and the size of the block when it's decoded.

CMPRHeader struct

struct CMPRHeader
{
    /*00*/ char cmprSignature[4]; // 'CMPR'
    /*04*/ u32  sizeEnc;          // size of encoded data
    /*08*/ u32  size;             // size of decoded data
    /*0C*/ u32  unused;           // probably padding
};

SMSRHeader struct

struct SMSRHeader
{
    /*00*/ char smsrSignature[6]; // 'SMSR00'
    /*06*/ u8   unused[2];        // padding
    /*08*/ u32  outputSize;       // size of decoded data
    /*0C*/ u32  dataOffset;       // offset of the data section in the smsr00 block
};

Decoder implementations

References

yoshis_story.1569538351.txt.gz · Last modified: 2019/09/26 22:52 by shygoo