summaryrefslogtreecommitdiff
path: root/src/message.c
diff options
context:
space:
mode:
authortheo3 <arisstephenson2@gmail.com>2021-12-28 13:53:53 -0800
committertheo3 <arisstephenson2@gmail.com>2021-12-28 13:53:53 -0800
commit3fd900f1bc81cbaf106de17f5b8d41e26de682a8 (patch)
treeb6226159f870a5974379851e986261eb8d1ce5fc /src/message.c
parent0f65f8fd5008d06defcfae6c9d66270ff45a8e49 (diff)
rename files, sort asm into subdirectories
Diffstat (limited to 'src/message.c')
-rw-r--r--src/message.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/src/message.c b/src/message.c
new file mode 100644
index 00000000..3560df71
--- /dev/null
+++ b/src/message.c
@@ -0,0 +1,809 @@
+#include "global.h"
+#include "sound.h"
+#include "asm.h"
+#include "room.h"
+#include "utils.h"
+#include "functions.h"
+#include "message.h"
+#include "save.h"
+
+#define MESSAGE_ADVANCE_KEYS (A_BUTTON | B_BUTTON | DPAD_ANY | R_BUTTON)
+#define MESSAGE_PRESS_ANY_ADVANCE_KEYS ((gInput.newKeys & MESSAGE_ADVANCE_KEYS) != 0)
+
+#define MESSAGE_WIDTH 0x20
+#define MESSAGE_POSITION_INDEX(window) ((window).yPos * MESSAGE_WIDTH + (window).xPos)
+
+#define MESSAGE_PALETTE 0xF
+
+// todo: move this to a shared header for ui tiles
+enum {
+ MSG_BORDER_CORNER = 0x7B,
+ MSG_BORDER_H_CORNER = 0x7C,
+ MSG_BORDER_H_STRAIGHT = 0x7D,
+ MSG_BORDER_V_CORNER = 0x7E,
+ MSG_BORDER_V_STRAIGHT = 0x7F,
+ MSG_CURSOR = 0x80,
+ MSG_BACKGROUND = 0x81,
+ MSG_TEXT_LINE1TOP = 0x82,
+ MSG_TEXT_LINE1BOTTOM = 0x83,
+ MSG_TEXT_LINE2TOP = 0xb6,
+ MSG_TEXT_LINE2BOTTOM = 0xb7,
+};
+
+extern void WriteBit(u32*, u32);
+extern void sub_0805EF40(Token* tok, const u8*);
+extern void RecoverUI(u32 bottomPt, u32 topPt);
+extern void RefreshUI(void);
+extern void sub_0805F918(u32, u32, u32);
+extern u32 sub_0801D51C(u32, u8*, u32);
+
+u32 sub_08056FEC(u32, u8*);
+u32 GetCharacter(Token* tok);
+extern void sub_0805EEB4(Token* tok, u32 textIdx);
+u16 sub_0805F7DC(u32, u8*);
+u32 GetFontStrWith(u8*, u32);
+
+static void StatusUpdate(u32 status);
+
+static u16 RunTextCommand(TextRender* this);
+static void PaletteChange(TextRender* this, u32 id);
+static void SwitchChoice(u32 to, u32 from);
+
+static void MsgChangeLine(u32 lineNo);
+static void SetDoTextBox(u32 doTextbox);
+
+static void DeleteWindow(void);
+static u32 ChangeWindowSize(u32 delta);
+static void CreateWindow(void);
+
+static void DispString(void);
+static void DispCursor(void);
+static void DrawCanvasLine(void);
+static void sub_08056F88(u32, u32);
+static void sub_08056FBC(TextRender*);
+
+typedef u32 (*MessageFunction)(void);
+static u32 MsgIdle(void);
+static u32 MsgInit(void);
+static u32 MsgUpdate(void);
+static u32 MsgOpen(void);
+static u32 MsgClose(void);
+static u32 MsgDie(void);
+typedef enum {
+ MSG_IDLE,
+ MSG_INIT,
+ MSG_UPDATE,
+ MSG_OPEN,
+ MSG_CLOSE,
+ MSG_DIE,
+} MessageStatus;
+
+typedef void (*TextRenderFunction)(TextRender*);
+static void TextDispInit(TextRender* this);
+static void TextDispUpdate(TextRender* this);
+static void TextDispDie(TextRender* this);
+static void TextDispWait(TextRender* this);
+static void TextDispRoll(TextRender* this);
+static void TextDispEnquiry(TextRender* this);
+typedef enum {
+ RENDER_INIT,
+ RENDER_UPDATE,
+ RENDER_DIE,
+ RENDER_WAIT,
+ RENDER_ROLL,
+ RENDER_ENQUIRY,
+} TextRenderStatus;
+
+typedef struct Window {
+ u8 unk0;
+ u8 active;
+ u8 unk2;
+ u8 unk3;
+ u8 xPos;
+ u8 yPos;
+ u8 width;
+ u8 height;
+} Window;
+extern Window gCurrentWindow;
+extern Window gNewWindow;
+
+extern struct {
+ u8 unk_00;
+ u8 unk_01[1];
+ s8 choiceCount;
+ s8 currentChoice;
+ u8 unk_04[4];
+ u16 unk_08[4];
+ u16 unk_10[4];
+} gMessageChoices;
+
+extern u8 gTextGfxBuffer[0xD00];
+
+s32 sub_08056338(void) {
+ s32 result;
+
+ result = -1;
+ if (((gMessage.doTextBox & 0x7f) == 0) && (gUnk_02000040.unk_00 == 3))
+ result = gUnk_02000040.unk_01;
+ return result;
+}
+
+void MessageClose(void) {
+ if ((gMessage.doTextBox & 0x7f) != 0) {
+ gMessage.doTextBox = 0x88;
+ }
+}
+
+void MessageFromTarget(u32 index) {
+ if (gRoomControls.cameraTarget != NULL) {
+ MessageNoOverlap(index, gRoomControls.cameraTarget);
+ } else {
+ MessageRequest(index);
+ }
+}
+
+void MessageNoOverlap(u32 index, Entity* entity) {
+ s16 y;
+ s16 height;
+
+ MessageRequest(index);
+
+ y = entity->y.HALF.HI;
+ height = entity->z.HALF.HI;
+
+ if (((y + height) - gRoomControls.roomScrollY) > 0x58) {
+ gMessage.textWindowPosY = 1;
+ }
+}
+
+void MessageAtHeight(u32 index, u32 y) {
+ MessageAtPos(index, 1, y);
+}
+
+void MessageAtPos(u32 index, u32 x, u32 y) {
+ MessageRequest(index);
+ gMessage.textWindowPosX = x;
+ gMessage.textWindowPosY = y;
+}
+
+void MessageRequest(u32 index) {
+ MemClear(&gMessage, sizeof(gMessage));
+ gMessage.textIndex = index;
+ gMessage.textSpeed = 99;
+ gMessage.textWindowWidth = 26;
+ gMessage.textWindowHeight = 4;
+ gMessage.textWindowPosX = 1;
+ gMessage.textWindowPosY = 12;
+ gMessage.doTextBox = 1;
+}
+
+void MessageInitialize(void) {
+ MemClear(&gMessage, sizeof(gMessage));
+ MemClear(&gTextRender, sizeof(gTextRender));
+ MemClear(&gNewWindow, sizeof(gNewWindow));
+ MemClear(&gCurrentWindow, sizeof(gCurrentWindow));
+ MemClear(&gUnk_02000040, 4);
+}
+
+void MessageMain(void) {
+ static const MessageFunction gMessageFunctions[] = {
+ [MSG_IDLE] = MsgIdle, [MSG_INIT] = MsgInit, [MSG_UPDATE] = MsgUpdate,
+ [MSG_OPEN] = MsgOpen, [MSG_CLOSE] = MsgClose, [MSG_DIE] = MsgDie,
+ };
+
+ if (gMessage.doTextBox == 1) {
+ MemClear((u32*)&gTextRender, sizeof(gTextRender));
+ StatusUpdate(MSG_INIT);
+ }
+
+ if (gTextRender.newlineDelay != 0) {
+ gTextRender.newlineDelay--;
+ } else {
+ int result;
+ do {
+ result = gMessageFunctions[gTextRender.msgStatus]();
+ } while (result != 0);
+ }
+ if (gTextRender.updateDraw) {
+ gTextRender.updateDraw = 0;
+ DrawCanvasLine();
+ }
+ DeleteWindow();
+ CreateWindow();
+}
+
+static void StatusUpdate(MessageStatus status) {
+ gTextRender.msgStatus = status;
+ gTextRender.renderStatus = RENDER_INIT;
+}
+
+static u32 MsgIdle(void) {
+ gTextRender._98.bytes.b1 = 0;
+ return 0;
+}
+
+extern u8 gUnk_020227DC, gUnk_020227E8, gUnk_020227F0, gUnk_020227F8, gUnk_02022800;
+u8* const gUnk_08107BE0[] = {
+ &gUnk_020227DC, &gUnk_020227E8, &gUnk_020227F0, &gUnk_020227F8, &gUnk_02022800,
+};
+
+// regalloc in loop
+NONMATCH("asm/non_matching/textbox/TextBoxFunction1.inc", static u32 MsgInit(void)) {
+ char* dest;
+ u32 i;
+
+ MemClear((void*)&gNewWindow, sizeof(gNewWindow));
+ MemClear((void*)&gMessageChoices, sizeof(gMessageChoices));
+ MemClear((void*)&gTextRender, sizeof(gTextRender));
+ MemCopy(&gMessage, &gTextRender, sizeof(gMessage));
+ if (gTextRender.message.textSpeed == 99) {
+ gTextRender.message.textSpeed = gSaveHeader->messageSpeed;
+ }
+ gTextRender._9c = 0xff;
+ sub_0805EEB4(&gTextRender.curToken, gTextRender.message.textIndex);
+ gTextRender.playerName[0] = 2;
+ gTextRender.playerName[1] = 0xe; // Green text color
+ dest = &gTextRender.playerName[2];
+
+ for (i = 0; i < FILENAME_LENGTH; ++i) {
+ char c = gSave.playerName[i];
+ if (c == '\0')
+ break;
+ *dest++ = c;
+ }
+
+ dest[0] = 2;
+ dest[1] = 0xf; // White text color
+ dest[2] = '\0';
+ sub_08056FBC(&gTextRender);
+ gTextRender.curToken._c = &gUnk_08107BE0;
+ gTextRender._50.unk8 = gTextGfxBuffer;
+ gTextRender._50.unk4 = 0xd0;
+ SetDoTextBox(2);
+ MsgChangeLine(0);
+ StatusUpdate(MSG_UPDATE);
+ return 1;
+}
+END_NONMATCH
+
+static u32 MsgOpen(void) {
+ if (gTextRender.renderStatus == RENDER_INIT) {
+ gTextRender.renderStatus = RENDER_UPDATE;
+ gTextRender._98.bytes.b1 = 1;
+ sub_08056F88(gTextRender.message.unk3, gTextRender._50.unk3);
+ SoundReq(SFX_TEXTBOX_OPEN);
+ }
+
+ if (ChangeWindowSize(1)) {
+ gTextRender._98.bytes.b1 = 2;
+ StatusUpdate(MSG_UPDATE);
+ }
+ return 0;
+}
+
+static u32 MsgClose(void) {
+ if (gTextRender.renderStatus == RENDER_INIT) {
+ gTextRender.renderStatus = RENDER_UPDATE;
+ gTextRender._98.bytes.b1 = 3;
+ MsgChangeLine(0);
+ SoundReq(SFX_TEXTBOX_CLOSE);
+ }
+
+ if (ChangeWindowSize(-1)) {
+ gTextRender._98.bytes.b1 = 0;
+ StatusUpdate(MSG_UPDATE);
+ }
+ return 0;
+}
+
+static u32 MsgDie(void) {
+ SetDoTextBox(0);
+ StatusUpdate(MSG_IDLE);
+ return 0;
+}
+
+static u32 MsgUpdate(void) {
+ static const TextRenderFunction gTextDispFunctions[] = {
+ [RENDER_INIT] = TextDispInit, [RENDER_UPDATE] = TextDispUpdate, [RENDER_DIE] = TextDispDie,
+ [RENDER_WAIT] = TextDispWait, [RENDER_ROLL] = TextDispRoll, [RENDER_ENQUIRY] = TextDispEnquiry,
+ };
+
+ SetDoTextBox(4);
+ gTextDispFunctions[gTextRender.renderStatus](&gTextRender);
+ ChangeWindowSize(0);
+ return 0;
+}
+
+static void TextDispInit(TextRender* this) {
+ if ((gTextRender.curToken.flags & 1) == 0) {
+ if (gTextRender._98.bytes.b1 == 0) {
+ StatusUpdate(MSG_DIE);
+ }
+ } else {
+ this->renderStatus = RENDER_UPDATE;
+ }
+}
+
+static void TextDispUpdate(TextRender* this) {
+ static const u8 speeds[] = { 5, 3, 1 };
+ u32 speedModifier;
+ s32 numCharsToRead, pxDrawn;
+
+ if (this->delay != 0) {
+ this->delay--;
+ return;
+ }
+
+ if ((gInput.heldKeys & B_BUTTON) != 0) {
+ speedModifier = 8;
+ } else {
+ speedModifier = 1;
+ }
+ this->typeSpeed -= speedModifier;
+
+ if (this->typeSpeed > 0) {
+ return;
+ }
+
+ numCharsToRead = 0;
+ do {
+ numCharsToRead++;
+ this->typeSpeed += speeds[this->message.textSpeed];
+ } while (this->typeSpeed <= 0);
+
+ pxDrawn = 0;
+ do {
+ u32 pxCnt = RunTextCommand(this);
+ if (pxCnt == 0 || this->delay != 0 || this->newlineDelay != 0)
+ break;
+ pxDrawn += pxCnt;
+ numCharsToRead--;
+ } while (0 < numCharsToRead);
+
+ if (pxDrawn != 0) {
+ gTextRender.updateDraw = 1;
+ } else {
+ this->typeSpeed = 0;
+ }
+}
+
+NONMATCH("asm/non_matching/textbox/sub_08056750.inc", static u16 RunTextCommand(TextRender* this)) {
+ s32 palette;
+ u32 chr = this->curToken.extended;
+
+ if (chr == 0) {
+ s32 r1;
+ chr = GetCharacter(&this->curToken);
+ switch (chr) {
+ case 0:
+ if (gUnk_02000040.unk_00 == 1) {
+ this->renderStatus = RENDER_ENQUIRY;
+ SwitchChoice(0, 0);
+ } else {
+ this->renderStatus = RENDER_DIE;
+ }
+ break;
+ case 1:
+ this->newlineDelay = 2;
+ if (this->_98.bytes.lineNo == 0) {
+ MsgChangeLine(1);
+ } else {
+ this->renderStatus = RENDER_WAIT;
+ }
+ break;
+ case 2:
+ StatusUpdate(MSG_OPEN);
+ break;
+ case 3:
+ StatusUpdate(MSG_CLOSE);
+ break;
+ case 4:
+ this->_50.unk6 += (this->_50.unk4 - this->_50.unk6 - GetFontStrWith(&this->curToken.flags, 0)) / 2;
+ break;
+ case 5:
+ gMessage.unk = this->curToken.param;
+ break;
+ case 6:
+ WriteBit(&gMessage.field_0xc, this->curToken.param & 0x1f);
+ break;
+ case 7:
+ switch (this->curToken.param) {
+ case 0xe:
+ this->_91 = this->_8f;
+ palette = 2;
+ break;
+ case 0xf:
+ palette = this->_91;
+ break;
+ default:
+ palette = this->curToken.param;
+ break;
+ }
+ this->_90 = 0;
+ PaletteChange(this, palette);
+ break;
+ case 8:
+ SoundReq(this->curToken.param);
+ break;
+ case 9:
+ gTextRender.message.unk3 = this->curToken.param;
+ sub_08056F88(this->curToken.param, this->_50.unk3);
+ break;
+ case 10:
+ this->message.textWindowPosY = this->curToken.param;
+ break;
+ case 12:
+ if (gUnk_02000040.unk_00 != 1) {
+ MemClear(&gUnk_02000040, 4);
+ MemClear(&gMessageChoices, sizeof(gMessageChoices));
+ gMessageChoices.unk_00 = 1;
+ gUnk_02000040.unk_00 = 1;
+ }
+ if (gMessageChoices.choiceCount > 3)
+ break;
+
+ gMessageChoices.unk_10[gMessageChoices.choiceCount] = this->curToken._6;
+ gMessageChoices.unk_08[gMessageChoices.choiceCount] = this->_50.unk6;
+ gMessageChoices.choiceCount++;
+ this->_50.unk6 += 8;
+ break;
+ case 13:
+ switch (this->curToken.param) {
+ case 0xff:
+ this->_8e = 1;
+ break;
+ case 0xfe:
+ this->_8e = 2;
+ break;
+ default:
+ this->delay = this->curToken.param;
+ break;
+ }
+ break;
+ case 14:
+ r1 = this->curToken.param;
+ this->_94 = r1;
+ break;
+ default:
+ break;
+ }
+ if (chr >> 8 == 0)
+ return 0;
+ }
+ if (gTextRender._98.bytes.b1 != 2) {
+ StatusUpdate(MSG_OPEN);
+ return 0;
+ }
+ if (gMessage.unk == 0) {
+ gMessage.unk = 0x80;
+ }
+ this->curToken.extended = '\0';
+ if (chr >> 8 == 7) {
+ this->_90 = this->_8f | 0x80;
+ PaletteChange(this, 0);
+ } else {
+ if ((this->_90 & 0x80) != 0) {
+ palette = this->_90;
+ this->_90 = 0;
+ PaletteChange(this, palette);
+ }
+ }
+ return sub_0805F7DC(chr, &this->_50.unk0);
+}
+END_NONMATCH
+
+static void PaletteChange(TextRender* this, u32 id) {
+ u32 temp = id % 8;
+ this->_8f = temp;
+ this->_50.unk2 = temp;
+}
+
+const u8 gUnk_08107C0F[] = { 0x8, 0x1e, 0x4, 0x12, 0x0 };
+const u8 gUnk_08107C14[] = { 0x8, 0x1e, 0x8, 0xFE, 0x0 };
+
+#ifdef EU
+ASM_FUNC("asm/non_matching/eu/TextBoxHandlerQuestion.inc", static void TextDispEnquiry(TextRender* ctb))
+#else
+
+static void TextDispEnquiry(TextRender* this) {
+ s32 nextTextIdx, choiceIdx, lastChoice;
+ u32 doSwitch;
+ const u8* src;
+
+ choiceIdx = gMessageChoices.currentChoice;
+ switch (gInput.newKeys) {
+ case START_BUTTON:
+ case A_BUTTON:
+ nextTextIdx = gMessageChoices.unk_10[choiceIdx];
+ if (nextTextIdx == 0) {
+ src = gUnk_08107C14;
+ } else {
+ this->message.textIndex = nextTextIdx;
+ sub_0805EEB4(&this->curToken, nextTextIdx);
+ src = gUnk_08107C0F;
+ }
+ sub_0805EF40(&this->curToken, src);
+ gUnk_02000040.unk_01 = gMessageChoices.currentChoice;
+ gUnk_02000040.unk_00 = 3;
+ MemClear(&gMessageChoices, sizeof(gMessageChoices));
+ SoundReq(SFX_TEXTBOX_SELECT);
+ this->renderStatus = RENDER_UPDATE;
+ break;
+ case DPAD_LEFT:
+ choiceIdx--;
+ break;
+ case DPAD_RIGHT:
+ choiceIdx++;
+ break;
+ default:
+ break;
+ }
+ choiceIdx = (choiceIdx + gMessageChoices.choiceCount) % gMessageChoices.choiceCount;
+ lastChoice = gMessageChoices.currentChoice;
+ if (choiceIdx != lastChoice) {
+ gMessageChoices.currentChoice = choiceIdx;
+ SoundReq(SFX_TEXTBOX_CHOICE);
+ doSwitch = 1;
+ } else {
+ doSwitch = 0;
+ }
+ if (gUnk_02000040.unk_00 == 1) {
+ gMessageChoices.unk_00 = gUnk_02000040.unk_00 = 2;
+ doSwitch = 1;
+ }
+ if (doSwitch) {
+ SwitchChoice(choiceIdx, lastChoice);
+ }
+}
+
+static void SwitchChoice(u32 to, u32 from) {
+ u16 t;
+ t = gTextRender._50.unk6;
+ gTextRender._50.unk6 = gMessageChoices.unk_08[from];
+ sub_0805F8E4(0, &gTextRender._50);
+ gTextRender._50.unk6 = gMessageChoices.unk_08[to];
+ sub_0805F8E4(1, &gTextRender._50);
+ gTextRender._50.unk6 = t;
+ gTextRender.updateDraw = 1;
+}
+
+#endif
+
+static void TextDispWait(TextRender* this) {
+ gMessage.unk = 0;
+ if (this->_94 != 0) {
+ if (--this->_94 == 0) {
+ this->renderStatus = RENDER_ROLL;
+ }
+ } else if (MESSAGE_PRESS_ANY_ADVANCE_KEYS) {
+ SoundReq(SFX_TEXTBOX_SWAP);
+ this->_98.bytes.b2 = 0;
+ this->renderStatus = RENDER_ROLL;
+ } else {
+ this->_98.bytes.b2++;
+ }
+}
+
+static void TextDispRoll(TextRender* this) {
+ MsgChangeLine(0);
+ PaletteChange(this, this->_8f | 0x40);
+ this->renderStatus = RENDER_UPDATE;
+}
+
+static void TextDispDie(TextRender* this) {
+ gMessage.unk = 0;
+ SetDoTextBox(7);
+ if ((this->_8e != 1) && (this->_8e == 2 || MESSAGE_PRESS_ANY_ADVANCE_KEYS)) {
+ StatusUpdate(MSG_CLOSE);
+ }
+}
+
+static void MsgChangeLine(u32 lineNo) {
+ gTextRender._98.bytes.lineNo = lineNo;
+ if (lineNo == 0) {
+ MemFill32(0xFFFFFFFF, gTextGfxBuffer, sizeof(gTextGfxBuffer));
+ DrawCanvasLine();
+ gTextRender.beginTiles[0] = (MESSAGE_PALETTE << 12) | MSG_TEXT_LINE1TOP;
+ gTextRender.beginTiles[1] = (MESSAGE_PALETTE << 12) | MSG_TEXT_LINE1BOTTOM;
+ gTextRender.beginTiles[2] = (MESSAGE_PALETTE << 12) | MSG_TEXT_LINE2TOP;
+ gTextRender.beginTiles[3] = (MESSAGE_PALETTE << 12) | MSG_TEXT_LINE2BOTTOM;
+ gTextRender._50.unk6 = 0;
+ gTextRender._50.unk4 = 0xd0;
+ } else {
+ gTextRender._50.unk6 = 0xd0;
+ gTextRender._50.unk4 = 0x1a0;
+ }
+ PaletteChange(&gTextRender, gTextRender._8f | 0x40);
+}
+
+static void SetDoTextBox(u32 doTextbox) {
+ gMessage.doTextBox = gTextRender.message.doTextBox = doTextbox;
+}
+
+static void DeleteWindow(void) {
+ Window* window = &gCurrentWindow;
+ if (window->active) {
+ u16* ptr;
+ int i;
+ window->active = 0;
+ ptr = &gBG0Buffer[MESSAGE_POSITION_INDEX(*window)];
+ i = window->height + 2;
+ do {
+ int j = 0;
+ do {
+ ptr[j] = 0;
+ } while (j++, j < window->width + 2);
+ ptr += MESSAGE_WIDTH;
+ i--;
+ } while (i > 0);
+ RecoverUI(window->yPos, window->height);
+ RefreshUI();
+ }
+}
+
+static u32 ChangeWindowSize(u32 delta) {
+ Window* window;
+ u32 result = 0;
+
+ gTextRender._98.bytes.sizeScale += delta;
+
+ if (gTextRender._98.bytes.sizeScale < 1) {
+ gTextRender._98.bytes.sizeScale = 0;
+ result = 1;
+ } else if (gTextRender._98.bytes.sizeScale >= 8) {
+ gTextRender._98.bytes.sizeScale = 8;
+ result = 1;
+ }
+
+ window = &gNewWindow;
+ if (gTextRender._98.bytes.sizeScale != 0) {
+ u32 scale = gTextRender._98.bytes.sizeScale;
+ window->width = (scale * (gTextRender.message.textWindowWidth << 1)) / 16;
+ if ((window->width & 1) != 0) {
+ window->width++;
+ }
+ window->xPos =
+ ((gTextRender.message.textWindowWidth / 2) + gTextRender.message.textWindowPosX) - (window->width / 2);
+
+ window->height = (scale * (gTextRender.message.textWindowHeight << 1)) / 16;
+ if ((window->height & 1) != 0) {
+ window->height++;
+ }
+ window->yPos =
+ ((gTextRender.message.textWindowHeight / 2) + gTextRender.message.textWindowPosY) - (window->height / 2);
+ } else {
+ window->yPos = -1;
+ window->xPos = -1;
+ window->height = -1;
+ window->width = -1;
+ }
+ window->active = 1;
+ return result;
+}
+
+static void CreateWindow(void) {
+ s32 borderBeginTile;
+ u16* ptr;
+
+ if (gTextRender._98.bytes.sizeScale <= 0)
+ return;
+ ptr = &gBG0Buffer[MESSAGE_POSITION_INDEX(gNewWindow)];
+ borderBeginTile = (MESSAGE_PALETTE << 12) | MSG_BORDER_CORNER;
+ DispMessageFrame(ptr, gNewWindow.width, gNewWindow.height, borderBeginTile);
+ DispString();
+ DispCursor();
+ gCurrentWindow = gNewWindow;
+ RefreshUI();
+}
+
+// Also used by figurine menu
+ASM_FUNC("asm/non_matching/textbox/DispMessageFrame.inc",
+ void DispMessageFrame(u16* buffer, u32 width_, u32 height_, u32 flags_))
+
+extern u16 gUnk_02034CB2[];
+extern u16 gUnk_0202281E[];
+
+/*
+ Each character is two tiles tall.
+ They are stored in VRAM in the following order:
+
+ 82 84 86 --> | | |\| |/
+ 83 85 87 --> |_ | | \ |\
+
+ b6 b8 ba --> | | |
+ b7 b9 bb --> . . .
+
+ There are 2 lines of text available, and each line is 20 chars long,
+ So the canvas is 20x4 tiles.
+*/
+
+static void DispString(void) {
+ u32 r0, tileAttr;
+ u16 *buf, *line;
+ s32 i, w, h;
+
+ Window* window = &gNewWindow;
+
+ if (window->width != 0) {
+ if (window->height != 0) {
+ h = window->height;
+ buf = &gBG0Buffer[MESSAGE_POSITION_INDEX(*window) + 1];
+ i = (4 - h) / 2;
+ do {
+ //! @bug abnormally big width causes tile idx to overwrite other tile attributes
+ w = window->width;
+ tileAttr = gTextRender.beginTiles[i];
+ buf += MESSAGE_WIDTH;
+ r0 = i + 1;
+ h--;
+ line = buf;
+ do {
+ *line = tileAttr;
+ line++;
+ tileAttr += 2;
+ w--;
+ } while (w > 0);
+ i = r0;
+ } while (h != 0);
+ }
+ }
+}
+
+static void DispCursor(void) {
+ u32 offset;
+ u16* ptr;
+
+ if ((gTextRender._98.word & 0x10ff00) == 0x100200) {
+ ptr = &gBG0Buffer[MESSAGE_POSITION_INDEX(gNewWindow)];
+ offset = (((gNewWindow.height + 1) * MESSAGE_WIDTH) - 2);
+ offset += gNewWindow.width;
+ ptr += offset;
+ *ptr = 0xf080;
+ }
+}
+
+static void DrawCanvasLine(void) {
+ LoadResourceAsync(gTextGfxBuffer, 0x0600D040, sizeof(gTextGfxBuffer));
+}
+
+void sub_08056F88(u32 unk_1, u32 unk_2) {
+ u32 uVar1;
+
+ if (0xf < unk_1) {
+ unk_1 = 0;
+ }
+ uVar1 = unk_1 << 4 | unk_2;
+ if (gTextRender._9c != uVar1) {
+ gTextRender._9c = uVar1;
+ sub_0805F918(unk_1, unk_2, 0x0600CF60);
+ }
+}
+
+static void sub_08056FBC(TextRender* ctb) {
+ sub_08056FEC(ctb->message.field_0x10, &ctb->_66[0x2]);
+ sub_08056FEC(ctb->message.field_0x14, &ctb->_66[0xa]);
+ sub_08056FEC(ctb->message.field_0x18, &ctb->_77[0x1]);
+ sub_08056FEC(ctb->message.field_0x1c, &ctb->_77[0x9]);
+}
+
+NONMATCH("asm/non_matching/textbox/sub_08056FEC.inc", u32 sub_08056FEC(u32 this, u8* param_2)) {
+ u32 uVar1;
+ int iVar2;
+ int iVar3;
+ int iVar4;
+ u8 local_1c[8];
+
+ uVar1 = sub_0801D51C(this, param_2, this);
+ uVar1 = uVar1 & 0xfffffff;
+ iVar4 = 0;
+ do {
+ local_1c[iVar4++] = uVar1 & 0xf;
+ uVar1 = uVar1 / 16;
+ } while (uVar1 != 0);
+ for (iVar3 = 0; --iVar4 >= 0; iVar3++) {
+ param_2[iVar3] = local_1c[iVar4] | 0x30;
+ }
+ param_2[iVar3] = 0;
+ return iVar4;
+}
+END_NONMATCH