diff options
Diffstat (limited to 'gflib')
-rw-r--r-- | gflib/bg.c | 1256 | ||||
-rw-r--r-- | gflib/bg.h | 85 | ||||
-rw-r--r-- | gflib/blit.c | 209 | ||||
-rw-r--r-- | gflib/blit.h | 17 | ||||
-rw-r--r-- | gflib/dma3.h | 55 | ||||
-rw-r--r-- | gflib/dma3_manager.c | 181 | ||||
-rw-r--r-- | gflib/gpu_regs.c | 195 | ||||
-rw-r--r-- | gflib/gpu_regs.h | 19 | ||||
-rw-r--r-- | gflib/malloc.c | 210 | ||||
-rw-r--r-- | gflib/malloc.h | 22 | ||||
-rw-r--r-- | gflib/sprite.c | 1758 | ||||
-rw-r--r-- | gflib/sprite.h | 319 | ||||
-rw-r--r-- | gflib/string_util.c | 784 | ||||
-rw-r--r-- | gflib/string_util.h | 46 | ||||
-rw-r--r-- | gflib/text.c | 2540 | ||||
-rw-r--r-- | gflib/text.h | 312 | ||||
-rw-r--r-- | gflib/window.c | 721 | ||||
-rw-r--r-- | gflib/window.h | 78 |
18 files changed, 8807 insertions, 0 deletions
diff --git a/gflib/bg.c b/gflib/bg.c new file mode 100644 index 000000000..ab4e8b60d --- /dev/null +++ b/gflib/bg.c @@ -0,0 +1,1256 @@ +#include "global.h" +#include "bg.h" +#include "dma3.h" +#include "gpu_regs.h" + +#define DISPCNT_ALL_BG_AND_MODE_BITS (DISPCNT_BG_ALL_ON | 0x7) + +struct BgControl +{ + struct BgConfig { + u16 visible:1; + u16 unknown_1:1; + u16 screenSize:2; + u16 priority:2; + u16 mosaic:1; + u16 wraparound:1; + + u16 charBaseIndex:2; + u16 mapBaseIndex:5; + u16 paletteMode:1; + + u8 unknown_2; + u8 unknown_3; + } configs[4]; + + u16 bgVisibilityAndMode; +}; + +struct BgConfig2 +{ + u32 baseTile:10; + u32 basePalette:4; + u32 unk_3:18; + + void* tilemap; + s32 bg_x; + s32 bg_y; +}; + +static struct BgControl sGpuBgConfigs; +static struct BgConfig2 sGpuBgConfigs2[4]; +static u32 sDmaBusyBitfield[4]; + +u32 gUnneededFireRedVariable; + +static const struct BgConfig sZeroedBgControlStruct = { 0 }; + +void ResetBgs(void) +{ + ResetBgControlStructs(); + sGpuBgConfigs.bgVisibilityAndMode = 0; + SetTextModeAndHideBgs(); +} + +static void SetBgModeInternal(u8 bgMode) +{ + sGpuBgConfigs.bgVisibilityAndMode &= 0xFFF8; + sGpuBgConfigs.bgVisibilityAndMode |= bgMode; +} + +u8 GetBgMode(void) +{ + return sGpuBgConfigs.bgVisibilityAndMode & 0x7; +} + +void ResetBgControlStructs(void) +{ + struct BgConfig* bgConfigs = &sGpuBgConfigs.configs[0]; + struct BgConfig zeroedConfig = sZeroedBgControlStruct; + int i; + + for (i = 0; i < 4; i++) + { + bgConfigs[i] = zeroedConfig; + } +} + +void Unused_ResetBgControlStruct(u8 bg) +{ + if (!IsInvalidBg(bg)) + { + sGpuBgConfigs.configs[bg] = sZeroedBgControlStruct; + } +} + +enum +{ + BG_CTRL_ATTR_VISIBLE = 1, + BG_CTRL_ATTR_CHARBASEINDEX = 2, + BG_CTRL_ATTR_MAPBASEINDEX = 3, + BG_CTRL_ATTR_SCREENSIZE = 4, + BG_CTRL_ATTR_PALETTEMODE = 5, + BG_CTRL_ATTR_PRIORITY = 6, + BG_CTRL_ATTR_MOSAIC = 7, + BG_CTRL_ATTR_WRAPAROUND = 8, +}; + +static void SetBgControlAttributes(u8 bg, u8 charBaseIndex, u8 mapBaseIndex, u8 screenSize, u8 paletteMode, u8 priority, u8 mosaic, u8 wraparound) +{ + if (!IsInvalidBg(bg)) + { + if (charBaseIndex != 0xFF) + { + sGpuBgConfigs.configs[bg].charBaseIndex = charBaseIndex & 0x3; + } + + if (mapBaseIndex != 0xFF) + { + sGpuBgConfigs.configs[bg].mapBaseIndex = mapBaseIndex & 0x1F; + } + + if (screenSize != 0xFF) + { + sGpuBgConfigs.configs[bg].screenSize = screenSize & 0x3; + } + + if (paletteMode != 0xFF) + { + sGpuBgConfigs.configs[bg].paletteMode = paletteMode; + } + + if (priority != 0xFF) + { + sGpuBgConfigs.configs[bg].priority = priority & 0x3; + } + + if (mosaic != 0xFF) + { + sGpuBgConfigs.configs[bg].mosaic = mosaic & 0x1; + } + + if (wraparound != 0xFF) + { + sGpuBgConfigs.configs[bg].wraparound = wraparound; + } + + sGpuBgConfigs.configs[bg].unknown_2 = 0; + sGpuBgConfigs.configs[bg].unknown_3 = 0; + + sGpuBgConfigs.configs[bg].visible = 1; + } +} + +static u16 GetBgControlAttribute(u8 bg, u8 attributeId) +{ + if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible) + { + switch (attributeId) + { + case BG_CTRL_ATTR_VISIBLE: + return sGpuBgConfigs.configs[bg].visible; + case BG_CTRL_ATTR_CHARBASEINDEX: + return sGpuBgConfigs.configs[bg].charBaseIndex; + case BG_CTRL_ATTR_MAPBASEINDEX: + return sGpuBgConfigs.configs[bg].mapBaseIndex; + case BG_CTRL_ATTR_SCREENSIZE: + return sGpuBgConfigs.configs[bg].screenSize; + case BG_CTRL_ATTR_PALETTEMODE: + return sGpuBgConfigs.configs[bg].paletteMode; + case BG_CTRL_ATTR_PRIORITY: + return sGpuBgConfigs.configs[bg].priority; + case BG_CTRL_ATTR_MOSAIC: + return sGpuBgConfigs.configs[bg].mosaic; + case BG_CTRL_ATTR_WRAPAROUND: + return sGpuBgConfigs.configs[bg].wraparound; + } + } + + return 0xFF; +} + +u8 LoadBgVram(u8 bg, const void *src, u16 size, u16 destOffset, u8 mode) +{ + u16 offset; + s8 cursor; + + if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible) + { + switch (mode) + { + case 0x1: + offset = sGpuBgConfigs.configs[bg].charBaseIndex * BG_CHAR_SIZE; + break; + case 0x2: + offset = sGpuBgConfigs.configs[bg].mapBaseIndex * BG_SCREEN_SIZE; + break; + default: + cursor = -1; + goto end; + } + + offset = destOffset + offset; + + cursor = RequestDma3Copy(src, (void*)(offset + BG_VRAM), size, 0); + + if (cursor == -1) + { + return -1; + } + } + else + { + return -1; + } + +end: + return cursor; +} + +static void ShowBgInternal(u8 bg) +{ + u16 value; + if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible) + { + value = sGpuBgConfigs.configs[bg].priority | + (sGpuBgConfigs.configs[bg].charBaseIndex << 2) | + (sGpuBgConfigs.configs[bg].mosaic << 6) | + (sGpuBgConfigs.configs[bg].paletteMode << 7) | + (sGpuBgConfigs.configs[bg].mapBaseIndex << 8) | + (sGpuBgConfigs.configs[bg].wraparound << 13) | + (sGpuBgConfigs.configs[bg].screenSize << 14); + + SetGpuReg((bg << 1) + REG_OFFSET_BG0CNT, value); + + sGpuBgConfigs.bgVisibilityAndMode |= 1 << (bg + 8); + sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS; + } +} + +static void HideBgInternal(u8 bg) +{ + if (!IsInvalidBg(bg)) + { + sGpuBgConfigs.bgVisibilityAndMode &= ~(1 << (bg + 8)); + sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS; + } +} + +static void SyncBgVisibilityAndMode(void) +{ + SetGpuReg(REG_OFFSET_DISPCNT, (GetGpuReg(REG_OFFSET_DISPCNT) & ~DISPCNT_ALL_BG_AND_MODE_BITS) | sGpuBgConfigs.bgVisibilityAndMode); +} + +void SetTextModeAndHideBgs(void) +{ + SetGpuReg(REG_OFFSET_DISPCNT, GetGpuReg(REG_OFFSET_DISPCNT) & ~DISPCNT_ALL_BG_AND_MODE_BITS); +} + +static void SetBgAffineInternal(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle) +{ + struct BgAffineSrcData src; + struct BgAffineDstData dest; + + switch (sGpuBgConfigs.bgVisibilityAndMode & 0x7) + { + case 1: + if (bg != 2) + return; + break; + case 2: + if (bg < 2 || bg > 3) + return; + break; + case 0: + default: + return; + } + + src.texX = srcCenterX; + src.texY = srcCenterY; + src.scrX = dispCenterX; + src.scrY = dispCenterY; + src.sx = scaleX; + src.sy = scaleY; + src.alpha = rotationAngle; + + BgAffineSet(&src, &dest, 1); + + SetGpuReg(REG_OFFSET_BG2PA, dest.pa); + SetGpuReg(REG_OFFSET_BG2PB, dest.pb); + SetGpuReg(REG_OFFSET_BG2PC, dest.pc); + SetGpuReg(REG_OFFSET_BG2PD, dest.pd); + SetGpuReg(REG_OFFSET_BG2PA, dest.pa); + SetGpuReg(REG_OFFSET_BG2X_L, (s16)(dest.dx)); + SetGpuReg(REG_OFFSET_BG2X_H, (s16)(dest.dx >> 16)); + SetGpuReg(REG_OFFSET_BG2Y_L, (s16)(dest.dy)); + SetGpuReg(REG_OFFSET_BG2Y_H, (s16)(dest.dy >> 16)); +} + +bool8 IsInvalidBg(u8 bg) +{ + if (bg > 3) + return TRUE; + else + return FALSE; +} + +int DummiedOutFireRedLeafGreenTileAllocFunc(int a1, int a2, int a3, int a4) +{ + return 0; +} + +void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable) +{ + int i; + ResetBgs(); + + for (i = 0; i < 4; i++) + { + sDmaBusyBitfield[i] = 0; + } + + gUnneededFireRedVariable = leftoverFireRedLeafGreenVariable; +} + +void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates) +{ + int i; + u8 bg; + + SetBgModeInternal(bgMode); + ResetBgControlStructs(); + + for (i = 0; i < numTemplates; i++) + { + bg = templates[i].bg; + if (bg < 4) + { + SetBgControlAttributes(bg, + templates[i].charBaseIndex, + templates[i].mapBaseIndex, + templates[i].screenSize, + templates[i].paletteMode, + templates[i].priority, + 0, + 0); + + sGpuBgConfigs2[bg].baseTile = templates[i].baseTile; + sGpuBgConfigs2[bg].basePalette = 0; + sGpuBgConfigs2[bg].unk_3 = 0; + + sGpuBgConfigs2[bg].tilemap = NULL; + sGpuBgConfigs2[bg].bg_x = 0; + sGpuBgConfigs2[bg].bg_y = 0; + } + } +} + +void InitBgFromTemplate(const struct BgTemplate *template) +{ + u8 bg = template->bg; + + if (bg < 4) + { + SetBgControlAttributes(bg, + template->charBaseIndex, + template->mapBaseIndex, + template->screenSize, + template->paletteMode, + template->priority, + 0, + 0); + + sGpuBgConfigs2[bg].baseTile = template->baseTile; + sGpuBgConfigs2[bg].basePalette = 0; + sGpuBgConfigs2[bg].unk_3 = 0; + + sGpuBgConfigs2[bg].tilemap = NULL; + sGpuBgConfigs2[bg].bg_x = 0; + sGpuBgConfigs2[bg].bg_y = 0; + } +} + +void SetBgMode(u8 bgMode) +{ + SetBgModeInternal(bgMode); +} + +u16 LoadBgTiles(u8 bg, const void* src, u16 size, u16 destOffset) +{ + u16 tileOffset; + u8 cursor; + + if (GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE) == 0) + { + tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x20; + } + else + { + tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x40; + } + + cursor = LoadBgVram(bg, src, size, tileOffset, DISPCNT_MODE_1); + + if (cursor == 0xFF) + { + return -1; + } + + sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20)); + + if (gUnneededFireRedVariable == 1) + { + DummiedOutFireRedLeafGreenTileAllocFunc(bg, tileOffset / 0x20, size / 0x20, 1); + } + + return cursor; +} + +u16 LoadBgTilemap(u8 bg, const void *src, u16 size, u16 destOffset) +{ + u8 cursor = LoadBgVram(bg, src, size, destOffset * 2, DISPCNT_MODE_2); + + if (cursor == 0xFF) + { + return -1; + } + + sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20)); + + return cursor; +} + +u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset) +{ + s8 cursor; + + if (!IsInvalidBg32(bg)) + { + u16 paletteOffset = (sGpuBgConfigs2[bg].basePalette * 0x20) + (destOffset * 2); + cursor = RequestDma3Copy(src, (void*)(paletteOffset + BG_PLTT), size, 0); + + if (cursor == -1) + { + return -1; + } + } + else + { + return -1; + } + + sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20)); + + return (u8)cursor; +} + +bool8 IsDma3ManagerBusyWithBgCopy(void) +{ + int i; + + for (i = 0; i < 0x80; i++) + { + u8 div = i / 0x20; + u8 mod = i % 0x20; + + if ((sDmaBusyBitfield[div] & (1 << mod))) + { + s8 reqSpace = CheckForSpaceForDma3Request(i); + if (reqSpace == -1) + { + return TRUE; + } + + sDmaBusyBitfield[div] &= ~(1 << mod); + } + } + + return FALSE; +} + +void ShowBg(u8 bg) +{ + ShowBgInternal(bg); + SyncBgVisibilityAndMode(); +} + +void HideBg(u8 bg) +{ + HideBgInternal(bg); + SyncBgVisibilityAndMode(); +} + +void SetBgAttribute(u8 bg, u8 attributeId, u8 value) +{ + switch (attributeId) + { + case BG_ATTR_CHARBASEINDEX: + SetBgControlAttributes(bg, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); + break; + case BG_ATTR_MAPBASEINDEX: + SetBgControlAttributes(bg, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); + break; + case BG_ATTR_SCREENSIZE: + SetBgControlAttributes(bg, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF); + break; + case BG_ATTR_PALETTEMODE: + SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF); + break; + case BG_ATTR_PRIORITY: + SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF); + break; + case BG_ATTR_MOSAIC: + SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF); + break; + case BG_ATTR_WRAPAROUND: + SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value); + break; + } +} + +u16 GetBgAttribute(u8 bg, u8 attributeId) +{ + switch (attributeId) + { + case BG_ATTR_CHARBASEINDEX: + return GetBgControlAttribute(bg, BG_CTRL_ATTR_CHARBASEINDEX); + case BG_ATTR_MAPBASEINDEX: + return GetBgControlAttribute(bg, BG_CTRL_ATTR_MAPBASEINDEX); + case BG_ATTR_SCREENSIZE: + return GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); + case BG_ATTR_PALETTEMODE: + return GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE); + case BG_ATTR_PRIORITY: + return GetBgControlAttribute(bg, BG_CTRL_ATTR_PRIORITY); + case BG_ATTR_MOSAIC: + return GetBgControlAttribute(bg, BG_CTRL_ATTR_MOSAIC); + case BG_ATTR_WRAPAROUND: + return GetBgControlAttribute(bg, BG_CTRL_ATTR_WRAPAROUND); + case BG_ATTR_METRIC: + switch (GetBgType(bg)) + { + case 0: + return GetBgMetricTextMode(bg, 0) * 0x800; + case 1: + return GetBgMetricAffineMode(bg, 0) * 0x100; + default: + return 0; + } + case BG_ATTR_TYPE: + return GetBgType(bg); + case BG_ATTR_BASETILE: + return sGpuBgConfigs2[bg].baseTile; + default: + return -1; + } +} + +s32 ChangeBgX(u8 bg, s32 value, u8 op) +{ + u8 mode; + u16 temp1; + u16 temp2; + + if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + { + return -1; + } + + switch (op) + { + case 0: + default: + sGpuBgConfigs2[bg].bg_x = value; + break; + case 1: + sGpuBgConfigs2[bg].bg_x += value; + break; + case 2: + sGpuBgConfigs2[bg].bg_x -= value; + break; + } + + mode = GetBgMode(); + + switch (bg) + { + case 0: + temp1 = sGpuBgConfigs2[0].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG0HOFS, temp1); + break; + case 1: + temp1 = sGpuBgConfigs2[1].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG1HOFS, temp1); + break; + case 2: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[2].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG2HOFS, temp1); + } + else + { + temp1 = sGpuBgConfigs2[2].bg_x >> 0x10; + temp2 = sGpuBgConfigs2[2].bg_x & 0xFFFF; + SetGpuReg(REG_OFFSET_BG2X_H, temp1); + SetGpuReg(REG_OFFSET_BG2X_L, temp2); + } + break; + case 3: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[3].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG3HOFS, temp1); + } + else if (mode == 2) + { + temp1 = sGpuBgConfigs2[3].bg_x >> 0x10; + temp2 = sGpuBgConfigs2[3].bg_x & 0xFFFF; + SetGpuReg(REG_OFFSET_BG3X_H, temp1); + SetGpuReg(REG_OFFSET_BG3X_L, temp2); + } + break; + } + + return sGpuBgConfigs2[bg].bg_x; +} + +s32 GetBgX(u8 bg) +{ + if (IsInvalidBg32(bg)) + return -1; + else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + return -1; + else + return sGpuBgConfigs2[bg].bg_x; +} + +s32 ChangeBgY(u8 bg, s32 value, u8 op) +{ + u8 mode; + u16 temp1; + u16 temp2; + + if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + { + return -1; + } + + switch (op) + { + case 0: + default: + sGpuBgConfigs2[bg].bg_y = value; + break; + case 1: + sGpuBgConfigs2[bg].bg_y += value; + break; + case 2: + sGpuBgConfigs2[bg].bg_y -= value; + break; + } + + mode = GetBgMode(); + + switch (bg) + { + case 0: + temp1 = sGpuBgConfigs2[0].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG0VOFS, temp1); + break; + case 1: + temp1 = sGpuBgConfigs2[1].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG1VOFS, temp1); + break; + case 2: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG2VOFS, temp1); + } + else + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF; + SetGpuReg(REG_OFFSET_BG2Y_H, temp1); + SetGpuReg(REG_OFFSET_BG2Y_L, temp2); + } + break; + case 3: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG3VOFS, temp1); + } + else if (mode == 2) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF; + SetGpuReg(REG_OFFSET_BG3Y_H, temp1); + SetGpuReg(REG_OFFSET_BG3Y_L, temp2); + } + break; + } + + return sGpuBgConfigs2[bg].bg_y; +} + +s32 ChangeBgY_ScreenOff(u8 bg, u32 value, u8 op) +{ + u8 mode; + u16 temp1; + u16 temp2; + + if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + { + return -1; + } + + switch (op) + { + case 0: + default: + sGpuBgConfigs2[bg].bg_y = value; + break; + case 1: + sGpuBgConfigs2[bg].bg_y += value; + break; + case 2: + sGpuBgConfigs2[bg].bg_y -= value; + break; + } + + mode = GetBgMode(); + + switch (bg) + { + case 0: + temp1 = sGpuBgConfigs2[0].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG0VOFS, temp1); + break; + case 1: + temp1 = sGpuBgConfigs2[1].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG1VOFS, temp1); + break; + case 2: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG2VOFS, temp1); + + } + else + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF; + SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_H, temp1); + SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_L, temp2); + } + break; + case 3: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG3VOFS, temp1); + } + else if (mode == 2) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF; + SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_H, temp1); + SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_L, temp2); + } + break; + } + + return sGpuBgConfigs2[bg].bg_y; +} + +s32 GetBgY(u8 bg) +{ + if (IsInvalidBg32(bg)) + return -1; + else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + return -1; + else + return sGpuBgConfigs2[bg].bg_y; +} + +void SetBgAffine(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle) +{ + SetBgAffineInternal(bg, srcCenterX, srcCenterY, dispCenterX, dispCenterY, scaleX, scaleY, rotationAngle); +} + +u8 Unused_AdjustBgMosaic(u8 a1, u8 a2) +{ + u16 result = GetGpuReg(REG_OFFSET_MOSAIC); + s16 test1 = result & 0xF; + s16 test2 = (result >> 4) & 0xF; + + result &= 0xFF00; + + switch (a2) + { + case 0: + default: + test1 = a1 & 0xF; + test2 = a1 >> 0x4; + break; + case 1: + test1 = a1 & 0xF; + break; + case 2: + if ((test1 + a1) > 0xF) + { + test1 = 0xF; + } + else + { + test1 += a1; + } + break; + case 3: + if ((test1 - a1) < 0) + { + test1 = 0x0; + } + else + { + test1 -= a1; + } + break; + case 4: + test2 = a1 & 0xF; + break; + case 5: + if ((test2 + a1) > 0xF) + { + test2 = 0xF; + } + else + { + test2 += a1; + } + break; + case 6: + if ((test2 - a1) < 0) + { + test2 = 0x0; + } + else + { + test2 -= a1; + } + break; + } + + result |= ((test2 << 0x4) & 0xF0); + result |= (test1 & 0xF); + + SetGpuReg(REG_OFFSET_MOSAIC, result); + + return result; +} + +void SetBgTilemapBuffer(u8 bg, void *tilemap) +{ + if (!IsInvalidBg32(bg) && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + { + sGpuBgConfigs2[bg].tilemap = tilemap; + } +} + +void UnsetBgTilemapBuffer(u8 bg) +{ + if (!IsInvalidBg32(bg) && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + { + sGpuBgConfigs2[bg].tilemap = NULL; + } +} + +void* GetBgTilemapBuffer(u8 bg) +{ + if (IsInvalidBg32(bg)) + return NULL; + else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + return NULL; + else + return sGpuBgConfigs2[bg].tilemap; +} + +void CopyToBgTilemapBuffer(u8 bg, const void *src, u16 mode, u16 destOffset) +{ + if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg)) + { + if (mode != 0) + CpuCopy16(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2)), mode); + else + LZ77UnCompWram(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2))); + } +} + +void CopyBgTilemapBufferToVram(u8 bg) +{ + u16 sizeToLoad; + + if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg)) + { + switch (GetBgType(bg)) + { + case 0: + sizeToLoad = GetBgMetricTextMode(bg, 0) * 0x800; + break; + case 1: + sizeToLoad = GetBgMetricAffineMode(bg, 0) * 0x100; + break; + default: + sizeToLoad = 0; + break; + } + LoadBgVram(bg, sGpuBgConfigs2[bg].tilemap, sizeToLoad, 0, 2); + } +} + +void CopyToBgTilemapBufferRect(u8 bg, const void* src, u8 destX, u8 destY, u8 width, u8 height) +{ + u16 destX16; + u16 destY16; + u16 mode; + + if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg)) + { + switch (GetBgType(bg)) + { + case 0: + { + const u16 * srcCopy = src; + for (destY16 = destY; destY16 < (destY + height); destY16++) + { + for (destX16 = destX; destX16 < (destX + width); destX16++) + { + ((u16*)sGpuBgConfigs2[bg].tilemap)[((destY16 * 0x20) + destX16)] = *srcCopy++; + } + } + break; + } + case 1: + { + const u8 * srcCopy = src; + mode = GetBgMetricAffineMode(bg, 0x1); + for (destY16 = destY; destY16 < (destY + height); destY16++) + { + for (destX16 = destX; destX16 < (destX + width); destX16++) + { + ((u8*)sGpuBgConfigs2[bg].tilemap)[((destY16 * mode) + destX16)] = *srcCopy++; + } + } + break; + } + } + } +} + +void CopyToBgTilemapBufferRect_ChangePalette(u8 bg, const void *src, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette) +{ + CopyRectToBgTilemapBufferRect(bg, src, 0, 0, rectWidth, rectHeight, destX, destY, rectWidth, rectHeight, palette, 0, 0); +} + +void CopyRectToBgTilemapBufferRect(u8 bg, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 unused, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, s16 palette1, s16 tileOffset) +{ + u16 screenWidth, screenHeight, screenSize; + u16 var; + const void *srcPtr; + u16 i, j; + + if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg)) + { + screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); + screenWidth = GetBgMetricTextMode(bg, 0x1) * 0x20; + screenHeight = GetBgMetricTextMode(bg, 0x2) * 0x20; + switch (GetBgType(bg)) + { + case 0: + srcPtr = src + ((srcY * srcWidth) + srcX) * 2; + for (i = destX; i < (destX + rectWidth); i++) + { + for (j = srcHeight; j < (srcHeight + destY); j++) + { + u16 index = GetTileMapIndexFromCoords(j, i, screenSize, screenWidth, screenHeight); + CopyTileMapEntry(srcPtr, sGpuBgConfigs2[bg].tilemap + (index * 2), rectHeight, palette1, tileOffset); + srcPtr += 2; + } + srcPtr += (srcWidth - destY) * 2; + } + break; + case 1: + srcPtr = src + ((srcY * srcWidth) + srcX); + var = GetBgMetricAffineMode(bg, 0x1); + for (i = destX; i < (destX + rectWidth); i++) + { + for (j = srcHeight; j < (srcHeight + destY); j++) + { + *(u8*)(sGpuBgConfigs2[bg].tilemap + ((var * i) + j)) = *(u8*)(srcPtr) + palette1; + srcPtr++; + } + srcPtr += (srcWidth - destY); + } + break; + } + } +} + +void FillBgTilemapBufferRect_Palette0(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height) +{ + u16 x16; + u16 y16; + u16 mode; + + if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg)) + { + switch (GetBgType(bg)) + { + case 0: + for (y16 = y; y16 < (y + height); y16++) + { + for (x16 = x; x16 < (x + width); x16++) + { + ((u16*)sGpuBgConfigs2[bg].tilemap)[((y16 * 0x20) + x16)] = tileNum; + } + } + break; + case 1: + mode = GetBgMetricAffineMode(bg, 0x1); + for (y16 = y; y16 < (y + height); y16++) + { + for (x16 = x; x16 < (x + width); x16++) + { + ((u8*)sGpuBgConfigs2[bg].tilemap)[((y16 * mode) + x16)] = tileNum; + } + } + break; + } + } +} + +void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette) +{ + WriteSequenceToBgTilemapBuffer(bg, tileNum, x, y, width, height, palette, 0); +} + +void WriteSequenceToBgTilemapBuffer(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumDelta) +{ + u16 mode; + u16 mode2; + u16 attribute; + u16 mode3; + u16 x16, y16; + + if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg)) + { + attribute = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); + mode = GetBgMetricTextMode(bg, 0x1) * 0x20; + mode2 = GetBgMetricTextMode(bg, 0x2) * 0x20; + switch (GetBgType(bg)) + { + case 0: + for (y16 = y; y16 < (y + height); y16++) + { + for (x16 = x; x16 < (x + width); x16++) + { + CopyTileMapEntry(&firstTileNum, &((u16*)sGpuBgConfigs2[bg].tilemap)[(u16)GetTileMapIndexFromCoords(x16, y16, attribute, mode, mode2)], paletteSlot, 0, 0); + firstTileNum = (firstTileNum & (METATILE_COLLISION_MASK | METATILE_ELEVATION_MASK)) + ((firstTileNum + tileNumDelta) & METATILE_ID_MASK); + } + } + break; + case 1: + mode3 = GetBgMetricAffineMode(bg, 0x1); + for (y16 = y; y16 < (y + height); y16++) + { + for (x16 = x; x16 < (x + width); x16++) + { + ((u8*)sGpuBgConfigs2[bg].tilemap)[(y16 * mode3) + x16] = firstTileNum; + firstTileNum = (firstTileNum & (METATILE_COLLISION_MASK | METATILE_ELEVATION_MASK)) + ((firstTileNum + tileNumDelta) & METATILE_ID_MASK); + } + } + break; + } + } +} + +u16 GetBgMetricTextMode(u8 bg, u8 whichMetric) +{ + u8 screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); + + switch (whichMetric) + { + case 0: + switch (screenSize) + { + case 0: + return 1; + case 1: + case 2: + return 2; + case 3: + return 4; + } + break; + case 1: + switch (screenSize) + { + case 0: + return 1; + case 1: + return 2; + case 2: + return 1; + case 3: + return 2; + } + break; + case 2: + switch (screenSize) + { + case 0: + case 1: + return 1; + case 2: + case 3: + return 2; + } + break; + } + return 0; +} + +u32 GetBgMetricAffineMode(u8 bg, u8 whichMetric) +{ + u8 screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); + + switch (whichMetric) + { + case 0: + switch (screenSize) + { + case 0: + return 0x1; + case 1: + return 0x4; + case 2: + return 0x10; + case 3: + return 0x40; + } + break; + case 1: + case 2: + return 0x10 << screenSize; + } + return 0; +} + +u32 GetTileMapIndexFromCoords(s32 x, s32 y, s32 screenSize, u32 screenWidth, u32 screenHeight) +{ + x = x & (screenWidth - 1); + y = y & (screenHeight - 1); + + switch (screenSize) + { + case 0: + case 2: + break; + case 3: + if (y >= 0x20) + y += 0x20; + case 1: + if (x >= 0x20) + { + x -= 0x20; + y += 0x20; + } + break; + } + return (y * 0x20) + x; +} + +void CopyTileMapEntry(const u16 *src, u16 *dest, s32 palette1, s32 tileOffset, s32 palette2) +{ + u16 var; + + if (palette1 == 16) + goto CASE_16; + switch (palette1) + { + case 0 ... 16: + var = ((*src + tileOffset) & 0xFFF) + ((palette1 + palette2) << 12); + break; + CASE_16: + var = *dest; + var &= 0xFC00; + var += palette2 << 12; + var |= (*src + tileOffset) & 0x3FF; + break; + default: + var = *src + tileOffset + (palette2 << 12); + break; + } + *dest = var; +} + +u32 GetBgType(u8 bg) +{ + u8 mode = GetBgMode(); + + switch (bg) + { + case 0: + case 1: + switch (mode) + { + case 0: + case 1: + return 0; + } + break; + case 2: + switch (mode) + { + case 0: + return 0; + case 1: + case 2: + return 1; + } + break; + case 3: + switch (mode) + { + case 0: + return 0; + case 2: + return 1; + } + break; + } + + return 0xFFFF; +} + +bool32 IsInvalidBg32(u8 bg) +{ + if (bg > 3) + return TRUE; + else + return FALSE; +} + +bool32 IsTileMapOutsideWram(u8 bg) +{ + if (sGpuBgConfigs2[bg].tilemap > (void*)IWRAM_END) + return TRUE; + else if (sGpuBgConfigs2[bg].tilemap == NULL) + return TRUE; + else + return FALSE; +} diff --git a/gflib/bg.h b/gflib/bg.h new file mode 100644 index 000000000..3c7eee292 --- /dev/null +++ b/gflib/bg.h @@ -0,0 +1,85 @@ +#ifndef GUARD_BG_H +#define GUARD_BG_H + +struct BGCntrlBitfield // for the I/O registers +{ + volatile u16 priority:2; + volatile u16 charBaseBlock:2; + volatile u16 field_0_2:4; + volatile u16 field_1_0:5; + volatile u16 areaOverflowMode:1; + volatile u16 screenSize:2; +}; + +enum +{ + BG_ATTR_CHARBASEINDEX = 1, + BG_ATTR_MAPBASEINDEX, + BG_ATTR_SCREENSIZE, + BG_ATTR_PALETTEMODE, + BG_ATTR_MOSAIC, + BG_ATTR_WRAPAROUND, + BG_ATTR_PRIORITY, + BG_ATTR_METRIC, + BG_ATTR_TYPE, + BG_ATTR_BASETILE, +}; + +struct BgTemplate +{ + u16 bg:2; // 0x1, 0x2 -> 0x3 + u16 charBaseIndex:2; // 0x4, 0x8 -> 0xC + u16 mapBaseIndex:5; // 0x10, 0x20, 0x40, 0x80, 0x100 -> 0x1F0 + u16 screenSize:2; // 0x200, 0x400 -> 0x600 + u16 paletteMode:1; // 0x800 + u16 priority:2; // 0x1000, 0x2000 > 0x3000 + u16 baseTile:10; +}; + +void ResetBgs(void); +u8 GetBgMode(void); +void ResetBgControlStructs(void); +void Unused_ResetBgControlStruct(u8 bg); +u8 LoadBgVram(u8 bg, const void *src, u16 size, u16 destOffset, u8 mode); +void SetTextModeAndHideBgs(void); +bool8 IsInvalidBg(u8 bg); +int DummiedOutFireRedLeafGreenTileAllocFunc(int a1, int a2, int a3, int a4); +void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable); +void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates); +void InitBgFromTemplate(const struct BgTemplate *template); +void SetBgMode(u8 bgMode); +u16 LoadBgTiles(u8 bg, const void* src, u16 size, u16 destOffset); +u16 LoadBgTilemap(u8 bg, const void *src, u16 size, u16 destOffset); +u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset); +bool8 IsDma3ManagerBusyWithBgCopy(void); +void ShowBg(u8 bg); +void HideBg(u8 bg); +void SetBgAttribute(u8 bg, u8 attributeId, u8 value); +u16 GetBgAttribute(u8 bg, u8 attributeId); +s32 ChangeBgX(u8 bg, s32 value, u8 op); +s32 GetBgX(u8 bg); +s32 ChangeBgY(u8 bg, s32 value, u8 op); +s32 ChangeBgY_ScreenOff(u8 bg, u32 value, u8 op); +s32 GetBgY(u8 bg); +void SetBgAffine(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle); +u8 Unused_AdjustBgMosaic(u8 a1, u8 a2); +void SetBgTilemapBuffer(u8 bg, void *tilemap); +void UnsetBgTilemapBuffer(u8 bg); +void* GetBgTilemapBuffer(u8 bg); +void CopyToBgTilemapBuffer(u8 bg, const void *src, u16 mode, u16 destOffset); +void CopyBgTilemapBufferToVram(u8 bg); +void CopyToBgTilemapBufferRect(u8 bg, const void* src, u8 destX, u8 destY, u8 width, u8 height); +void CopyToBgTilemapBufferRect_ChangePalette(u8 bg, const void *src, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette); +void CopyRectToBgTilemapBufferRect(u8 bg, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 unused, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, s16 palette1, s16 tileOffset); +void FillBgTilemapBufferRect_Palette0(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height); +void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette); +void WriteSequenceToBgTilemapBuffer(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumDelta); +u16 GetBgMetricTextMode(u8 bg, u8 whichMetric); +u32 GetBgMetricAffineMode(u8 bg, u8 whichMetric); +u32 GetTileMapIndexFromCoords(s32 x, s32 y, s32 screenSize, u32 screenWidth, u32 screenHeight); +void CopyTileMapEntry(const u16 *src, u16 *dest, s32 palette1, s32 tileOffset, s32 palette2); +u32 GetBgType(u8 bg); +bool32 IsInvalidBg32(u8 bg); +bool32 IsTileMapOutsideWram(u8 bg); + +#endif // GUARD_BG_H diff --git a/gflib/blit.c b/gflib/blit.c new file mode 100644 index 000000000..26a63fe9b --- /dev/null +++ b/gflib/blit.c @@ -0,0 +1,209 @@ +#include "global.h" +#include "blit.h" + +void BlitBitmapRect4BitWithoutColorKey(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height) +{ + BlitBitmapRect4Bit(src, dst, srcX, srcY, dstX, dstY, width, height, 0xFF); +} + +void BlitBitmapRect4Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey) +{ + s32 xEnd; + s32 yEnd; + s32 multiplierSrcY; + s32 multiplierDstY; + s32 loopSrcY, loopDstY; + s32 loopSrcX, loopDstX; + const u8 *pixelsSrc; + u8 *pixelsDst; + s32 toOrr; + s32 toAnd; + s32 toShift; + + if (dst->width - dstX < width) + xEnd = (dst->width - dstX) + srcX; + else + xEnd = srcX + width; + + if (dst->height - dstY < height) + yEnd = (dst->height - dstY) + srcY; + else + yEnd = height + srcY; + + multiplierSrcY = (src->width + (src->width & 7)) >> 3; + multiplierDstY = (dst->width + (dst->width & 7)) >> 3; + + if (colorKey == 0xFF) + { + for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++) + { + for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++) + { + pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1B); + pixelsDst = dst->pixels + ((loopDstX >> 1) & 3) + ((loopDstX >> 3) << 5) + (((loopDstY >> 3) * multiplierDstY) << 5) + ((u32)(loopDstY << 0x1d) >> 0x1B); + toOrr = ((*pixelsSrc >> ((loopSrcX & 1) << 2)) & 0xF); + toShift = ((loopDstX & 1) << 2); + toOrr <<= toShift; + toAnd = 0xF0 >> (toShift); + *pixelsDst = toOrr | (*pixelsDst & toAnd); + } + } + } + else + { + for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++) + { + for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++) + { + pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1B); + pixelsDst = dst->pixels + ((loopDstX >> 1) & 3) + ((loopDstX >> 3) << 5) + (((loopDstY >> 3) * multiplierDstY) << 5) + ((u32)(loopDstY << 0x1d) >> 0x1B); + toOrr = ((*pixelsSrc >> ((loopSrcX & 1) << 2)) & 0xF); + if (toOrr != colorKey) + { + toShift = ((loopDstX & 1) << 2); + toOrr <<= toShift; + toAnd = 0xF0 >> (toShift); + *pixelsDst = toOrr | (*pixelsDst & toAnd); + } + } + } + } +} + +void FillBitmapRect4Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue) +{ + s32 xEnd; + s32 yEnd; + s32 multiplierY; + s32 loopX, loopY; + s32 toOrr1, toOrr2; + + xEnd = x + width; + if (xEnd > surface->width) + xEnd = surface->width; + + yEnd = y + height; + if (yEnd > surface->height) + yEnd = surface->height; + + multiplierY = (surface->width + (surface->width & 7)) >> 3; + toOrr1 = (u32)(fillValue << 0x1C) >> 0x18; + toOrr2 = (fillValue & 0xF); + + for (loopY = y; loopY < yEnd; loopY++) + { + for (loopX = x; loopX < xEnd; loopX++) + { + u8 *pixels = surface->pixels + ((loopX >> 1) & 3) + ((loopX >> 3) << 5) + (((loopY >> 3) * multiplierY) << 5) + ((u32)(loopY << 0x1d) >> 0x1B); + if ((loopX << 0x1F) != 0) + *pixels = toOrr1 | (*pixels & 0xF); + else + *pixels = toOrr2 | (*pixels & 0xF0); + } + } +} + +void BlitBitmapRect4BitTo8Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey, u8 paletteOffset) +{ + s32 palOffsetBits; + s32 xEnd; + s32 yEnd; + s32 multiplierSrcY; + s32 multiplierDstY; + s32 loopSrcY, loopDstY; + s32 loopSrcX, loopDstX; + const u8 *pixelsSrc; + u8 *pixelsDst; + s32 colorKeyBits; + + palOffsetBits = (u32)(paletteOffset << 0x1C) >> 0x18; + colorKeyBits = (u32)(colorKey << 0x1C) >> 0x18; + + if (dst->width - dstX < width) + xEnd = (dst->width - dstX) + srcX; + else + xEnd = width + srcX; + + if (dst->height - dstY < height) + yEnd = (srcY + dst->height) - dstY; + else + yEnd = srcY + height; + + multiplierSrcY = (src->width + (src->width & 7)) >> 3; + multiplierDstY = (dst->width + (dst->width & 7)) >> 3; + + if (colorKey == 0xFF) + { + for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++) + { + pixelsSrc = src->pixels + ((srcX >> 1) & 3) + ((srcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b); + for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++) + { + pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a); + if (loopSrcX & 1) + { + *pixelsDst = palOffsetBits + (*pixelsSrc >> 4); + } + else + { + pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b); + *pixelsDst = palOffsetBits + (*pixelsSrc & 0xF); + } + } + } + } + else + { + for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++) + { + pixelsSrc = src->pixels + ((srcX >> 1) & 3) + ((srcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b); + for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++) + { + if (loopSrcX & 1) + { + if ((*pixelsSrc & 0xF0) != colorKeyBits) + { + pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a); + *pixelsDst = palOffsetBits + (*pixelsSrc >> 4); + } + } + else + { + pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b); + if ((*pixelsSrc & 0xF) != colorKey) + { + pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a); + *pixelsDst = palOffsetBits + (*pixelsSrc & 0xF); + } + } + } + } + } +} + +void FillBitmapRect8Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue) +{ + s32 xEnd; + s32 yEnd; + s32 multiplierY; + s32 loopX, loopY; + + xEnd = x + width; + if (xEnd > surface->width) + xEnd = surface->width; + + yEnd = y + height; + if (yEnd > surface->height) + yEnd = surface->height; + + multiplierY = (surface->width + (surface->width & 7)) >> 3; + + for (loopY = y; loopY < yEnd; loopY++) + { + for (loopX = x; loopX < xEnd; loopX++) + { + u8 *pixels = surface->pixels + (loopX & 7) + ((loopX >> 3) << 6) + (((loopY >> 3) * multiplierY) << 6) + ((u32)(loopY << 0x1d) >> 0x1a); + *pixels = fillValue; + } + } +} diff --git a/gflib/blit.h b/gflib/blit.h new file mode 100644 index 000000000..78f67766e --- /dev/null +++ b/gflib/blit.h @@ -0,0 +1,17 @@ +#ifndef GUARD_BLIT_H +#define GUARD_BLIT_H + +struct Bitmap +{ + u8 *pixels; + u32 width:16; + u32 height:16; +}; + +void BlitBitmapRect4BitWithoutColorKey(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height); +void BlitBitmapRect4Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey); +void FillBitmapRect4Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue); +void BlitBitmapRect4BitTo8Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey, u8 paletteOffset); +void FillBitmapRect8Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue); + +#endif // GUARD_BLIT_H diff --git a/gflib/dma3.h b/gflib/dma3.h new file mode 100644 index 000000000..8eff34f55 --- /dev/null +++ b/gflib/dma3.h @@ -0,0 +1,55 @@ +#ifndef GUARD_DMA3_H +#define GUARD_DMA3_H + +// Maximum amount of data we will transfer in one operation +#define MAX_DMA_BLOCK_SIZE 0x1000 + +#define Dma3CopyLarge_(src, dest, size, bit) \ +{ \ + const void *_src = src; \ + void *_dest = dest; \ + u32 _size = size; \ + while (1) \ + { \ + if (_size <= MAX_DMA_BLOCK_SIZE) \ + { \ + DmaCopy##bit(3, _src, _dest, _size); \ + break; \ + } \ + DmaCopy##bit(3, _src, _dest, MAX_DMA_BLOCK_SIZE); \ + _src += MAX_DMA_BLOCK_SIZE; \ + _dest += MAX_DMA_BLOCK_SIZE; \ + _size -= MAX_DMA_BLOCK_SIZE; \ + } \ +} + +#define Dma3CopyLarge16_(src, dest, size) Dma3CopyLarge_(src, dest, size, 16) +#define Dma3CopyLarge32_(src, dest, size) Dma3CopyLarge_(src, dest, size, 32) + +#define Dma3FillLarge_(value, dest, size, bit) \ +{ \ + void *_dest = dest; \ + u32 _size = size; \ + while (1) \ + { \ + if (_size <= MAX_DMA_BLOCK_SIZE) \ + { \ + DmaFill##bit(3, value, _dest, _size); \ + break; \ + } \ + DmaFill##bit(3, value, _dest, MAX_DMA_BLOCK_SIZE); \ + _dest += MAX_DMA_BLOCK_SIZE; \ + _size -= MAX_DMA_BLOCK_SIZE; \ + } \ +} + +#define Dma3FillLarge16_(value, dest, size) Dma3FillLarge_(value, dest, size, 16) +#define Dma3FillLarge32_(value, dest, size) Dma3FillLarge_(value, dest, size, 32) + +void ClearDma3Requests(void); +void ProcessDma3Requests(void); +s16 RequestDma3Copy(const void *src, void *dest, u16 size, u8 mode); +s16 RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode); +s16 CheckForSpaceForDma3Request(s16 index); + +#endif // GUARD_DMA3_H diff --git a/gflib/dma3_manager.c b/gflib/dma3_manager.c new file mode 100644 index 000000000..43744883f --- /dev/null +++ b/gflib/dma3_manager.c @@ -0,0 +1,181 @@ +#include "global.h" +#include "dma3.h" + +#define MAX_DMA_REQUESTS 128 + +#define DMA_REQUEST_COPY32 1 +#define DMA_REQUEST_FILL32 2 +#define DMA_REQUEST_COPY16 3 +#define DMA_REQUEST_FILL16 4 + +BSS_DATA struct +{ + const u8 *src; + u8 *dest; + u16 size; + u16 mode; + u32 value; +} gDma3Requests[MAX_DMA_REQUESTS]; + +static volatile bool8 gDma3ManagerLocked; +static u8 gDma3RequestCursor; + +void ClearDma3Requests(void) +{ + int i; + + gDma3ManagerLocked = TRUE; + gDma3RequestCursor = 0; + + for (i = 0; i < MAX_DMA_REQUESTS; i++) + { + gDma3Requests[i].size = 0; + gDma3Requests[i].src = NULL; + gDma3Requests[i].dest = NULL; + } + + gDma3ManagerLocked = FALSE; +} + +void ProcessDma3Requests(void) +{ + u16 bytesTransferred; + + if (gDma3ManagerLocked) + return; + + bytesTransferred = 0; + + // as long as there are DMA requests to process (unless size or vblank is an issue), do not exit + while (gDma3Requests[gDma3RequestCursor].size != 0) + { + bytesTransferred += gDma3Requests[gDma3RequestCursor].size; + + if (bytesTransferred > 40 * 1024) + return; // don't transfer more than 40 KiB + if (*(u8 *)REG_ADDR_VCOUNT > 224) + return; // we're about to leave vblank, stop + + switch (gDma3Requests[gDma3RequestCursor].mode) + { + case DMA_REQUEST_COPY32: // regular 32-bit copy + Dma3CopyLarge32_(gDma3Requests[gDma3RequestCursor].src, + gDma3Requests[gDma3RequestCursor].dest, + gDma3Requests[gDma3RequestCursor].size); + break; + case DMA_REQUEST_FILL32: // repeat a single 32-bit value across RAM + Dma3FillLarge32_(gDma3Requests[gDma3RequestCursor].value, + gDma3Requests[gDma3RequestCursor].dest, + gDma3Requests[gDma3RequestCursor].size); + break; + case DMA_REQUEST_COPY16: // regular 16-bit copy + Dma3CopyLarge16_(gDma3Requests[gDma3RequestCursor].src, + gDma3Requests[gDma3RequestCursor].dest, + gDma3Requests[gDma3RequestCursor].size); + break; + case DMA_REQUEST_FILL16: // repeat a single 16-bit value across RAM + Dma3FillLarge16_(gDma3Requests[gDma3RequestCursor].value, + gDma3Requests[gDma3RequestCursor].dest, + gDma3Requests[gDma3RequestCursor].size); + break; + } + + // Free the request + gDma3Requests[gDma3RequestCursor].src = NULL; + gDma3Requests[gDma3RequestCursor].dest = NULL; + gDma3Requests[gDma3RequestCursor].size = 0; + gDma3Requests[gDma3RequestCursor].mode = 0; + gDma3Requests[gDma3RequestCursor].value = 0; + gDma3RequestCursor++; + + if (gDma3RequestCursor >= MAX_DMA_REQUESTS) // loop back to the first DMA request + gDma3RequestCursor = 0; + } +} + +s16 RequestDma3Copy(const void *src, void *dest, u16 size, u8 mode) +{ + int cursor; + int i = 0; + + gDma3ManagerLocked = TRUE; + cursor = gDma3RequestCursor; + + while (i < MAX_DMA_REQUESTS) + { + if (gDma3Requests[cursor].size == 0) // an empty request was found. + { + gDma3Requests[cursor].src = src; + gDma3Requests[cursor].dest = dest; + gDma3Requests[cursor].size = size; + + if (mode == 1) + gDma3Requests[cursor].mode = DMA_REQUEST_COPY32; + else + gDma3Requests[cursor].mode = DMA_REQUEST_COPY16; + + gDma3ManagerLocked = FALSE; + return cursor; + } + if (++cursor >= MAX_DMA_REQUESTS) // loop back to start. + cursor = 0; + i++; + } + gDma3ManagerLocked = FALSE; + return -1; // no free DMA request was found +} + +s16 RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode) +{ + int cursor; + int i = 0; + + cursor = gDma3RequestCursor; + gDma3ManagerLocked = TRUE; + + while (i < MAX_DMA_REQUESTS) + { + if (gDma3Requests[cursor].size == 0) // an empty request was found. + { + gDma3Requests[cursor].dest = dest; + gDma3Requests[cursor].size = size; + gDma3Requests[cursor].mode = mode; + gDma3Requests[cursor].value = value; + + if(mode == 1) + gDma3Requests[cursor].mode = DMA_REQUEST_FILL32; + else + gDma3Requests[cursor].mode = DMA_REQUEST_FILL16; + + gDma3ManagerLocked = FALSE; + return cursor; + } + if (++cursor >= MAX_DMA_REQUESTS) // loop back to start. + cursor = 0; + i++; + } + gDma3ManagerLocked = FALSE; + return -1; // no free DMA request was found +} + +s16 CheckForSpaceForDma3Request(s16 index) +{ + int i = 0; + + if (index == -1) // check if all requests are free + { + while (i < MAX_DMA_REQUESTS) + { + if (gDma3Requests[i].size != 0) + return -1; + i++; + } + return 0; + } + else // check the specified request + { + if (gDma3Requests[index].size != 0) + return -1; + return 0; + } +} diff --git a/gflib/gpu_regs.c b/gflib/gpu_regs.c new file mode 100644 index 000000000..3bcc4fd93 --- /dev/null +++ b/gflib/gpu_regs.c @@ -0,0 +1,195 @@ +#include "global.h" +#include "gpu_regs.h" + +#define GPU_REG_BUF_SIZE 0x60 + +#define GPU_REG_BUF(offset) (*(u16 *)(&sGpuRegBuffer[offset])) +#define GPU_REG(offset) (*(vu16 *)(REG_BASE + offset)) + +#define EMPTY_SLOT 0xFF + +static u8 sGpuRegBuffer[GPU_REG_BUF_SIZE]; +static u8 sGpuRegWaitingList[GPU_REG_BUF_SIZE]; +static volatile bool8 sGpuRegBufferLocked; +static volatile bool8 sShouldSyncRegIE; +static vu16 sRegIE; + +static void CopyBufferedValueToGpuReg(u8 regOffset); +static void SyncRegIE(void); +static void UpdateRegDispstatIntrBits(u16 regIE); + +void InitGpuRegManager(void) +{ + s32 i; + + for (i = 0; i < GPU_REG_BUF_SIZE; i++) + { + sGpuRegBuffer[i] = 0; + sGpuRegWaitingList[i] = EMPTY_SLOT; + } + + sGpuRegBufferLocked = FALSE; + sShouldSyncRegIE = FALSE; + sRegIE = 0; +} + +static void CopyBufferedValueToGpuReg(u8 regOffset) +{ + if (regOffset == REG_OFFSET_DISPSTAT) + { + REG_DISPSTAT &= ~(DISPSTAT_HBLANK_INTR | DISPSTAT_VBLANK_INTR); + REG_DISPSTAT |= GPU_REG_BUF(REG_OFFSET_DISPSTAT); + } + else + { + GPU_REG(regOffset) = GPU_REG_BUF(regOffset); + } +} + +void CopyBufferedValuesToGpuRegs(void) +{ + if (!sGpuRegBufferLocked) + { + s32 i; + + for (i = 0; i < GPU_REG_BUF_SIZE; i++) + { + u8 regOffset = sGpuRegWaitingList[i]; + if (regOffset == EMPTY_SLOT) + return; + CopyBufferedValueToGpuReg(regOffset); + sGpuRegWaitingList[i] = EMPTY_SLOT; + } + } +} + +void SetGpuReg(u8 regOffset, u16 value) +{ + if (regOffset < GPU_REG_BUF_SIZE) + { + u16 vcount; + + GPU_REG_BUF(regOffset) = value; + vcount = REG_VCOUNT & 0xFF; + + if ((vcount >= 161 && vcount <= 225) || (REG_DISPCNT & DISPCNT_FORCED_BLANK)) + { + CopyBufferedValueToGpuReg(regOffset); + } + else + { + s32 i; + + sGpuRegBufferLocked = TRUE; + + for (i = 0; i < GPU_REG_BUF_SIZE && sGpuRegWaitingList[i] != EMPTY_SLOT; i++) + { + if (sGpuRegWaitingList[i] == regOffset) + { + sGpuRegBufferLocked = FALSE; + return; + } + } + + sGpuRegWaitingList[i] = regOffset; + sGpuRegBufferLocked = FALSE; + } + } +} + +void SetGpuReg_ForcedBlank(u8 regOffset, u16 value) +{ + if (regOffset < GPU_REG_BUF_SIZE) + { + GPU_REG_BUF(regOffset) = value; + + if (REG_DISPCNT & DISPCNT_FORCED_BLANK) + { + CopyBufferedValueToGpuReg(regOffset); + } + else + { + s32 i; + + sGpuRegBufferLocked = TRUE; + + for (i = 0; i < GPU_REG_BUF_SIZE && sGpuRegWaitingList[i] != EMPTY_SLOT; i++) + { + if (sGpuRegWaitingList[i] == regOffset) + { + sGpuRegBufferLocked = FALSE; + return; + } + } + + sGpuRegWaitingList[i] = regOffset; + sGpuRegBufferLocked = FALSE; + } + } +} + +u16 GetGpuReg(u8 regOffset) +{ + if (regOffset == REG_OFFSET_DISPSTAT) + return REG_DISPSTAT; + + if (regOffset == REG_OFFSET_VCOUNT) + return REG_VCOUNT; + + return GPU_REG_BUF(regOffset); +} + +void SetGpuRegBits(u8 regOffset, u16 mask) +{ + u16 regValue = GPU_REG_BUF(regOffset); + SetGpuReg(regOffset, regValue | mask); +} + +void ClearGpuRegBits(u8 regOffset, u16 mask) +{ + u16 regValue = GPU_REG_BUF(regOffset); + SetGpuReg(regOffset, regValue & ~mask); +} + +static void SyncRegIE(void) +{ + if (sShouldSyncRegIE) + { + u16 temp = REG_IME; + REG_IME = 0; + REG_IE = sRegIE; + REG_IME = temp; + sShouldSyncRegIE = FALSE; + } +} + +void EnableInterrupts(u16 mask) +{ + sRegIE |= mask; + sShouldSyncRegIE = TRUE; + SyncRegIE(); + UpdateRegDispstatIntrBits(sRegIE); +} + +void DisableInterrupts(u16 mask) +{ + sRegIE &= ~mask; + sShouldSyncRegIE = TRUE; + SyncRegIE(); + UpdateRegDispstatIntrBits(sRegIE); +} + +static void UpdateRegDispstatIntrBits(u16 regIE) +{ + u16 oldValue = GetGpuReg(REG_OFFSET_DISPSTAT) & (DISPSTAT_HBLANK_INTR | DISPSTAT_VBLANK_INTR); + u16 newValue = 0; + + if (regIE & INTR_FLAG_VBLANK) + newValue |= DISPSTAT_VBLANK_INTR; + + if (regIE & INTR_FLAG_HBLANK) + newValue |= DISPSTAT_HBLANK_INTR; + + if (oldValue != newValue) + SetGpuReg(REG_OFFSET_DISPSTAT, newValue); +} diff --git a/gflib/gpu_regs.h b/gflib/gpu_regs.h new file mode 100644 index 000000000..89e0cb64b --- /dev/null +++ b/gflib/gpu_regs.h @@ -0,0 +1,19 @@ +#ifndef GUARD_GPU_REGS_H +#define GUARD_GPU_REGS_H + +// Exported type declarations + +// Exported RAM declarations + +// Exported ROM declarations +void InitGpuRegManager(void); +void CopyBufferedValuesToGpuRegs(void); +void SetGpuReg(u8 regOffset, u16 value); +void SetGpuReg_ForcedBlank(u8 regOffset, u16 value); +u16 GetGpuReg(u8 regOffset); +void SetGpuRegBits(u8 regOffset, u16 mask); +void ClearGpuRegBits(u8 regOffset, u16 mask); +void EnableInterrupts(u16 mask); +void DisableInterrupts(u16 mask); + +#endif //GUARD_GPU_REGS_H diff --git a/gflib/malloc.c b/gflib/malloc.c new file mode 100644 index 000000000..4d1a9fe5c --- /dev/null +++ b/gflib/malloc.c @@ -0,0 +1,210 @@ +#include "global.h" + +static void *sHeapStart; +static u32 sHeapSize; +static u32 malloc_c_unused_0300000c; // needed to align dma3_manager.o(.bss) + +#define MALLOC_SYSTEM_ID 0xA3A3 + +struct MemBlock { + // Whether this block is currently allocated. + bool16 flag; + + // Magic number used for error checking. Should equal MALLOC_SYSTEM_ID. + u16 magic; + + // Size of the block (not including this header struct). + u32 size; + + // Previous block pointer. Equals sHeapStart if this is the first block. + struct MemBlock *prev; + + // Next block pointer. Equals sHeapStart if this is the last block. + struct MemBlock *next; + + // Data in the memory block. (Arrays of length 0 are a GNU extension.) + u8 data[0]; +}; + +void PutMemBlockHeader(void *block, struct MemBlock *prev, struct MemBlock *next, u32 size) +{ + struct MemBlock *header = (struct MemBlock *)block; + + header->flag = FALSE; + header->magic = MALLOC_SYSTEM_ID; + header->size = size; + header->prev = prev; + header->next = next; +} + +void PutFirstMemBlockHeader(void *block, u32 size) +{ + PutMemBlockHeader(block, (struct MemBlock *)block, (struct MemBlock *)block, size - sizeof(struct MemBlock)); +} + +void *AllocInternal(void *heapStart, u32 size) +{ + struct MemBlock *pos = (struct MemBlock *)heapStart; + struct MemBlock *head = pos; + struct MemBlock *splitBlock; + u32 foundBlockSize; + + // Alignment + if (size & 3) + size = 4 * ((size / 4) + 1); + + for (;;) { + // Loop through the blocks looking for unused block that's big enough. + + if (!pos->flag) { + foundBlockSize = pos->size; + + if (foundBlockSize >= size) { + if (foundBlockSize - size < 2 * sizeof(struct MemBlock)) { + // The block isn't much bigger than the requested size, + // so just use it. + pos->flag = TRUE; + } else { + // The block is significantly bigger than the requested + // size, so split the rest into a separate block. + foundBlockSize -= sizeof(struct MemBlock); + foundBlockSize -= size; + + splitBlock = (struct MemBlock *)(pos->data + size); + + pos->flag = TRUE; + pos->size = size; + + PutMemBlockHeader(splitBlock, pos, pos->next, foundBlockSize); + + pos->next = splitBlock; + + if (splitBlock->next != head) + splitBlock->next->prev = splitBlock; + } + + return pos->data; + } + } + + if (pos->next == head) + return NULL; + + pos = pos->next; + } +} + +void FreeInternal(void *heapStart, void *pointer) +{ + if (pointer) { + struct MemBlock *head = (struct MemBlock *)heapStart; + struct MemBlock *block = (struct MemBlock *)((u8 *)pointer - sizeof(struct MemBlock)); + block->flag = FALSE; + + // If the freed block isn't the last one, merge with the next block + // if it's not in use. + if (block->next != head) { + if (!block->next->flag) { + block->size += sizeof(struct MemBlock) + block->next->size; + block->next->magic = 0; + block->next = block->next->next; + if (block->next != head) + block->next->prev = block; + } + } + + // If the freed block isn't the first one, merge with the previous block + // if it's not in use. + if (block != head) { + if (!block->prev->flag) { + block->prev->next = block->next; + + if (block->next != head) + block->next->prev = block->prev; + + block->magic = 0; + block->prev->size += sizeof(struct MemBlock) + block->size; + } + } + } +} + +void *AllocZeroedInternal(void *heapStart, u32 size) +{ + void *mem = AllocInternal(heapStart, size); + + if (mem != NULL) { + if (size & 3) + size = 4 * ((size / 4) + 1); + + CpuFill32(0, mem, size); + } + + return mem; +} + +bool32 CheckMemBlockInternal(void *heapStart, void *pointer) +{ + struct MemBlock *head = (struct MemBlock *)heapStart; + struct MemBlock *block = (struct MemBlock *)((u8 *)pointer - sizeof(struct MemBlock)); + + if (block->magic != MALLOC_SYSTEM_ID) + return FALSE; + + if (block->next->magic != MALLOC_SYSTEM_ID) + return FALSE; + + if (block->next != head && block->next->prev != block) + return FALSE; + + if (block->prev->magic != MALLOC_SYSTEM_ID) + return FALSE; + + if (block->prev != head && block->prev->next != block) + return FALSE; + + if (block->next != head && block->next != (struct MemBlock *)(block->data + block->size)) + return FALSE; + + return TRUE; +} + +void InitHeap(void *heapStart, u32 heapSize) +{ + sHeapStart = heapStart; + sHeapSize = heapSize; + PutFirstMemBlockHeader(heapStart, heapSize); +} + +void *Alloc(u32 size) +{ + return AllocInternal(sHeapStart, size); +} + +void *AllocZeroed(u32 size) +{ + return AllocZeroedInternal(sHeapStart, size); +} + +void Free(void *pointer) +{ + FreeInternal(sHeapStart, pointer); +} + +bool32 CheckMemBlock(void *pointer) +{ + return CheckMemBlockInternal(sHeapStart, pointer); +} + +bool32 CheckHeap() +{ + struct MemBlock *pos = (struct MemBlock *)sHeapStart; + + do { + if (!CheckMemBlockInternal(sHeapStart, pos->data)) + return FALSE; + pos = pos->next; + } while (pos != (struct MemBlock *)sHeapStart); + + return TRUE; +} diff --git a/gflib/malloc.h b/gflib/malloc.h new file mode 100644 index 000000000..f2dcf6d46 --- /dev/null +++ b/gflib/malloc.h @@ -0,0 +1,22 @@ +#ifndef GUARD_ALLOC_H +#define GUARD_ALLOC_H + +#define HEAP_SIZE 0x1C000 +#define malloc Alloc +#define calloc(ct, sz) AllocZeroed((ct) * (sz)) +#define free Free + +#define FREE_AND_SET_NULL(ptr) \ +{ \ + free(ptr); \ + ptr = NULL; \ +} + +extern u8 gHeap[]; + +void *Alloc(u32 size); +void *AllocZeroed(u32 size); +void Free(void *pointer); +void InitHeap(void *pointer, u32 size); + +#endif // GUARD_ALLOC_H diff --git a/gflib/sprite.c b/gflib/sprite.c new file mode 100644 index 000000000..e25eac62e --- /dev/null +++ b/gflib/sprite.c @@ -0,0 +1,1758 @@ +#include "global.h" +#include "sprite.h" +#include "main.h" +#include "palette.h" + +#define MAX_SPRITE_COPY_REQUESTS 64 + +#define OAM_MATRIX_COUNT 32 + +#define SET_SPRITE_TILE_RANGE(index, start, count) \ +{ \ + sSpriteTileRanges[index * 2] = start; \ + (sSpriteTileRanges + 1)[index * 2] = count; \ +} + +#define ALLOC_SPRITE_TILE(n) \ +{ \ + sSpriteTileAllocBitmap[(n) / 8] |= (1 << ((n) % 8)); \ +} + +#define FREE_SPRITE_TILE(n) \ +{ \ + sSpriteTileAllocBitmap[(n) / 8] &= ~(1 << ((n) % 8)); \ +} + +#define SPRITE_TILE_IS_ALLOCATED(n) ((sSpriteTileAllocBitmap[(n) / 8] >> ((n) % 8)) & 1) + + +struct SpriteCopyRequest +{ + const u8 *src; + u8 *dest; + u16 size; +}; + +struct OamDimensions +{ + s8 width; + s8 height; +}; + +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, const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority); +static void ResetOamMatrices(void); +static void ResetSprite(struct Sprite *sprite); +static s16 AllocSpriteTiles(u16 tileCount); +static void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images); +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 u8 IndexOfSpriteTileTag(u16 tag); +static void AllocSpriteTileRange(u16 tag, u16 start, u16 count); +static void DoLoadSpritePalette(const u16 *src, u16 paletteOffset); +static void obj_update_pos2(struct Sprite* sprite, s32 a1, s32 a2); + +typedef void (*AnimFunc)(struct Sprite *); +typedef void (*AnimCmdFunc)(struct Sprite *); +typedef void (*AffineAnimCmdFunc)(u8 matrixNum, struct Sprite *); + +#define DUMMY_OAM_DATA \ +{ \ + .y = 160, \ + .affineMode = 0, \ + .objMode = 0, \ + .mosaic = 0, \ + .bpp = 0, \ + .shape = SPRITE_SHAPE(8x8), \ + .x = 304, \ + .matrixNum = 0, \ + .size = SPRITE_SIZE(8x8), \ + .tileNum = 0, \ + .priority = 3, /* lowest priority */ \ + .paletteNum = 0, \ + .affineParam = 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 data. Also unreferenced in R/S. +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 = +{ + .oam = DUMMY_OAM_DATA, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .template = &gDummySpriteTemplate, + .subspriteTables = NULL, + .callback = SpriteCallbackDummy, + .pos1 = { 304, 160 }, + .pos2 = { 0, 0 }, + .centerToCornerVecX = 0, + .centerToCornerVecY = 0, + .animNum = 0, + .animCmdIndex = 0, + .animDelayCounter = 0, + .animPaused = 0, + .affineAnimPaused = 0, + .animLoopCounter = 0, + .data = {0, 0, 0, 0, 0, 0, 0}, + .inUse = 0, + .coordOffsetEnabled = 0, + .invisible = FALSE, + .flags_3 = 0, + .flags_4 = 0, + .flags_5 = 0, + .flags_6 = 0, + .flags_7 = 0, + .hFlip = 0, + .vFlip = 0, + .animBeginning = 0, + .affineAnimBeginning = 0, + .animEnded = 0, + .affineAnimEnded = 0, + .usingSheet = 0, + .flags_f = 0, + .sheetTileStart = 0, + .subspriteTableNum = 0, + .subspriteMode = 0, + .subpriority = 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 = +{ + .tileTag = 0, + .paletteTag = 0xFFFF, + .oam = &gDummyOamData, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy +}; + +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 s32 sUnknown_082EC6F4[3][4][2] = +{ + { + {8, 8}, + {0x10, 0x10}, + {0x20, 0x20}, + {0x40, 0x40}, + }, + { + {0x10, 8}, + {0x20, 8}, + {0x20, 0x10}, + {0x40, 0x20}, + }, + { + {8, 0x10}, + {8, 0x20}, + {0x10, 0x20}, + {0x20, 0x40}, + }, +}; + +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 }, + }, +}; + +// iwram bss +static u16 sSpriteTileRangeTags[MAX_SPRITES]; +static u16 sSpriteTileRanges[MAX_SPRITES * 2]; +static struct AffineAnimState sAffineAnimStates[OAM_MATRIX_COUNT]; +static u16 sSpritePaletteTags[16]; + +// iwram common +u32 gOamMatrixAllocBitmap; +u8 gReservedSpritePaletteCount; + +EWRAM_DATA struct Sprite gSprites[MAX_SPRITES + 1] = {0}; +EWRAM_DATA static u16 sSpritePriorities[MAX_SPRITES] = {0}; +EWRAM_DATA static u8 sSpriteOrder[MAX_SPRITES] = {0}; +EWRAM_DATA static bool8 sShouldProcessSpriteCopyRequests = 0; +EWRAM_DATA static u8 sSpriteCopyRequestCount = 0; +EWRAM_DATA static struct SpriteCopyRequest sSpriteCopyRequests[MAX_SPRITES] = {0}; +EWRAM_DATA u8 gOamLimit = 0; +EWRAM_DATA u16 gReservedSpriteTileCount = 0; +EWRAM_DATA static u8 sSpriteTileAllocBitmap[128] = {0}; +EWRAM_DATA s16 gSpriteCoordOffsetX = 0; +EWRAM_DATA s16 gSpriteCoordOffsetY = 0; +EWRAM_DATA struct OamMatrix gOamMatrices[OAM_MATRIX_COUNT] = {0}; +EWRAM_DATA bool8 gAffineAnimsDisabled = FALSE; + +void ResetSpriteData(void) +{ + ResetOamRange(0, 128); + ResetAllSprites(); + ClearSpriteCopyRequests(); + ResetAffineAnimData(); + FreeSpriteTileRanges(); + 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; + sShouldProcessSpriteCopyRequests = TRUE; +} + +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; + } + } + } +} + +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); + sSpritePriorities[i] = priority; + } +} + +void SortSprites(void) +{ + u8 i; + for (i = 1; i < MAX_SPRITES; i++) + { + u8 j = i; + struct Sprite *sprite1 = &gSprites[sSpriteOrder[i - 1]]; + struct Sprite *sprite2 = &gSprites[sSpriteOrder[i]]; + u16 sprite1Priority = sSpritePriorities[sSpriteOrder[i - 1]]; + u16 sprite2Priority = sSpritePriorities[sSpriteOrder[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 = sSpriteOrder[j]; + sSpriteOrder[j] = sSpriteOrder[j - 1]; + sSpriteOrder[j - 1] = temp; + + // UB: If j equals 1, then j-- makes j equal 0. + // Then, sSpriteOrder[-1] gets accessed below. + // Although this doesn't result in a bug in the ROM, + // the behavior is undefined. + j--; + + sprite1 = &gSprites[sSpriteOrder[j - 1]]; + sprite2 = &gSprites[sSpriteOrder[j]]; + sprite1Priority = sSpritePriorities[sSpriteOrder[j - 1]]; + sprite2Priority = sSpritePriorities[sSpriteOrder[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; + } + } + } + } +} + +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; + } +} + +void AddSpritesToOamBuffer(void) +{ + u8 i = 0; + u8 oamIndex = 0; + + while (i < MAX_SPRITES) + { + struct Sprite *sprite = &gSprites[sSpriteOrder[i]]; + if (sprite->inUse && !sprite->invisible && AddSpriteToOamBuffer(sprite, &oamIndex)) + return; + i++; + } + + while (oamIndex < gOamLimit) + { + gMain.oamBuffer[oamIndex] = gDummyOamData; + oamIndex++; + } +} + +u8 CreateSprite(const struct SpriteTemplate *template, s16 x, s16 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(const struct SpriteTemplate *template, s16 x, s16 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(&gDummySpriteTemplate, 0, 0, 31); + + if (index == MAX_SPRITES) + { + return MAX_SPRITES; + } + else + { + gSprites[index].invisible = TRUE; + gSprites[index].callback = callback; + return index; + } +} + +u8 CreateSpriteAt(u8 index, const 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(const 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++) + FREE_SPRITE_TILE(i); + } + 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)); +} + +void ClearSpriteCopyRequests(void) +{ + u8 i; + + sShouldProcessSpriteCopyRequests = FALSE; + sSpriteCopyRequestCount = 0; + + for (i = 0; i < MAX_SPRITE_COPY_REQUESTS; i++) + { + sSpriteCopyRequests[i].src = 0; + sSpriteCopyRequests[i].dest = 0; + sSpriteCopyRequests[i].size = 0; + } +} + +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; +} + +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; +} + +s16 AllocSpriteTiles(u16 tileCount) +{ + u16 i; + s16 start; + u16 numTilesFound; + + if (tileCount == 0) + { + // Free all unreserved tiles if the tile count is 0. + for (i = gReservedSpriteTileCount; i < TOTAL_OBJ_TILE_COUNT; i++) + FREE_SPRITE_TILE(i); + + return 0; + } + + i = gReservedSpriteTileCount; + + for (;;) + { + while (SPRITE_TILE_IS_ALLOCATED(i)) + { + 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 (!SPRITE_TILE_IS_ALLOCATED(i)) + numTilesFound++; + else + break; + } + + if (numTilesFound == tileCount) + break; + } + + for (i = start; i < tileCount + start; i++) + ALLOC_SPRITE_TILE(i); + + return start; +} + +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); + sSpriteTileAllocBitmap[index] &= val; + } + else if (op == 1) + { + val = (1 << val); + sSpriteTileAllocBitmap[index] |= val; + } + else + { + retVal = 1 << shift; + retVal &= sSpriteTileAllocBitmap[index]; + } + + return retVal; +} + +void SpriteCallbackDummy(struct Sprite *sprite) +{ +} + +void ProcessSpriteCopyRequests(void) +{ + if (sShouldProcessSpriteCopyRequests) + { + u8 i = 0; + + while (sSpriteCopyRequestCount > 0) + { + CpuCopy16(sSpriteCopyRequests[i].src, sSpriteCopyRequests[i].dest, sSpriteCopyRequests[i].size); + sSpriteCopyRequestCount--; + i++; + } + + sShouldProcessSpriteCopyRequests = FALSE; + } +} + +void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images) +{ + if (sSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS) + { + sSpriteCopyRequests[sSpriteCopyRequestCount].src = images[index].data; + sSpriteCopyRequests[sSpriteCopyRequestCount].dest = (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileNum; + sSpriteCopyRequests[sSpriteCopyRequestCount].size = images[index].size; + sSpriteCopyRequestCount++; + } +} + +void RequestSpriteCopy(const u8 *src, u8 *dest, u16 size) +{ + if (sSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS) + { + sSpriteCopyRequests[sSpriteCopyRequestCount].src = src; + sSpriteCopyRequests[sSpriteCopyRequestCount].dest = dest; + sSpriteCopyRequests[sSpriteCopyRequestCount].size = size; + sSpriteCopyRequestCount++; + } +} + +void CopyFromSprites(u8 *dest) +{ + u32 i; + u8 *src = (u8 *)gSprites; + for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++) + { + *dest = *src; + dest++; + src++; + } +} + +void CopyToSprites(u8 *src) +{ + u32 i; + u8 *dest = (u8 *)gSprites; + for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++) + { + *dest = *src; + src++; + dest++; + } +} + +void ResetAllSprites(void) +{ + u8 i; + + for (i = 0; i < MAX_SPRITES; i++) + { + ResetSprite(&gSprites[i]); + sSpriteOrder[i] = i; + } + + ResetSprite(&gSprites[i]); +} + +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 AnimateSprite(struct Sprite *sprite) +{ + sAnimFuncs[sprite->animBeginning](sprite); + + if (!gAffineAnimsDisabled) + sAffineAnimFuncs[sprite->affineAnimBeginning](sprite); +} + +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); + } +} + +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); + } +} + +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); +} + +void AnimCmd_end(struct Sprite *sprite) +{ + sprite->animCmdIndex--; + sprite->animEnded = TRUE; +} + +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); +} + +void AnimCmd_loop(struct Sprite *sprite) +{ + if (sprite->animLoopCounter) + ContinueAnimLoop(sprite); + else + BeginAnimLoop(sprite); +} + +void BeginAnimLoop(struct Sprite *sprite) +{ + sprite->animLoopCounter = sprite->anims[sprite->animNum][sprite->animCmdIndex].loop.count; + JumpToTopOfAnimLoop(sprite); + ContinueAnim(sprite); +} + +void ContinueAnimLoop(struct Sprite *sprite) +{ + sprite->animLoopCounter--; + JumpToTopOfAnimLoop(sprite); + ContinueAnim(sprite); +} + +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--; + } +} + +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; + if (sprite->flags_f) + obj_update_pos2(sprite, sprite->data[6], sprite->data[7]); + } +} + +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) + return; + else + { + 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); + } + if (sprite->flags_f) + obj_update_pos2(sprite, sprite->data[6], sprite->data[7]); + } +} + +void AffineAnimDelay(u8 matrixNum, struct Sprite *sprite) +{ + if (!DecrementAffineAnimDelayCounter(sprite, matrixNum)) + { + struct AffineAnimFrameCmd frameCmd; + GetAffineAnimFrame(matrixNum, sprite, &frameCmd); + ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &frameCmd); + } +} + +void AffineAnimCmd_loop(u8 matrixNum, struct Sprite *sprite) +{ + if (sAffineAnimStates[matrixNum].loopCounter) + ContinueAffineAnimLoop(matrixNum, sprite); + else + BeginAffineAnimLoop(matrixNum, sprite); +} + +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); +} + +void ContinueAffineAnimLoop(u8 matrixNum, struct Sprite *sprite) +{ + sAffineAnimStates[matrixNum].loopCounter--; + JumpToTopOfAffineAnimLoop(matrixNum, sprite); + ContinueAffineAnim(sprite); +} + +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--; + } +} + +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; +} + +void AffineAnimCmd_end(u8 matrixNum, struct Sprite *sprite) +{ + struct AffineAnimFrameCmd dummyFrameCmd = {0}; + sprite->affineAnimEnded = TRUE; + sAffineAnimStates[matrixNum].animCmdIndex--; + ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &dummyFrameCmd); +} + +void AffineAnimCmd_frame(u8 matrixNum, struct Sprite *sprite) +{ + struct AffineAnimFrameCmd frameCmd; + GetAffineAnimFrame(matrixNum, sprite, &frameCmd); + ApplyAffineAnimFrame(matrixNum, &frameCmd); + sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration; +} + +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; +} + +u8 GetSpriteMatrixNum(struct Sprite *sprite) +{ + u8 matrixNum = 0; + if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) + matrixNum = sprite->oam.matrixNum; + return matrixNum; +} + +void sub_8007E18(struct Sprite* sprite, s16 a2, s16 a3) +{ + sprite->data[6] = a2; + sprite->data[7] = a3; + sprite->flags_f = 1; +} + +s32 sub_8007E28(s32 a0, s32 a1, s32 a2) +{ + s32 subResult, var1; + + subResult = a1 - a0; + if (subResult < 0) + var1 = -(subResult) >> 9; + else + var1 = -(subResult >> 9); + return a2 - ((u32)(a2 * a1) / (u32)(a0) + var1); +} + +void obj_update_pos2(struct Sprite *sprite, s32 a1, s32 a2) +{ + s32 var0, var1, var2; + + u32 matrixNum = sprite->oam.matrixNum; + if (a1 != 0x800) + { + var0 = sUnknown_082EC6F4[sprite->oam.shape][sprite->oam.size][0]; + var1 = var0 << 8; + var2 = (var0 << 16) / gOamMatrices[matrixNum].a; + sprite->pos2.x = sub_8007E28(var1, var2, a1); + } + if (a2 != 0x800) + { + var0 = sUnknown_082EC6F4[sprite->oam.shape][sprite->oam.size][1]; + var1 = var0 << 8; + var2 = (var0 << 16) / gOamMatrices[matrixNum].d; + sprite->pos2.y = sub_8007E28(var1, var2, a2); + } +} + +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); +} + +void AffineAnimStateRestartAnim(u8 matrixNum) +{ + sAffineAnimStates[matrixNum].animCmdIndex = 0; + sAffineAnimStates[matrixNum].delayCounter = 0; + sAffineAnimStates[matrixNum].loopCounter = 0; +} + +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; +} + +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; +} + +void ApplyAffineAnimFrameAbsolute(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd) +{ + sAffineAnimStates[matrixNum].xScale = frameCmd->xScale; + sAffineAnimStates[matrixNum].yScale = frameCmd->yScale; + sAffineAnimStates[matrixNum].rotation = frameCmd->rotation << 8; +} + +void DecrementAnimDelayCounter(struct Sprite *sprite) +{ + if (!sprite->animPaused) + sprite->animDelayCounter--; +} + +bool8 DecrementAffineAnimDelayCounter(struct Sprite *sprite, u8 matrixNum) +{ + if (!sprite->affineAnimPaused) + --sAffineAnimStates[matrixNum].delayCounter; + return sprite->affineAnimPaused; +} + +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); +} + +s16 ConvertScaleParam(s16 scale) +{ + s32 val = 0x10000; + return val / scale; +} + +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; +} + +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; + } +} + +void ResetAffineAnimData(void) +{ + u8 i; + + gAffineAnimsDisabled = FALSE; + 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(const struct SpriteSheet *sheet) +{ + s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP); + + if (tileStart < 0) + { + return 0; + } + else + { + AllocSpriteTileRange(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(const struct SpriteSheet *sheets) +{ + u8 i; + for (i = 0; sheets[i].data != NULL; i++) + LoadSpriteSheet(&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++) + FREE_SPRITE_TILE(i); + + sSpriteTileRangeTags[index] = 0xFFFF; + } +} + +void FreeSpriteTileRanges(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]; +} + +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; +} + +void AllocSpriteTileRange(u16 tag, u16 start, u16 count) +{ + u8 freeIndex = IndexOfSpriteTileTag(0xFFFF); + sSpriteTileRangeTags[freeIndex] = tag; + SET_SPRITE_TILE_RANGE(freeIndex, start, count); +} + +void FreeAllSpritePalettes(void) +{ + u8 i; + gReservedSpritePaletteCount = 0; + for (i = 0; i < 16; i++) + sSpritePaletteTags[i] = 0xFFFF; +} + +u8 LoadSpritePalette(const 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; + DoLoadSpritePalette(palette->data, index * 16); + return index; + } +} + +void LoadSpritePalettes(const struct SpritePalette *palettes) +{ + u8 i; + for (i = 0; palettes[i].data != NULL; i++) + if (LoadSpritePalette(&palettes[i]) == 0xFF) + break; +} + +void DoLoadSpritePalette(const u16 *src, u16 paletteOffset) +{ + LoadPalette(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, const 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) +{ + const 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; +} diff --git a/gflib/sprite.h b/gflib/sprite.h new file mode 100644 index 000000000..9753837fd --- /dev/null +++ b/gflib/sprite.h @@ -0,0 +1,319 @@ +#ifndef GUARD_SPRITE_H +#define GUARD_SPRITE_H + +#define MAX_SPRITES 64 +#define SPRITE_INVALID_TAG 0xFFFF + +struct SpriteSheet +{ + const void *data; // Raw uncompressed pixel data + u16 size; + u16 tag; +}; + +struct CompressedSpriteSheet +{ + const u32 *data; // LZ77 compressed pixel data + u16 size; // Uncompressed size of pixel data + u16 tag; +}; + +struct SpriteFrameImage +{ + const void *data; + u16 size; +}; + +#define obj_frame_tiles(ptr) {.data = (u8 *)ptr, .size = sizeof ptr} + +#define overworld_frame(ptr, width, height, frame) {.data = (u8 *)ptr + (width * height * frame * 64)/2, .size = (width * height * 64)/2} + +struct SpritePalette +{ + const u16 *data; // Raw uncompressed palette data + u16 tag; +}; + +struct CompressedSpritePalette +{ + const u32 *data; // LZ77 compressed palette data + u16 tag; +}; + +struct AnimFrameCmd +{ + // If the sprite has an array of images, this is the array index. + // If the sprite has a sheet, this is the tile offset. + u32 imageValue:16; + + u32 duration:6; + u32 hFlip:1; + u32 vFlip:1; +}; + +struct AnimLoopCmd +{ + u32 type:16; + u32 count:6; +}; + +struct AnimJumpCmd +{ + u32 type:16; + u32 target:6; +}; + +// The first halfword of this union specifies the type of command. +// If it -2, then it is a jump command. If it is -1, then it is the end of the script. +// Otherwise, it is the imageValue for a frame command. +union AnimCmd +{ + s16 type; + struct AnimFrameCmd frame; + struct AnimLoopCmd loop; + struct AnimJumpCmd jump; +}; + +#define ANIMCMD_FRAME(...) \ + {.frame = {__VA_ARGS__}} +#define ANIMCMD_LOOP(_count) \ + {.loop = {.type = -3, .count = _count}} +#define ANIMCMD_JUMP(_target) \ + {.jump = {.type = -2, .target = _target}} +#define ANIMCMD_END \ + {.type = -1} + +struct AffineAnimFrameCmd +{ + s16 xScale; + s16 yScale; + u8 rotation; + u8 duration; +}; + +struct AffineAnimLoopCmd +{ + s16 type; + s16 count; +}; + +struct AffineAnimJumpCmd +{ + s16 type; + u16 target; +}; + +struct AffineAnimEndCmdAlt +{ + s16 type; + u16 val; +}; + +union AffineAnimCmd +{ + s16 type; + struct AffineAnimFrameCmd frame; + struct AffineAnimLoopCmd loop; + struct AffineAnimJumpCmd jump; + struct AffineAnimEndCmdAlt end; // unused in code +}; + +#define AFFINEANIMCMDTYPE_LOOP 0x7FFD +#define AFFINEANIMCMDTYPE_JUMP 0x7FFE +#define AFFINEANIMCMDTYPE_END 0x7FFF + +#define AFFINEANIMCMD_FRAME(_xScale, _yScale, _rotation, _duration) \ + {.frame = {.xScale = _xScale, .yScale = _yScale, .rotation = _rotation, .duration = _duration}} +#define AFFINEANIMCMD_LOOP(_count) \ + {.loop = {.type = AFFINEANIMCMDTYPE_LOOP, .count = _count}} +#define AFFINEANIMCMD_JUMP(_target) \ + {.jump = {.type = AFFINEANIMCMDTYPE_JUMP, .target = _target}} +#define AFFINEANIMCMD_END \ + {.type = AFFINEANIMCMDTYPE_END} +#define AFFINEANIMCMD_END_ALT(_val) \ + {.end = {.type = AFFINEANIMCMDTYPE_END, .val = _val}} + +struct AffineAnimState +{ + u8 animNum; + u8 animCmdIndex; + u8 delayCounter; + u8 loopCounter; + s16 xScale; + s16 yScale; + u16 rotation; +}; + +enum +{ + SUBSPRITES_OFF, + SUBSPRITES_ON, + SUBSPRITES_IGNORE_PRIORITY, // on but priority is ignored +}; + +struct Subsprite +{ + s8 x; // was u16 in R/S + s8 y; // was u16 in R/S + u16 shape:2; + u16 size:2; + u16 tileOffset:10; + u16 priority:2; +}; + +struct SubspriteTable +{ + u8 subspriteCount; + const struct Subsprite *subsprites; +}; + +struct Sprite; + +typedef void (*SpriteCallback)(struct Sprite *); + +struct SpriteTemplate +{ + u16 tileTag; + u16 paletteTag; + const struct OamData *oam; + const union AnimCmd *const *anims; + const struct SpriteFrameImage *images; + const union AffineAnimCmd *const *affineAnims; + SpriteCallback callback; +}; + +struct Sprite +{ + /*0x00*/ struct OamData oam; + /*0x08*/ const union AnimCmd *const *anims; + /*0x0C*/ const struct SpriteFrameImage *images; + /*0x10*/ const union AffineAnimCmd *const *affineAnims; + /*0x14*/ const struct SpriteTemplate *template; + /*0x18*/ const struct SubspriteTable *subspriteTables; + /*0x1C*/ SpriteCallback callback; + + /*0x20*/ struct Coords16 pos1; + /*0x24*/ struct Coords16 pos2; + /*0x28*/ s8 centerToCornerVecX; + /*0x29*/ s8 centerToCornerVecY; + + /*0x2A*/ u8 animNum; + /*0x2B*/ u8 animCmdIndex; + /*0x2C*/ u8 animDelayCounter:6; + bool8 animPaused:1; + bool8 affineAnimPaused:1; + /*0x2D*/ u8 animLoopCounter; + + // general purpose data fields + /*0x2E*/ s16 data[8]; + + /*0x3E*/ bool16 inUse:1; //1 + bool16 coordOffsetEnabled:1; //2 + bool16 invisible:1; //4 + bool16 flags_3:1; //8 + bool16 flags_4:1; //0x10 + bool16 flags_5:1; //0x20 + bool16 flags_6:1; //0x40 + bool16 flags_7:1; //0x80 + /*0x3F*/ bool16 hFlip:1; //1 + bool16 vFlip:1; //2 + bool16 animBeginning:1; //4 + bool16 affineAnimBeginning:1; //8 + bool16 animEnded:1; //0x10 + bool16 affineAnimEnded:1; //0x20 + bool16 usingSheet:1; //0x40 + bool16 flags_f:1; //0x80 + + /*0x40*/ u16 sheetTileStart; + + /*0x42*/ u8 subspriteTableNum:6; + u8 subspriteMode:2; + + /*0x43*/ u8 subpriority; +}; + +struct OamMatrix +{ + s16 a; + s16 b; + s16 c; + s16 d; +}; + +extern const struct OamData gDummyOamData; +extern const union AnimCmd *const gDummySpriteAnimTable[]; +extern const union AffineAnimCmd *const gDummySpriteAffineAnimTable[]; +extern const struct SpriteTemplate gDummySpriteTemplate; + +extern u8 gReservedSpritePaletteCount; +extern struct Sprite gSprites[]; +extern u8 gOamLimit; +extern u16 gReservedSpriteTileCount; +extern s16 gSpriteCoordOffsetX; +extern s16 gSpriteCoordOffsetY; +extern struct OamMatrix gOamMatrices[]; +extern bool8 gAffineAnimsDisabled; + +void ResetSpriteData(void); +void AnimateSprites(void); +void BuildOamBuffer(void); +u8 CreateSprite(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority); +u8 CreateSpriteAtEnd(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority); +u8 CreateInvisibleSprite(void (*callback)(struct Sprite *)); +u8 CreateSpriteAndAnimate(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority); +void DestroySprite(struct Sprite *sprite); +void ResetOamRange(u8 a, u8 b); +void LoadOam(void); +void SetOamMatrix(u8 matrixNum, u16 a, u16 b, u16 c, u16 d); +void CalcCenterToCornerVec(struct Sprite *sprite, u8 shape, u8 size, u8 affineMode); +void SpriteCallbackDummy(struct Sprite *sprite); +void ProcessSpriteCopyRequests(void); +void RequestSpriteCopy(const u8 *src, u8 *dest, u16 size); +void FreeSpriteTiles(struct Sprite *sprite); +void FreeSpritePalette(struct Sprite *sprite); +void FreeSpriteOamMatrix(struct Sprite *sprite); +void DestroySpriteAndFreeResources(struct Sprite *sprite); +void sub_800142C(u32 a1, u32 a2, u16 *a3, u16 a4, u32 a5); +void AnimateSprite(struct Sprite *sprite); +void sub_8007E18(struct Sprite* sprite, s16 a2, s16 a3); +void StartSpriteAnim(struct Sprite *sprite, u8 animNum); +void StartSpriteAnimIfDifferent(struct Sprite *sprite, u8 animNum); +void SeekSpriteAnim(struct Sprite *sprite, u8 animCmdIndex); +void StartSpriteAffineAnim(struct Sprite *sprite, u8 animNum); +void StartSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum); +void ChangeSpriteAffineAnim(struct Sprite *sprite, u8 animNum); +void ChangeSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum); +void SetSpriteSheetFrameTileNum(struct Sprite *sprite); +u8 AllocOamMatrix(void); +void FreeOamMatrix(u8 matrixNum); +void InitSpriteAffineAnim(struct Sprite *sprite); +void SetOamMatrixRotationScaling(u8 matrixNum, s16 xScale, s16 yScale, u16 rotation); +u16 LoadSpriteSheet(const struct SpriteSheet *sheet); +void LoadSpriteSheets(const struct SpriteSheet *sheets); +u16 AllocTilesForSpriteSheet(struct SpriteSheet *sheet); +void AllocTilesForSpriteSheets(struct SpriteSheet *sheets); +void LoadTilesForSpriteSheet(const struct SpriteSheet *sheet); +void LoadTilesForSpriteSheets(struct SpriteSheet *sheets); +void FreeSpriteTilesByTag(u16 tag); +void FreeSpriteTileRanges(void); +u16 GetSpriteTileStartByTag(u16 tag); +u16 GetSpriteTileTagByTileStart(u16 start); +void RequestSpriteSheetCopy(const struct SpriteSheet *sheet); +u16 LoadSpriteSheetDeferred(const struct SpriteSheet *sheet); +void FreeAllSpritePalettes(void); +u8 LoadSpritePalette(const struct SpritePalette *palette); +void LoadSpritePalettes(const struct SpritePalette *palettes); +u8 AllocSpritePalette(u16 tag); +u8 IndexOfSpritePaletteTag(u16 tag); +u16 GetSpritePaletteTagByPaletteNum(u8 paletteNum); +void FreeSpritePaletteByTag(u16 tag); +void SetSubspriteTables(struct Sprite *sprite, const struct SubspriteTable *subspriteTables); +bool8 AddSpriteToOamBuffer(struct Sprite *object, u8 *oamIndex); +bool8 AddSubspritesToOamBuffer(struct Sprite *sprite, struct OamData *destOam, u8 *oamIndex); +void CopyToSprites(u8 *src); +void CopyFromSprites(u8 *dest); +u8 SpriteTileAllocBitmapOp(u16 bit, u8 op); +void ClearSpriteCopyRequests(void); +void ResetAffineAnimData(void); + +#endif //GUARD_SPRITE_H diff --git a/gflib/string_util.c b/gflib/string_util.c new file mode 100644 index 000000000..bce299057 --- /dev/null +++ b/gflib/string_util.c @@ -0,0 +1,784 @@ +#include "global.h" +#include "string_util.h" +#include "text.h" + +EWRAM_DATA u8 gStringVar1[0x100] = {0}; +EWRAM_DATA u8 gStringVar2[0x100] = {0}; +EWRAM_DATA u8 gStringVar3[0x100] = {0}; +EWRAM_DATA u8 gStringVar4[0x3E8] = {0}; +EWRAM_DATA static u8 sUnknownStringVar[16] = {0}; + +static const u8 sDigits[] = __("0123456789ABCDEF"); + +static const s32 sPowersOfTen[] = +{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, +}; + +extern const u8 gExpandedPlaceholder_Empty[]; +extern const u8 gExpandedPlaceholder_Kun[]; +extern const u8 gExpandedPlaceholder_Chan[]; +extern const u8 gExpandedPlaceholder_Sapphire[]; +extern const u8 gExpandedPlaceholder_Ruby[]; +extern const u8 gExpandedPlaceholder_Emerald[]; +extern const u8 gExpandedPlaceholder_Aqua[]; +extern const u8 gExpandedPlaceholder_Magma[]; +extern const u8 gExpandedPlaceholder_Archie[]; +extern const u8 gExpandedPlaceholder_Maxie[]; +extern const u8 gExpandedPlaceholder_Kyogre[]; +extern const u8 gExpandedPlaceholder_Groudon[]; +extern const u8 gExpandedPlaceholder_Brendan[]; +extern const u8 gExpandedPlaceholder_May[]; + +u8 *StringCopy10(u8 *dest, const u8 *src) +{ + u8 i; + u32 limit = 10; + + for (i = 0; i < limit; i++) + { + dest[i] = src[i]; + + if (dest[i] == EOS) + return &dest[i]; + } + + dest[i] = EOS; + return &dest[i]; +} + +u8 *StringGetEnd10(u8 *str) +{ + u8 i; + u32 limit = 10; + + for (i = 0; i < limit; i++) + if (str[i] == EOS) + return &str[i]; + + str[i] = EOS; + return &str[i]; +} + +u8 *StringCopy7(u8 *dest, const u8 *src) +{ + s32 i; + s32 limit = 7; + + for (i = 0; i < limit; i++) + { + dest[i] = src[i]; + + if (dest[i] == EOS) + return &dest[i]; + } + + dest[i] = EOS; + return &dest[i]; +} + +u8 *StringCopy(u8 *dest, const u8 *src) +{ + while (*src != EOS) + { + *dest = *src; + dest++; + src++; + } + + *dest = EOS; + return dest; +} + +u8 *StringAppend(u8 *dest, const u8 *src) +{ + while (*dest != EOS) + dest++; + + return StringCopy(dest, src); +} + +u8 *StringCopyN(u8 *dest, const u8 *src, u8 n) +{ + u16 i; + + for (i = 0; i < n; i++) + dest[i] = src[i]; + + return &dest[n]; +} + +u8 *StringAppendN(u8 *dest, const u8 *src, u8 n) +{ + while (*dest != EOS) + dest++; + + return StringCopyN(dest, src, n); +} + +u16 StringLength(const u8 *str) +{ + u16 length = 0; + + while (str[length] != EOS) + length++; + + return length; +} + +s32 StringCompare(const u8 *str1, const u8 *str2) +{ + while (*str1 == *str2) + { + if (*str1 == EOS) + return 0; + str1++; + str2++; + } + + return *str1 - *str2; +} + +s32 StringCompareN(const u8 *str1, const u8 *str2, u32 n) +{ + while (*str1 == *str2) + { + if (*str1 == EOS) + return 0; + str1++; + str2++; + if (--n == 0) + return 0; + } + + return *str1 - *str2; +} + +bool8 IsStringLengthAtLeast(const u8 *str, s32 n) +{ + u8 i; + + for (i = 0; i < n; i++) + if (str[i] && str[i] != EOS) + return TRUE; + + return FALSE; +} + +u8 *ConvertIntToDecimalStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n) +{ + enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state; + s32 powerOfTen; + s32 largestPowerOfTen = sPowersOfTen[n - 1]; + + state = WAITING_FOR_NONZERO_DIGIT; + + if (mode == STR_CONV_MODE_RIGHT_ALIGN) + state = WRITING_SPACES; + + if (mode == STR_CONV_MODE_LEADING_ZEROS) + state = WRITING_DIGITS; + + for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10) + { + u8 c; + u16 digit = value / powerOfTen; + s32 temp = value - (powerOfTen * digit); + + if (state == WRITING_DIGITS) + { + u8 *out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (digit != 0 || powerOfTen == 1) + { + u8 *out; + state = WRITING_DIGITS; + out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (state == WRITING_SPACES) + { + *dest++ = 0x77; + } + + value = temp; + } + + *dest = EOS; + return dest; +} + +u8 *ConvertUIntToDecimalStringN(u8 *dest, u32 value, enum StringConvertMode mode, u8 n) +{ + enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state; + s32 powerOfTen; + s32 largestPowerOfTen = sPowersOfTen[n - 1]; + + state = WAITING_FOR_NONZERO_DIGIT; + + if (mode == STR_CONV_MODE_RIGHT_ALIGN) + state = WRITING_SPACES; + + if (mode == STR_CONV_MODE_LEADING_ZEROS) + state = WRITING_DIGITS; + + for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10) + { + u8 c; + u16 digit = value / powerOfTen; + u32 temp = value - (powerOfTen * digit); + + if (state == WRITING_DIGITS) + { + u8 *out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (digit != 0 || powerOfTen == 1) + { + u8 *out; + state = WRITING_DIGITS; + out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (state == WRITING_SPACES) + { + *dest++ = 0x77; + } + + value = temp; + } + + *dest = EOS; + return dest; +} + +u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n) +{ + enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state; + u8 i; + s32 powerOfSixteen; + s32 largestPowerOfSixteen = 1; + + for (i = 1; i < n; i++) + largestPowerOfSixteen *= 16; + + state = WAITING_FOR_NONZERO_DIGIT; + + if (mode == STR_CONV_MODE_RIGHT_ALIGN) + state = WRITING_SPACES; + + if (mode == STR_CONV_MODE_LEADING_ZEROS) + state = WRITING_DIGITS; + + for (powerOfSixteen = largestPowerOfSixteen; powerOfSixteen > 0; powerOfSixteen /= 16) + { + u8 c; + u32 digit = value / powerOfSixteen; + s32 temp = value % powerOfSixteen; + + if (state == WRITING_DIGITS) + { + char *out = dest++; + + if (digit <= 0xF) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (digit != 0 || powerOfSixteen == 1) + { + char *out; + state = WRITING_DIGITS; + out = dest++; + + if (digit <= 0xF) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (state == WRITING_SPACES) + { + *dest++ = 0x77; + } + + value = temp; + } + + *dest = EOS; + return dest; +} + +u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) +{ + for (;;) + { + u8 c = *src++; + u8 placeholderId; + const u8 *expandedString; + + switch (c) + { + case PLACEHOLDER_BEGIN: + placeholderId = *src++; + expandedString = GetExpandedPlaceholder(placeholderId); + dest = StringExpandPlaceholders(dest, expandedString); + break; + case EXT_CTRL_CODE_BEGIN: + *dest++ = c; + c = *src++; + *dest++ = c; + + switch (c) + { + case 0x07: + case 0x09: + case 0x0F: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + break; + case 0x04: + *dest++ = *src++; + case 0x0B: + *dest++ = *src++; + default: + *dest++ = *src++; + } + break; + case EOS: + *dest = EOS; + return dest; + case CHAR_PROMPT_SCROLL: + case CHAR_PROMPT_CLEAR: + case CHAR_NEWLINE: + default: + *dest++ = c; + } + } +} + +u8 *StringBraille(u8 *dest, const u8 *src) +{ + u8 setBrailleFont[] = { EXT_CTRL_CODE_BEGIN, 0x06, 0x06, EOS }; + u8 gotoLine2[] = { CHAR_NEWLINE, EXT_CTRL_CODE_BEGIN, 0x0E, 0x02, EOS }; + + dest = StringCopy(dest, setBrailleFont); + + for (;;) + { + u8 c = *src++; + + switch (c) + { + case EOS: + *dest = c; + return dest; + case CHAR_NEWLINE: + dest = StringCopy(dest, gotoLine2); + break; + default: + *dest++ = c; + *dest++ = c + 0x40; + break; + } + } +} + +static const u8 *ExpandPlaceholder_UnknownStringVar(void) +{ + return sUnknownStringVar; +} + +static const u8 *ExpandPlaceholder_PlayerName(void) +{ + return gSaveBlock2Ptr->playerName; +} + +static const u8 *ExpandPlaceholder_StringVar1(void) +{ + return gStringVar1; +} + +static const u8 *ExpandPlaceholder_StringVar2(void) +{ + return gStringVar2; +} + +static const u8 *ExpandPlaceholder_StringVar3(void) +{ + return gStringVar3; +} + +static const u8 *ExpandPlaceholder_KunChan(void) +{ + if (gSaveBlock2Ptr->playerGender == MALE) + return gExpandedPlaceholder_Kun; + else + return gExpandedPlaceholder_Chan; +} + +static const u8 *ExpandPlaceholder_RivalName(void) +{ + if (gSaveBlock2Ptr->playerGender == MALE) + return gExpandedPlaceholder_May; + else + return gExpandedPlaceholder_Brendan; +} + +static const u8 *ExpandPlaceholder_Version(void) +{ + return gExpandedPlaceholder_Emerald; +} + +static const u8 *ExpandPlaceholder_Aqua(void) +{ + return gExpandedPlaceholder_Aqua; +} + +static const u8 *ExpandPlaceholder_Magma(void) +{ + return gExpandedPlaceholder_Magma; +} + +static const u8 *ExpandPlaceholder_Archie(void) +{ + return gExpandedPlaceholder_Archie; +} + +static const u8 *ExpandPlaceholder_Maxie(void) +{ + return gExpandedPlaceholder_Maxie; +} + +static const u8 *ExpandPlaceholder_Kyogre(void) +{ + return gExpandedPlaceholder_Kyogre; +} + +static const u8 *ExpandPlaceholder_Groudon(void) +{ + return gExpandedPlaceholder_Groudon; +} + +const u8 *GetExpandedPlaceholder(u32 id) +{ + typedef const u8 *(*ExpandPlaceholderFunc)(void); + + static const ExpandPlaceholderFunc funcs[] = + { + [PLACEHOLDER_ID_UNKNOWN] = ExpandPlaceholder_UnknownStringVar, + [PLACEHOLDER_ID_PLAYER] = ExpandPlaceholder_PlayerName, + [PLACEHOLDER_ID_STRING_VAR_1] = ExpandPlaceholder_StringVar1, + [PLACEHOLDER_ID_STRING_VAR_2] = ExpandPlaceholder_StringVar2, + [PLACEHOLDER_ID_STRING_VAR_3] = ExpandPlaceholder_StringVar3, + [PLACEHOLDER_ID_KUN] = ExpandPlaceholder_KunChan, + [PLACEHOLDER_ID_RIVAL] = ExpandPlaceholder_RivalName, + [PLACEHOLDER_ID_VERSION] = ExpandPlaceholder_Version, + [PLACEHOLDER_ID_AQUA] = ExpandPlaceholder_Aqua, + [PLACEHOLDER_ID_MAGMA] = ExpandPlaceholder_Magma, + [PLACEHOLDER_ID_ARCHIE] = ExpandPlaceholder_Archie, + [PLACEHOLDER_ID_MAXIE] = ExpandPlaceholder_Maxie, + [PLACEHOLDER_ID_KYOGRE] = ExpandPlaceholder_Kyogre, + [PLACEHOLDER_ID_GROUDON] = ExpandPlaceholder_Groudon, + }; + + if (id >= ARRAY_COUNT(funcs)) + return gExpandedPlaceholder_Empty; + else + return funcs[id](); +} + +u8 *StringFill(u8 *dest, u8 c, u16 n) +{ + u16 i; + + for (i = 0; i < n; i++) + *dest++ = c; + + *dest = EOS; + return dest; +} + +u8 *StringCopyPadded(u8 *dest, const u8 *src, u8 c, u16 n) +{ + while (*src != EOS) + { + *dest++ = *src++; + + if (n) + n--; + } + + n--; + + while (n != (u16)-1) + { + *dest++ = c; + n--; + } + + *dest = EOS; + return dest; +} + +u8 *StringFillWithTerminator(u8 *dest, u16 n) +{ + return StringFill(dest, EOS, n); +} + +u8 *StringCopyN_Multibyte(u8 *dest, u8 *src, u32 n) +{ + u32 i; + + for (i = n - 1; i != (u32)-1; i--) + { + if (*src == EOS) + { + break; + } + else + { + *dest++ = *src++; + if (*(src - 1) == CHAR_SPECIAL_F9) + *dest++ = *src++; + } + } + + *dest = EOS; + return dest; +} + +u32 StringLength_Multibyte(const u8 *str) +{ + u32 length = 0; + + while (*str != EOS) + { + if (*str == CHAR_SPECIAL_F9) + str++; + str++; + length++; + } + + return length; +} + +u8 *WriteColorChangeControlCode(u8 *dest, u32 colorType, u8 color) +{ + *dest = EXT_CTRL_CODE_BEGIN; + dest++; + + switch (colorType) + { + case 0: + *dest = 1; + dest++; + break; + case 1: + *dest = 3; + dest++; + break; + case 2: + *dest = 2; + dest++; + break; + } + + *dest = color; + dest++; + *dest = EOS; + return dest; +} + +bool32 IsStringJapanese(u8 *str) +{ + while (*str != EOS) + { + if (*str <= 0xA0) + if (*str != CHAR_SPACE) + return TRUE; + str++; + } + + return FALSE; +} + +bool32 sub_800924C(u8 *str, s32 n) +{ + s32 i; + + for (i = 0; *str != EOS && i < n; i++) + { + if (*str <= 0xA0) + if (*str != CHAR_SPACE) + return TRUE; + str++; + } + + return FALSE; +} + +u8 GetExtCtrlCodeLength(u8 code) +{ + static const u8 lengths[] = + { + 1, + 2, + 2, + 2, + 4, + 2, + 2, + 1, + 2, + 1, + 1, + 3, + 2, + 2, + 2, + 1, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + }; + + u8 length = 0; + if (code < ARRAY_COUNT(lengths)) + length = lengths[code]; + return length; +} + +static const u8 *SkipExtCtrlCode(const u8 *s) +{ + while (*s == EXT_CTRL_CODE_BEGIN) + { + s++; + s += GetExtCtrlCodeLength(*s); + } + + return s; +} + +s32 StringCompareWithoutExtCtrlCodes(const u8 *str1, const u8 *str2) +{ + s32 retVal = 0; + + while (1) + { + str1 = SkipExtCtrlCode(str1); + str2 = SkipExtCtrlCode(str2); + + if (*str1 > *str2) + break; + + if (*str1 < *str2) + { + retVal = -1; + if (*str2 == EOS) + retVal = 1; + } + + if (*str1 == EOS) + return retVal; + + str1++; + str2++; + } + + retVal = 1; + + if (*str1 == EOS) + retVal = -1; + + return retVal; +} + +void ConvertInternationalString(u8 *s, u8 language) +{ + if (language == LANGUAGE_JAPANESE) + { + u8 i; + + StripExtCtrlCodes(s); + i = StringLength(s); + s[i++] = EXT_CTRL_CODE_BEGIN; + s[i++] = 22; + s[i++] = EOS; + + i--; + + while (i != (u8)-1) + { + s[i + 2] = s[i]; + i--; + } + + s[0] = EXT_CTRL_CODE_BEGIN; + s[1] = 21; + } +} + +void StripExtCtrlCodes(u8 *str) +{ + u16 srcIndex = 0; + u16 destIndex = 0; + while (str[srcIndex] != EOS) + { + if (str[srcIndex] == EXT_CTRL_CODE_BEGIN) + { + srcIndex++; + srcIndex += GetExtCtrlCodeLength(str[srcIndex]); + } + else + { + str[destIndex++] = str[srcIndex++]; + } + } + str[destIndex] = EOS; +} diff --git a/gflib/string_util.h b/gflib/string_util.h new file mode 100644 index 000000000..b921d2391 --- /dev/null +++ b/gflib/string_util.h @@ -0,0 +1,46 @@ +#ifndef GUARD_STRING_UTIL_H +#define GUARD_STRING_UTIL_H + +extern u8 gStringVar1[]; +extern u8 gStringVar2[]; +extern u8 gStringVar3[]; +extern u8 gStringVar4[]; + +enum StringConvertMode +{ + STR_CONV_MODE_LEFT_ALIGN, + STR_CONV_MODE_RIGHT_ALIGN, + STR_CONV_MODE_LEADING_ZEROS +}; + +u8 *StringCopy10(u8 *dest, const u8 *src); +u8 *StringGetEnd10(u8 *str); +u8 *StringCopy7(u8 *dest, const u8 *src); +u8 *StringCopy(u8 *dest, const u8 *src); +u8 *StringAppend(u8 *dest, const u8 *src); +u8 *StringCopyN(u8 *dest, const u8 *src, u8 n); +u8 *StringAppendN(u8 *dest, const u8 *src, u8 n); +u16 StringLength(const u8 *str); +s32 StringCompare(const u8 *str1, const u8 *str2); +s32 StringCompareN(const u8 *str1, const u8 *str2, u32 n); +bool8 IsStringLengthAtLeast(const u8 *str, s32 n); +u8 *ConvertIntToDecimalStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n); +u8 *ConvertUIntToDecimalStringN(u8 *dest, u32 value, enum StringConvertMode mode, u8 n); +u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n); +u8 *StringExpandPlaceholders(u8 *dest, const u8 *src); +u8 *StringBraille(u8 *dest, const u8 *src); +const u8 *GetExpandedPlaceholder(u32 id); +u8 *StringFill(u8 *dest, u8 c, u16 n); +u8 *StringCopyPadded(u8 *dest, const u8 *src, u8 c, u16 n); +u8 *StringFillWithTerminator(u8 *dest, u16 n); +u8 *StringCopyN_Multibyte(u8 *dest, u8 *src, u32 n); +u32 StringLength_Multibyte(const u8 *str); +u8 *WriteColorChangeControlCode(u8 *dest, u32 colorType, u8 color); +bool32 IsStringJapanese(u8 *str); +bool32 sub_800924C(u8 *str, s32 n); +u8 GetExtCtrlCodeLength(u8 code); +s32 StringCompareWithoutExtCtrlCodes(const u8 *str1, const u8 *str2); +void ConvertInternationalString(u8 *s, u8 language); +void StripExtCtrlCodes(u8 *str); + +#endif // GUARD_STRING_UTIL_H diff --git a/gflib/text.c b/gflib/text.c new file mode 100644 index 000000000..7e4fa7104 --- /dev/null +++ b/gflib/text.c @@ -0,0 +1,2540 @@ +#include "global.h" +#include "battle.h" +#include "main.h" +#include "m4a.h" +#include "palette.h" +#include "sound.h" +#include "constants/songs.h" +#include "string_util.h" +#include "window.h" +#include "text.h" +#include "blit.h" +#include "menu.h" +#include "dynamic_placeholder_text_util.h" + +EWRAM_DATA struct TextPrinter gTempTextPrinter = {0}; +EWRAM_DATA struct TextPrinter gTextPrinters[NUM_TEXT_PRINTERS] = {0}; + +static u16 gFontHalfRowLookupTable[0x51]; +static u16 gLastTextBgColor; +static u16 gLastTextFgColor; +static u16 gLastTextShadowColor; + +const struct FontInfo *gFonts; +u8 gUnknown_03002F84; +struct Struct_03002F90 gUnknown_03002F90; +TextFlags gTextFlags; + +const u8 gFontHalfRowOffsets[] = +{ + 0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00, + 0x09, 0x0A, 0x0B, 0x09, 0x0C, 0x0D, 0x0E, 0x0C, 0x0F, 0x10, 0x11, 0x0F, 0x09, 0x0A, 0x0B, 0x09, + 0x12, 0x13, 0x14, 0x12, 0x15, 0x16, 0x17, 0x15, 0x18, 0x19, 0x1A, 0x18, 0x12, 0x13, 0x14, 0x12, + 0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00, + 0x1B, 0x1C, 0x1D, 0x1B, 0x1E, 0x1F, 0x20, 0x1E, 0x21, 0x22, 0x23, 0x21, 0x1B, 0x1C, 0x1D, 0x1B, + 0x24, 0x25, 0x26, 0x24, 0x27, 0x28, 0x29, 0x27, 0x2A, 0x2B, 0x2C, 0x2A, 0x24, 0x25, 0x26, 0x24, + 0x2D, 0x2E, 0x2F, 0x2D, 0x30, 0x31, 0x32, 0x30, 0x33, 0x34, 0x35, 0x33, 0x2D, 0x2E, 0x2F, 0x2D, + 0x1B, 0x1C, 0x1D, 0x1B, 0x1E, 0x1F, 0x20, 0x1E, 0x21, 0x22, 0x23, 0x21, 0x1B, 0x1C, 0x1D, 0x1B, + 0x36, 0x37, 0x38, 0x36, 0x39, 0x3A, 0x3B, 0x39, 0x3C, 0x3D, 0x3E, 0x3C, 0x36, 0x37, 0x38, 0x36, + 0x3F, 0x40, 0x41, 0x3F, 0x42, 0x43, 0x44, 0x42, 0x45, 0x46, 0x47, 0x45, 0x3F, 0x40, 0x41, 0x3F, + 0x48, 0x49, 0x4A, 0x48, 0x4B, 0x4C, 0x4D, 0x4B, 0x4E, 0x4F, 0x50, 0x4E, 0x48, 0x49, 0x4A, 0x48, + 0x36, 0x37, 0x38, 0x36, 0x39, 0x3A, 0x3B, 0x39, 0x3C, 0x3D, 0x3E, 0x3C, 0x36, 0x37, 0x38, 0x36, + 0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00, + 0x09, 0x0A, 0x0B, 0x09, 0x0C, 0x0D, 0x0E, 0x0C, 0x0F, 0x10, 0x11, 0x0F, 0x09, 0x0A, 0x0B, 0x09, + 0x12, 0x13, 0x14, 0x12, 0x15, 0x16, 0x17, 0x15, 0x18, 0x19, 0x1A, 0x18, 0x12, 0x13, 0x14, 0x12, + 0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00 +}; + +const u8 gDownArrowTiles[] = INCBIN_U8("data/graphics/fonts/down_arrow.4bpp"); +const u8 gDarkDownArrowTiles[] = INCBIN_U8("data/graphics/fonts/down_arrow_RS.4bpp"); +const u8 gUnusedFRLGBlankedDownArrow[] = INCBIN_U8("data/graphics/fonts/unused_frlg_blanked_down_arrow.4bpp"); +const u8 gUnusedFRLGDownArrow[] = INCBIN_U8("data/graphics/fonts/unused_frlg_down_arrow.4bpp"); +const u8 gDownArrowYCoords[] = { 0x0, 0x1, 0x2, 0x1 }; +const u8 gWindowVerticalScrollSpeeds[] = { 0x1, 0x2, 0x4, 0x0 }; + +const struct GlyphWidthFunc gGlyphWidthFuncs[] = +{ + { 0x0, GetGlyphWidthFont0 }, + { 0x1, GetGlyphWidthFont1 }, + { 0x2, GetGlyphWidthFont2 }, + { 0x3, GetGlyphWidthFont2 }, + { 0x4, GetGlyphWidthFont2 }, + { 0x5, GetGlyphWidthFont2 }, + { 0x6, GetGlyphWidthFont6 }, + { 0x7, GetGlyphWidthFont7 }, + { 0x8, GetGlyphWidthFont8 } +}; + +const struct KeypadIcon gKeypadIcons[] = +{ + { 0x0, 0x8, 0xC }, + { 0x1, 0x8, 0xC }, + { 0x2, 0x10, 0xC }, + { 0x4, 0x10, 0xC }, + { 0x6, 0x18, 0xC }, + { 0x9, 0x18, 0xC }, + { 0xC, 0x8, 0xC }, + { 0xD, 0x8, 0xC }, + { 0xE, 0x8, 0xC }, + { 0xF, 0x8, 0xC }, + { 0x20, 0x8, 0xC }, + { 0x21, 0x8, 0xC }, + { 0x22, 0x8, 0xC } +}; + +const u8 gKeypadIconTiles[] = INCBIN_U8("data/graphics/fonts/keypad_icons.4bpp"); + +const struct FontInfo gFontInfos[] = +{ + { Font0Func, 0x5, 0xC, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font1Func, 0x6, 0x10, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font2Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font3Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font4Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font5Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font6Func, 0x8, 0x10, 0x0, 0x8, 0x0, 0x2, 0x1, 0x3 }, + { Font7Func, 0x5, 0x10, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font8Func, 0x5, 0x8, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { NULL, 0x8, 0x8, 0x0, 0x0, 0x0, 0x1, 0x2, 0xF } +}; + +const u8 gMenuCursorDimensions[][2] = +{ + { 0x8, 0xC }, + { 0x8, 0xF }, + { 0x8, 0xE }, + { 0x8, 0xE }, + { 0x8, 0xE }, + { 0x8, 0xE }, + { 0x8, 0x10 }, + { 0x8, 0xF }, + { 0x8, 0x8 }, + { 0x0, 0x0 } +}; + +const u16 gFont9JapaneseGlyphs[] = INCBIN_U16("data/graphics/fonts/font9.hwjpnfont"); + +extern const u16 gFont8LatinGlyphs[]; +extern const u8 gFont8LatinGlyphWidths[]; +extern const u16 gFont0LatinGlyphs[]; +extern const u8 gFont0LatinGlyphWidths[]; +extern const u16 gFont7LatinGlyphs[]; +extern const u8 gFont7LatinGlyphWidths[]; +extern const u16 gFont2LatinGlyphs[]; +extern const u8 gFont2LatinGlyphWidths[]; +extern const u16 gFont1LatinGlyphs[]; +extern const u8 gFont1LatinGlyphWidths[]; +extern const u16 gFont0JapaneseGlyphs[]; +extern const u16 gFont1JapaneseGlyphs[]; +extern const u16 gFont2JapaneseGlyphs[]; +extern const u8 gFont2JapaneseGlyphWidths[]; + +void SetFontsPointer(const struct FontInfo *fonts) +{ + gFonts = fonts; +} + +void DeactivateAllTextPrinters(void) +{ + int printer; + for (printer = 0; printer < NUM_TEXT_PRINTERS; ++printer) + gTextPrinters[printer].active = 0; +} + +u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) +{ + struct TextPrinterTemplate printerTemplate; + + printerTemplate.currentChar = str; + printerTemplate.windowId = windowId; + printerTemplate.fontId = fontId; + printerTemplate.x = x; + printerTemplate.y = y; + printerTemplate.currentX = x; + printerTemplate.currentY = y; + printerTemplate.letterSpacing = gFonts[fontId].letterSpacing; + printerTemplate.lineSpacing = gFonts[fontId].lineSpacing; + printerTemplate.unk = gFonts[fontId].unk; + printerTemplate.fgColor = gFonts[fontId].fgColor; + printerTemplate.bgColor = gFonts[fontId].bgColor; + printerTemplate.shadowColor = gFonts[fontId].shadowColor; + return AddTextPrinter(&printerTemplate, speed, callback); +} + +bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) +{ + int i; + u16 j; + u8 *ptr; + + if (!gFonts) + return FALSE; + + gTempTextPrinter.active = 1; + gTempTextPrinter.state = 0; + gTempTextPrinter.textSpeed = speed; + gTempTextPrinter.delayCounter = 0; + gTempTextPrinter.scrollDistance = 0; + + for (i = 0; i < 7; i++) + { + gTempTextPrinter.subUnion.fields[i] = 0; + } + + gTempTextPrinter.printerTemplate = *printerTemplate; + gTempTextPrinter.callback = callback; + gTempTextPrinter.minLetterSpacing = 0; + gTempTextPrinter.japanese = 0; + + GenerateFontHalfRowLookupTable(printerTemplate->fgColor, printerTemplate->bgColor, printerTemplate->shadowColor); + if (speed != TEXT_SPEED_FF && speed != 0x0) + { + --gTempTextPrinter.textSpeed; + gTextPrinters[printerTemplate->windowId] = gTempTextPrinter; + } + else + { + gTempTextPrinter.textSpeed = 0; + for (j = 0; j < 0x400; ++j) + { + if (RenderFont(&gTempTextPrinter) == 1) + break; + } + + if (speed != TEXT_SPEED_FF) + CopyWindowToVram(gTempTextPrinter.printerTemplate.windowId, 2); + gTextPrinters[printerTemplate->windowId].active = 0; + } + gUnknown_03002F84 = 0; + return TRUE; +} + +void RunTextPrinters(void) +{ + int i; + + if (gUnknown_03002F84 == 0) + { + for (i = 0; i < 0x20; ++i) + { + if (gTextPrinters[i].active) + { + u16 temp = RenderFont(&gTextPrinters[i]); + switch (temp) + { + case 0: + CopyWindowToVram(gTextPrinters[i].printerTemplate.windowId, 2); + case 3: + if (gTextPrinters[i].callback != 0) + gTextPrinters[i].callback(&gTextPrinters[i].printerTemplate, temp); + break; + case 1: + gTextPrinters[i].active = 0; + break; + } + } + } + } +} + +bool16 IsTextPrinterActive(u8 id) +{ + return gTextPrinters[id].active; +} + +u32 RenderFont(struct TextPrinter *textPrinter) +{ + u32 ret; + while (TRUE) + { + ret = gFonts[textPrinter->printerTemplate.fontId].fontFunction(textPrinter); + if (ret != 2) + return ret; + } +} + +void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadowColor) +{ + u32 fg12, bg12, shadow12; + u32 temp; + + u16 *current = gFontHalfRowLookupTable; + + gLastTextBgColor = bgColor; + gLastTextFgColor = fgColor; + gLastTextShadowColor = shadowColor; + + bg12 = bgColor << 12; + fg12 = fgColor << 12; + shadow12 = shadowColor << 12; + + temp = (bgColor << 8) | (bgColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (bgColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (bgColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (fgColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (fgColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (fgColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (shadowColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (shadowColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (shadowColor << 4) | bgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (bgColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (bgColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (bgColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (fgColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (fgColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (fgColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (shadowColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (shadowColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (shadowColor << 4) | fgColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (bgColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (bgColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (bgColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (fgColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (fgColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (fgColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (bgColor << 8) | (shadowColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (fgColor << 8) | (shadowColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; + + temp = (shadowColor << 8) | (shadowColor << 4) | shadowColor; + *(current++) = (bg12) | temp; + *(current++) = (fg12) | temp; + *(current++) = (shadow12) | temp; +} + +void SaveTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor) +{ + *bgColor = gLastTextBgColor; + *fgColor = gLastTextFgColor; + *shadowColor = gLastTextShadowColor; +} + +void RestoreTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor) +{ + GenerateFontHalfRowLookupTable(*fgColor, *bgColor, *shadowColor); +} + +void DecompressGlyphTile(const void *src_, void *dest_) +{ + u32 temp; + const u16 *src = src_; + u32 *dest = dest_; + + temp = *(src++); + *(dest)++ = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); +} + +u8 GetLastTextColor(u8 colorType) +{ + switch (colorType) + { + case 0: + return gLastTextFgColor; + case 2: + return gLastTextBgColor; + case 1: + return gLastTextShadowColor; + default: + return 0; + } +} + +#ifdef NONMATCHING + +#define GLYPH_COPY(fromY_, toY_, fromX_, toX_, unk) \ +{ \ + u32 i, j, *ptr, toY, fromX, toX, r5, toOrr, bits; \ + u8 *dst; \ + \ + j = fromX_; \ + i = fromY_; \ + ptr = unk; \ + toX = toX_; \ + toY = toY_; \ + fromX = fromX_; \ + \ + for (; i < toY; i++) \ + { \ + r5 = *(ptr++); \ + for (j = fromX; j < toX; j++) \ + { \ + toOrr = r5 & 0xF; \ + if (toOrr) \ + { \ + dst = windowTiles + ((j / 8) * 32) + ((j & 7) / 2) + ((i / 8) * widthOffset) + ((i & 7) * 4); \ + bits = ((j & 1) << 2); \ + *dst = ((toOrr << bits) | (*dst & (0xF0 >> bits))); \ + } \ + r5 >>= 4; \ + } \ + } \ +} + +void CopyGlyphToWindow(struct TextPrinter *textPrinter) +{ + struct Window *win; + struct WindowTemplate *winTempl; + struct Struct_03002F90 *unkStruct; + u32 currX, widthOffset, currY; + s32 r4, r0; + u8 *windowTiles; + + win = &gWindows[textPrinter->printerTemplate.windowId]; + winTempl = &win->window; + + r4 = (winTempl->width * 8) - textPrinter->printerTemplate.currentX; + if (r4 > gUnknown_03002F90.unk80) + r4 = gUnknown_03002F90.unk80; + + r0 = (winTempl->height * 8) - textPrinter->printerTemplate.currentY; + if (r0 > gUnknown_03002F90.unk81) + r0 = gUnknown_03002F90.unk81; + + currX = textPrinter->printerTemplate.currentX; + currY = textPrinter->printerTemplate.currentY; + unkStruct = &gUnknown_03002F90; + windowTiles = win->tileData; + widthOffset = winTempl->width * 32; + + if (r4 <= 8) + { + if (r0 <= 8) + { + GLYPH_COPY(currY, currY + r0, currX, currX + r4, unkStruct->unk0); + } + else + { + u32 temp; + GLYPH_COPY(currY, currY + 8, currX, currX + r4, unkStruct->unk0); + + temp = currY + 8; + GLYPH_COPY(temp, (temp - 8) + r0, currX, currX + r4, unkStruct->unk40); + } + } + else + { + if (r0 <= 8) + { + u32 temp; + GLYPH_COPY(currY, currY + r0, currX, currX + 8, unkStruct->unk0); + + temp = currX + 8; + GLYPH_COPY(currY, currY + r0, temp, (temp - 8) + r4, unkStruct->unk20); + } + else + { + u32 temp; + GLYPH_COPY(currY, currY + 8, currX, currX + 8, unkStruct->unk0); + + temp = currX + 8; + GLYPH_COPY(currY, currY + 8, temp, temp - 8 + r4, unkStruct->unk20); + + temp = currY + 8; + GLYPH_COPY(temp, temp - 8 + r0, currX, currX + 8, unkStruct->unk40); + { + u32 tempX, tempY; + tempX = currX + 8; + tempY = currY + 8; + GLYPH_COPY(tempY, tempY - 8 + r0, tempX, tempX - 8 + r4, unkStruct->unk60); + } + } + } +} +#else +NAKED +void CopyGlyphToWindow(struct TextPrinter *x) +{ + asm("push {r4-r7,lr}\n\ + mov r7, r10\n\ + mov r6, r9\n\ + mov r5, r8\n\ + push {r5-r7}\n\ + sub sp, #0x8C\n\ + add r3, r0, #0\n\ + ldrb r1, [r3, #0x4]\n\ + lsl r0, r1, #1\n\ + add r0, r1\n\ + lsl r0, #2\n\ + ldr r1, =gWindows\n\ + add r1, r0, r1\n\ + add r2, r1, #0\n\ + ldrb r7, [r1, #0x3]\n\ + lsl r0, r7, #3\n\ + ldrb r6, [r3, #0x8]\n\ + sub r4, r0, r6\n\ + ldr r5, =gUnknown_03002F90\n\ + add r0, r5, #0\n\ + add r0, #0x80\n\ + ldrb r0, [r0]\n\ + cmp r4, r0\n\ + ble _08004DD2\n\ + add r4, r0, #0\n\ +_08004DD2:\n\ + ldrb r0, [r1, #0x4]\n\ + lsl r0, #3\n\ + ldrb r3, [r3, #0x9]\n\ + sub r0, r3\n\ + add r1, r5, #0\n\ + add r1, #0x81\n\ + ldrb r1, [r1]\n\ + cmp r0, r1\n\ + ble _08004DE6\n\ + add r0, r1, #0\n\ +_08004DE6:\n\ + str r6, [sp]\n\ + mov r8, r3\n\ + add r3, r5, #0\n\ + ldr r2, [r2, #0x8]\n\ + mov r9, r2\n\ + lsl r1, r7, #5\n\ + str r1, [sp, #0x4]\n\ + cmp r4, #0x8\n\ + ble _08004DFA\n\ + b _08004F94\n\ +_08004DFA:\n\ + cmp r0, #0x8\n\ + bgt _08004E84\n\ + mov r1, r8\n\ + str r3, [sp, #0x8]\n\ + add r2, r6, #0\n\ + add r2, r4\n\ + mov r8, r2\n\ + add r0, r1, r0\n\ + str r0, [sp, #0xC]\n\ + str r6, [sp, #0x10]\n\ + cmp r1, r0\n\ + bcc _08004E14\n\ + b _080052AA\n\ +_08004E14:\n\ + ldr r3, [sp, #0x8]\n\ + ldm r3!, {r5}\n\ + str r3, [sp, #0x8]\n\ + ldr r4, [sp, #0x10]\n\ + add r0, r1, #0x1\n\ + mov r10, r0\n\ + cmp r4, r8\n\ + bcs _08004E72\n\ + mov r2, #0x7\n\ + mov r12, r2\n\ + lsr r0, r1, #3\n\ + ldr r2, [sp, #0x4]\n\ + add r3, r0, #0\n\ + mul r3, r2\n\ + add r7, r3, #0\n\ + mov r3, r12\n\ + and r1, r3\n\ + lsl r6, r1, #2\n\ +_08004E38:\n\ + add r3, r5, #0\n\ + mov r0, #0xF\n\ + and r3, r0\n\ + cmp r3, #0\n\ + beq _08004E6A\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, r12\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_08004E6A:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r8\n\ + bcc _08004E38\n\ +_08004E72:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0xC]\n\ + cmp r1, r2\n\ + bcc _08004E14\n\ + b _080052AA\n\ + .pool\n\ +_08004E84:\n\ + mov r1, r8\n\ + str r3, [sp, #0x14]\n\ + ldr r3, [sp]\n\ + add r3, r4\n\ + mov r12, r3\n\ + add r2, r1, #0\n\ + add r2, #0x8\n\ + str r2, [sp, #0x18]\n\ + ldr r3, [sp]\n\ + str r3, [sp, #0x1C]\n\ + mov r2, r12\n\ + str r2, [sp, #0x74]\n\ + ldr r3, [sp, #0x18]\n\ + str r3, [sp, #0x88]\n\ + sub r0, #0x8\n\ + str r0, [sp, #0x80]\n\ + cmp r1, r3\n\ + bcs _08004F0E\n\ +_08004EA8:\n\ + ldr r0, [sp, #0x14]\n\ + ldm r0!, {r5}\n\ + str r0, [sp, #0x14]\n\ + ldr r4, [sp, #0x1C]\n\ + add r2, r1, #0x1\n\ + mov r8, r2\n\ + cmp r4, r12\n\ + bcs _08004F06\n\ + mov r3, #0x7\n\ + mov r10, r3\n\ + lsr r0, r1, #3\n\ + ldr r3, [sp, #0x4]\n\ + add r2, r0, #0\n\ + mul r2, r3\n\ + add r7, r2, #0\n\ + mov r0, r10\n\ + and r1, r0\n\ + lsl r6, r1, #2\n\ +_08004ECC:\n\ + add r3, r5, #0\n\ + mov r1, #0xF\n\ + and r3, r1\n\ + cmp r3, #0\n\ + beq _08004EFE\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, r10\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_08004EFE:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r12\n\ + bcc _08004ECC\n\ +_08004F06:\n\ + mov r1, r8\n\ + ldr r2, [sp, #0x18]\n\ + cmp r1, r2\n\ + bcc _08004EA8\n\ +_08004F0E:\n\ + ldr r1, [sp, #0x88]\n\ + ldr r3, =gUnknown_03002F90 + 0x40\n\ + str r3, [sp, #0x20]\n\ + ldr r0, [sp, #0x74]\n\ + mov r8, r0\n\ + ldr r2, [sp, #0x80]\n\ + add r2, r1, r2\n\ + str r2, [sp, #0x24]\n\ + ldr r3, [sp]\n\ + str r3, [sp, #0x28]\n\ + cmp r1, r2\n\ + bcc _08004F28\n\ + b _080052AA\n\ +_08004F28:\n\ + ldr r0, [sp, #0x20]\n\ + ldm r0!, {r5}\n\ + str r0, [sp, #0x20]\n\ + ldr r4, [sp, #0x28]\n\ + add r2, r1, #0x1\n\ + mov r10, r2\n\ + cmp r4, r8\n\ + bcs _08004F86\n\ + mov r3, #0x7\n\ + mov r12, r3\n\ + lsr r0, r1, #3\n\ + ldr r3, [sp, #0x4]\n\ + add r2, r0, #0\n\ + mul r2, r3\n\ + add r7, r2, #0\n\ + mov r0, r12\n\ + and r1, r0\n\ + lsl r6, r1, #2\n\ +_08004F4C:\n\ + add r3, r5, #0\n\ + mov r1, #0xF\n\ + and r3, r1\n\ + cmp r3, #0\n\ + beq _08004F7E\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, r12\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_08004F7E:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r8\n\ + bcc _08004F4C\n\ +_08004F86:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0x24]\n\ + cmp r1, r2\n\ + bcc _08004F28\n\ + b _080052AA\n\ + .pool\n\ +_08004F94:\n\ + cmp r0, #0x8\n\ + ble _08004F9A\n\ + b _080050A4\n\ +_08004F9A:\n\ + mov r1, r8\n\ + str r3, [sp, #0x2C]\n\ + ldr r3, [sp]\n\ + add r3, #0x8\n\ + mov r12, r3\n\ + add r0, r8\n\ + str r0, [sp, #0x30]\n\ + ldr r0, [sp]\n\ + str r0, [sp, #0x34]\n\ + ldr r2, [sp, #0x30]\n\ + str r2, [sp, #0x78]\n\ + str r3, [sp, #0x84]\n\ + sub r4, #0x8\n\ + str r4, [sp, #0x7C]\n\ + cmp r8, r2\n\ + bcs _0800501C\n\ +_08004FBA:\n\ + ldr r0, [sp, #0x2C]\n\ + ldm r0!, {r5}\n\ + str r0, [sp, #0x2C]\n\ + ldr r4, [sp, #0x34]\n\ + add r2, r1, #0x1\n\ + mov r10, r2\n\ + cmp r4, r12\n\ + bcs _08005014\n\ + lsr r0, r1, #3\n\ + ldr r2, [sp, #0x4]\n\ + add r3, r0, #0\n\ + mul r3, r2\n\ + add r7, r3, #0\n\ + mov r3, #0x7\n\ + and r1, r3\n\ + lsl r6, r1, #2\n\ +_08004FDA:\n\ + add r3, r5, #0\n\ + mov r0, #0xF\n\ + and r3, r0\n\ + cmp r3, #0\n\ + beq _0800500C\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, #0x7\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_0800500C:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r12\n\ + bcc _08004FDA\n\ +_08005014:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0x30]\n\ + cmp r1, r2\n\ + bcc _08004FBA\n\ +_0800501C:\n\ + mov r1, r8\n\ + ldr r3, =gUnknown_03002F90 + 0x20\n\ + str r3, [sp, #0x38]\n\ + ldr r0, [sp, #0x84]\n\ + ldr r2, [sp, #0x7C]\n\ + add r0, r2\n\ + mov r8, r0\n\ + ldr r3, [sp, #0x78]\n\ + str r3, [sp, #0x3C]\n\ + ldr r0, [sp, #0x84]\n\ + str r0, [sp, #0x40]\n\ + cmp r1, r3\n\ + bcc _08005038\n\ + b _080052AA\n\ +_08005038:\n\ + ldr r2, [sp, #0x38]\n\ + ldm r2!, {r5}\n\ + str r2, [sp, #0x38]\n\ + ldr r4, [sp, #0x40]\n\ + add r3, r1, #0x1\n\ + mov r10, r3\n\ + cmp r4, r8\n\ + bcs _08005096\n\ + mov r0, #0x7\n\ + mov r12, r0\n\ + lsr r0, r1, #3\n\ + ldr r3, [sp, #0x4]\n\ + add r2, r0, #0\n\ + mul r2, r3\n\ + add r7, r2, #0\n\ + mov r0, r12\n\ + and r1, r0\n\ + lsl r6, r1, #2\n\ +_0800505C:\n\ + add r3, r5, #0\n\ + mov r1, #0xF\n\ + and r3, r1\n\ + cmp r3, #0\n\ + beq _0800508E\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, r12\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_0800508E:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r8\n\ + bcc _0800505C\n\ +_08005096:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0x3C]\n\ + cmp r1, r2\n\ + bcc _08005038\n\ + b _080052AA\n\ + .pool\n\ +_080050A4:\n\ + mov r1, r8\n\ + str r5, [sp, #0x44]\n\ + ldr r3, [sp]\n\ + add r3, #0x8\n\ + mov r12, r3\n\ + mov r2, r8\n\ + add r2, #0x8\n\ + str r2, [sp, #0x48]\n\ + ldr r3, [sp]\n\ + str r3, [sp, #0x4C]\n\ + str r2, [sp, #0x88]\n\ + sub r0, #0x8\n\ + str r0, [sp, #0x80]\n\ + mov r0, r12\n\ + str r0, [sp, #0x84]\n\ + sub r4, #0x8\n\ + str r4, [sp, #0x7C]\n\ + cmp r8, r2\n\ + bcs _0800512C\n\ +_080050CA:\n\ + ldr r2, [sp, #0x44]\n\ + ldm r2!, {r5}\n\ + str r2, [sp, #0x44]\n\ + ldr r4, [sp, #0x4C]\n\ + add r3, r1, #0x1\n\ + mov r10, r3\n\ + cmp r4, r12\n\ + bcs _08005124\n\ + lsr r0, r1, #3\n\ + ldr r3, [sp, #0x4]\n\ + add r2, r0, #0\n\ + mul r2, r3\n\ + add r7, r2, #0\n\ + mov r0, #0x7\n\ + and r1, r0\n\ + lsl r6, r1, #2\n\ +_080050EA:\n\ + add r3, r5, #0\n\ + mov r1, #0xF\n\ + and r3, r1\n\ + cmp r3, #0\n\ + beq _0800511C\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, #0x7\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_0800511C:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r12\n\ + bcc _080050EA\n\ +_08005124:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0x48]\n\ + cmp r1, r2\n\ + bcc _080050CA\n\ +_0800512C:\n\ + mov r1, r8\n\ + ldr r3, =gUnknown_03002F90 + 0x20\n\ + str r3, [sp, #0x50]\n\ + ldr r0, [sp, #0x84]\n\ + ldr r2, [sp, #0x7C]\n\ + add r0, r2\n\ + mov r8, r0\n\ + ldr r3, [sp, #0x88]\n\ + str r3, [sp, #0x54]\n\ + ldr r0, [sp, #0x84]\n\ + str r0, [sp, #0x58]\n\ + cmp r1, r3\n\ + bcs _080051AC\n\ +_08005146:\n\ + ldr r2, [sp, #0x50]\n\ + ldm r2!, {r5}\n\ + str r2, [sp, #0x50]\n\ + ldr r4, [sp, #0x58]\n\ + add r3, r1, #0x1\n\ + mov r10, r3\n\ + cmp r4, r8\n\ + bcs _080051A4\n\ + mov r0, #0x7\n\ + mov r12, r0\n\ + lsr r0, r1, #3\n\ + ldr r3, [sp, #0x4]\n\ + add r2, r0, #0\n\ + mul r2, r3\n\ + add r7, r2, #0\n\ + mov r0, r12\n\ + and r1, r0\n\ + lsl r6, r1, #2\n\ +_0800516A:\n\ + add r3, r5, #0\n\ + mov r1, #0xF\n\ + and r3, r1\n\ + cmp r3, #0\n\ + beq _0800519C\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, r12\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_0800519C:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r8\n\ + bcc _0800516A\n\ +_080051A4:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0x54]\n\ + cmp r1, r2\n\ + bcc _08005146\n\ +_080051AC:\n\ + ldr r1, [sp, #0x88]\n\ + ldr r3, =gUnknown_03002F90 + 0x40\n\ + str r3, [sp, #0x5C]\n\ + ldr r0, [sp, #0x84]\n\ + mov r8, r0\n\ + ldr r2, [sp, #0x80]\n\ + add r2, r1, r2\n\ + str r2, [sp, #0x60]\n\ + ldr r3, [sp]\n\ + str r3, [sp, #0x64]\n\ + cmp r1, r2\n\ + bcs _0800522A\n\ +_080051C4:\n\ + ldr r0, [sp, #0x5C]\n\ + ldm r0!, {r5}\n\ + str r0, [sp, #0x5C]\n\ + ldr r4, [sp, #0x64]\n\ + add r2, r1, #0x1\n\ + mov r10, r2\n\ + cmp r4, r8\n\ + bcs _08005222\n\ + mov r3, #0x7\n\ + mov r12, r3\n\ + lsr r0, r1, #3\n\ + ldr r3, [sp, #0x4]\n\ + add r2, r0, #0\n\ + mul r2, r3\n\ + add r7, r2, #0\n\ + mov r0, r12\n\ + and r1, r0\n\ + lsl r6, r1, #2\n\ +_080051E8:\n\ + add r3, r5, #0\n\ + mov r1, #0xF\n\ + and r3, r1\n\ + cmp r3, #0\n\ + beq _0800521A\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, r12\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_0800521A:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r8\n\ + bcc _080051E8\n\ +_08005222:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0x60]\n\ + cmp r1, r2\n\ + bcc _080051C4\n\ +_0800522A:\n\ + ldr r4, [sp, #0x84]\n\ + ldr r1, [sp, #0x88]\n\ + ldr r3, =gUnknown_03002F90 + 0x60\n\ + str r3, [sp, #0x68]\n\ + ldr r0, [sp, #0x7C]\n\ + add r0, r4\n\ + mov r8, r0\n\ + ldr r2, [sp, #0x80]\n\ + add r2, r1, r2\n\ + str r2, [sp, #0x6C]\n\ + str r4, [sp, #0x70]\n\ + cmp r1, r2\n\ + bcs _080052AA\n\ +_08005244:\n\ + ldr r3, [sp, #0x68]\n\ + ldm r3!, {r5}\n\ + str r3, [sp, #0x68]\n\ + ldr r4, [sp, #0x70]\n\ + add r0, r1, #0x1\n\ + mov r10, r0\n\ + cmp r4, r8\n\ + bcs _080052A2\n\ + mov r2, #0x7\n\ + mov r12, r2\n\ + lsr r0, r1, #3\n\ + ldr r2, [sp, #0x4]\n\ + add r3, r0, #0\n\ + mul r3, r2\n\ + add r7, r3, #0\n\ + mov r3, r12\n\ + and r1, r3\n\ + lsl r6, r1, #2\n\ +_08005268:\n\ + add r3, r5, #0\n\ + mov r0, #0xF\n\ + and r3, r0\n\ + cmp r3, #0\n\ + beq _0800529A\n\ + lsr r2, r4, #3\n\ + lsl r2, #5\n\ + add r2, r9\n\ + add r0, r4, #0\n\ + mov r1, r12\n\ + and r0, r1\n\ + lsr r0, #1\n\ + add r2, r0\n\ + add r2, r7\n\ + add r2, r6\n\ + mov r1, #0x1\n\ + and r1, r4\n\ + lsl r1, #2\n\ + lsl r3, r1\n\ + mov r0, #0xF0\n\ + asr r0, r1\n\ + ldrb r1, [r2]\n\ + and r0, r1\n\ + orr r3, r0\n\ + strb r3, [r2]\n\ +_0800529A:\n\ + lsr r5, #4\n\ + add r4, #0x1\n\ + cmp r4, r8\n\ + bcc _08005268\n\ +_080052A2:\n\ + mov r1, r10\n\ + ldr r2, [sp, #0x6C]\n\ + cmp r1, r2\n\ + bcc _08005244\n\ +_080052AA:\n\ + add sp, #0x8C\n\ + pop {r3-r5}\n\ + mov r8, r3\n\ + mov r9, r4\n\ + mov r10, r5\n\ + pop {r4-r7}\n\ + pop {r0}\n\ + bx r0\n\ + .pool"); +} +#endif // NONMATCHING + +void ClearTextSpan(struct TextPrinter *textPrinter, u32 width) +{ + struct Window *window; + struct Bitmap pixels_data; + struct Struct_03002F90 *gUnk; + u8* glyphHeight; + + if (gLastTextBgColor != 0) + { + window = &gWindows[textPrinter->printerTemplate.windowId]; + pixels_data.pixels = window->tileData; + pixels_data.width = window->window.width << 3; + pixels_data.height = window->window.height << 3; + + gUnk = &gUnknown_03002F90; + glyphHeight = &gUnk->unk81; + + FillBitmapRect4Bit( + &pixels_data, + textPrinter->printerTemplate.currentX, + textPrinter->printerTemplate.currentY, + width, + *glyphHeight, + gLastTextBgColor); + } +} + +u16 Font0Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 0; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font1Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 1; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font2Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 2; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font3Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 3; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font4Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 4; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font5Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 5; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font7Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 7; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font8Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + textPrinter->subUnion.sub.glyphId = 8; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +void TextPrinterInitDownArrowCounters(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (gTextFlags.autoScroll == 1) + subStruct->autoScrollDelay = 0; + else + { + subStruct->downArrowYPosIdx = 0; + subStruct->downArrowDelay = 0; + } +} + +void TextPrinterDrawDownArrow(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + const u8 *arrowTiles; + + if (gTextFlags.autoScroll == 0) + { + if (subStruct->downArrowDelay != 0) + { + subStruct->downArrowDelay = ((*(u32*)&textPrinter->subUnion.sub) << 19 >> 27) - 1; // convoluted way of getting downArrowDelay, necessary to match + } + else + { + FillWindowPixelRect( + textPrinter->printerTemplate.windowId, + textPrinter->printerTemplate.bgColor << 4 | textPrinter->printerTemplate.bgColor, + textPrinter->printerTemplate.currentX, + textPrinter->printerTemplate.currentY, + 0x8, + 0x10); + + switch (gTextFlags.useAlternateDownArrow) + { + case 0: + default: + arrowTiles = gDownArrowTiles; + break; + case 1: + arrowTiles = gDarkDownArrowTiles; + break; + } + + BlitBitmapRectToWindow( + textPrinter->printerTemplate.windowId, + arrowTiles, + 0, + gDownArrowYCoords[*(u32*)subStruct << 17 >> 30], // subStruct->downArrowYPosIdx but again, stupidly retrieved + 0x8, + 0x10, + textPrinter->printerTemplate.currentX, + textPrinter->printerTemplate.currentY, + 0x8, + 0x10); + CopyWindowToVram(textPrinter->printerTemplate.windowId, 0x2); + + subStruct->downArrowDelay = 0x8; + subStruct->downArrowYPosIdx = (*(u32*)subStruct << 17 >> 30) + 1; + } + } +} + +void TextPrinterClearDownArrow(struct TextPrinter *textPrinter) +{ + FillWindowPixelRect( + textPrinter->printerTemplate.windowId, + textPrinter->printerTemplate.bgColor << 4 | textPrinter->printerTemplate.bgColor, + textPrinter->printerTemplate.currentX, + textPrinter->printerTemplate.currentY, + 0x8, + 0x10); + CopyWindowToVram(textPrinter->printerTemplate.windowId, 0x2); +} + +bool8 TextPrinterWaitAutoMode(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + + if (subStruct->autoScrollDelay == 49) + { + return TRUE; + } + else + { + ++subStruct->autoScrollDelay; + return FALSE; + } +} + +bool16 TextPrinterWaitWithDownArrow(struct TextPrinter *textPrinter) +{ + bool8 result = FALSE; + if (gTextFlags.autoScroll != 0) + { + result = TextPrinterWaitAutoMode(textPrinter); + } + else + { + TextPrinterDrawDownArrow(textPrinter); + if (gMain.newKeys & (A_BUTTON | B_BUTTON)) + { + result = TRUE; + PlaySE(SE_SELECT); + } + } + return result; +} + +bool16 TextPrinterWait(struct TextPrinter *textPrinter) +{ + bool16 result = FALSE; + if (gTextFlags.autoScroll != 0) + { + result = TextPrinterWaitAutoMode(textPrinter); + } + else + { + if (gMain.newKeys & (A_BUTTON | B_BUTTON)) + { + result = TRUE; + PlaySE(SE_SELECT); + } + } + return result; +} + +void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *counter, u8 *yCoordIndex) +{ + const u8 *arrowTiles; + + if (*counter != 0) + { + --*counter; + } + else + { + FillWindowPixelRect(windowId, (bgColor << 4) | bgColor, x, y, 0x8, 0x10); + if (drawArrow == 0) + { + switch (gTextFlags.useAlternateDownArrow) + { + case 0: + default: + arrowTiles = gDownArrowTiles; + break; + case 1: + arrowTiles = gDarkDownArrowTiles; + break; + } + + BlitBitmapRectToWindow( + windowId, + arrowTiles, + 0, + gDownArrowYCoords[*yCoordIndex & 3], + 0x8, + 0x10, + x, + y - 2, + 0x8, + 0x10); + CopyWindowToVram(windowId, 0x2); + *counter = 8; + ++*yCoordIndex; + } + } +} + +u16 RenderText(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub; + u16 currChar; + s32 width; + s32 widthHelper; + + switch (textPrinter->state) + { + case 0: + if ((gMain.heldKeys & (A_BUTTON | B_BUTTON)) && subStruct->hasPrintBeenSpedUp) + textPrinter->delayCounter = 0; + + if (textPrinter->delayCounter && textPrinter->textSpeed) + { + textPrinter->delayCounter--; + if (gTextFlags.canABSpeedUpPrint && (gMain.newKeys & (A_BUTTON | B_BUTTON))) + { + subStruct->hasPrintBeenSpedUp = TRUE; + textPrinter->delayCounter = 0; + } + return 3; + } + + if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll) + textPrinter->delayCounter = 3; + else + textPrinter->delayCounter = textPrinter->textSpeed; + + currChar = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + + switch (currChar) + { + case CHAR_NEWLINE: + textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x; + textPrinter->printerTemplate.currentY += (gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing); + return 2; + case PLACEHOLDER_BEGIN: + textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_BEGIN: + currChar = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + switch (currChar) + { + case 1: + textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor); + return 2; + case 2: + textPrinter->printerTemplate.bgColor = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor); + return 2; + case 3: + textPrinter->printerTemplate.shadowColor = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor); + return 2; + case 4: + textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + textPrinter->printerTemplate.bgColor = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + textPrinter->printerTemplate.shadowColor = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor); + return 2; + case 5: + textPrinter->printerTemplate.currentChar++; + return 2; + case 6: + subStruct->glyphId = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_UNKNOWN_7: + return 2; + case 8: + textPrinter->delayCounter = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + textPrinter->state = 6; + return 2; + case 9: + textPrinter->state = 1; + if (gTextFlags.autoScroll) + subStruct->autoScrollDelay = 0; + return 3; + case 10: + textPrinter->state = 5; + return 3; + case 11: + currChar = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + currChar |= *textPrinter->printerTemplate.currentChar << 8; + textPrinter->printerTemplate.currentChar++; + PlayBGM(currChar); + return 2; + case 12: + currChar = *textPrinter->printerTemplate.currentChar | 0x100; + textPrinter->printerTemplate.currentChar++; + break; + case 16: + currChar = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + currChar |= (*textPrinter->printerTemplate.currentChar << 8); + textPrinter->printerTemplate.currentChar++; + PlaySE(currChar); + return 2; + case 13: + textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x + *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + return 2; + case 14: + textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y + *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + return 2; + case 15: + FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor)); + textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x; + textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y; + return 2; + case 23: + m4aMPlayStop(&gMPlayInfo_BGM); + return 2; + case 24: + m4aMPlayContinue(&gMPlayInfo_BGM); + return 2; + case EXT_CTRL_CODE_CLEAR: + width = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + if (width > 0) + { + ClearTextSpan(textPrinter, width); + textPrinter->printerTemplate.currentX += width; + return 0; + } + return 2; + case 18: + textPrinter->printerTemplate.currentX = *textPrinter->printerTemplate.currentChar + textPrinter->printerTemplate.x; + textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_CLEAR_TO: + { + widthHelper = *textPrinter->printerTemplate.currentChar; + widthHelper += textPrinter->printerTemplate.x; + textPrinter->printerTemplate.currentChar++; + width = widthHelper - textPrinter->printerTemplate.currentX; + if (width > 0) + { + ClearTextSpan(textPrinter, width); + textPrinter->printerTemplate.currentX += width; + return 0; + } + } + return 2; + case EXT_CTRL_CODE_MIN_LETTER_SPACING: + textPrinter->minLetterSpacing = *textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_JPN: + textPrinter->japanese = 1; + return 2; + case EXT_CTRL_CODE_ENG: + textPrinter->japanese = 0; + return 2; + } + break; + case CHAR_PROMPT_CLEAR: + textPrinter->state = 2; + TextPrinterInitDownArrowCounters(textPrinter); + return 3; + case CHAR_PROMPT_SCROLL: + textPrinter->state = 3; + TextPrinterInitDownArrowCounters(textPrinter); + return 3; + case CHAR_SPECIAL_F9: + currChar = *textPrinter->printerTemplate.currentChar | 0x100; + textPrinter->printerTemplate.currentChar++; + break; + case CHAR_SPECIAL_F8: + currChar = *textPrinter->printerTemplate.currentChar++; + gUnknown_03002F90.unk80 = DrawKeypadIcon(textPrinter->printerTemplate.windowId, currChar, textPrinter->printerTemplate.currentX, textPrinter->printerTemplate.currentY); + textPrinter->printerTemplate.currentX += gUnknown_03002F90.unk80 + textPrinter->printerTemplate.letterSpacing; + return 0; + case EOS: + return 1; + } + + switch (subStruct->glyphId) + { + case 0: + DecompressGlyphFont0(currChar, textPrinter->japanese); + break; + case 1: + DecompressGlyphFont1(currChar, textPrinter->japanese); + break; + case 2: + case 3: + case 4: + case 5: + DecompressGlyphFont2(currChar, textPrinter->japanese); + break; + case 7: + DecompressGlyphFont7(currChar, textPrinter->japanese); + break; + case 8: + DecompressGlyphFont8(currChar, textPrinter->japanese); + break; + case 6: + break; + } + + CopyGlyphToWindow(textPrinter); + + if (textPrinter->minLetterSpacing) + { + textPrinter->printerTemplate.currentX += gUnknown_03002F90.unk80; + width = textPrinter->minLetterSpacing - gUnknown_03002F90.unk80; + if (width > 0) + { + ClearTextSpan(textPrinter, width); + textPrinter->printerTemplate.currentX += width; + } + } + else + { + if (textPrinter->japanese) + textPrinter->printerTemplate.currentX += (gUnknown_03002F90.unk80 + textPrinter->printerTemplate.letterSpacing); + else + textPrinter->printerTemplate.currentX += gUnknown_03002F90.unk80; + } + return 0; + case 1: + if (TextPrinterWait(textPrinter)) + textPrinter->state = 0; + return 3; + case 2: + if (TextPrinterWaitWithDownArrow(textPrinter)) + { + FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor)); + textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x; + textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y; + textPrinter->state = 0; + } + return 3; + case 3: + if (TextPrinterWaitWithDownArrow(textPrinter)) + { + TextPrinterClearDownArrow(textPrinter); + textPrinter->scrollDistance = gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing; + textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x; + textPrinter->state = 4; + } + return 3; + case 4: + if (textPrinter->scrollDistance) + { + int scrollSpeed = GetPlayerTextSpeed(); + int speed = gWindowVerticalScrollSpeeds[scrollSpeed]; + if (textPrinter->scrollDistance < speed) + { + ScrollWindow(textPrinter->printerTemplate.windowId, 0, textPrinter->scrollDistance, PIXEL_FILL(textPrinter->printerTemplate.bgColor)); + textPrinter->scrollDistance = 0; + } + else + { + ScrollWindow(textPrinter->printerTemplate.windowId, 0, speed, PIXEL_FILL(textPrinter->printerTemplate.bgColor)); + textPrinter->scrollDistance -= speed; + } + CopyWindowToVram(textPrinter->printerTemplate.windowId, 2); + } + else + { + textPrinter->state = 0; + } + return 3; + case 5: + if (!IsSEPlaying()) + textPrinter->state = 0; + return 3; + case 6: + if (textPrinter->delayCounter != 0) + textPrinter->delayCounter--; + else + textPrinter->state = 0; + return 3; + } + + return 1; +} + +u32 GetStringWidthFixedWidthFont(const u8 *str, u8 fontId, u8 letterSpacing) +{ + int i; + u8 width; + int temp; + int temp2; + u8 line; + int strPos; + u8 lineWidths[8]; + const u8 *strLocal; + + for (i = 0; i < 8; i++) + { + lineWidths[i] = 0; + } + + width = 0; + line = 0; + strLocal = str; + strPos = 0; + + do + { + temp = strLocal[strPos++]; + switch (temp) + { + case CHAR_NEWLINE: + case EOS: + lineWidths[line] = width; + width = 0; + line++; + break; + case EXT_CTRL_CODE_BEGIN: + temp2 = strLocal[strPos++]; + switch (temp2) + { + case 0x4: + ++strPos; + case 0xB: + case 0x10: + ++strPos; + case 0x1: + case 0x2: + case 0x3: + case 0x5: + case 0x6: + case 0x8: + case 0xC: + case 0xD: + case 0xE: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + ++strPos; + break; + case EXT_CTRL_CODE_UNKNOWN_7: + case 0x9: + case 0xA: + case 0xF: + case EXT_CTRL_CODE_JPN: + case EXT_CTRL_CODE_ENG: + default: + break; + } + break; + case CHAR_SPECIAL_F7: + case PLACEHOLDER_BEGIN: + ++strPos; + break; + case CHAR_PROMPT_SCROLL: + case CHAR_PROMPT_CLEAR: + break; + case CHAR_SPECIAL_F8: + case CHAR_SPECIAL_F9: + ++strPos; + default: + ++width; + break; + } + } while (temp != EOS); + + for (width = 0, strPos = 0; strPos < 8; ++strPos) + { + if (width < lineWidths[strPos]) + width = lineWidths[strPos]; + } + + return (u8)(GetFontAttribute(fontId, FONTATTR_MAX_LETTER_WIDTH) + letterSpacing) * width; +} + +u32 (*GetFontWidthFunc(u8 glyphId))(u16, bool32) +{ + u32 i; + + for (i = 0; i < 9; ++i) + { + if (glyphId == gGlyphWidthFuncs[i].fontId) + return gGlyphWidthFuncs[i].func; + } + + return NULL; +} + +s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing) +{ + bool8 isJapanese; + int minGlyphWidth; + u32 (*func)(u16 glyphId, bool32 isJapanese); + s32 result; + int localLetterSpacing; + u32 lineWidth; + const u8 *bufferPointer; + int glyphWidth; + s32 width; + + isJapanese = 0; + minGlyphWidth = 0; + + func = GetFontWidthFunc(fontId); + if (func == NULL) + return 0; + + if (letterSpacing == -1) + localLetterSpacing = GetFontAttribute(fontId, FONTATTR_LETTER_SPACING); + else + localLetterSpacing = letterSpacing; + + width = 0; + lineWidth = 0; + bufferPointer = 0; + + while (*str != EOS) + { + switch (*str) + { + case CHAR_NEWLINE: + if (lineWidth > width) + width = lineWidth; + lineWidth = 0; + break; + case PLACEHOLDER_BEGIN: + switch (*++str) + { + case 0x2: + bufferPointer = gStringVar1; + break; + case 0x3: + bufferPointer = gStringVar2; + break; + case 0x4: + bufferPointer = gStringVar3; + break; + default: + return 0; + } + case CHAR_SPECIAL_F7: + if (bufferPointer == NULL) + bufferPointer = DynamicPlaceholderTextUtil_GetPlaceholderPtr(*++str); + while (*bufferPointer != EOS) + { + glyphWidth = func(*bufferPointer++, isJapanese); + if (minGlyphWidth > 0) + { + if (glyphWidth < minGlyphWidth) + glyphWidth = minGlyphWidth; + lineWidth += glyphWidth; + } + else + { + lineWidth += glyphWidth; + if (isJapanese && str[1] != EOS) + lineWidth += localLetterSpacing; + } + } + bufferPointer = 0; + break; + case EXT_CTRL_CODE_BEGIN: + switch (*++str) + { + case 0x4: + ++str; + case 0xB: + case 0x10: + ++str; + case 0x1: + case 0x2: + case 0x3: + case 0x5: + case 0x8: + case 0xC: + case 0xD: + case 0xE: + ++str; + break; + case 0x6: + func = GetFontWidthFunc(*++str); + if (func == NULL) + return 0; + if (letterSpacing == -1) + localLetterSpacing = GetFontAttribute(*str, FONTATTR_LETTER_SPACING); + break; + case 0x11: + glyphWidth = *++str; + lineWidth += glyphWidth; + break; + case 0x12: + lineWidth = *++str; + break; + case 0x13: + if (*++str > lineWidth) + lineWidth = *str; + break; + case 0x14: + minGlyphWidth = *++str; + break; + case EXT_CTRL_CODE_JPN: + isJapanese = 1; + break; + case EXT_CTRL_CODE_ENG: + isJapanese = 0; + break; + case EXT_CTRL_CODE_UNKNOWN_7: + case 0x9: + case 0xA: + case 0xF: + default: + break; + } + break; + case CHAR_SPECIAL_F8: + case CHAR_SPECIAL_F9: + if (*str == CHAR_SPECIAL_F9) + glyphWidth = func(*++str | 0x100, isJapanese); + else + glyphWidth = GetKeypadIconWidth(*++str); + + if (minGlyphWidth > 0) + { + if (glyphWidth < minGlyphWidth) + glyphWidth = minGlyphWidth; + lineWidth += glyphWidth; + } + else + { + lineWidth += glyphWidth; + if (isJapanese && str[1] != EOS) + lineWidth += localLetterSpacing; + } + break; + case CHAR_PROMPT_SCROLL: + case CHAR_PROMPT_CLEAR: + break; + default: + glyphWidth = func(*str, isJapanese); + if (minGlyphWidth > 0) + { + if (glyphWidth < minGlyphWidth) + glyphWidth = minGlyphWidth; + lineWidth += glyphWidth; + } + else + { + lineWidth += glyphWidth; + if (isJapanese && str[1] != EOS) + lineWidth += localLetterSpacing; + } + break; + } + ++str; + } + + if (lineWidth > width) + return lineWidth; + else + return width; +} + +u8 RenderTextFont9(u8 *pixels, u8 fontId, u8 *str) +{ + u8 shadowColor; + u8 *strLocal; + int strPos; + int temp; + int temp2; + u8 colorBackup[3]; + u8 fgColor; + u8 bgColor; + + SaveTextColors(&colorBackup[0], &colorBackup[1], &colorBackup[2]); + + fgColor = 1; + bgColor = 0; + shadowColor = 3; + + GenerateFontHalfRowLookupTable(1, 0, 3); + strLocal = str; + strPos = 0; + + do + { + temp = strLocal[strPos++]; + switch (temp) + { + case EXT_CTRL_CODE_BEGIN: + temp2 = strLocal[strPos++]; + switch (temp2) + { + case 0x4: + fgColor = strLocal[strPos++]; + bgColor = strLocal[strPos++]; + shadowColor = strLocal[strPos++]; + GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor); + continue; + case 0x1: + fgColor = strLocal[strPos++]; + GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor); + continue; + case 0x2: + bgColor = strLocal[strPos++]; + GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor); + continue; + case 0x3: + shadowColor = strLocal[strPos++]; + GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor); + continue; + case 0x6: + fontId = strLocal[strPos++]; + break; + case 0xB: + case 0x10: + ++strPos; + case 0x5: + case 0x8: + case 0xC: + case 0xD: + case 0xE: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + ++strPos; + break; + case EXT_CTRL_CODE_UNKNOWN_7: + case 0x9: + case 0xA: + case 0xF: + case EXT_CTRL_CODE_JPN: + case EXT_CTRL_CODE_ENG: + default: + continue; + } + break; + case CHAR_SPECIAL_F7: + case CHAR_SPECIAL_F8: + case CHAR_SPECIAL_F9: + case PLACEHOLDER_BEGIN: + ++strPos; + break; + case CHAR_PROMPT_SCROLL: + case CHAR_PROMPT_CLEAR: + case CHAR_NEWLINE: + case EOS: + break; + default: + switch (fontId) + { + case 9: + DecompressGlyphFont9(temp); + break; + case 1: + default: + DecompressGlyphFont1(temp, 1); + break; + } + CpuCopy32(gUnknown_03002F90.unk0, pixels, 0x20); + CpuCopy32(gUnknown_03002F90.unk40, pixels + 0x20, 0x20); + pixels += 0x40; + break; + } + } + while (temp != EOS); + + RestoreTextColors(&colorBackup[0], &colorBackup[1], &colorBackup[2]); + return 1; +} + +u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y) +{ + BlitBitmapRectToWindow( + windowId, + gKeypadIconTiles + (gKeypadIcons[keypadIconId].tileOffset * 0x20), + 0, + 0, + 0x80, + 0x80, + x, + y, + gKeypadIcons[keypadIconId].width, + gKeypadIcons[keypadIconId].height); + return gKeypadIcons[keypadIconId].width; +} + +u8 GetKeypadIconTileOffset(u8 keypadIconId) +{ + return gKeypadIcons[keypadIconId].tileOffset; +} + +u8 GetKeypadIconWidth(u8 keypadIconId) +{ + return gKeypadIcons[keypadIconId].width; +} + +u8 GetKeypadIconHeight(u8 keypadIconId) +{ + return gKeypadIcons[keypadIconId].height; +} + +void SetDefaultFontsPointer(void) +{ + SetFontsPointer(&gFontInfos[0]); +} + +u8 GetFontAttribute(u8 fontId, u8 attributeId) +{ + int result = 0; + switch (attributeId) + { + case FONTATTR_MAX_LETTER_WIDTH: + result = gFontInfos[fontId].maxLetterWidth; + break; + case FONTATTR_MAX_LETTER_HEIGHT: + result = gFontInfos[fontId].maxLetterHeight; + break; + case FONTATTR_LETTER_SPACING: + result = gFontInfos[fontId].letterSpacing; + break; + case FONTATTR_LINE_SPACING: + result = gFontInfos[fontId].lineSpacing; + break; + case FONTATTR_UNKNOWN: + result = gFontInfos[fontId].unk; + break; + case FONTATTR_COLOR_FOREGROUND: + result = gFontInfos[fontId].fgColor; + break; + case FONTATTR_COLOR_BACKGROUND: + result = gFontInfos[fontId].bgColor; + break; + case FONTATTR_COLOR_SHADOW: + result = gFontInfos[fontId].shadowColor; + break; + } + return result; +} + +u8 GetMenuCursorDimensionByFont(u8 fontId, u8 whichDimension) +{ + return gMenuCursorDimensions[fontId][whichDimension]; +} + +void DecompressGlyphFont0(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == 1) + { + glyphs = gFont0JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & 0xF)); + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x80, gUnknown_03002F90.unk40); // gUnknown_03002F90 + 0x40 + gUnknown_03002F90.unk80 = 8; // gGlyphWidth + gUnknown_03002F90.unk81 = 12; // gGlyphHeight + } + else + { + glyphs = gFont0LatinGlyphs + (0x20 * glyphId); + gUnknown_03002F90.unk80 = gFont0LatinGlyphWidths[glyphId]; + + if (gUnknown_03002F90.unk80 <= 8) + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + } + else + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x8, gUnknown_03002F90.unk20); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + DecompressGlyphTile(glyphs + 0x18, gUnknown_03002F90.unk60); + } + + gUnknown_03002F90.unk81 = 13; + } +} + +u32 GetGlyphWidthFont0(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont0LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont7(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + int eff; + glyphs = gFont1JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & (eff = 0xF))); // shh, no questions, only matching now + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x80, gUnknown_03002F90.unk40); // gUnknown_03002F90 + 0x40 + gUnknown_03002F90.unk80 = 8; // gGlyphWidth + gUnknown_03002F90.unk81 = 15; // gGlyphHeight + } + else + { + glyphs = gFont7LatinGlyphs + (0x20 * glyphId); + gUnknown_03002F90.unk80 = gFont7LatinGlyphWidths[glyphId]; + + if (gUnknown_03002F90.unk80 <= 8) + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + } + else + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x8, gUnknown_03002F90.unk20); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + DecompressGlyphTile(glyphs + 0x18, gUnknown_03002F90.unk60); + } + + gUnknown_03002F90.unk81 = 15; + } +} + +u32 GetGlyphWidthFont7(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont7LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont8(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFont0JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & 0xF)); + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x80, gUnknown_03002F90.unk40); // gUnknown_03002F90 + 0x40 + gUnknown_03002F90.unk80 = 8; // gGlyphWidth + gUnknown_03002F90.unk81 = 12; // gGlyphHeight + } + else + { + glyphs = gFont8LatinGlyphs + (0x20 * glyphId); + gUnknown_03002F90.unk80 = gFont8LatinGlyphWidths[glyphId]; + + if (gUnknown_03002F90.unk80 <= 8) + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + } + else + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x8, gUnknown_03002F90.unk20); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + DecompressGlyphTile(glyphs + 0x18, gUnknown_03002F90.unk60); + } + + gUnknown_03002F90.unk81 = 12; + } +} + +u32 GetGlyphWidthFont8(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont8LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont2(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFont2JapaneseGlyphs + (0x100 * (glyphId >> 0x3)) + (0x10 * (glyphId & 0x7)); + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x8, gUnknown_03002F90.unk20); // gUnknown_03002F90 + 0x40 + DecompressGlyphTile(glyphs + 0x80, gUnknown_03002F90.unk40); // gUnknown_03002F90 + 0x20 + DecompressGlyphTile(glyphs + 0x88, gUnknown_03002F90.unk60); // gUnknown_03002F90 + 0x60 + gUnknown_03002F90.unk80 = gFont2JapaneseGlyphWidths[glyphId]; // gGlyphWidth + gUnknown_03002F90.unk81 = 14; // gGlyphHeight + } + else + { + glyphs = gFont2LatinGlyphs + (0x20 * glyphId); + gUnknown_03002F90.unk80 = gFont2LatinGlyphWidths[glyphId]; + + if (gUnknown_03002F90.unk80 <= 8) + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + } + else + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x8, gUnknown_03002F90.unk20); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + DecompressGlyphTile(glyphs + 0x18, gUnknown_03002F90.unk60); + } + + gUnknown_03002F90.unk81 = 14; + } +} + +u32 GetGlyphWidthFont2(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return gFont2JapaneseGlyphWidths[glyphId]; + else + return gFont2LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont1(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + int eff; + glyphs = gFont1JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & (eff = 0xF))); // shh, no questions, only matching now + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x80, gUnknown_03002F90.unk40); // gUnknown_03002F90 + 0x40 + gUnknown_03002F90.unk80 = 8; // gGlyphWidth + gUnknown_03002F90.unk81 = 15; // gGlyphHeight + } + else + { + glyphs = gFont1LatinGlyphs + (0x20 * glyphId); + gUnknown_03002F90.unk80 = gFont1LatinGlyphWidths[glyphId]; + + if (gUnknown_03002F90.unk80 <= 8) + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + } + else + { + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x8, gUnknown_03002F90.unk20); + DecompressGlyphTile(glyphs + 0x10, gUnknown_03002F90.unk40); + DecompressGlyphTile(glyphs + 0x18, gUnknown_03002F90.unk60); + } + + gUnknown_03002F90.unk81 = 15; + } +} + +u32 GetGlyphWidthFont1(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont1LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont9(u16 glyphId) +{ + const u16* glyphs; + + glyphs = gFont9JapaneseGlyphs + (0x100 * (glyphId >> 4)) + (0x8 * (glyphId & 0xF)); + DecompressGlyphTile(glyphs, gUnknown_03002F90.unk0); + DecompressGlyphTile(glyphs + 0x80, gUnknown_03002F90.unk40); + gUnknown_03002F90.unk80 = 8; + gUnknown_03002F90.unk81 = 12; +} diff --git a/gflib/text.h b/gflib/text.h new file mode 100644 index 000000000..e37773475 --- /dev/null +++ b/gflib/text.h @@ -0,0 +1,312 @@ +#ifndef GUARD_TEXT_H +#define GUARD_TEXT_H + +#define CHAR_SPACE 0x00 +#define CHAR_PLUS 0x2E +#define CHAR_0 0xA1 +#define CHAR_1 0xA2 +#define CHAR_2 0xA3 +#define CHAR_3 0xA4 +#define CHAR_4 0xA5 +#define CHAR_5 0xA6 +#define CHAR_6 0xA7 +#define CHAR_7 0xA8 +#define CHAR_8 0xA9 +#define CHAR_9 0xAA +#define CHAR_EXCL_MARK 0xAB +#define CHAR_QUESTION_MARK 0xAC +#define CHAR_PERIOD 0xAD +#define CHAR_HYPHEN 0xAE +#define CHAR_ELLIPSIS 0xB0 +#define CHAR_DBL_QUOT_LEFT 0xB1 +#define CHAR_DBL_QUOT_RIGHT 0xB2 +#define CHAR_SGL_QUOT_LEFT 0xB3 +#define CHAR_SGL_QUOT_RIGHT 0xB4 +#define CHAR_MALE 0xB5 +#define CHAR_FEMALE 0xB6 +#define CHAR_CURRENCY 0xB7 +#define CHAR_COMMA 0xB8 +#define CHAR_MULT_SIGN 0xB9 +#define CHAR_SLASH 0xBA +#define CHAR_A 0xBB +#define CHAR_B 0xBC +#define CHAR_C 0xBD +#define CHAR_D 0xBE +#define CHAR_E 0xBF +#define CHAR_F 0xC0 +#define CHAR_G 0xC1 +#define CHAR_H 0xC2 +#define CHAR_I 0xC3 +#define CHAR_J 0xC4 +#define CHAR_K 0xC5 +#define CHAR_L 0xC6 +#define CHAR_M 0xC7 +#define CHAR_N 0xC8 +#define CHAR_O 0xC9 +#define CHAR_P 0xCA +#define CHAR_Q 0xCB +#define CHAR_R 0xCC +#define CHAR_S 0xCD +#define CHAR_T 0xCE +#define CHAR_U 0xCF +#define CHAR_V 0xD0 +#define CHAR_W 0xD1 +#define CHAR_X 0xD2 +#define CHAR_Y 0xD3 +#define CHAR_Z 0xD4 +#define CHAR_a 0xD5 +#define CHAR_b 0xD6 +#define CHAR_c 0xD7 +#define CHAR_d 0xD8 +#define CHAR_e 0xD9 +#define CHAR_f 0xDA +#define CHAR_g 0xDB +#define CHAR_h 0xDC +#define CHAR_i 0xDD +#define CHAR_j 0xDE +#define CHAR_k 0xDF +#define CHAR_l 0xE0 +#define CHAR_m 0xE1 +#define CHAR_n 0xE2 +#define CHAR_o 0xE3 +#define CHAR_p 0xE4 +#define CHAR_q 0xE5 +#define CHAR_r 0xE6 +#define CHAR_s 0xE7 +#define CHAR_t 0xE8 +#define CHAR_u 0xE9 +#define CHAR_v 0xEA +#define CHAR_w 0xEB +#define CHAR_x 0xEC +#define CHAR_y 0xED +#define CHAR_z 0xEE +#define CHAR_SPECIAL_F7 0xF7 +#define CHAR_SPECIAL_F8 0xF8 +#define CHAR_SPECIAL_F9 0xF9 +#define CHAR_COLON 0xF0 +#define CHAR_PROMPT_SCROLL 0xFA // waits for button press and scrolls dialog +#define CHAR_PROMPT_CLEAR 0xFB // waits for button press and clears dialog +#define EXT_CTRL_CODE_BEGIN 0xFC // extended control code +#define PLACEHOLDER_BEGIN 0xFD // string placeholder +#define CHAR_NEWLINE 0xFE +#define EOS 0xFF // end of string + +#define EXT_CTRL_CODE_COLOR 0x1 +#define EXT_CTRL_CODE_HIGHLIGHT 0x2 +#define EXT_CTRL_CODE_SHADOW 0x3 +// +#define EXT_CTRL_CODE_UNKNOWN_7 0x7 +// +#define EXT_CTRL_CODE_CLEAR 0x11 +// +#define EXT_CTRL_CODE_CLEAR_TO 0x13 +#define EXT_CTRL_CODE_MIN_LETTER_SPACING 0x14 +#define EXT_CTRL_CODE_JPN 0x15 +#define EXT_CTRL_CODE_ENG 0x16 + +#define TEXT_COLOR_TRANSPARENT 0x0 +#define TEXT_COLOR_WHITE 0x1 +#define TEXT_COLOR_DARK_GREY 0x2 +// 0x3 +#define TEXT_COLOR_RED 0x4 +// 0x5 +#define TEXT_COLOR_GREEN 0x6 +// 0x7 +#define TEXT_COLOR_BLUE 0x8 + +#define PLACEHOLDER_ID_UNKNOWN 0x0 +#define PLACEHOLDER_ID_PLAYER 0x1 +#define PLACEHOLDER_ID_STRING_VAR_1 0x2 +#define PLACEHOLDER_ID_STRING_VAR_2 0x3 +#define PLACEHOLDER_ID_STRING_VAR_3 0x4 +#define PLACEHOLDER_ID_KUN 0x5 +#define PLACEHOLDER_ID_RIVAL 0x6 +#define PLACEHOLDER_ID_VERSION 0x7 +#define PLACEHOLDER_ID_AQUA 0x8 +#define PLACEHOLDER_ID_MAGMA 0x9 +#define PLACEHOLDER_ID_ARCHIE 0xA +#define PLACEHOLDER_ID_MAXIE 0xB +#define PLACEHOLDER_ID_KYOGRE 0xC +#define PLACEHOLDER_ID_GROUDON 0xD + +// battle placeholders are located in battle_message.h + +#define NUM_TEXT_PRINTERS 32 + +#define TEXT_SPEED_FF 0xFF + +enum +{ + FONTATTR_MAX_LETTER_WIDTH, + FONTATTR_MAX_LETTER_HEIGHT, + FONTATTR_LETTER_SPACING, + FONTATTR_LINE_SPACING, + FONTATTR_UNKNOWN, // dunno what this is yet + FONTATTR_COLOR_FOREGROUND, + FONTATTR_COLOR_BACKGROUND, + FONTATTR_COLOR_SHADOW +}; + +struct TextPrinterSubStruct +{ + u8 glyphId:4; // 0x14 + bool8 hasPrintBeenSpedUp:1; + u8 unk:3; + u8 downArrowDelay:5; + u8 downArrowYPosIdx:2; + bool8 hasGlyphIdBeenSet:1; + u8 autoScrollDelay; +}; + +struct TextPrinterTemplate +{ + const u8* currentChar; + u8 windowId; + u8 fontId; + u8 x; + u8 y; + u8 currentX; // 0x8 + u8 currentY; + u8 letterSpacing; + u8 lineSpacing; + u8 unk:4; // 0xC + u8 fgColor:4; + u8 bgColor:4; + u8 shadowColor:4; +}; + +struct TextPrinter +{ + struct TextPrinterTemplate printerTemplate; + + void (*callback)(struct TextPrinterTemplate *, u16); // 0x10 + + union +#if !MODERN + __attribute__((packed)) +#endif + { + struct TextPrinterSubStruct sub; + u8 fields[7]; + } subUnion; + + u8 active; + u8 state; // 0x1C + u8 textSpeed; + u8 delayCounter; + u8 scrollDistance; + u8 minLetterSpacing; // 0x20 + u8 japanese; +}; + +struct FontInfo +{ + u16 (*fontFunction)(struct TextPrinter *x); + u8 maxLetterWidth; + u8 maxLetterHeight; + u8 letterSpacing; + u8 lineSpacing; + u8 unk:4; + u8 fgColor:4; + u8 bgColor:4; + u8 shadowColor:4; +}; + +extern const struct FontInfo *gFonts; + +struct GlyphWidthFunc +{ + u32 fontId; + u32 (*func)(u16 glyphId, bool32 isJapanese); +}; + +struct KeypadIcon +{ + u16 tileOffset; + u8 width; + u8 height; +}; + +typedef struct { + bool8 canABSpeedUpPrint:1; + bool8 useAlternateDownArrow:1; + bool8 autoScroll:1; + bool8 forceMidTextSpeed:1; +} TextFlags; + +struct Struct_03002F90 +{ + u32 unk0[8]; + u32 unk20[8]; + u32 unk40[8]; + u32 unk60[8]; + u8 unk80; + u8 unk81; +}; + +extern TextFlags gTextFlags; + +extern u8 gUnknown_03002F84; +extern struct Struct_03002F90 gUnknown_03002F90; + +void SetFontsPointer(const struct FontInfo *fonts); +void DeactivateAllTextPrinters(void); +u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); +bool16 AddTextPrinter(struct TextPrinterTemplate *template, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); +void RunTextPrinters(void); +bool16 IsTextPrinterActive(u8 id); +u32 RenderFont(struct TextPrinter *textPrinter); +void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadowColor); +void SaveTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor); +void RestoreTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor); +void DecompressGlyphTile(const void *src_, void *dest_); +u8 GetLastTextColor(u8 colorType); +void CopyGlyphToWindow(struct TextPrinter *x); +void ClearTextSpan(struct TextPrinter *textPrinter, u32 width); +u8 GetMenuCursorDimensionByFont(u8, u8); + +u16 Font0Func(struct TextPrinter *textPrinter); +u16 Font1Func(struct TextPrinter *textPrinter); +u16 Font2Func(struct TextPrinter *textPrinter); +u16 Font3Func(struct TextPrinter *textPrinter); +u16 Font4Func(struct TextPrinter *textPrinter); +u16 Font5Func(struct TextPrinter *textPrinter); +u16 Font7Func(struct TextPrinter *textPrinter); +u16 Font8Func(struct TextPrinter *textPrinter); + +void TextPrinterInitDownArrowCounters(struct TextPrinter *textPrinter); +void TextPrinterDrawDownArrow(struct TextPrinter *textPrinter); +void TextPrinterClearDownArrow(struct TextPrinter *textPrinter); +bool8 TextPrinterWaitAutoMode(struct TextPrinter *textPrinter); +bool16 TextPrinterWaitWithDownArrow(struct TextPrinter *textPrinter); +bool16 TextPrinterWait(struct TextPrinter *textPrinter); +void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *counter, u8 *yCoordIndex); +u16 RenderText(struct TextPrinter *textPrinter); +u32 GetStringWidthFixedWidthFont(const u8 *str, u8 fontId, u8 letterSpacing); +u32 (*GetFontWidthFunc(u8 glyphId))(u16, bool32); +s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing); +u8 RenderTextFont9(u8 *pixels, u8 fontId, u8 *str); +u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y); +u8 GetKeypadIconTileOffset(u8 keypadIconId); +u8 GetKeypadIconWidth(u8 keypadIconId); +u8 GetKeypadIconHeight(u8 keypadIconId); +void SetDefaultFontsPointer(void); +u8 GetFontAttribute(u8 fontId, u8 attributeId); +u8 GetMenuCursorDimensionByFont(u8 fontId, u8 whichDimension); +void DecompressGlyphFont0(u16 glyphId, bool32 isJapanese); +u32 GetGlyphWidthFont0(u16 glyphId, bool32 isJapanese); +void DecompressGlyphFont7(u16 glyphId, bool32 isJapanese); +u32 GetGlyphWidthFont7(u16 glyphId, bool32 isJapanese); +void DecompressGlyphFont8(u16 glyphId, bool32 isJapanese); +u32 GetGlyphWidthFont8(u16 glyphId, bool32 isJapanese); +void DecompressGlyphFont2(u16 glyphId, bool32 isJapanese); +u32 GetGlyphWidthFont2(u16 glyphId, bool32 isJapanese); +void DecompressGlyphFont1(u16 glyphId, bool32 isJapanese); +u32 GetGlyphWidthFont1(u16 glyphId, bool32 isJapanese); +void DecompressGlyphFont9(u16 glyphId); + +// unk_text_util_2.c +u16 Font6Func(struct TextPrinter *textPrinter); +u32 GetGlyphWidthFont6(u16 glyphId, bool32 isJapanese); + +#endif // GUARD_TEXT_H diff --git a/gflib/window.c b/gflib/window.c new file mode 100644 index 000000000..7c87ea86d --- /dev/null +++ b/gflib/window.c @@ -0,0 +1,721 @@ +#include "global.h" +#include "window.h" +#include "malloc.h" +#include "bg.h" +#include "blit.h" + +u32 filler_03002F58; +u32 filler_03002F5C; +// This global is set to 0 and never changed. +u8 gTransparentTileNumber; +u32 filler_03002F64; +void *gUnknown_03002F70[4]; +extern u32 gUnneededFireRedVariable; + +#define WINDOWS_MAX 32 + +EWRAM_DATA struct Window gWindows[WINDOWS_MAX] = {0}; +EWRAM_DATA static struct Window* sWindowPtr = NULL; +EWRAM_DATA static u16 sWindowSize = 0; + +static u8 GetNumActiveWindowsOnBg(u8 bgId); +static u8 GetNumActiveWindowsOnBg8Bit(u8 bgId); + +static const struct WindowTemplate sDummyWindowTemplate = DUMMY_WIN_TEMPLATE; + +static void nullsub_8(void) +{ + +} + +bool16 InitWindows(const struct WindowTemplate *templates) +{ + int i; + void *bgTilemapBuffer; + int j; + u8 bgLayer; + u16 attrib; + u8* allocatedTilemapBuffer; + int allocatedBaseBlock; + + for (i = 0; i < 0x4; ++i) + { + bgTilemapBuffer = GetBgTilemapBuffer(i); + if (bgTilemapBuffer != NULL) + gUnknown_03002F70[i] = nullsub_8; + else + gUnknown_03002F70[i] = bgTilemapBuffer; + } + + for (i = 0; i < 0x20; ++i) + { + gWindows[i].window = sDummyWindowTemplate; + gWindows[i].tileData = NULL; + } + + for (i = 0, allocatedBaseBlock = 0, bgLayer = templates[i].bg; bgLayer != 0xFF && i < 0x20; ++i, bgLayer = templates[i].bg) + { + if (gUnneededFireRedVariable == 1) + { + allocatedBaseBlock = DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, 0, templates[i].width * templates[i].height, 0); + if (allocatedBaseBlock == -1) + return FALSE; + } + + if (gUnknown_03002F70[bgLayer] == NULL) + { + attrib = GetBgAttribute(bgLayer, BG_ATTR_METRIC); + + if (attrib != 0xFFFF) + { + allocatedTilemapBuffer = AllocZeroed(attrib); + + if (allocatedTilemapBuffer == NULL) + { + FreeAllWindowBuffers(); + return FALSE; + } + + for (j = 0; j < attrib; ++j) + allocatedTilemapBuffer[j] = 0; + + gUnknown_03002F70[bgLayer] = allocatedTilemapBuffer; + SetBgTilemapBuffer(bgLayer, allocatedTilemapBuffer); + } + } + + allocatedTilemapBuffer = AllocZeroed((u16)(0x20 * (templates[i].width * templates[i].height))); + + if (allocatedTilemapBuffer == NULL) + { + if ((GetNumActiveWindowsOnBg(bgLayer) == 0) && (gUnknown_03002F70[bgLayer] != nullsub_8)) + { + Free(gUnknown_03002F70[bgLayer]); + gUnknown_03002F70[bgLayer] = allocatedTilemapBuffer; + } + + return FALSE; + } + + gWindows[i].tileData = allocatedTilemapBuffer; + gWindows[i].window = templates[i]; + + if (gUnneededFireRedVariable == 1) + { + gWindows[i].window.baseBlock = allocatedBaseBlock; + DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, allocatedBaseBlock, templates[i].width * templates[i].height, 1); + } + } + + gTransparentTileNumber = 0; + return TRUE; +} + +u16 AddWindow(const struct WindowTemplate *template) +{ + u16 win; + u8 bgLayer; + int allocatedBaseBlock; + u16 attrib; + u8 *allocatedTilemapBuffer; + int i; + + for (win = 0; win < WINDOWS_MAX; ++win) + { + if ((bgLayer = gWindows[win].window.bg) == 0xFF) + break; + } + + if (win == WINDOWS_MAX) + return 0xFF; + + bgLayer = template->bg; + allocatedBaseBlock = 0; + + if (gUnneededFireRedVariable == 1) + { + allocatedBaseBlock = DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, 0, template->width * template->height, 0); + + if (allocatedBaseBlock == -1) + return 0xFF; + } + + if (gUnknown_03002F70[bgLayer] == NULL) + { + attrib = GetBgAttribute(bgLayer, BG_ATTR_METRIC); + + if (attrib != 0xFFFF) + { + allocatedTilemapBuffer = AllocZeroed(attrib); + + if (allocatedTilemapBuffer == NULL) + return 0xFF; + + for (i = 0; i < attrib; ++i) + allocatedTilemapBuffer[i] = 0; + + gUnknown_03002F70[bgLayer] = allocatedTilemapBuffer; + SetBgTilemapBuffer(bgLayer, allocatedTilemapBuffer); + } + } + + allocatedTilemapBuffer = AllocZeroed((u16)(0x20 * (template->width * template->height))); + + if (allocatedTilemapBuffer == NULL) + { + if ((GetNumActiveWindowsOnBg(bgLayer) == 0) && (gUnknown_03002F70[bgLayer] != nullsub_8)) + { + Free(gUnknown_03002F70[bgLayer]); + gUnknown_03002F70[bgLayer] = allocatedTilemapBuffer; + } + return 0xFF; + } + + gWindows[win].tileData = allocatedTilemapBuffer; + gWindows[win].window = *template; + + if (gUnneededFireRedVariable == 1) + { + gWindows[win].window.baseBlock = allocatedBaseBlock; + DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, allocatedBaseBlock, gWindows[win].window.width * gWindows[win].window.height, 1); + } + + return win; +} + +int AddWindowWithoutTileMap(const struct WindowTemplate *template) +{ + u16 win; + u8 bgLayer; + int allocatedBaseBlock; + + for (win = 0; win < WINDOWS_MAX; ++win) + { + if (gWindows[win].window.bg == 0xFF) + break; + } + + if (win == WINDOWS_MAX) + return 0xFF; + + bgLayer = template->bg; + allocatedBaseBlock = 0; + + if (gUnneededFireRedVariable == 1) + { + allocatedBaseBlock = DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, 0, template->width * template->height, 0); + + if (allocatedBaseBlock == -1) + return 0xFF; + } + + gWindows[win].window = *template; + + if (gUnneededFireRedVariable == 1) + { + gWindows[win].window.baseBlock = allocatedBaseBlock; + DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, allocatedBaseBlock, gWindows[win].window.width * gWindows[win].window.height, 1); + } + + return win; +} + +void RemoveWindow(u8 windowId) +{ + u8 bgLayer = gWindows[windowId].window.bg; + + if (gUnneededFireRedVariable == 1) + { + DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, gWindows[windowId].window.baseBlock, gWindows[windowId].window.width * gWindows[windowId].window.height, 2); + } + + gWindows[windowId].window = sDummyWindowTemplate; + + if (GetNumActiveWindowsOnBg(bgLayer) == 0) + { + if (gUnknown_03002F70[bgLayer] != nullsub_8) + { + Free(gUnknown_03002F70[bgLayer]); + gUnknown_03002F70[bgLayer] = 0; + } + } + + if (gWindows[windowId].tileData != NULL) + { + Free(gWindows[windowId].tileData); + gWindows[windowId].tileData = NULL; + } +} + +void FreeAllWindowBuffers(void) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if (gUnknown_03002F70[i] != NULL && gUnknown_03002F70[i] != nullsub_8) + { + Free(gUnknown_03002F70[i]); + gUnknown_03002F70[i] = NULL; + } + } + + for (i = 0; i < 0x20; ++i) + { + if (gWindows[i].tileData != NULL) + { + Free(gWindows[i].tileData); + gWindows[i].tileData = NULL; + } + } +} + +void CopyWindowToVram(u8 windowId, u8 mode) +{ + struct Window windowLocal = gWindows[windowId]; + u16 windowSize = 32 * (windowLocal.window.width * windowLocal.window.height); + + switch (mode) + { + case 1: + CopyBgTilemapBufferToVram(windowLocal.window.bg); + break; + case 2: + LoadBgTiles(windowLocal.window.bg, windowLocal.tileData, windowSize, windowLocal.window.baseBlock); + break; + case 3: + LoadBgTiles(windowLocal.window.bg, windowLocal.tileData, windowSize, windowLocal.window.baseBlock); + CopyBgTilemapBufferToVram(windowLocal.window.bg); + break; + } +} + +void CopyWindowRectToVram(u32 windowId, u32 mode, u32 x, u32 y, u32 w, u32 h) +{ + struct Window windowLocal; + int rectSize; + int rectPos; + + if (w != 0 && h != 0) + { + windowLocal = gWindows[windowId]; + + rectSize = ((h - 1) * windowLocal.window.width); + rectSize += (windowLocal.window.width - x); + rectSize -= (windowLocal.window.width - (x + w)); + rectSize *= 32; + + rectPos = (y * windowLocal.window.width) + x; + + switch (mode) + { + case 1: + CopyBgTilemapBufferToVram(windowLocal.window.bg); + break; + case 2: + LoadBgTiles(windowLocal.window.bg, windowLocal.tileData + (rectPos * 32), rectSize, windowLocal.window.baseBlock + rectPos); + break; + case 3: + LoadBgTiles(windowLocal.window.bg, windowLocal.tileData + (rectPos * 32), rectSize, windowLocal.window.baseBlock + rectPos); + CopyBgTilemapBufferToVram(windowLocal.window.bg); + break; + } + } +} + +void PutWindowTilemap(u8 windowId) +{ + struct Window windowLocal = gWindows[windowId]; + + WriteSequenceToBgTilemapBuffer( + windowLocal.window.bg, + GetBgAttribute(windowLocal.window.bg, BG_ATTR_BASETILE) + windowLocal.window.baseBlock, + windowLocal.window.tilemapLeft, + windowLocal.window.tilemapTop, + windowLocal.window.width, + windowLocal.window.height, + windowLocal.window.paletteNum, + 1); +} + +void PutWindowRectTilemapOverridePalette(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 palette) +{ + struct Window windowLocal = gWindows[windowId]; + u16 currentRow = windowLocal.window.baseBlock + (y * windowLocal.window.width) + x + GetBgAttribute(windowLocal.window.bg, BG_ATTR_BASETILE); + int i; + + for (i = 0; i < height; ++i) + { + WriteSequenceToBgTilemapBuffer( + windowLocal.window.bg, + currentRow, + windowLocal.window.tilemapLeft + x, + windowLocal.window.tilemapTop + y + i, + width, + 1, + palette, + 1); + + currentRow += windowLocal.window.width; + } +} + +// Fills a window with transparent tiles. +void ClearWindowTilemap(u8 windowId) +{ + struct Window windowLocal = gWindows[windowId]; + + FillBgTilemapBufferRect( + windowLocal.window.bg, + gTransparentTileNumber, + windowLocal.window.tilemapLeft, + windowLocal.window.tilemapTop, + windowLocal.window.width, + windowLocal.window.height, + windowLocal.window.paletteNum); +} + +void PutWindowRectTilemap(u8 windowId, u8 x, u8 y, u8 width, u8 height) +{ + struct Window windowLocal = gWindows[windowId]; + u16 currentRow = windowLocal.window.baseBlock + (y * windowLocal.window.width) + x + GetBgAttribute(windowLocal.window.bg, BG_ATTR_BASETILE); + int i; + + for (i = 0; i < height; ++i) + { + WriteSequenceToBgTilemapBuffer( + windowLocal.window.bg, + currentRow, + windowLocal.window.tilemapLeft + x, + windowLocal.window.tilemapTop + y + i, + width, + 1, + windowLocal.window.paletteNum, + 1); + + currentRow += windowLocal.window.width; + } +} + +void BlitBitmapToWindow(u8 windowId, const u8 *pixels, u16 x, u16 y, u16 width, u16 height) +{ + BlitBitmapRectToWindow(windowId, pixels, 0, 0, width, height, x, y, width, height); +} + +void BlitBitmapRectToWindow(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight) +{ + struct Bitmap sourceRect; + struct Bitmap destRect; + + sourceRect.pixels = (u8*)pixels; + sourceRect.width = srcWidth; + sourceRect.height = srcHeight; + + destRect.pixels = gWindows[windowId].tileData; + destRect.width = 8 * gWindows[windowId].window.width; + destRect.height = 8 * gWindows[windowId].window.height; + + BlitBitmapRect4Bit(&sourceRect, &destRect, srcX, srcY, destX, destY, rectWidth, rectHeight, 0); +} + +static void BlitBitmapRectToWindowWithColorKey(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight, u8 colorKey) +{ + struct Bitmap sourceRect; + struct Bitmap destRect; + + sourceRect.pixels = (u8*)pixels; + sourceRect.width = srcWidth; + sourceRect.height = srcHeight; + + destRect.pixels = gWindows[windowId].tileData; + destRect.width = 8 * gWindows[windowId].window.width; + destRect.height = 8 * gWindows[windowId].window.height; + + BlitBitmapRect4Bit(&sourceRect, &destRect, srcX, srcY, destX, destY, rectWidth, rectHeight, colorKey); +} + +void FillWindowPixelRect(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height) +{ + struct Bitmap pixelRect; + + pixelRect.pixels = gWindows[windowId].tileData; + pixelRect.width = 8 * gWindows[windowId].window.width; + pixelRect.height = 8 * gWindows[windowId].window.height; + + FillBitmapRect4Bit(&pixelRect, x, y, width, height, fillValue); +} + +void CopyToWindowPixelBuffer(u8 windowId, const void *src, u16 size, u16 tileOffset) +{ + if (size != 0) + CpuCopy16(src, gWindows[windowId].tileData + (0x20 * tileOffset), size); + else + LZ77UnCompWram(src, gWindows[windowId].tileData + (0x20 * tileOffset)); +} + +// Sets all pixels within the window to the fillValue color. +void FillWindowPixelBuffer(u8 windowId, u8 fillValue) +{ + int fillSize = gWindows[windowId].window.width * gWindows[windowId].window.height; + CpuFastFill8(fillValue, gWindows[windowId].tileData, 0x20 * fillSize); +} + +#define MOVE_TILES_DOWN(a) \ +{ \ + destOffset = i + (a); \ + srcOffset = i + (((width * (distanceLoop & ~7)) | (distanceLoop & 7)) * 4); \ + if (srcOffset < size) \ + *(u32*)(tileData + destOffset) = *(u32*)(tileData + srcOffset); \ + else \ + *(u32*)(tileData + destOffset) = fillValue32; \ + distanceLoop++; \ +} + +#define MOVE_TILES_UP(a) \ +{ \ + destOffset = i + (a); \ + srcOffset = i + (((width * (distanceLoop & ~7)) | (distanceLoop & 7)) * 4); \ + if (srcOffset < size) \ + *(u32*)(tileData - destOffset) = *(u32*)(tileData - srcOffset); \ + else \ + *(u32*)(tileData - destOffset) = fillValue32; \ + distanceLoop++; \ +} + +void ScrollWindow(u8 windowId, u8 direction, u8 distance, u8 fillValue) +{ + struct WindowTemplate window = gWindows[windowId].window; + u8 *tileData = gWindows[windowId].tileData; + u32 fillValue32 = (fillValue << 24) | (fillValue << 16) | (fillValue << 8) | fillValue; + s32 size = window.height * window.width * 32; + u32 width = window.width; + s32 i; + s32 srcOffset, destOffset; + u32 distanceLoop; + + switch (direction) + { + case 0: + for (i = 0; i < size; i += 32) + { + distanceLoop = distance; + MOVE_TILES_DOWN(0) + MOVE_TILES_DOWN(4) + MOVE_TILES_DOWN(8) + MOVE_TILES_DOWN(12) + MOVE_TILES_DOWN(16) + MOVE_TILES_DOWN(20) + MOVE_TILES_DOWN(24) + MOVE_TILES_DOWN(28) + } + break; + case 1: + tileData += size - 4; + for (i = 0; i < size; i += 32) + { + distanceLoop = distance; + MOVE_TILES_UP(0) + MOVE_TILES_UP(4) + MOVE_TILES_UP(8) + MOVE_TILES_UP(12) + MOVE_TILES_UP(16) + MOVE_TILES_UP(20) + MOVE_TILES_UP(24) + MOVE_TILES_UP(28) + } + break; + case 2: + break; + } +} + +void CallWindowFunction(u8 windowId, void ( *func)(u8, u8, u8, u8, u8, u8)) +{ + struct WindowTemplate window = gWindows[windowId].window; + func(window.bg, window.tilemapLeft, window.tilemapTop, window.width, window.height, window.paletteNum); +} + +bool8 SetWindowAttribute(u8 windowId, u8 attributeId, u32 value) +{ + switch (attributeId) + { + case WINDOW_TILEMAP_LEFT: + gWindows[windowId].window.tilemapLeft = value; + return FALSE; + case WINDOW_TILEMAP_TOP: + gWindows[windowId].window.tilemapTop = value; + return FALSE; + case WINDOW_PALETTE_NUM: + gWindows[windowId].window.paletteNum = value; + return FALSE; + case WINDOW_BASE_BLOCK: + gWindows[windowId].window.baseBlock = value; + return FALSE; + case WINDOW_TILE_DATA: + gWindows[windowId].tileData = (u8*)(value); + return TRUE; + case WINDOW_BG: + case WINDOW_WIDTH: + case WINDOW_HEIGHT: + default: + return TRUE; + } +} + +u32 GetWindowAttribute(u8 windowId, u8 attributeId) +{ + switch (attributeId) + { + case WINDOW_BG: + return gWindows[windowId].window.bg; + case WINDOW_TILEMAP_LEFT: + return gWindows[windowId].window.tilemapLeft; + case WINDOW_TILEMAP_TOP: + return gWindows[windowId].window.tilemapTop; + case WINDOW_WIDTH: + return gWindows[windowId].window.width; + case WINDOW_HEIGHT: + return gWindows[windowId].window.height; + case WINDOW_PALETTE_NUM: + return gWindows[windowId].window.paletteNum; + case WINDOW_BASE_BLOCK: + return gWindows[windowId].window.baseBlock; + case WINDOW_TILE_DATA: + return (u32)(gWindows[windowId].tileData); + default: + return 0; + } +} + +static u8 GetNumActiveWindowsOnBg(u8 bgId) +{ + u8 windowsNum = 0; + s32 i; + for (i = 0; i < WINDOWS_MAX; i++) + { + if (gWindows[i].window.bg == bgId) + windowsNum++; + } + return windowsNum; +} + +static void nullsub_9(void) +{ + +} + +u16 AddWindow8Bit(const struct WindowTemplate *template) +{ + u16 windowId; + u8* memAddress; + u8 bgLayer; + + for (windowId = 0; windowId < 32; windowId++) + { + if (gWindows[windowId].window.bg == 0xFF) + break; + } + if (windowId == WINDOWS_MAX) + return 0xFF; + bgLayer = template->bg; + if (gUnknown_03002F70[bgLayer] == 0) + { + u16 attribute = GetBgAttribute(bgLayer, BG_ATTR_METRIC); + if (attribute != 0xFFFF) + { + s32 i; + memAddress = Alloc(attribute); + if (memAddress == NULL) + return 0xFF; + for (i = 0; i < attribute; i++) // if we're going to zero out the memory anyway, why not call AllocZeroed? + memAddress[i] = 0; + gUnknown_03002F70[bgLayer] = memAddress; + SetBgTilemapBuffer(bgLayer, memAddress); + } + } + memAddress = Alloc((u16)(0x40 * (template->width * template->height))); + if (memAddress == NULL) + { + if (GetNumActiveWindowsOnBg8Bit(bgLayer) == 0 && gUnknown_03002F70[bgLayer] != nullsub_9) + { + Free(gUnknown_03002F70[bgLayer]); + gUnknown_03002F70[bgLayer] = NULL; + } + return 0xFF; + } + else + { + gWindows[windowId].tileData = memAddress; + gWindows[windowId].window = *template; + return windowId; + } +} + +void FillWindowPixelBuffer8Bit(u8 windowId, u8 fillValue) +{ + s32 i; + s32 size; + + size = (u16)(0x40 * (gWindows[windowId].window.width * gWindows[windowId].window.height)); + for (i = 0; i < size; i++) + gWindows[windowId].tileData[i] = fillValue; +} + +void FillWindowPixelRect8Bit(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height) +{ + struct Bitmap pixelRect; + + pixelRect.pixels = gWindows[windowId].tileData; + pixelRect.width = 8 * gWindows[windowId].window.width; + pixelRect.height = 8 * gWindows[windowId].window.height; + + FillBitmapRect8Bit(&pixelRect, x, y, width, height, fillValue); +} + +void BlitBitmapRectToWindow4BitTo8Bit(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight, u8 paletteNum) +{ + struct Bitmap sourceRect; + struct Bitmap destRect; + + sourceRect.pixels = (u8*) pixels; + sourceRect.width = srcWidth; + sourceRect.height = srcHeight; + + destRect.pixels = gWindows[windowId].tileData; + destRect.width = 8 * gWindows[windowId].window.width; + destRect.height = 8 * gWindows[windowId].window.height; + + BlitBitmapRect4BitTo8Bit(&sourceRect, &destRect, srcX, srcY, destX, destY, rectWidth, rectHeight, 0, paletteNum); +} + +void CopyWindowToVram8Bit(u8 windowId, u8 mode) +{ + sWindowPtr = &gWindows[windowId]; + sWindowSize = 0x40 * (sWindowPtr->window.width * sWindowPtr->window.height); + + switch (mode) + { + case 1: + CopyBgTilemapBufferToVram(sWindowPtr->window.bg); + break; + case 2: + LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock); + break; + case 3: + LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock); + CopyBgTilemapBufferToVram(sWindowPtr->window.bg); + break; + } +} + +static u8 GetNumActiveWindowsOnBg8Bit(u8 bgId) +{ + u8 windowsNum = 0; + s32 i; + for (i = 0; i < WINDOWS_MAX; i++) + { + if (gWindows[i].window.bg == bgId) + windowsNum++; + } + return windowsNum; +} diff --git a/gflib/window.h b/gflib/window.h new file mode 100644 index 000000000..10e447789 --- /dev/null +++ b/gflib/window.h @@ -0,0 +1,78 @@ +#ifndef GUARD_WINDOW_H +#define GUARD_WINDOW_H + +#define PIXEL_FILL(num) ((num) | ((num) << 4)) + +enum +{ + WINDOW_BG, + WINDOW_TILEMAP_LEFT, + WINDOW_TILEMAP_TOP, + WINDOW_WIDTH, + WINDOW_HEIGHT, + WINDOW_PALETTE_NUM, + WINDOW_BASE_BLOCK, + WINDOW_TILE_DATA +}; + +struct WindowTemplate +{ + u8 bg; + u8 tilemapLeft; + u8 tilemapTop; + u8 width; + u8 height; + u8 paletteNum; + u16 baseBlock; +}; + +#define DUMMY_WIN_TEMPLATE \ +{ \ + 0xFF, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ +} + +struct Window +{ + struct WindowTemplate window; + u8 *tileData; +}; + +bool16 InitWindows(const struct WindowTemplate *templates); +u16 AddWindow(const struct WindowTemplate *template); +int AddWindowWithoutTileMap(const struct WindowTemplate *template); +void RemoveWindow(u8 windowId); +void FreeAllWindowBuffers(void); +void CopyWindowToVram(u8 windowId, u8 mode); +void CopyWindowRectToVram(u32 windowId, u32 mode, u32 x, u32 y, u32 w, u32 h); +void PutWindowTilemap(u8 windowId); +void PutWindowRectTilemapOverridePalette(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 palette); +void ClearWindowTilemap(u8 windowId); +void PutWindowRectTilemap(u8 windowId, u8 x, u8 y, u8 width, u8 height); +void BlitBitmapToWindow(u8 windowId, const u8 *pixels, u16 x, u16 y, u16 width, u16 height); +void BlitBitmapRectToWindow(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight); +void FillWindowPixelRect(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height); +void CopyToWindowPixelBuffer(u8 windowId, const void *src, u16 size, u16 tileOffset); +void FillWindowPixelBuffer(u8 windowId, u8 fillValue); +void ScrollWindow(u8 windowId, u8 direction, u8 distance, u8 fillValue); +void CallWindowFunction(u8 windowId, void ( *func)(u8, u8, u8, u8, u8, u8)); +bool8 SetWindowAttribute(u8 windowId, u8 attributeId, u32 value); +u32 GetWindowAttribute(u8 windowId, u8 attributeId); +u16 AddWindow8Bit(const struct WindowTemplate *template); +void FillWindowPixelBuffer8Bit(u8 windowId, u8 fillValue); +void FillWindowPixelRect8Bit(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height); +void BlitBitmapRectToWindow4BitTo8Bit(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight, u8 paletteNum); +void CopyWindowToVram8Bit(u8 windowId, u8 mode); + +extern struct Window gWindows[]; +extern void* gUnknown_03002F70[]; +extern u32 filler_03002F58; +extern u32 filler_03002F5C; +extern u32 filler_03002F64; + +#endif // GUARD_WINDOW_H |