diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.c | 45 | ||||
| -rw-r--r-- | src/sprite.c | 1795 | 
2 files changed, 1796 insertions, 44 deletions
| diff --git a/src/main.c b/src/main.c index 9e029eeb2..0c5055e60 100644 --- a/src/main.c +++ b/src/main.c @@ -3,50 +3,7 @@  #include "gba/flash_internal.h"  #include "siirtc.h"  #include "rtc.h" - -typedef void (*MainCallback)(void); -typedef void (*IntrCallback)(void); -typedef void (*IntrFunc)(void); - -struct OamData -{ -    u32 a, b; -}; - -struct Main -{ -    MainCallback callback1; -    MainCallback callback2; - -    u32 field_8; - -    IntrCallback vblankCallback; -    IntrCallback hblankCallback; -    IntrCallback vcountCallback; -    IntrCallback serialCallback; - -    vu16 intrCheck; - -    u32 vblankCounter1; -    u32 vblankCounter2; - -    u16 heldKeysRaw;           // held keys without L=A remapping -    u16 newKeysRaw;            // newly pressed keys without L=A remapping -    u16 heldKeys;              // held keys with L=A remapping -    u16 newKeys;               // newly pressed keys with L=A remapping -    u16 newAndRepeatedKeys;    // newly pressed keys plus key repeat -    u16 keyRepeatCounter;      // counts down to 0, triggering key repeat -    bool16 watchedKeysPressed; // whether one of the watched keys was pressed -    u16 watchedKeysMask;       // bit mask for watched keys - -    u32 field_38; - -    struct OamData oamBuffer[128]; - -    u8 state; - -    bool8 oamLoadDisabled; -}; +#include "main.h"  extern struct SoundInfo gSoundInfo;  extern u16 gUnknown_3002A20; diff --git a/src/sprite.c b/src/sprite.c new file mode 100644 index 000000000..1a586a63d --- /dev/null +++ b/src/sprite.c @@ -0,0 +1,1795 @@ +#include "global.h" +#include "main.h" +#include "sprite.h" + +#define MAX_SPRITES 64 + +#define MAX_SPRITE_COPY_REQUESTS 64 + +#define OAM_MATRIX_COUNT 32 + +#define SET_SPRITE_TILE_RANGE(index, start, count) \ +{                                                  \ +    u16 *rangeStarts;                              \ +    u16 *rangeCounts;                              \ +    rangeStarts = sSpriteTileRanges;               \ +    rangeStarts[index * 2] = start;                \ +    rangeCounts = sSpriteTileRanges + 1;           \ +    rangeCounts[index * 2] = count;                \ +} + +struct OamMatrix +{ +    s16 a; +    s16 b; +    s16 c; +    s16 d; +}; + +struct SpriteCopyRequest +{ +    u8 *src; +    u8 *dest; +    u16 size; +}; + +struct OamDimensions +{ +    s8 width; +    s8 height; +}; + +void gpu_pal_apply(u8 *, u16, u32); +void sub_814A590(void); + +static void UpdateOamCoords(void); +static void BuildSpritePriorities(void); +static void SortSprites(void); +static void CopyMatricesToOamBuffer(void); +static void AddSpritesToOamBuffer(void); +static u8 CreateSpriteAt(u8 index, struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority); +static void ClearSpriteCopyRequests(void); +static void ResetOamMatrices(void); +static void ResetSprite(struct Sprite *sprite); +static s16 AllocSpriteTiles(u16 tileCount); +static u8 SpriteTileAllocBitmapOp(u16 bit, u8 op); +static void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, struct SpriteFrameImage *images); +static void CopyFromSprites(u8 *dest); +static void CopyToSprites(u8 *src); +static void ResetAllSprites(void); +static void BeginAnim(struct Sprite *sprite); +static void ContinueAnim(struct Sprite *sprite); +static void AnimCmd_frame(struct Sprite *sprite); +static void AnimCmd_end(struct Sprite *sprite); +static void AnimCmd_jump(struct Sprite *sprite); +static void AnimCmd_loop(struct Sprite *sprite); +static void BeginAnimLoop(struct Sprite *sprite); +static void ContinueAnimLoop(struct Sprite *sprite); +static void JumpToTopOfAnimLoop(struct Sprite *sprite); +static void BeginAffineAnim(struct Sprite *sprite); +static void ContinueAffineAnim(struct Sprite *sprite); +static void AffineAnimDelay(u8 matrixNum, struct Sprite *sprite); +static void AffineAnimCmd_loop(u8 matrixNum, struct Sprite *sprite); +static void BeginAffineAnimLoop(u8 matrixNum, struct Sprite *sprite); +static void ContinueAffineAnimLoop(u8 matrixNum, struct Sprite *sprite); +static void JumpToTopOfAffineAnimLoop(u8 matrixNum, struct Sprite *sprite); +static void AffineAnimCmd_jump(u8 matrixNum, struct Sprite *sprite); +static void AffineAnimCmd_end(u8 matrixNum, struct Sprite *sprite); +static void AffineAnimCmd_frame(u8 matrixNum, struct Sprite *sprite); +static void CopyOamMatrix(u8 destMatrixIndex, struct OamMatrix *srcMatrix); +static u8 GetSpriteMatrixNum(struct Sprite *sprite); +static void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip); +static void AffineAnimStateRestartAnim(u8 matrixNum); +static void AffineAnimStateStartAnim(u8 matrixNum, u8 animNum); +static void AffineAnimStateReset(u8 matrixNum); +static void ApplyAffineAnimFrameAbsolute(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd); +static void DecrementAnimDelayCounter(struct Sprite *sprite); +static bool8 DecrementAffineAnimDelayCounter(struct Sprite *sprite, u8 matrixNum); +static void ApplyAffineAnimFrameRelativeAndUpdateMatrix(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd); +static s16 ConvertScaleParam(s16 scale); +static void GetAffineAnimFrame(u8 matrixNum, struct Sprite *sprite, struct AffineAnimFrameCmd *frameCmd); +static void ApplyAffineAnimFrame(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd); +static void ResetAffineAnimData(void); +static u8 IndexOfSpriteTileTag(u16 tag); +static void AddSpriteTileRange(u16 tag, u16 start, u16 count); +static void ApplySpritePalette(u8 *src, u16 paletteOffset); + +typedef void (*AnimFunc)(struct Sprite *); +typedef void (*AnimCmdFunc)(struct Sprite *); +typedef void (*AffineAnimCmdFunc)(u8 matrixNum, struct Sprite *); + +#define DUMMY_OAM_DATA        \ +{                             \ +    160, /* Y (off-screen) */ \ +    0,                        \ +    0,                        \ +    0,                        \ +    0,                        \ +    0,                        \ +    304, /* X */              \ +    0,                        \ +    0,                        \ +    0,                        \ +    3, /* lowest priority */  \ +    0,                        \ +    0                         \ +} + +#define ANIM_END        0xFFFF +#define AFFINE_ANIM_END 0x7FFF + +// forward declarations +const union AnimCmd * const gDummySpriteAnimTable[]; +const union AffineAnimCmd * const gDummySpriteAffineAnimTable[]; +const struct SpriteTemplate gDummySpriteTemplate; + +// Unreferenced error message. +// It means "The DMA transfer request table has exceeded its limit." +static const u8 sDmaOverErrorMsg[] = +    _"DMA OVER\n" +     "DMAてんそう\n" +     "リクエストテーブルが\n" +     "オーバーしました"; + +// Unreferenced data. +static const u8 sUnknownData[24] = +{ +    0x01, 0x04, 0x10, 0x40, +    0x02, 0x04, 0x08, 0x20, +    0x02, 0x04, 0x08, 0x20, +    0x01, 0x04, 0x10, 0x40, +    0x02, 0x04, 0x08, 0x20, +    0x02, 0x04, 0x08, 0x20, +}; + +static const u8 sCenterToCornerVecTable[3][4][2] = +{ +    {   // square +        {  -4,  -4 }, +        {  -8,  -8 }, +        { -16, -16 }, +        { -32, -32 }, +    }, +    {   // horizontal rectangle +        {  -8,  -4 }, +        { -16,  -4 }, +        { -16,  -8 }, +        { -32, -16 }, +    }, +    {   // vertical rectangle +        {  -4,  -8 }, +        {  -4, -16 }, +        {  -8, -16 }, +        { -16, -32 }, +    }, +}; + +static const struct Sprite sDummySprite = +{ +    DUMMY_OAM_DATA, +    (union AnimCmd **)gDummySpriteAnimTable, +    NULL, +    (union AffineAnimCmd **)gDummySpriteAffineAnimTable, +    (struct SpriteTemplate *)&gDummySpriteTemplate, +    NULL, +    SpriteCallbackDummy, +    { 304, 160 }, +    {   0,   0 }, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0xFF +}; + +const struct OamData gDummyOamData = DUMMY_OAM_DATA; + +static const union AnimCmd sDummyAnim = { ANIM_END }; + +const union AnimCmd * const gDummySpriteAnimTable[] = { &sDummyAnim }; + +static const union AffineAnimCmd sDummyAffineAnim = { AFFINE_ANIM_END }; + +const union AffineAnimCmd * const gDummySpriteAffineAnimTable[] = { &sDummyAffineAnim }; + +const struct SpriteTemplate gDummySpriteTemplate = +{ +    0, +    0xFFFF, +    (struct OamData *)&gDummyOamData, +    (union AnimCmd **)gDummySpriteAnimTable, +    NULL, +    (union AffineAnimCmd **)gDummySpriteAffineAnimTable, +    SpriteCallbackDummy +}; + +// TODO: Find out what these are used for. +static const u16 sOamBitmasks[9] = +{ +    0xFF00, 0x00FF, 0x001F, +    0xFE00, 0x01FF, 0x03E0, +    0xFC00, 0x03FF, 0xFC00, +}; + +static const AnimFunc sAnimFuncs[] = +{ +    ContinueAnim, +    BeginAnim, +}; + +static const AnimFunc sAffineAnimFuncs[] = +{ +    ContinueAffineAnim, +    BeginAffineAnim, +}; + +static const AnimCmdFunc sAnimCmdFuncs[] = +{ +    AnimCmd_loop, +    AnimCmd_jump, +    AnimCmd_end, +    AnimCmd_frame, +}; + +static const AffineAnimCmdFunc sAffineAnimCmdFuncs[] = +{ +    AffineAnimCmd_loop, +    AffineAnimCmd_jump, +    AffineAnimCmd_end, +    AffineAnimCmd_frame, +}; + +static const struct OamDimensions sOamDimensions[3][4] = +{ +    {   // square +        {  8,  8 }, +        { 16, 16 }, +        { 32, 32 }, +        { 64, 64 }, +    }, +    {   // horizontal rectangle +        { 16,  8 }, +        { 32,  8 }, +        { 32, 16 }, +        { 64, 32 }, +    }, +    {   // vertical rectangle +        {  8, 16 }, +        {  8, 32 }, +        { 16, 32 }, +        { 32, 64 }, +    }, +}; + +extern struct Main gMain; + +static u16 sSpriteTileRangeTags[MAX_SPRITES]; +static u16 sSpriteTileRanges[MAX_SPRITES * 2]; +static struct AffineAnimState sAffineAnimStates[OAM_MATRIX_COUNT]; +static u16 sSpritePaletteTags[16]; + +COMM_4(u8 gSpriteOrder[MAX_SPRITES]) +COMM_4(u8 gSpriteTileAllocBitmap[128]) +COMM_2(s16 gSpriteCoordOffsetX) +COMM_2(u8 gOamLimit) +COMM_2(u16 gReservedSpriteTileCount) +COMM_2(u8 gSpriteCopyRequestCount) +COMM_4(struct SpriteCopyRequest gSpriteCopyRequests[MAX_SPRITE_COPY_REQUESTS]) +COMM_2(s16 gSpriteCoordOffsetY) +COMM_4(struct OamMatrix gOamMatrices[OAM_MATRIX_COUNT]) +COMM_2(bool8 gShouldProcessSpriteCopyRequests) +COMM_2(u32 gOamMatrixAllocBitmap) +COMM_2(u8 gReservedSpritePaletteCount) + +EWRAM_DATA struct Sprite gSprites[MAX_SPRITES + 1] = {0}; +EWRAM_DATA u16 gSpritePriorities[MAX_SPRITES] = {0}; +EWRAM_DATA u8 gAffineAnimsDisabled = {0}; + +void ResetSpriteData(void) +{ +    ResetOamRange(0, 128); +    ResetAllSprites(); +    ClearSpriteCopyRequests(); +    ResetAffineAnimData(); +    FreeAllSpriteTiles(); +    gOamLimit = 64; +    gReservedSpriteTileCount = 0; +    AllocSpriteTiles(0); +    gSpriteCoordOffsetX = 0; +    gSpriteCoordOffsetY = 0; +} + +void AnimateSprites(void) +{ +    u8 i; +    for (i = 0; i < MAX_SPRITES; i++) +    { +        struct Sprite *sprite = &gSprites[i]; + +        if (sprite->inUse) +        { +            sprite->callback(sprite); + +            if (sprite->inUse) +                AnimateSprite(sprite); +        } +    } +} + +void BuildOamBuffer(void) +{ +    u8 temp; +    UpdateOamCoords(); +    BuildSpritePriorities(); +    SortSprites(); +    temp = gMain.oamLoadDisabled; +    gMain.oamLoadDisabled = TRUE; +    AddSpritesToOamBuffer(); +    CopyMatricesToOamBuffer(); +    gMain.oamLoadDisabled = temp; +    gShouldProcessSpriteCopyRequests = TRUE; +} + +static void UpdateOamCoords(void) +{ +    u8 i; +    for (i = 0; i < MAX_SPRITES; i++) +    { +        struct Sprite *sprite = &gSprites[i]; +        if (sprite->inUse && !sprite->invisible) +        { +            if (sprite->coordOffsetEnabled) +            { +                sprite->oam.x = sprite->pos1.x + sprite->pos2.x + sprite->centerToCornerVecX + gSpriteCoordOffsetX; +                sprite->oam.y = sprite->pos1.y + sprite->pos2.y + sprite->centerToCornerVecY + gSpriteCoordOffsetY; +            } +            else +            { +                sprite->oam.x = sprite->pos1.x + sprite->pos2.x + sprite->centerToCornerVecX; +                sprite->oam.y = sprite->pos1.y + sprite->pos2.y + sprite->centerToCornerVecY; +            } +        } +    } +} + +static void BuildSpritePriorities(void) +{ +    u16 i; +    for (i = 0; i < MAX_SPRITES; i++) +    { +        struct Sprite *sprite = &gSprites[i]; +        u16 priority = sprite->subpriority | (sprite->oam.priority << 8); +        gSpritePriorities[i] = priority; +    } +} + +static void SortSprites(void) +{ +    u8 i; +    for (i = 1; i < MAX_SPRITES; i++) +    { +        u8 j = i; +        struct Sprite *sprite1 = &gSprites[gSpriteOrder[i - 1]]; +        struct Sprite *sprite2 = &gSprites[gSpriteOrder[i]]; +        u16 sprite1Priority = gSpritePriorities[gSpriteOrder[i - 1]]; +        u16 sprite2Priority = gSpritePriorities[gSpriteOrder[i]]; +        s16 sprite1Y = sprite1->oam.y; +        s16 sprite2Y = sprite2->oam.y; + +        if (sprite1Y >= DISPLAY_HEIGHT) +            sprite1Y = sprite1Y - 256; + +        if (sprite2Y >= DISPLAY_HEIGHT) +            sprite2Y = sprite2Y - 256; + +        if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE +         && sprite1->oam.size == 3) +        { +            u32 shape = sprite1->oam.shape; +            if (shape == ST_OAM_SQUARE || shape == 2) +            { +                if (sprite1Y > 128) +                    sprite1Y = sprite1Y - 256; +            } +        } + +        if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE +         && sprite2->oam.size == 3) +        { +            u32 shape = sprite2->oam.shape; +            if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE) +            { +                if (sprite2Y > 128) +                    sprite2Y = sprite2Y - 256; +            } +        } + +        while (j > 0 +            && ((sprite1Priority > sprite2Priority) +             || (sprite1Priority == sprite2Priority && sprite1Y < sprite2Y))) +        { +            u8 temp = gSpriteOrder[j]; +            gSpriteOrder[j] = gSpriteOrder[j - 1]; +            gSpriteOrder[j - 1] = temp; + +            j--; + +            sprite1 = &gSprites[gSpriteOrder[j - 1]]; +            sprite2 = &gSprites[gSpriteOrder[j]]; +            sprite1Priority = gSpritePriorities[gSpriteOrder[j - 1]]; +            sprite2Priority = gSpritePriorities[gSpriteOrder[j]]; +            sprite1Y = sprite1->oam.y; +            sprite2Y = sprite2->oam.y; + +            if (sprite1Y >= DISPLAY_HEIGHT) +                sprite1Y = sprite1Y - 256; + +            if (sprite2Y >= DISPLAY_HEIGHT) +                sprite2Y = sprite2Y - 256; + +            if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE +             && sprite1->oam.size == 3) +            { +                u32 shape = sprite1->oam.shape; +                if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE) +                { +                    if (sprite1Y > 128) +                        sprite1Y = sprite1Y - 256; +                } +            } + +            if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE +             && sprite2->oam.size == 3) +            { +                u32 shape = sprite2->oam.shape; +                if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE) +                { +                    if (sprite2Y > 128) +                        sprite2Y = sprite2Y - 256; +                } +            } +        } +    } +} + +static void CopyMatricesToOamBuffer(void) +{ +    u8 i; +    for (i = 0; i < OAM_MATRIX_COUNT; i++) +    { +        u32 base = 4 * i; +        gMain.oamBuffer[base + 0].affineParam = gOamMatrices[i].a; +        gMain.oamBuffer[base + 1].affineParam = gOamMatrices[i].b; +        gMain.oamBuffer[base + 2].affineParam = gOamMatrices[i].c; +        gMain.oamBuffer[base + 3].affineParam = gOamMatrices[i].d; +    } +} + +static void AddSpritesToOamBuffer(void) +{ +    u8 i = 0; +    u8 oamIndex = 0; + +    while (i < MAX_SPRITES) +    { +        struct Sprite *sprite = &gSprites[gSpriteOrder[i]]; +        if (sprite->inUse && !sprite->invisible && AddSpriteToOamBuffer(sprite, &oamIndex)) +            break; +        i++; +    } + +    gMain.objCount = oamIndex; + +    while (oamIndex < gOamLimit) +    { +        gMain.oamBuffer[oamIndex] = gDummyOamData; +        oamIndex++; +    } +} + +u8 CreateSprite(struct SpriteTemplate *template, u16 x, u16 y, u8 subpriority) +{ +    u8 i; + +    for (i = 0; i < MAX_SPRITES; i++) +        if (!gSprites[i].inUse) +            return CreateSpriteAt(i, template, x, y, subpriority); + +    return MAX_SPRITES; +} + +u8 CreateSpriteAtEnd(struct SpriteTemplate *template, u16 x, u16 y, u8 subpriority) +{ +    s16 i; + +    for (i = MAX_SPRITES - 1; i > -1; i--) +        if (!gSprites[i].inUse) +            return CreateSpriteAt(i, template, x, y, subpriority); + +    return MAX_SPRITES; +} + +u8 CreateInvisibleSprite(void (*callback)(struct Sprite *)) +{ +    u8 index = CreateSprite((struct SpriteTemplate *)&gDummySpriteTemplate, 0, 0, 31); + +    if (index == MAX_SPRITES) +    { +        return MAX_SPRITES; +    } +    else +    { +        gSprites[index].invisible = TRUE; +        gSprites[index].callback = callback; +        return index; +    } +} + +static u8 CreateSpriteAt(u8 index, struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority) +{ +    struct Sprite *sprite = &gSprites[index]; + +    ResetSprite(sprite); + +    sprite->inUse = TRUE; +    sprite->animBeginning = TRUE; +    sprite->affineAnimBeginning = TRUE; +    sprite->usingSheet = TRUE; + +    sprite->subpriority = subpriority; +    sprite->oam = *template->oam; +    sprite->anims = template->anims; +    sprite->affineAnims = template->affineAnims; +    sprite->template = template; +    sprite->callback = template->callback; +    sprite->pos1.x = x; +    sprite->pos1.y = y; + +    CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode); + +    if (template->tileTag == 0xFFFF) +    { +        s16 tileNum; +        sprite->images = template->images; +        tileNum = AllocSpriteTiles((u8)(sprite->images->size / TILE_SIZE_4BPP)); +        if (tileNum == -1) +        { +            ResetSprite(sprite); +            return MAX_SPRITES; +        } +        sprite->oam.tileNum = tileNum; +        sprite->usingSheet = FALSE; +        sprite->sheetTileStart = 0; +    } +    else +    { +        sprite->sheetTileStart = GetSpriteTileStartByTag(template->tileTag); +        SetSpriteSheetFrameTileNum(sprite); +    } + +    if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) +        InitSpriteAffineAnim(sprite); + +    if (template->paletteTag != 0xFFFF) +        sprite->oam.paletteNum = IndexOfSpritePaletteTag(template->paletteTag); + +    return index; +} + +u8 CreateSpriteAndAnimate(struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority) +{ +    u8 i; + +    for (i = 0; i < MAX_SPRITES; i++) +    { +        struct Sprite *sprite = &gSprites[i]; + +        if (!gSprites[i].inUse) +        { +            u8 index = CreateSpriteAt(i, template, x, y, subpriority); + +            if (index == MAX_SPRITES) +                return MAX_SPRITES; + +            gSprites[i].callback(sprite); + +            if (gSprites[i].inUse) +                AnimateSprite(sprite); + +            return index; +        } +    } + +    return MAX_SPRITES; +} + +void DestroySprite(struct Sprite *sprite) +{ +    if (sprite->inUse) +    { +        if (!sprite->usingSheet) +        { +            u16 i; +            u16 tileEnd = (sprite->images->size / TILE_SIZE_4BPP) + sprite->oam.tileNum; +            for (i = sprite->oam.tileNum; i < tileEnd; i++) +                gSpriteTileAllocBitmap[i / 8] &= ~(1 << (i % 8)); +        } +        ResetSprite(sprite); +    } +} + +void ResetOamRange(u8 a, u8 b) +{ +    u8 i; + +    for (i = a; i < b; i++) +    { +        struct OamData *oamBuffer = gMain.oamBuffer; +        oamBuffer[i] = *(struct OamData *)&gDummyOamData; +    } +} + +void LoadOam(void) +{ +    if (!gMain.oamLoadDisabled) +        CpuCopy32(gMain.oamBuffer, (void *)OAM, sizeof(gMain.oamBuffer)); +} + +static void ClearSpriteCopyRequests(void) +{ +    u8 i; + +    gShouldProcessSpriteCopyRequests = FALSE; +    gSpriteCopyRequestCount = 0; + +    for (i = 0; i < MAX_SPRITE_COPY_REQUESTS; i++) +    { +        gSpriteCopyRequests[i].src = 0; +        gSpriteCopyRequests[i].dest = 0; +        gSpriteCopyRequests[i].size = 0; +    } +} + +static void ResetOamMatrices(void) +{ +    u8 i; +    for (i = 0; i < OAM_MATRIX_COUNT; i++) +    { +        // set to identity matrix +        gOamMatrices[i].a = 0x0100; +        gOamMatrices[i].b = 0x0000; +        gOamMatrices[i].c = 0x0000; +        gOamMatrices[i].d = 0x0100; +    } +} + +void SetOamMatrix(u8 matrixNum, u16 a, u16 b, u16 c, u16 d) +{ +    gOamMatrices[matrixNum].a = a; +    gOamMatrices[matrixNum].b = b; +    gOamMatrices[matrixNum].c = c; +    gOamMatrices[matrixNum].d = d; +} + +static void ResetSprite(struct Sprite *sprite) +{ +    *sprite = sDummySprite; +} + +void CalcCenterToCornerVec(struct Sprite *sprite, u8 shape, u8 size, u8 affineMode) +{ +    u8 x = sCenterToCornerVecTable[shape][size][0]; +    u8 y = sCenterToCornerVecTable[shape][size][1]; + +    if (affineMode & ST_OAM_AFFINE_DOUBLE_MASK) +    { +        x *= 2; +        y *= 2; +    } + +    sprite->centerToCornerVecX = x; +    sprite->centerToCornerVecY = y; +} + +static s16 AllocSpriteTiles(u16 tileCount) +{ +    u16 i; +    s16 start; +    u16 numTilesFound; + +    if (tileCount == 0) +    { +        for (i = gReservedSpriteTileCount; i < TOTAL_OBJ_TILE_COUNT; i++) +            gSpriteTileAllocBitmap[i / 8] &= ~(1 << (i % 8)); + +        return 0; +    } + +    i = gReservedSpriteTileCount; + +    for (;;) +    { +        while ((gSpriteTileAllocBitmap[i / 8] >> (i % 8)) & 1) +        { +            i++; + +            if (i == TOTAL_OBJ_TILE_COUNT) +                return -1; +        } + +        start = i; +        numTilesFound = 1; + +        while (numTilesFound != tileCount) +        { +            i++; + +            if (i == TOTAL_OBJ_TILE_COUNT) +                return -1; + +            if (!((gSpriteTileAllocBitmap[i / 8] >> (i % 8)) & 1)) +                numTilesFound++; +            else +                break; +        } + +        if (numTilesFound == tileCount) +            break; +    } + +    for (i = start; i < tileCount + start; i++) +        gSpriteTileAllocBitmap[i / 8] |= (1 << (i % 8)); + +    return start; +} + +static u8 SpriteTileAllocBitmapOp(u16 bit, u8 op) +{ +    u8 index = bit / 8; +    u8 shift = bit % 8; +    u8 val = bit % 8; +    u8 retVal = 0; + +    if (op == 0) +    { +        val = ~(1 << val); +        gSpriteTileAllocBitmap[index] &= val; +    } +    else if (op == 1) +    { +        val = (1 << val); +        gSpriteTileAllocBitmap[index] |= val; +    } +    else +    { +        retVal = 1 << shift; +        retVal &= gSpriteTileAllocBitmap[index]; +    } + +    return retVal; +} + +void SpriteCallbackDummy(struct Sprite *sprite) +{ +} + +void ProcessSpriteCopyRequests(void) +{ +    if (gShouldProcessSpriteCopyRequests) +    { +        u8 i = 0; + +        while (gSpriteCopyRequestCount > 0) +        { +            CpuCopy16(gSpriteCopyRequests[i].src, gSpriteCopyRequests[i].dest, gSpriteCopyRequests[i].size); +            gSpriteCopyRequestCount--; +            i++; +        } + +        gShouldProcessSpriteCopyRequests = FALSE; +    } +} + +static void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, struct SpriteFrameImage *images) +{ +    if (gSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS) +    { +        gSpriteCopyRequests[gSpriteCopyRequestCount].src = images[index].data; +        gSpriteCopyRequests[gSpriteCopyRequestCount].dest = (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileNum; +        gSpriteCopyRequests[gSpriteCopyRequestCount].size = images[index].size; +        gSpriteCopyRequestCount++; +    } +} + +void RequestSpriteCopy(u8 *src, u8 *dest, u16 size) +{ +    if (gSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS) +    { +        gSpriteCopyRequests[gSpriteCopyRequestCount].src = src; +        gSpriteCopyRequests[gSpriteCopyRequestCount].dest = dest; +        gSpriteCopyRequests[gSpriteCopyRequestCount].size = size; +        gSpriteCopyRequestCount++; +    } +} + +static void CopyFromSprites(u8 *dest) +{ +    u32 i; +    u8 *src = (u8 *)gSprites; +    for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++) +    { +        *dest = *src; +        dest++; +        src++; +    } +} + +static void CopyToSprites(u8 *src) +{ +    u32 i; +    u8 *dest = (u8 *)gSprites; +    for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++) +    { +        *dest = *src; +        src++; +        dest++; +    } +} + +static void ResetAllSprites(void) +{ +    u8 i; + +    for (i = 0; i < MAX_SPRITES; i++) +    { +        ResetSprite(&gSprites[i]); +        gSpriteOrder[i] = i; +    } + +    ResetSprite(&gSprites[i]); +    sub_814A590(); +} + +void FreeSpriteTiles(struct Sprite *sprite) +{ +    if (sprite->template->tileTag != 0xFFFF) +        FreeSpriteTilesByTag(sprite->template->tileTag); +} + +void FreeSpritePalette(struct Sprite *sprite) +{ +    FreeSpritePaletteByTag(sprite->template->paletteTag); +} + +void FreeSpriteOamMatrix(struct Sprite *sprite) +{ +    if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) +    { +        FreeOamMatrix(sprite->oam.matrixNum); +        sprite->oam.affineMode = ST_OAM_AFFINE_OFF; +    } +} + +void DestroySpriteAndFreeResources(struct Sprite *sprite) +{ +    FreeSpriteTiles(sprite); +    FreeSpritePalette(sprite); +    FreeSpriteOamMatrix(sprite); +    DestroySprite(sprite); +} + +void sub_800142C(u32 a1, u32 a2, u16 *a3, u16 a4, u32 a5) +{ +    u16 *d = a3; +    struct OamData *oam = &gMain.oamBuffer[gMain.objCount]; +    while (!(gMain.objCount & 0x80) && (s16)(d[0] + 1) != 0) +    { +        u16 *x = (u16 *)oam; +        x[0] = (d[0] & sOamBitmasks[0]) | ((d[0] + a2) & sOamBitmasks[1]) | ((a4 & sOamBitmasks[2]) << 8); +        x[1] = (d[1] & sOamBitmasks[3]) | ((d[1] + a1) & sOamBitmasks[4]) | ((a4 & sOamBitmasks[5]) << 4); +        x[2] = (d[2] & sOamBitmasks[6]) | ((d[2] + a5) & sOamBitmasks[7]) | (a4 & sOamBitmasks[8]); +        oam++; +        gMain.objCount++; +        d += 3; +    } +} + +void AnimateSprite(struct Sprite *sprite) +{ +    sAnimFuncs[sprite->animBeginning](sprite); + +    if (!gAffineAnimsDisabled) +        sAffineAnimFuncs[sprite->affineAnimBeginning](sprite); +} + +static void BeginAnim(struct Sprite *sprite) +{ +    s16 imageValue; +    u8 duration; +    u8 hFlip; +    u8 vFlip; + +    sprite->animCmdIndex = 0; +    sprite->animEnded = FALSE; +    sprite->animLoopCounter = 0; +    imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue; + +    if (imageValue != -1) +    { +        sprite->animBeginning = FALSE; +        duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration; +        hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip; +        vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip; + +        if (duration) +            duration--; + +        sprite->animDelayCounter = duration; + +        if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)) +            SetSpriteOamFlipBits(sprite, hFlip, vFlip); + +        if (sprite->usingSheet) +            sprite->oam.tileNum = sprite->sheetTileStart + imageValue; +        else +            RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images); +    } +} + +static void ContinueAnim(struct Sprite *sprite) +{ +    if (sprite->animDelayCounter) +    { +        u8 hFlip; +        u8 vFlip; +        DecrementAnimDelayCounter(sprite); +        hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip; +        vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip; +        if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)) +            SetSpriteOamFlipBits(sprite, hFlip, vFlip); +    } +    else if (!sprite->animPaused) +    { +        s16 type; +        s16 funcIndex; +        sprite->animCmdIndex++; +        type = sprite->anims[sprite->animNum][sprite->animCmdIndex].type; +        funcIndex = 3; +        if (type < 0) +            funcIndex = type + 3; +        sAnimCmdFuncs[funcIndex](sprite); +    } +} + +static void AnimCmd_frame(struct Sprite *sprite) +{ +    s16 imageValue; +    u8 duration; +    u8 hFlip; +    u8 vFlip; + +    imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue; +    duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration; +    hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip; +    vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip; + +    if (duration) +        duration--; + +    sprite->animDelayCounter = duration; + +    if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)) +        SetSpriteOamFlipBits(sprite, hFlip, vFlip); + +    if (sprite->usingSheet) +        sprite->oam.tileNum = sprite->sheetTileStart + imageValue; +    else +        RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images); +} + +static void AnimCmd_end(struct Sprite *sprite) +{ +    sprite->animCmdIndex--; +    sprite->animEnded = TRUE; +} + +static void AnimCmd_jump(struct Sprite *sprite) +{ +    s16 imageValue; +    u8 duration; +    u8 hFlip; +    u8 vFlip; + +    sprite->animCmdIndex = sprite->anims[sprite->animNum][sprite->animCmdIndex].jump.target; + +    imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue; +    duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration; +    hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip; +    vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip; + +    if (duration) +        duration--; + +    sprite->animDelayCounter = duration; + +    if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)) +        SetSpriteOamFlipBits(sprite, hFlip, vFlip); + +    if (sprite->usingSheet) +        sprite->oam.tileNum = sprite->sheetTileStart + imageValue; +    else +        RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images); +} + +static void AnimCmd_loop(struct Sprite *sprite) +{ +    if (sprite->animLoopCounter) +        ContinueAnimLoop(sprite); +    else +        BeginAnimLoop(sprite); +} + +static void BeginAnimLoop(struct Sprite *sprite) +{ +    sprite->animLoopCounter = sprite->anims[sprite->animNum][sprite->animCmdIndex].loop.count; +    JumpToTopOfAnimLoop(sprite); +    ContinueAnim(sprite); +} + +static void ContinueAnimLoop(struct Sprite *sprite) +{ +    sprite->animLoopCounter--; +    JumpToTopOfAnimLoop(sprite); +    ContinueAnim(sprite); +} + +static void JumpToTopOfAnimLoop(struct Sprite *sprite) +{ +    if (sprite->animLoopCounter) +    { +        sprite->animCmdIndex--; + +        while (sprite->anims[sprite->animNum][sprite->animCmdIndex - 1].type != -3) +        { +            if (sprite->animCmdIndex == 0) +                break; +            sprite->animCmdIndex--; +        } + +        sprite->animCmdIndex--; +    } +} + +static void BeginAffineAnim(struct Sprite *sprite) +{ +    if ((sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) && sprite->affineAnims[0][0].type != 32767) +    { +        struct AffineAnimFrameCmd frameCmd; +        u8 matrixNum = GetSpriteMatrixNum(sprite); +        AffineAnimStateRestartAnim(matrixNum); +        GetAffineAnimFrame(matrixNum, sprite, &frameCmd); +        sprite->affineAnimBeginning = FALSE; +        sprite->affineAnimEnded = FALSE; +        ApplyAffineAnimFrame(matrixNum, &frameCmd); +        sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration; +    } +} + +static void ContinueAffineAnim(struct Sprite *sprite) +{ +    if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) +    { +        u8 matrixNum = GetSpriteMatrixNum(sprite); + +        if (sAffineAnimStates[matrixNum].delayCounter) +        { +            AffineAnimDelay(matrixNum, sprite); +        } +        else if (!sprite->affineAnimPaused) +        { +            s16 type; +            s16 funcIndex; +            sAffineAnimStates[matrixNum].animCmdIndex++; +            type = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].type; +            funcIndex = 3; +            if (type >= 32765) +                funcIndex = type - 32765; +            sAffineAnimCmdFuncs[funcIndex](matrixNum, sprite); +        } +    } +} + +static void AffineAnimDelay(u8 matrixNum, struct Sprite *sprite) +{ +    if (!DecrementAffineAnimDelayCounter(sprite, matrixNum)) +    { +        struct AffineAnimFrameCmd frameCmd; +        GetAffineAnimFrame(matrixNum, sprite, &frameCmd); +        ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &frameCmd); +    } +} + +static void AffineAnimCmd_loop(u8 matrixNum, struct Sprite *sprite) +{ +    if (sAffineAnimStates[matrixNum].loopCounter) +        ContinueAffineAnimLoop(matrixNum, sprite); +    else +        BeginAffineAnimLoop(matrixNum, sprite); +} + +static void BeginAffineAnimLoop(u8 matrixNum, struct Sprite *sprite) +{ +    sAffineAnimStates[matrixNum].loopCounter = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].loop.count; +    JumpToTopOfAffineAnimLoop(matrixNum, sprite); +    ContinueAffineAnim(sprite); +} + +static void ContinueAffineAnimLoop(u8 matrixNum, struct Sprite *sprite) +{ +    sAffineAnimStates[matrixNum].loopCounter--; +    JumpToTopOfAffineAnimLoop(matrixNum, sprite); +    ContinueAffineAnim(sprite); +} + +static void JumpToTopOfAffineAnimLoop(u8 matrixNum, struct Sprite *sprite) +{ +    if (sAffineAnimStates[matrixNum].loopCounter) +    { +        sAffineAnimStates[matrixNum].animCmdIndex--; + +        while (sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex - 1].type != 32765) +        { +            if (sAffineAnimStates[matrixNum].animCmdIndex == 0) +                break; +            sAffineAnimStates[matrixNum].animCmdIndex--; +        } + +        sAffineAnimStates[matrixNum].animCmdIndex--; +    } +} + +static void AffineAnimCmd_jump(u8 matrixNum, struct Sprite *sprite) +{ +    struct AffineAnimFrameCmd frameCmd; +    sAffineAnimStates[matrixNum].animCmdIndex = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].jump.target; +    GetAffineAnimFrame(matrixNum, sprite, &frameCmd); +    ApplyAffineAnimFrame(matrixNum, &frameCmd); +    sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration; +} + +static void AffineAnimCmd_end(u8 matrixNum, struct Sprite *sprite) +{ +    struct AffineAnimFrameCmd dummyFrameCmd = {0}; +    sprite->affineAnimEnded = TRUE; +    sAffineAnimStates[matrixNum].animCmdIndex--; +    ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &dummyFrameCmd); +} + +static void AffineAnimCmd_frame(u8 matrixNum, struct Sprite *sprite) +{ +    struct AffineAnimFrameCmd frameCmd; +    GetAffineAnimFrame(matrixNum, sprite, &frameCmd); +    ApplyAffineAnimFrame(matrixNum, &frameCmd); +    sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration; +} + +static void CopyOamMatrix(u8 destMatrixIndex, struct OamMatrix *srcMatrix) +{ +    gOamMatrices[destMatrixIndex].a = srcMatrix->a; +    gOamMatrices[destMatrixIndex].b = srcMatrix->b; +    gOamMatrices[destMatrixIndex].c = srcMatrix->c; +    gOamMatrices[destMatrixIndex].d = srcMatrix->d; +} + +static u8 GetSpriteMatrixNum(struct Sprite *sprite) +{ +    u8 matrixNum = 0; +    if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) +        matrixNum = sprite->oam.matrixNum; +    return matrixNum; +} + +static void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip) +{ +    sprite->oam.matrixNum &= 0x7; +    sprite->oam.matrixNum |= (((hFlip ^ sprite->hFlip) & 1) << 3); +    sprite->oam.matrixNum |= (((vFlip ^ sprite->vFlip) & 1) << 4); +} + +static void AffineAnimStateRestartAnim(u8 matrixNum) +{ +    sAffineAnimStates[matrixNum].animCmdIndex = 0; +    sAffineAnimStates[matrixNum].delayCounter = 0; +    sAffineAnimStates[matrixNum].loopCounter = 0; +} + +static void AffineAnimStateStartAnim(u8 matrixNum, u8 animNum) +{ +    sAffineAnimStates[matrixNum].animNum = animNum; +    sAffineAnimStates[matrixNum].animCmdIndex = 0; +    sAffineAnimStates[matrixNum].delayCounter = 0; +    sAffineAnimStates[matrixNum].loopCounter = 0; +    sAffineAnimStates[matrixNum].xScale = 0x0100; +    sAffineAnimStates[matrixNum].yScale = 0x0100; +    sAffineAnimStates[matrixNum].rotation = 0; +} + +static void AffineAnimStateReset(u8 matrixNum) +{ +    sAffineAnimStates[matrixNum].animNum = 0; +    sAffineAnimStates[matrixNum].animCmdIndex = 0; +    sAffineAnimStates[matrixNum].delayCounter = 0; +    sAffineAnimStates[matrixNum].loopCounter = 0; +    sAffineAnimStates[matrixNum].xScale = 0x0100; +    sAffineAnimStates[matrixNum].yScale = 0x0100; +    sAffineAnimStates[matrixNum].rotation = 0; +} + +static void ApplyAffineAnimFrameAbsolute(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd) +{ +    sAffineAnimStates[matrixNum].xScale = frameCmd->xScale; +    sAffineAnimStates[matrixNum].yScale = frameCmd->yScale; +    sAffineAnimStates[matrixNum].rotation = frameCmd->rotation << 8; +} + +static void DecrementAnimDelayCounter(struct Sprite *sprite) +{ +    if (!sprite->animPaused) +        sprite->animDelayCounter--; +} + +static bool8 DecrementAffineAnimDelayCounter(struct Sprite *sprite, u8 matrixNum) +{ +    if (!sprite->affineAnimPaused) +        --sAffineAnimStates[matrixNum].delayCounter; +    return sprite->affineAnimPaused; +} + +static void ApplyAffineAnimFrameRelativeAndUpdateMatrix(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd) +{ +    struct ObjAffineSrcData srcData; +    struct OamMatrix matrix; +    sAffineAnimStates[matrixNum].xScale += frameCmd->xScale; +    sAffineAnimStates[matrixNum].yScale += frameCmd->yScale; +    sAffineAnimStates[matrixNum].rotation = (sAffineAnimStates[matrixNum].rotation + (frameCmd->rotation << 8)) & ~0xFF; +    srcData.xScale = ConvertScaleParam(sAffineAnimStates[matrixNum].xScale); +    srcData.yScale = ConvertScaleParam(sAffineAnimStates[matrixNum].yScale); +    srcData.rotation = sAffineAnimStates[matrixNum].rotation; +    ObjAffineSet(&srcData, &matrix, 1, 2); +    CopyOamMatrix(matrixNum, &matrix); +} + +static s16 ConvertScaleParam(s16 scale) +{ +    s32 val = 0x10000; +    return val / scale; +} + +static void GetAffineAnimFrame(u8 matrixNum, struct Sprite *sprite, struct AffineAnimFrameCmd *frameCmd) +{ +    frameCmd->xScale = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.xScale; +    frameCmd->yScale = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.yScale; +    frameCmd->rotation = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.rotation; +    frameCmd->duration = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.duration; +} + +static void ApplyAffineAnimFrame(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd) +{ +    struct AffineAnimFrameCmd dummyFrameCmd = {0}; + +    if (frameCmd->duration) +    { +        frameCmd->duration--; +        ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, frameCmd); +    } +    else +    { +        ApplyAffineAnimFrameAbsolute(matrixNum, frameCmd); +        ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &dummyFrameCmd); +    } +} + +void StartSpriteAnim(struct Sprite *sprite, u8 animNum) +{ +    sprite->animNum = animNum; +    sprite->animBeginning = TRUE; +    sprite->animEnded = FALSE; +} + +void StartSpriteAnimIfDifferent(struct Sprite *sprite, u8 animNum) +{ +    if (sprite->animNum != animNum) +        StartSpriteAnim(sprite, animNum); +} + +void SeekSpriteAnim(struct Sprite *sprite, u8 animCmdIndex) +{ +    u8 temp = sprite->animPaused; +    sprite->animCmdIndex = animCmdIndex - 1; +    sprite->animDelayCounter = 0; +    sprite->animBeginning = FALSE; +    sprite->animEnded = FALSE; +    sprite->animPaused = FALSE; +    ContinueAnim(sprite); +    if (sprite->animDelayCounter) +        sprite->animDelayCounter++; +    sprite->animPaused = temp; +} + +void StartSpriteAffineAnim(struct Sprite *sprite, u8 animNum) +{ +    u8 matrixNum = GetSpriteMatrixNum(sprite); +    AffineAnimStateStartAnim(matrixNum, animNum); +    sprite->affineAnimBeginning = TRUE; +    sprite->affineAnimEnded = FALSE; +} + +void StartSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum) +{ +    u8 matrixNum = GetSpriteMatrixNum(sprite); +    if (sAffineAnimStates[matrixNum].animNum != animNum) +        StartSpriteAffineAnim(sprite, animNum); +} + +void ChangeSpriteAffineAnim(struct Sprite *sprite, u8 animNum) +{ +    u8 matrixNum = GetSpriteMatrixNum(sprite); +    sAffineAnimStates[matrixNum].animNum = animNum; +    sprite->affineAnimBeginning = TRUE; +    sprite->affineAnimEnded = FALSE; +} + +void ChangeSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum) +{ +    u8 matrixNum = GetSpriteMatrixNum(sprite); +    if (sAffineAnimStates[matrixNum].animNum != animNum) +        ChangeSpriteAffineAnim(sprite, animNum); +} + +void SetSpriteSheetFrameTileNum(struct Sprite *sprite) +{ +    if (sprite->usingSheet) +    { +        s16 tileOffset = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue; +        if (tileOffset < 0) +            tileOffset = 0; +        sprite->oam.tileNum = sprite->sheetTileStart + tileOffset; +    } +} + +static void ResetAffineAnimData(void) +{ +    u8 i; + +    gAffineAnimsDisabled = 0; +    gOamMatrixAllocBitmap = 0; + +    ResetOamMatrices(); + +    for (i = 0; i < OAM_MATRIX_COUNT; i++) +        AffineAnimStateReset(i); +} + +u8 AllocOamMatrix(void) +{ +    u8 i = 0; +    u32 bit = 1; +    u32 bitmap = gOamMatrixAllocBitmap; + +    while (i < OAM_MATRIX_COUNT) +    { +        if (!(bitmap & bit)) +        { +            gOamMatrixAllocBitmap |= bit; +            return i; +        } + +        i++; +        bit <<= 1; +    } + +    return 0xFF; +} + +void FreeOamMatrix(u8 matrixNum) +{ +    u8 i = 0; +    u32 bit = 1; + +    while (i < matrixNum) +    { +        i++; +        bit <<= 1; +    } + +    gOamMatrixAllocBitmap &= ~bit; +    SetOamMatrix(matrixNum, 0x100, 0, 0, 0x100); +} + +void InitSpriteAffineAnim(struct Sprite *sprite) +{ +    u8 matrixNum = AllocOamMatrix(); +    if (matrixNum != 0xFF) +    { +        CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode); +        sprite->oam.matrixNum = matrixNum; +        sprite->affineAnimBeginning = TRUE; +        AffineAnimStateReset(matrixNum); +    } +} + +void SetOamMatrixRotationScaling(u8 matrixNum, s16 xScale, s16 yScale, u16 rotation) +{ +    struct ObjAffineSrcData srcData; +    struct OamMatrix matrix; +    srcData.xScale = ConvertScaleParam(xScale); +    srcData.yScale = ConvertScaleParam(yScale); +    srcData.rotation = rotation; +    ObjAffineSet(&srcData, &matrix, 1, 2); +    CopyOamMatrix(matrixNum, &matrix); +} + +u16 LoadSpriteSheet(struct SpriteSheet *sheet) +{ +    s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP); + +    if (tileStart < 0) +    { +        return 0; +    } +    else +    { +        AddSpriteTileRange(sheet->tag, (u16)tileStart, sheet->size / TILE_SIZE_4BPP); +        CpuCopy16(sheet->data, (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileStart, sheet->size); +        return (u16)tileStart; +    } +} + +void LoadSpriteSheets(struct SpriteSheet *sheets) +{ +    u8 i; +    for (i = 0; sheets[i].data != NULL; i++) +        LoadSpriteSheet(&sheets[i]); +} + +u16 AllocTilesForSpriteSheet(struct SpriteSheet *sheet) +{ +    s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP); + +    if (tileStart < 0) +    { +        return 0; +    } +    else +    { +        AddSpriteTileRange(sheet->tag, (u16)tileStart, sheet->size / TILE_SIZE_4BPP); +        return (u16)tileStart; +    } +} + +void AllocTilesForSpriteSheets(struct SpriteSheet *sheets) +{ +    u8 i; +    for (i = 0; sheets[i].data != NULL; i++) +        AllocTilesForSpriteSheet(&sheets[i]); +} + +void LoadTilesForSpriteSheet(struct SpriteSheet *sheet) +{ +    u8 *data = sheet->data; +    u16 tileStart = GetSpriteTileStartByTag(sheet->tag); +    CpuCopy16(data, (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileStart, sheet->size); +} + +void LoadTilesForSpriteSheets(struct SpriteSheet *sheets) +{ +    u8 i; +    for (i = 0; sheets[i].data != NULL; i++) +        LoadTilesForSpriteSheet(&sheets[i]); +} + +void FreeSpriteTilesByTag(u16 tag) +{ +    u8 index = IndexOfSpriteTileTag(tag); +    if (index != 0xFF) +    { +        u16 i; +        u16 *rangeStarts; +        u16 *rangeCounts; +        u16 start; +        u16 count; +        rangeStarts = sSpriteTileRanges; +        start = rangeStarts[index * 2]; +        rangeCounts = sSpriteTileRanges + 1; +        count = rangeCounts[index * 2]; + +        for (i = start; i < start + count; i++) +            gSpriteTileAllocBitmap[i / 8] &= ~(1 << (i % 8)); + +        sSpriteTileRangeTags[index] = 0xFFFF; +    } +} + +void FreeAllSpriteTiles(void) +{ +    u8 i; + +    for (i = 0; i < MAX_SPRITES; i++) +    { +        sSpriteTileRangeTags[i] = 0xFFFF; +        SET_SPRITE_TILE_RANGE(i, 0, 0); +    } +} + +u16 GetSpriteTileStartByTag(u16 tag) +{ +    u8 index = IndexOfSpriteTileTag(tag); +    if (index == 0xFF) +        return 0xFFFF; +    return sSpriteTileRanges[index * 2]; +} + +static u8 IndexOfSpriteTileTag(u16 tag) +{ +    u8 i; + +    for (i = 0; i < MAX_SPRITES; i++) +        if (sSpriteTileRangeTags[i] == tag) +            return i; + +    return 0xFF; +} + +u16 GetSpriteTileTagByTileStart(u16 start) +{ +    u8 i; + +    for (i = 0; i < MAX_SPRITES; i++) +    { +        if (sSpriteTileRangeTags[i] != 0xFFFF && sSpriteTileRanges[i * 2] == start) +            return sSpriteTileRangeTags[i]; +    } + +    return 0xFFFF; +} + +static void AddSpriteTileRange(u16 tag, u16 start, u16 count) +{ +    u8 freeIndex = IndexOfSpriteTileTag(0xFFFF); +    sSpriteTileRangeTags[freeIndex] = tag; +    SET_SPRITE_TILE_RANGE(freeIndex, start, count); +} + +void RequestSpriteSheetCopy(struct SpriteSheet *sheet) +{ +    u8 *data = sheet->data; +    u16 tileStart = GetSpriteTileStartByTag(sheet->tag); +    RequestSpriteCopy(data, (u8 *)OBJ_VRAM0 + tileStart * TILE_SIZE_4BPP, sheet->size); +} + +u16 LoadSpriteSheetDeferred(struct SpriteSheet *sheet) +{ +    s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP); + +    if (tileStart < 0) +    { +        return 0; +    } +    else +    { +        AddSpriteTileRange(sheet->tag, (u16)tileStart, sheet->size / TILE_SIZE_4BPP); +        RequestSpriteSheetCopy(sheet); +        return (u16)tileStart; +    } +} + +void FreeAllSpritePalettes(void) +{ +    u8 i; +    gReservedSpritePaletteCount = 0; +    for (i = 0; i < 16; i++) +        sSpritePaletteTags[i] = 0xFFFF; +} + +u8 LoadSpritePalette(struct SpritePalette *palette) +{ +    u8 index = IndexOfSpritePaletteTag(palette->tag); + +    if (index != 0xFF) +        return index; + +    index = IndexOfSpritePaletteTag(0xFFFF); + +    if (index == 0xFF) +    { +        return 0xFF; +    } +    else +    { +        sSpritePaletteTags[index] = palette->tag; +        ApplySpritePalette(palette->data, index * 16); +        return index; +    } +} + +void LoadSpritePalettes(struct SpritePalette *palettes) +{ +    u8 i; +    for (i = 0; palettes[i].data != NULL; i++) +        if (LoadSpritePalette(&palettes[i]) == 0xFF) +            break; +} + +static void ApplySpritePalette(u8 *src, u16 paletteOffset) +{ +    gpu_pal_apply(src, paletteOffset + 0x100, 32); +} + +u8 AllocSpritePalette(u16 tag) +{ +    u8 index = IndexOfSpritePaletteTag(0xFFFF); +    if (index == 0xFF) +    { +        return 0xFF; +    } +    else +    { +        sSpritePaletteTags[index] = tag; +        return index; +    } +} + +u8 IndexOfSpritePaletteTag(u16 tag) +{ +    u8 i; +    for (i = gReservedSpritePaletteCount; i < 16; i++) +        if (sSpritePaletteTags[i] == tag) +            return i; + +    return 0xFF; +} + +u16 GetSpritePaletteTagByPaletteNum(u8 paletteNum) +{ +    return sSpritePaletteTags[paletteNum]; +} + +void FreeSpritePaletteByTag(u16 tag) +{ +    u8 index = IndexOfSpritePaletteTag(tag); +    if (index != 0xFF) +        sSpritePaletteTags[index] = 0xFFFF; +} + +void SetSubspriteTables(struct Sprite *sprite, struct SubspriteTable *subspriteTables) +{ +    sprite->subspriteTables = subspriteTables; +    sprite->subspriteTableNum = 0; +    sprite->subspriteMode = SUBSPRITES_ON; +} + +bool8 AddSpriteToOamBuffer(struct Sprite *sprite, u8 *oamIndex) +{ +    if (*oamIndex >= gOamLimit) +        return 1; + +    if (!sprite->subspriteTables || sprite->subspriteMode == SUBSPRITES_OFF) +    { +        gMain.oamBuffer[*oamIndex] = sprite->oam; +        (*oamIndex)++; +        return 0; +    } +    else +    { +        return AddSubspritesToOamBuffer(sprite, &gMain.oamBuffer[*oamIndex], oamIndex); +    } +} + +bool8 AddSubspritesToOamBuffer(struct Sprite *sprite, struct OamData *destOam, u8 *oamIndex) +{ +    struct SubspriteTable *subspriteTable; +    struct OamData *oam; + +    if (*oamIndex >= gOamLimit) +        return 1; + +    subspriteTable = &sprite->subspriteTables[sprite->subspriteTableNum]; +    oam = &sprite->oam; + +    if (!subspriteTable || !subspriteTable->subsprites) +    { +        *destOam = *oam; +        (*oamIndex)++; +        return 0; +    } +    else +    { +        u16 tileNum; +        u16 baseX; +        u16 baseY; +        u8 subspriteCount; +        u8 hFlip; +        u8 vFlip; +        u8 i; + +        tileNum = oam->tileNum; +        subspriteCount = subspriteTable->subspriteCount; +        hFlip = ((s32)oam->matrixNum >> 3) & 1; +        vFlip = ((s32)oam->matrixNum >> 4) & 1; +        baseX = oam->x - sprite->centerToCornerVecX; +        baseY = oam->y - sprite->centerToCornerVecY; + +        for (i = 0; i < subspriteCount; i++, (*oamIndex)++) +        { +            u16 x; +            u16 y; + +            if (*oamIndex >= gOamLimit) +                return 1; + +            x = subspriteTable->subsprites[i].x; +            y = subspriteTable->subsprites[i].y; + +            if (hFlip) +            { +                s8 width = sOamDimensions[subspriteTable->subsprites[i].shape][subspriteTable->subsprites[i].size].width; +                s16 right = x; +                right += width; +                x = right; +                x = ~x + 1; +            } + +            if (vFlip) +            { +                s8 height = sOamDimensions[subspriteTable->subsprites[i].shape][subspriteTable->subsprites[i].size].height; +                s16 bottom = y; +                bottom += height; +                y = bottom; +                y = ~y + 1; +            } + +            destOam[i] = *oam; +            destOam[i].shape = subspriteTable->subsprites[i].shape; +            destOam[i].size = subspriteTable->subsprites[i].size; +            destOam[i].x = (s16)baseX + (s16)x; +            destOam[i].y = baseY + y; +            destOam[i].tileNum = tileNum + subspriteTable->subsprites[i].tileOffset; + +            if (sprite->subspriteMode != SUBSPRITES_IGNORE_PRIORITY) +                destOam[i].priority = subspriteTable->subsprites[i].priority; +        } +    } + +    return 0; +} | 
