sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

ingame_menu.c (116174B)


      1 #include <ultra64.h>
      2 
      3 #include "actors/common1.h"
      4 #include "area.h"
      5 #include "audio/external.h"
      6 #include "camera.h"
      7 #include "course_table.h"
      8 #include "dialog_ids.h"
      9 #include "engine/math_util.h"
     10 #include "eu_translation.h"
     11 #include "game_init.h"
     12 #include "gfx_dimensions.h"
     13 #include "ingame_menu.h"
     14 #include "level_update.h"
     15 #include "levels/castle_grounds/header.h"
     16 #include "memory.h"
     17 #include "print.h"
     18 #include "save_file.h"
     19 #include "segment2.h"
     20 #include "segment7.h"
     21 #include "seq_ids.h"
     22 #include "sm64.h"
     23 #include "text_strings.h"
     24 #include "types.h"
     25 
     26 #ifdef VERSION_EU
     27 #undef LANGUAGE_FUNCTION
     28 #define LANGUAGE_FUNCTION gInGameLanguage
     29 #endif
     30 
     31 FORCE_BSS u16 gMenuTextColorTransTimer;
     32 FORCE_BSS s8 gLastDialogLineNum;
     33 FORCE_BSS s32 gDialogVariable;
     34 FORCE_BSS u16 gMenuTextAlpha;
     35 #ifdef VERSION_EU
     36 s16 gDialogX;
     37 s16 gDialogY;
     38 #endif
     39 FORCE_BSS s16 gCutsceneMsgXOffset;
     40 FORCE_BSS s16 gCutsceneMsgYOffset;
     41 s8 gRedCoinsCollected;
     42 
     43 extern u8 gLastCompletedCourseNum;
     44 extern u8 gLastCompletedStarNum;
     45 
     46 enum MenuState {
     47     MENU_STATE_0,
     48     MENU_STATE_1,
     49     MENU_STATE_2,
     50     MENU_STATE_3,
     51     MENU_STATE_DEFAULT = MENU_STATE_0,
     52 
     53     // Dialog
     54     MENU_STATE_DIALOG_OPENING = MENU_STATE_0,
     55     MENU_STATE_DIALOG_OPEN = MENU_STATE_1,
     56     MENU_STATE_DIALOG_SCROLLING = MENU_STATE_2,
     57     MENU_STATE_DIALOG_CLOSING = MENU_STATE_3,
     58 
     59     // Pause Screen
     60     MENU_STATE_PAUSE_SCREEN_OPENING = MENU_STATE_0,
     61     MENU_STATE_PAUSE_SCREEN_COURSE = MENU_STATE_1,
     62     MENU_STATE_PAUSE_SCREEN_CASTLE = MENU_STATE_2,
     63 
     64     // Course Complete Screen
     65     MENU_STATE_COURSE_COMPLETE_SCREEN_OPENING = MENU_STATE_0,
     66     MENU_STATE_COURSE_COMPLETE_SCREEN_OPEN = MENU_STATE_1
     67 };
     68 
     69 enum DialogBoxPageState {
     70     DIALOG_PAGE_STATE_NONE,
     71     DIALOG_PAGE_STATE_SCROLL,
     72     DIALOG_PAGE_STATE_END
     73 };
     74 
     75 enum DialogBoxType {
     76     DIALOG_TYPE_ROTATE, // used in NPCs and level messages
     77     DIALOG_TYPE_ZOOM    // used in signposts and wall signs and etc
     78 };
     79 
     80 #define DIALOG_BOX_ANGLE_DEFAULT 90.0f
     81 #define DIALOG_BOX_SCALE_DEFAULT 19.0f
     82 
     83 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN)
     84 u8 gDialogCharWidths[256] = { // TODO: Is there a way to auto generate this?
     85     7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  6,  6,  6,  6,  6,  6,
     86     6,  6,  5,  6,  6,  5,  8,  8,  6,  6,  6,  6,  6,  5,  6,  6,
     87     8,  7,  6,  6,  6,  5,  5,  6,  5,  5,  6,  5,  4,  5,  5,  3,
     88     7,  5,  5,  5,  6,  5,  5,  5,  5,  5,  7,  7,  5,  5,  4,  4,
     89     8,  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     90     8,  8,  8,  8,  7,  7,  6,  7,  7,  0,  0,  0,  0,  0,  0,  0,
     91 #ifdef VERSION_EU
     92     6,  6,  6,  0,  6,  6,  6,  0,  0,  0,  0,  0,  0,  0,  0,  4,
     93     5,  5,  5,  5,  6,  6,  6,  6,  0,  0,  0,  0,  0,  0,  0,  0,
     94     5,  5,  5,  0,  6,  6,  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     95     0,  5,  5,  0,  0,  6,  6,  0,  0,  0,  0,  0,  0,  0,  5,  6,
     96     0,  4,  4,  0,  0,  5,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     97 #else
     98     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  4,
     99     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    100     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    101     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  6,
    102     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    103 #endif
    104     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    105     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    106     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    107 #ifdef VERSION_EU
    108     7,  5, 10,  5,  9,  8,  4,  0,  0,  0,  0,  5,  5,  6,  5,  0,
    109 #else
    110     7,  5, 10,  5,  9,  8,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    111 #endif
    112     0,  0,  5,  7,  7,  6,  6,  8,  0,  8, 10,  6,  4, 10,  0,  0
    113 };
    114 #endif
    115 
    116 s8 gMenuState = MENU_STATE_DEFAULT;
    117 f32 gDialogBoxAngle = DIALOG_BOX_ANGLE_DEFAULT;
    118 f32 gDialogBoxScale = DIALOG_BOX_SCALE_DEFAULT;
    119 s16 gDialogScrollOffsetY = 0;
    120 s8 gDialogBoxType = DIALOG_TYPE_ROTATE;
    121 s16 gDialogID = DIALOG_NONE;
    122 s16 gNextDialogPageStartStrIndex = 0;
    123 s16 gDialogPageStartStrIndex = 0;
    124 #ifdef VERSION_EU
    125 s32 gInGameLanguage = LANGUAGE_ENGLISH;
    126 #endif
    127 s8 gMenuLineNum = 1;
    128 s8 gDialogWithChoice = FALSE;
    129 u8 gMenuHoldKeyIndex = 0;
    130 u8 gMenuHoldKeyTimer = 0;
    131 s32 gDialogResponse = DIALOG_RESPONSE_NONE;
    132 
    133 void create_dl_identity_matrix(void) {
    134     Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
    135 
    136     if (matrix == NULL) {
    137         return;
    138     }
    139 
    140 #ifndef GBI_FLOATS
    141     matrix->m[0][0] = 0x00010000;    matrix->m[1][0] = 0x00000000;    matrix->m[2][0] = 0x00000000;    matrix->m[3][0] = 0x00000000;
    142     matrix->m[0][1] = 0x00000000;    matrix->m[1][1] = 0x00010000;    matrix->m[2][1] = 0x00000000;    matrix->m[3][1] = 0x00000000;
    143     matrix->m[0][2] = 0x00000001;    matrix->m[1][2] = 0x00000000;    matrix->m[2][2] = 0x00000000;    matrix->m[3][2] = 0x00000000;
    144     matrix->m[0][3] = 0x00000000;    matrix->m[1][3] = 0x00000001;    matrix->m[2][3] = 0x00000000;    matrix->m[3][3] = 0x00000000;
    145 #else
    146     guMtxIdent(matrix);
    147 #endif
    148 
    149     gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
    150     gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
    151 }
    152 
    153 void create_dl_translation_matrix(s8 pushOp, f32 x, f32 y, f32 z) {
    154     Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
    155 
    156     if (matrix == NULL) {
    157         return;
    158     }
    159 
    160     guTranslate(matrix, x, y, z);
    161 
    162     if (pushOp == MENU_MTX_PUSH) {
    163         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH);
    164     }
    165 
    166     if (pushOp == MENU_MTX_NOPUSH) {
    167         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
    168     }
    169 }
    170 
    171 void create_dl_rotation_matrix(s8 pushOp, f32 a, f32 x, f32 y, f32 z) {
    172     Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
    173 
    174     if (matrix == NULL) {
    175         return;
    176     }
    177 
    178     guRotate(matrix, a, x, y, z);
    179 
    180     if (pushOp == MENU_MTX_PUSH) {
    181         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH);
    182     }
    183 
    184     if (pushOp == MENU_MTX_NOPUSH) {
    185         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
    186     }
    187 }
    188 
    189 void create_dl_scale_matrix(s8 pushOp, f32 x, f32 y, f32 z) {
    190     Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
    191 
    192     if (matrix == NULL) {
    193         return;
    194     }
    195 
    196     guScale(matrix, x, y, z);
    197 
    198     if (pushOp == MENU_MTX_PUSH) {
    199         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH);
    200     }
    201 
    202     if (pushOp == MENU_MTX_NOPUSH) {
    203         gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
    204     }
    205 }
    206 
    207 void create_dl_ortho_matrix(void) {
    208     Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
    209 
    210     if (matrix == NULL) {
    211         return;
    212     }
    213 
    214     create_dl_identity_matrix();
    215 
    216     guOrtho(matrix, 0.0f, SCREEN_WIDTH, 0.0f, SCREEN_HEIGHT, -10.0f, 10.0f, 1.0f);
    217 
    218     // Should produce G_RDPHALF_1 in Fast3D
    219     gSPPerspNormalize(gDisplayListHead++, 0xFFFF);
    220 
    221     gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH)
    222 }
    223 
    224 #if defined(VERSION_US) || defined(VERSION_EU)
    225 UNUSED
    226 #endif
    227 #ifdef VERSION_CN
    228 static u8 *alloc_ia8_text_from_i1(u8 *in) {
    229     s32 i, j;
    230     u8 l, r;
    231     u8 bitMask = 0x80;
    232     u8 *out = alloc_display_list(8 * 8);
    233 
    234     if (out == NULL) {
    235         return NULL;
    236     }
    237 
    238     for (i = 0; i < 16; i++) {
    239         for (j = 0; j < 8;) {
    240             r = 0;
    241             l = ((in[i] & (bitMask >> j)) >> (7 - j)) != 0 ? 0xF0 : 0x00;
    242             j++;
    243             r = ((in[i] & (bitMask >> j)) >> (7 - j)) != 0 ? 0x0F : 0x00;
    244             out[i * 4 + j / 2] = l | r;
    245             CN_DEBUG_PRINTF(("%x, ", out[i * 4 + j / 2]));
    246             j++;
    247         }
    248         CN_DEBUG_PRINTF(("\n"));
    249     }
    250 
    251     return out;
    252 }
    253 #else
    254 static u8 *alloc_ia8_text_from_i1(u16 *in, s16 width, s16 height) {
    255     s32 inPos;
    256     u16 bitMask;
    257     u8 *out;
    258     s16 outPos = 0;
    259 
    260     out = (u8 *) alloc_display_list((u32) width * (u32) height);
    261 
    262     if (out == NULL) {
    263         return NULL;
    264     }
    265 
    266     for (inPos = 0; inPos < (width * height) / 16; inPos++) {
    267         bitMask = 0x8000;
    268 
    269         while (bitMask != 0) {
    270             if (in[inPos] & bitMask) {
    271                 out[outPos] = 0xFF;
    272             } else {
    273                 out[outPos] = 0x00;
    274             }
    275 
    276             bitMask /= 2;
    277             outPos++;
    278         }
    279     }
    280 
    281     return out;
    282 }
    283 #endif
    284 
    285 #ifdef VERSION_CN
    286 void render_generic_char(u16 c)
    287 #else
    288 void render_generic_char(u8 c)
    289 #endif
    290 {
    291     void **fontLUT = segmented_to_virtual(main_font_lut);
    292     void *packedTexture = segmented_to_virtual(fontLUT[c]);
    293 #if defined(VERSION_JP) || defined(VERSION_SH)
    294     void *unpackedTexture = alloc_ia8_text_from_i1(packedTexture, 8, 16);
    295 #endif
    296 
    297 #ifndef VERSION_EU
    298     gDPPipeSync(gDisplayListHead++);
    299 #endif
    300 #if defined(VERSION_JP) || defined(VERSION_SH)
    301     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture));
    302 #else
    303     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(packedTexture));
    304 #endif
    305     gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings);
    306 #ifdef VERSION_EU
    307     gSPTextureRectangleFlip(gDisplayListHead++, gDialogX << 2, (gDialogY - 16) << 2,
    308                             (gDialogX + 8) << 2, gDialogY << 2, G_TX_RENDERTILE, 8 << 6, 4 << 6, 1 << 10, 1 << 10);
    309 #endif
    310 }
    311 
    312 #ifdef VERSION_EU
    313 u8 *alloc_ia4_tex_from_i1(u8 *in, s16 width, s16 height) {
    314     u32 size = (u32) width * (u32) height;
    315     u8 *out;
    316     s32 inPos;
    317     s16 outPos = 0;
    318     u8 bitMask;
    319 
    320     out = (u8 *) alloc_display_list(size);
    321 
    322     if (out == NULL) {
    323         return NULL;
    324     }
    325 
    326     for (inPos = 0; inPos < (width * height) / 4; inPos++) {
    327         bitMask = 0x80;
    328 
    329         while (bitMask != 0) {
    330             out[outPos] = (in[inPos] & bitMask) ? 0xF0 : 0x00;
    331             bitMask /= 2;
    332             out[outPos] = (in[inPos] & bitMask) ? out[outPos] + 0x0F : out[outPos];
    333             bitMask /= 2;
    334             outPos++;
    335         }
    336     }
    337 
    338     return out;
    339 }
    340 
    341 void render_generic_char_at_pos(s16 xPos, s16 yPos, u8 c) {
    342     void **fontLUT = segmented_to_virtual(main_font_lut);
    343     void *packedTexture = segmented_to_virtual(fontLUT[c]);
    344     void *unpackedTexture = alloc_ia4_tex_from_i1(packedTexture, 8, 8);
    345 
    346     gDPPipeSync(gDisplayListHead++);
    347     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture));
    348     gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings);
    349     gSPTextureRectangleFlip(gDisplayListHead++, xPos << 2, (yPos - 16) << 2, (xPos + 8) << 2, yPos << 2,
    350                             G_TX_RENDERTILE, 8 << 6, 4 << 6, 1 << 10, 1 << 10);
    351 }
    352 
    353 void render_lowercase_diacritic(s16 *xPos, s16 *yPos, u8 letter, u8 diacritic) {
    354     render_generic_char_at_pos(*xPos, *yPos, letter);
    355     render_generic_char_at_pos(*xPos, *yPos, diacritic + 0xE7);
    356     *xPos += gDialogCharWidths[letter];
    357 }
    358 
    359 void render_uppercase_diacritic(s16 *xPos, s16 *yPos, u8 letter, u8 diacritic) {
    360     render_generic_char_at_pos(*xPos, *yPos, letter);
    361     render_generic_char_at_pos(*xPos, *yPos - 4, diacritic + 0xE3);
    362     *xPos += gDialogCharWidths[letter];
    363 }
    364 #endif // VERSION_EU
    365 
    366 #ifdef VERSION_CN
    367 void render_generic_char_cn(u16 c) {
    368     void **fontLUT = segmented_to_virtual(main_font_lut);
    369     void *packedTexture = segmented_to_virtual(fontLUT[c]);
    370     void *unpackedTexture = alloc_ia8_text_from_i1(packedTexture);
    371 
    372     gDPPipeSync(gDisplayListHead++);
    373     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture));
    374     gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings);
    375 }
    376 #endif
    377 
    378 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN)
    379 // First byte is length, rest is string
    380 struct MultiTextEntry {
    381     u8 str[5];
    382 };
    383 
    384 #define TEXT_THE_RAW ASCII_TO_DIALOG('t'), ASCII_TO_DIALOG('h'), ASCII_TO_DIALOG('e'), 0x00
    385 #define TEXT_YOU_RAW ASCII_TO_DIALOG('y'), ASCII_TO_DIALOG('o'), ASCII_TO_DIALOG('u'), 0x00
    386 
    387 enum MultiStringIDs { STRING_THE, STRING_YOU };
    388 
    389 /*
    390  * Place the multi-text string according to the ID passed. (US, EU)
    391  * 0: 'the'
    392  * 1: 'you'
    393  */
    394 #if defined(VERSION_US) || defined(VERSION_CN)
    395 void render_multi_text_string(s8 multiTextID)
    396 #elif defined(VERSION_EU)
    397 void render_multi_text_string(s16 *xPos, s16 *yPos, s8 multiTextID)
    398 #endif
    399 {
    400     s8 i;
    401     struct MultiTextEntry textLengths[2] = {
    402         { { 3, TEXT_THE_RAW } },
    403         { { 3, TEXT_YOU_RAW } },
    404     };
    405 
    406     for (i = 0; i < textLengths[multiTextID].str[0]; i++) {
    407 #if defined(VERSION_US) || defined(VERSION_CN)
    408         render_generic_char(textLengths[multiTextID].str[1 + i]);
    409         create_dl_translation_matrix(
    410             MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[textLengths[multiTextID].str[1 + i]]), 0.0f, 0.0f);
    411 #elif defined(VERSION_EU)
    412         render_generic_char_at_pos(*xPos, *yPos, textLengths[multiTextID].str[1 + i]);
    413         *xPos += gDialogCharWidths[textLengths[multiTextID].str[1 + i]];
    414 #endif
    415     }
    416 }
    417 #endif
    418 
    419 #if defined(VERSION_JP) || defined(VERSION_SH)
    420     #define CUR_CHAR str[strPos]
    421     #define MAX_STRING_WIDTH 18
    422     #define CHAR_WIDTH_SPACE 5.0f
    423     #define CHAR_WIDTH_DEFAULT 10.0f
    424 #elif defined(VERSION_CN)
    425     #define CUR_CHAR strChar
    426     #define MAX_STRING_WIDTH 18
    427     #define CHAR_WIDTH_SPACE gDialogCharWidths[DIALOG_CHAR_SPACE]
    428     #define CHAR_WIDTH_DEFAULT gDialogCharWidths[CUR_CHAR]
    429 #else
    430     #define CUR_CHAR str[strPos]
    431     #define MAX_STRING_WIDTH 16
    432     #define CHAR_WIDTH_SPACE gDialogCharWidths[DIALOG_CHAR_SPACE]
    433     #define CHAR_WIDTH_DEFAULT gDialogCharWidths[CUR_CHAR]
    434 #endif
    435 
    436 /**
    437  * Prints a generic white string.
    438  * In JP/EU a IA1 texture is used but in US a IA4 texture is used.
    439  */
    440 void print_generic_string(s16 x, s16 y, const u8 *str) {
    441     UNUSED s8 mark = DIALOG_MARK_NONE; // unused in EU
    442     s32 strPos = 0;
    443     u8 lineNum = 1;
    444 #ifdef VERSION_EU
    445     s16 xCoord = x;
    446     s16 yCoord = 240 - y;
    447 #endif
    448 #ifdef VERSION_CN
    449     s32 strChar;
    450 #endif
    451 
    452 #ifndef VERSION_EU
    453     create_dl_translation_matrix(MENU_MTX_PUSH, x, y, 0.0f);
    454 #endif
    455 
    456 #ifdef VERSION_CN
    457     while (str[strPos] != DIALOG_CHAR_SPECIAL_MODIFIER || str[strPos + 1] != DIALOG_CHAR_TERMINATOR) {
    458         strChar = str[strPos] << 8 | str[strPos + 1];
    459 #else
    460     while (str[strPos] != SPECIAL_CHAR(DIALOG_CHAR_TERMINATOR)) {
    461 #endif
    462         switch (CUR_CHAR) {
    463 #ifdef VERSION_CN
    464             case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE):
    465                 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
    466                 create_dl_translation_matrix(MENU_MTX_PUSH, x, y - (lineNum * MAX_STRING_WIDTH), 0.0f);
    467                 lineNum++;
    468                 break;
    469             case SPECIAL_CHAR(DIALOG_CHAR_STAR_OPEN): // ???
    470                 create_dl_translation_matrix(MENU_MTX_NOPUSH, 32.0f, 0.0f, 0.0f);
    471                 break;
    472 #endif
    473 #ifdef VERSION_EU
    474             case SPECIAL_CHAR(DIALOG_CHAR_SPACE):
    475                 xCoord += 5;
    476                 break;
    477             case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE):
    478                 yCoord += 16;
    479                 xCoord = x;
    480                 lineNum++;
    481                 break;
    482             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_GRAVE):
    483             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_CIRCUMFLEX):
    484             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_UMLAUT):
    485                 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('a'), CUR_CHAR & 0xF);
    486                 break;
    487             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_UMLAUT): // @bug grave and circumflex (0x64-0x65) are absent here
    488                 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('A'), CUR_CHAR & 0xF);
    489                 break;
    490             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_GRAVE):
    491             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_CIRCUMFLEX):
    492             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_UMLAUT):
    493             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_ACUTE):
    494                 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('e'), CUR_CHAR & 0xF);
    495                 break;
    496             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_GRAVE):
    497             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_CIRCUMFLEX):
    498             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_UMLAUT):
    499             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_ACUTE):
    500                 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('E'), CUR_CHAR & 0xF);
    501                 break;
    502             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_GRAVE):
    503             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_CIRCUMFLEX):
    504             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_UMLAUT):
    505                 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('u'), CUR_CHAR & 0xF);
    506                 break;
    507             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_UMLAUT): // @bug grave and circumflex (0x84-0x85) are absent here
    508                 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('U'), CUR_CHAR & 0xF);
    509                 break;
    510             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_CIRCUMFLEX):
    511             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_UMLAUT):
    512                 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('o'), CUR_CHAR & 0xF);
    513                 break;
    514             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_O_UMLAUT): // @bug circumflex (0x95) is absent here
    515                 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('O'), CUR_CHAR & 0xF);
    516                 break;
    517             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_CIRCUMFLEX):
    518             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_UMLAUT):
    519                 render_lowercase_diacritic(&xCoord, &yCoord, DIALOG_CHAR_I_NO_DIA, CUR_CHAR & 0xF);
    520                 break;
    521 #else // i.e. not EU
    522             case SPECIAL_CHAR(DIALOG_CHAR_DAKUTEN):
    523                 mark = DIALOG_MARK_DAKUTEN;
    524                 break;
    525             case SPECIAL_CHAR(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN):
    526                 mark = DIALOG_MARK_HANDAKUTEN;
    527                 break;
    528 #ifndef VERSION_CN
    529             case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE):
    530                 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
    531                 create_dl_translation_matrix(MENU_MTX_PUSH, x, y - (lineNum * MAX_STRING_WIDTH), 0.0f);
    532                 lineNum++;
    533                 break;
    534 #endif
    535             case SPECIAL_CHAR(DIALOG_CHAR_PERIOD):
    536                 create_dl_translation_matrix(MENU_MTX_PUSH, -2.0f, -5.0f, 0.0f);
    537                 render_generic_char(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN);
    538                 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
    539                 break;
    540 #endif
    541 
    542 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN)
    543             case SPECIAL_CHAR(DIALOG_CHAR_SLASH):
    544 #ifdef VERSION_EU
    545                 xCoord += gDialogCharWidths[DIALOG_CHAR_SPACE] * 2;
    546 #else
    547                 create_dl_translation_matrix(
    548                     MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * 2), 0.0f, 0.0f);
    549 #endif
    550                 break;
    551             case SPECIAL_CHAR(DIALOG_CHAR_MULTI_THE):
    552 #ifdef VERSION_EU
    553                 render_multi_text_string(&xCoord, &yCoord, STRING_THE);
    554 #else
    555                 render_multi_text_string(STRING_THE);
    556 #endif
    557                 break;
    558             case SPECIAL_CHAR(DIALOG_CHAR_MULTI_YOU):
    559 #ifdef VERSION_EU
    560                 render_multi_text_string(&xCoord, &yCoord, STRING_YOU);
    561 #else
    562                 render_multi_text_string(STRING_YOU);
    563 #endif
    564                 break;
    565 #endif
    566 
    567 #ifndef VERSION_EU
    568             case SPECIAL_CHAR(DIALOG_CHAR_SPACE):
    569                 create_dl_translation_matrix(MENU_MTX_NOPUSH, CHAR_WIDTH_SPACE, 0.0f, 0.0f);
    570                 break;
    571 #ifdef VERSION_JP
    572                 break; // ? needed to match
    573 #endif
    574 #endif
    575 
    576             default:
    577 #ifdef VERSION_EU
    578                 render_generic_char_at_pos(xCoord, yCoord, CUR_CHAR);
    579                 xCoord += gDialogCharWidths[CUR_CHAR];
    580 #else
    581 #ifdef VERSION_CN
    582                 if (strChar >= 0x0100) {
    583                     strChar = (strChar - 0xFF) * 2 + 0xFE;
    584                     render_generic_char_cn(strChar);
    585                     create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f);
    586                     render_generic_char_cn(strChar + 1);
    587                     create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f);
    588                     break;
    589                 }
    590 #endif
    591                 render_generic_char(CUR_CHAR);
    592                 if (mark != DIALOG_MARK_NONE) {
    593                     create_dl_translation_matrix(MENU_MTX_PUSH, 5.0f, 5.0f, 0.0f);
    594                     render_generic_char(DIALOG_CHAR_MARK_START + mark);
    595                     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
    596                     mark = DIALOG_MARK_NONE;
    597                 }
    598 
    599                 create_dl_translation_matrix(MENU_MTX_NOPUSH, CHAR_WIDTH_DEFAULT, 0.0f, 0.0f);
    600 #endif
    601 #ifndef VERSION_JP
    602                 break; // what an odd difference. US (and probably later) versions added a useless break here.
    603 #endif
    604         }
    605 
    606 #ifdef VERSION_CN
    607         strPos += 2;
    608 #else
    609         strPos++;
    610 #endif
    611     }
    612 
    613 #ifndef VERSION_EU
    614     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
    615 #endif
    616 }
    617 
    618 #ifdef VERSION_EU
    619 void print_hud_char_umlaut(s16 x, s16 y, u8 chr) {
    620     void **fontLUT = segmented_to_virtual(main_hud_lut);
    621 
    622     gDPPipeSync(gDisplayListHead++);
    623     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, fontLUT[chr]);
    624     gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block);
    625     gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 16) << 2, (y + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    626 
    627     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, fontLUT[GLYPH_UMLAUT]);
    628     gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block);
    629     gSPTextureRectangle(gDisplayListHead++, x << 2, (y - 4) << 2, (x + 16) << 2, (y + 12) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    630 }
    631 #endif
    632 
    633 /**
    634  * Prints a hud string depending of the hud table list defined.
    635  */
    636 void print_hud_lut_string(s8 hudLUT, s16 x, s16 y, const u8 *str) {
    637     s32 strPos = 0;
    638     void **hudLUT1 = segmented_to_virtual(menu_hud_lut); // Japanese Menu HUD Color font
    639     void **hudLUT2 = segmented_to_virtual(main_hud_lut); // 0-9 A-Z HUD Color Font
    640     u32 curX = x;
    641     u32 curY = y;
    642 
    643 #ifdef VERSION_CN
    644     u32 xOffset = 0;
    645 #else
    646     u32 xStride; // X separation
    647 
    648     if (hudLUT == HUD_LUT_JPMENU) {
    649         xStride = 16;
    650     } else { // HUD_LUT_GLOBAL
    651 #ifdef VERSION_JP
    652         xStride = 14;
    653 #else
    654         xStride = 12; //? Shindou uses this.
    655 #endif
    656     }
    657 #endif
    658 
    659     while (str[strPos] != GLOBAL_CHAR_TERMINATOR) {
    660 #ifndef VERSION_JP
    661         switch (str[strPos]) {
    662 #ifdef VERSION_CN
    663             case GLOBAL_CHAR_NEWLINE:
    664                 curY += 16;
    665                 curX -= xOffset;
    666                 break;
    667 #elif defined(VERSION_EU)
    668             case GLOBAL_CHAR_SPACE:
    669                 curX += xStride / 2;
    670                 break;
    671             case HUD_CHAR_A_UMLAUT:
    672                 print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('A'));
    673                 curX += xStride;
    674                 break;
    675             case HUD_CHAR_O_UMLAUT:
    676                 print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('O'));
    677                 curX += xStride;
    678                 break;
    679             case HUD_CHAR_U_UMLAUT:
    680                 print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('U'));
    681                 curX += xStride;
    682                 break;
    683 #else
    684             case GLOBAL_CHAR_SPACE:
    685                 curX += 8;
    686                 break;
    687 #endif
    688             default:
    689 #endif
    690                 gDPPipeSync(gDisplayListHead++);
    691 
    692 #ifndef VERSION_CN
    693                 if (hudLUT == HUD_LUT_JPMENU) {
    694                     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT1[str[strPos]]);
    695                 }
    696 
    697                 if (hudLUT == HUD_LUT_GLOBAL) {
    698 #endif
    699                     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT2[str[strPos]]);
    700 #ifndef VERSION_CN
    701                 }
    702 #endif
    703 
    704                 gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block);
    705                 gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 16) << 2,
    706                                     (curY + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    707 
    708 #ifdef VERSION_CN
    709                 if (str[strPos] < 0x3A) {
    710                     curX += 12;
    711                     xOffset += 12;
    712                 } else {
    713                     curX += 16;
    714                     xOffset += 16;
    715                 }
    716 #else
    717                 curX += xStride;
    718 #endif
    719 #ifndef VERSION_JP
    720         }
    721 #endif
    722         strPos++;
    723     }
    724 }
    725 
    726 #ifdef VERSION_EU
    727 void print_menu_char_umlaut(s16 x, s16 y, u8 chr) {
    728     void **fontLUT = segmented_to_virtual(menu_font_lut);
    729 
    730     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[chr]);
    731     gDPLoadSync(gDisplayListHead++);
    732     gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES));
    733     gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 8) << 2, (y + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    734 
    735     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[DIALOG_CHAR_UMLAUT]);
    736     gDPLoadSync(gDisplayListHead++);
    737     gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES));
    738     gSPTextureRectangle(gDisplayListHead++, x << 2, (y - 4) << 2, (x + 8) << 2, (y + 4) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    739 }
    740 #endif
    741 
    742 void print_menu_generic_string(s16 x, s16 y, const u8 *str) {
    743     UNUSED s8 mark = DIALOG_MARK_NONE; // unused in EU
    744     s32 strPos = 0;
    745     u32 curX = x;
    746     u32 curY = y;
    747     void **fontLUT = segmented_to_virtual(menu_font_lut);
    748 
    749     while (str[strPos] != DIALOG_CHAR_TERMINATOR) {
    750         switch (str[strPos]) {
    751 #ifdef VERSION_EU
    752             case DIALOG_CHAR_UPPER_A_UMLAUT:
    753                 print_menu_char_umlaut(curX, curY, ASCII_TO_DIALOG('A'));
    754                 curX += gDialogCharWidths[str[strPos]];
    755                 break;
    756             case DIALOG_CHAR_UPPER_U_UMLAUT:
    757                 print_menu_char_umlaut(curX, curY, ASCII_TO_DIALOG('U'));
    758                 curX += gDialogCharWidths[str[strPos]];
    759                 break;
    760             case DIALOG_CHAR_UPPER_O_UMLAUT:
    761                 print_menu_char_umlaut(curX, curY, ASCII_TO_DIALOG('O'));
    762                 curX += gDialogCharWidths[str[strPos]];
    763                 break;
    764 #else
    765             case DIALOG_CHAR_DAKUTEN:
    766                 mark = DIALOG_MARK_DAKUTEN;
    767                 break;
    768             case DIALOG_CHAR_PERIOD_OR_HANDAKUTEN:
    769                 mark = DIALOG_MARK_HANDAKUTEN;
    770                 break;
    771 #endif
    772             case DIALOG_CHAR_SPACE:
    773                 curX += 4;
    774                 break;
    775             default:
    776                 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[str[strPos]]);
    777                 gDPLoadSync(gDisplayListHead++);
    778                 gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES));
    779                 gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 8) << 2,
    780                                     (curY + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    781 
    782 #ifndef VERSION_EU
    783                 if (mark != DIALOG_MARK_NONE) {
    784                     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[DIALOG_CHAR_MARK_START + mark]);
    785                     gDPLoadSync(gDisplayListHead++);
    786                     gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES));
    787                     gSPTextureRectangle(gDisplayListHead++, (curX + 6) << 2, (curY - 7) << 2,
    788                                         (curX + 6 + 8) << 2, (curY - 7 + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    789 
    790                     mark = DIALOG_MARK_NONE;
    791                 }
    792 #endif
    793 #if defined(VERSION_JP) || defined(VERSION_SH) || defined(VERSION_CN)
    794                 curX += 9;
    795 #else
    796                 curX += gDialogCharWidths[str[strPos]];
    797 #endif
    798         }
    799         strPos++;
    800     }
    801 }
    802 
    803 void print_credits_string(s16 x, s16 y, const u8 *str) {
    804     s32 strPos = 0;
    805     void **fontLUT = segmented_to_virtual(main_credits_font_lut);
    806     u32 curX = x;
    807     u32 curY = y;
    808 
    809     gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0,
    810                 G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD);
    811     gDPTileSync(gDisplayListHead++);
    812     gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 2, 0, G_TX_RENDERTILE, 0,
    813                 G_TX_CLAMP, 3, G_TX_NOLOD, G_TX_CLAMP, 3, G_TX_NOLOD);
    814     gDPSetTileSize(gDisplayListHead++, G_TX_RENDERTILE, 0, 0, (8 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC);
    815 
    816     while (str[strPos] != GLOBAL_CHAR_TERMINATOR) {
    817         switch (str[strPos]) {
    818             case GLOBAL_CHAR_SPACE:
    819                 curX += 4;
    820                 break;
    821             default:
    822                 gDPPipeSync(gDisplayListHead++);
    823                 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, fontLUT[str[strPos]]);
    824                 gDPLoadSync(gDisplayListHead++);
    825                 gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_16b_BYTES));
    826                 gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 8) << 2,
    827                                     (curY + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
    828                 curX += 7;
    829                 break;
    830         }
    831         strPos++;
    832     }
    833 }
    834 
    835 void handle_menu_scrolling(s8 scrollDirection, s8 *currentIndex, s8 minIndex, s8 maxIndex) {
    836     u8 index = 0;
    837 
    838     if (scrollDirection == MENU_SCROLL_VERTICAL) {
    839         if (gPlayer3Controller->rawStickY > 60) {
    840             index++;
    841         }
    842 
    843         if (gPlayer3Controller->rawStickY < -60) {
    844             index += 2;
    845         }
    846     } else if (scrollDirection == MENU_SCROLL_HORIZONTAL) {
    847         if (gPlayer3Controller->rawStickX > 60) {
    848             index += 2;
    849         }
    850 
    851         if (gPlayer3Controller->rawStickX < -60) {
    852             index++;
    853         }
    854     }
    855 
    856     if (((index ^ gMenuHoldKeyIndex) & index) == 2) {
    857         if (*currentIndex == maxIndex) {
    858             //! Probably originally a >=, but later replaced with an == and an else statement.
    859             *currentIndex = maxIndex;
    860         } else {
    861             play_sound(SOUND_MENU_CHANGE_SELECT, gGlobalSoundSource);
    862             (*currentIndex)++;
    863         }
    864     }
    865 
    866     if (((index ^ gMenuHoldKeyIndex) & index) == 1) {
    867         if (*currentIndex == minIndex) {
    868             // Same applies to here as above
    869         } else {
    870             play_sound(SOUND_MENU_CHANGE_SELECT, gGlobalSoundSource);
    871             (*currentIndex)--;
    872         }
    873     }
    874 
    875     if (gMenuHoldKeyTimer == 10) {
    876         gMenuHoldKeyTimer = 8;
    877         gMenuHoldKeyIndex = 0;
    878     } else {
    879         gMenuHoldKeyTimer++;
    880         gMenuHoldKeyIndex = index;
    881     }
    882 
    883     if ((index & 3) == 0) {
    884         gMenuHoldKeyTimer = 0;
    885     }
    886 }
    887 
    888 // EU has both get_str_x_pos_from_center and get_str_x_pos_from_center_scale
    889 // JP, US, Shindou and iQue only implement one or the other
    890 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN)
    891 s16 get_str_x_pos_from_center(s16 centerPos, u8 *str, UNUSED f32 scale) {
    892     s16 strPos = 0;
    893     f32 spacesWidth = 0.0f;
    894 
    895 #ifdef VERSION_CN
    896     while (!(str[strPos] == DIALOG_CHAR_SPECIAL_MODIFIER && str[strPos + 1] == DIALOG_CHAR_TERMINATOR)) {
    897         u16 character = (str[strPos] << 8) | str[strPos + 1];
    898         if (character <= 0xFF) {
    899             spacesWidth += gDialogCharWidths[character];
    900         } else {
    901             spacesWidth += 16.0;
    902         }
    903         strPos += 2;
    904     }
    905 #else
    906     while (str[strPos] != DIALOG_CHAR_TERMINATOR) {
    907         spacesWidth += gDialogCharWidths[str[strPos]];
    908         strPos++;
    909     }
    910 #endif
    911     // return the x position of where the string starts as half the string's
    912     // length from the position of the provided center.
    913     return (s16)(centerPos - (s16)(spacesWidth / 2.0));
    914 }
    915 #endif
    916 
    917 #if defined(VERSION_JP) || defined(VERSION_EU) || defined(VERSION_SH)
    918 s16 get_str_x_pos_from_center_scale(s16 centerPos, u8 *str, f32 scale) {
    919     s16 strPos = 0;
    920     f32 charsWidth = 0.0f;
    921     f32 spacesWidth = 0.0f;
    922 
    923     while (str[strPos] != DIALOG_CHAR_TERMINATOR) {
    924         //! EU checks for dakuten and handakuten despite dialog code unable to handle it
    925         if (str[strPos] == DIALOG_CHAR_SPACE) {
    926             spacesWidth += 1.0;
    927         } else if (str[strPos] != DIALOG_CHAR_DAKUTEN
    928                    && str[strPos] != DIALOG_CHAR_PERIOD_OR_HANDAKUTEN) {
    929             charsWidth += 1.0;
    930         }
    931         strPos++;
    932     }
    933     // return the x position of where the string starts as half the string's
    934     // length from the position of the provided center.
    935     return (f32) centerPos - (scale * (charsWidth / 2.0)) - ((scale / 2.0) * (spacesWidth / 2.0));
    936 }
    937 #endif
    938 
    939 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN)
    940 s16 get_string_width(u8 *str) {
    941     s16 strPos = 0;
    942     s16 width = 0;
    943 
    944 #ifdef VERSION_CN
    945     while (!(str[strPos] == DIALOG_CHAR_SPECIAL_MODIFIER && str[strPos + 1] == DIALOG_CHAR_TERMINATOR)) {
    946         u16 character = (str[strPos] << 8) | str[strPos + 1];
    947         if (character <= 0xFF) {
    948             width += gDialogCharWidths[character];
    949         } else {
    950             width += 16;
    951         }
    952         strPos += 2;
    953     }
    954 #else
    955     while (str[strPos] != DIALOG_CHAR_TERMINATOR) {
    956         width += gDialogCharWidths[str[strPos]];
    957         strPos++;
    958     }
    959 #endif
    960     return width;
    961 }
    962 #endif
    963 
    964 u8 gHudSymCoin[] = { GLYPH_COIN, GLYPH_SPACE };
    965 u8 gHudSymX[] = { GLYPH_MULTIPLY, GLYPH_SPACE };
    966 
    967 void print_hud_my_score_coins(s32 useCourseCoinScore, s8 fileIndex, s8 courseIndex, s16 x, s16 y) {
    968     u8 strNumCoins[4];
    969     s16 numCoins;
    970 
    971     if (!useCourseCoinScore) {
    972         numCoins = (u16)(save_file_get_max_coin_score(courseIndex) & 0xFFFF);
    973     } else {
    974         numCoins = save_file_get_course_coin_score(fileIndex, courseIndex);
    975     }
    976 
    977     if (numCoins != 0) {
    978         print_hud_lut_string(HUD_LUT_GLOBAL, x, y, gHudSymCoin);
    979         print_hud_lut_string(HUD_LUT_GLOBAL, x + 16, y, gHudSymX);
    980         int_to_str(numCoins, strNumCoins);
    981         print_hud_lut_string(HUD_LUT_GLOBAL, x + 32, y, strNumCoins);
    982     }
    983 }
    984 
    985 void print_hud_my_score_stars(s8 fileIndex, s8 courseIndex, s16 x, s16 y) {
    986     u8 strStarCount[4];
    987     s16 starCount;
    988     u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE };
    989     UNUSED u16 unused;
    990     u8 textSymX[] = { GLYPH_MULTIPLY, GLYPH_SPACE };
    991 
    992     starCount = save_file_get_course_star_count(fileIndex, courseIndex);
    993 
    994     if (starCount != 0) {
    995         print_hud_lut_string(HUD_LUT_GLOBAL, x, y, textSymStar);
    996         print_hud_lut_string(HUD_LUT_GLOBAL, x + 16, y, textSymX);
    997         int_to_str(starCount, strStarCount);
    998         print_hud_lut_string(HUD_LUT_GLOBAL, x + 32, y, strStarCount);
    999     }
   1000 }
   1001 
   1002 void int_to_str(s32 num, u8 *dst) {
   1003     s32 digit1;
   1004     s32 digit2;
   1005     s32 digit3;
   1006 
   1007     s8 pos = 0;
   1008 
   1009     if (num > 999) {
   1010         dst[0] = 0x00; dst[1] = DIALOG_CHAR_TERMINATOR;
   1011         return;
   1012     }
   1013 
   1014     digit1 = num / 100;
   1015     digit2 = (num - digit1 * 100) / 10;
   1016     digit3 = (num - digit1 * 100) - (digit2 * 10);
   1017 
   1018     if (digit1 != 0) {
   1019         dst[pos] = digit1; pos++;
   1020     }
   1021 
   1022     if (digit2 != 0 || digit1 != 0) {
   1023         dst[pos] = digit2; pos++;
   1024     }
   1025 
   1026     dst[pos] = digit3;
   1027     pos++;
   1028     dst[pos] = DIALOG_CHAR_TERMINATOR;
   1029 }
   1030 
   1031 #ifdef VERSION_CN
   1032 void int_to_str_2(s32 num, u8 *dst) {
   1033     s32 digit1;
   1034     s32 digit2;
   1035     s32 digit3;
   1036 
   1037     s8 pos = 0;
   1038 
   1039     if (num > 999) {
   1040         dst[0] = 0x00;
   1041         dst[1] = 0x00;
   1042         dst[2] = DIALOG_CHAR_SPECIAL_MODIFIER;
   1043         dst[3] = DIALOG_CHAR_TERMINATOR;
   1044         return;
   1045     }
   1046 
   1047     digit1 = num / 100;
   1048     digit2 = (num - digit1 * 100) / 10;
   1049     digit3 = (num - digit1 * 100) - (digit2 * 10);
   1050 
   1051     if (digit1 != 0) {
   1052         dst[pos] = 0x00;
   1053         pos++;
   1054         dst[pos] = digit1;
   1055         pos++;
   1056     }
   1057 
   1058     if (digit2 != 0 || digit1 != 0) {
   1059         dst[pos] = 0x00;
   1060         pos++;
   1061         dst[pos] = digit2;
   1062         pos++;
   1063     }
   1064 
   1065     dst[pos] = 0x00;
   1066     pos++;
   1067     dst[pos] = digit3;
   1068     pos++;
   1069     dst[pos] = DIALOG_CHAR_SPECIAL_MODIFIER;
   1070     pos++;
   1071     dst[pos] = DIALOG_CHAR_TERMINATOR;
   1072 }
   1073 #endif
   1074 
   1075 s16 get_dialog_id(void) {
   1076     return gDialogID;
   1077 }
   1078 
   1079 void create_dialog_box(s16 dialog) {
   1080     if (gDialogID == DIALOG_NONE) {
   1081         gDialogID = dialog;
   1082         gDialogBoxType = DIALOG_TYPE_ROTATE;
   1083     }
   1084 }
   1085 
   1086 void create_dialog_box_with_var(s16 dialog, s32 dialogVar) {
   1087     if (gDialogID == DIALOG_NONE) {
   1088         gDialogID = dialog;
   1089         gDialogVariable = dialogVar;
   1090         gDialogBoxType = DIALOG_TYPE_ROTATE;
   1091     }
   1092 }
   1093 
   1094 void create_dialog_inverted_box(s16 dialog) {
   1095     if (gDialogID == DIALOG_NONE) {
   1096         gDialogID = dialog;
   1097         gDialogBoxType = DIALOG_TYPE_ZOOM;
   1098     }
   1099 }
   1100 
   1101 void create_dialog_box_with_response(s16 dialog) {
   1102     if (gDialogID == DIALOG_NONE) {
   1103         gDialogID = dialog;
   1104         gDialogBoxType = DIALOG_TYPE_ROTATE;
   1105         gDialogWithChoice = TRUE;
   1106     }
   1107 }
   1108 
   1109 void reset_dialog_render_state(void) {
   1110     level_set_transition(0, NULL);
   1111 
   1112     if (gDialogBoxType == DIALOG_TYPE_ZOOM) {
   1113         trigger_cutscene_dialog(2);
   1114     }
   1115 
   1116     gDialogBoxScale = DIALOG_BOX_SCALE_DEFAULT;
   1117     gDialogBoxAngle = DIALOG_BOX_ANGLE_DEFAULT;
   1118     gMenuState = MENU_STATE_DEFAULT;
   1119     gDialogID = DIALOG_NONE;
   1120     gDialogPageStartStrIndex = 0;
   1121     gDialogWithChoice = FALSE;
   1122     gNextDialogPageStartStrIndex = 0;
   1123     gDialogResponse = DIALOG_RESPONSE_NONE;
   1124 }
   1125 
   1126 #if defined(VERSION_JP) || defined(VERSION_SH)
   1127     #define X_VAL1 -5.0f
   1128     #define Y_VAL1 2.0
   1129     #define Y_VAL2 4
   1130 #else
   1131     #define X_VAL1 -7.0f
   1132     #define Y_VAL1 5.0
   1133     #define Y_VAL2 5.0f
   1134 #endif
   1135 
   1136 void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) {
   1137 #ifndef VERSION_CN
   1138     UNUSED u8 filler[4];
   1139 #endif
   1140 
   1141     create_dl_translation_matrix(MENU_MTX_NOPUSH, dialog->leftOffset, dialog->width, 0);
   1142 
   1143     switch (gDialogBoxType) {
   1144         case DIALOG_TYPE_ROTATE: // Renders a dialog black box with zoom and rotation
   1145             if (gMenuState == MENU_STATE_DIALOG_OPENING || gMenuState == MENU_STATE_DIALOG_CLOSING) {
   1146                 create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f);
   1147                 // convert the speed into angle
   1148                 create_dl_rotation_matrix(MENU_MTX_NOPUSH, gDialogBoxAngle * 4.0f, 0, 0, 1.0f);
   1149             }
   1150             gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 150);
   1151             break;
   1152         case DIALOG_TYPE_ZOOM: // Renders a dialog white box with zoom
   1153             if (gMenuState == MENU_STATE_DIALOG_OPENING || gMenuState == MENU_STATE_DIALOG_CLOSING) {
   1154                 create_dl_translation_matrix(MENU_MTX_NOPUSH, 65.0 - (65.0 / gDialogBoxScale),
   1155                                               (40.0 / gDialogBoxScale) - 40, 0);
   1156                 create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f);
   1157             }
   1158             gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 150);
   1159             break;
   1160     }
   1161 
   1162     create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL1, Y_VAL1, 0);
   1163 #ifdef VERSION_CN
   1164     create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.1f, ((f32)(linesPerBox + 1) / Y_VAL2) + 0.1, 1.0f);
   1165 #else
   1166     create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.1f, ((f32) linesPerBox / Y_VAL2) + 0.1, 1.0f);
   1167 #endif
   1168 
   1169     gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
   1170     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   1171 }
   1172 
   1173 void change_and_flash_dialog_text_color_lines(s8 colorMode, s8 lineNum) {
   1174     u8 color;
   1175 
   1176     if (colorMode == 1) { // unused
   1177         if (lineNum == 1) {
   1178             gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
   1179         } else {
   1180             if (lineNum == gMenuLineNum) {
   1181                 color = sins(gMenuTextColorTransTimer) * 50.0f + 200.0f;
   1182                 gDPSetEnvColor(gDisplayListHead++, color, color, color, 255);
   1183             } else {
   1184                 gDPSetEnvColor(gDisplayListHead++, 200, 200, 200, 255);
   1185             }
   1186         }
   1187     } else {
   1188         switch (gDialogBoxType) {
   1189             case DIALOG_TYPE_ROTATE:
   1190                 break;
   1191             case DIALOG_TYPE_ZOOM:
   1192                 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
   1193                 break;
   1194         }
   1195     }
   1196 }
   1197 
   1198 #ifdef VERSION_EU
   1199 void render_generic_dialog_char_at_pos(struct DialogEntry *dialog, s16 x, s16 y, u8 c) {
   1200     s16 width = (8.0 - (gDialogBoxScale * 0.8));
   1201     s16 height = (16.0 - (gDialogBoxScale * 0.8));
   1202     s16 tmpX = (dialog->leftOffset + (65.0 - (65.0 / gDialogBoxScale)));
   1203     s16 tmpY = ((240 - dialog->width) - ((40.0 / gDialogBoxScale) - 40));
   1204     s16 xCoord = (tmpX + (x / gDialogBoxScale));
   1205     s16 yCoord = (tmpY + (y / gDialogBoxScale));
   1206 
   1207     void **fontLUT = segmented_to_virtual(main_font_lut);
   1208     void *packedTexture = segmented_to_virtual(fontLUT[c]);
   1209     void *unpackedTexture = alloc_ia4_tex_from_i1(packedTexture, 8, 8);
   1210 
   1211     gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture));
   1212     gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings);
   1213     gSPTextureRectangleFlip(gDisplayListHead++, xCoord << 2, (yCoord - height) << 2,
   1214                             (xCoord + width) << 2, yCoord << 2, G_TX_RENDERTILE, 8 << 6, 4 << 6, 1 << 10, 1 << 10);
   1215 }
   1216 #endif
   1217 
   1218 #if defined(VERSION_JP) || defined(VERSION_SH)
   1219     #define X_VAL3 5.0f
   1220     #define Y_VAL3 20
   1221 #elif defined(VERSION_CN)
   1222     #define X_VAL3 0.0f
   1223     #define Y_VAL3 20
   1224 #else
   1225     #define X_VAL3 0.0f
   1226     #define Y_VAL3 16
   1227 #endif
   1228 
   1229 #ifdef VERSION_EU
   1230 void handle_dialog_scroll_page_state(s8 lineNum, s8 totalLines, s8 *pageState, s8 *xMatrix)
   1231 #else
   1232 void handle_dialog_scroll_page_state(s8 lineNum, s8 totalLines, s8 *pageState, s8 *xMatrix, s16 *linePos)
   1233 #endif
   1234 {
   1235 #ifndef VERSION_EU
   1236     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   1237 #endif
   1238 
   1239     if (lineNum == totalLines) {
   1240         *pageState = DIALOG_PAGE_STATE_SCROLL;
   1241         return;
   1242     }
   1243 #ifdef VERSION_EU
   1244     gDialogY += 16;
   1245 #else
   1246     create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL3, 2 - (lineNum * Y_VAL3), 0);
   1247 
   1248     *linePos = 0;
   1249 #endif
   1250     *xMatrix = 1;
   1251 }
   1252 
   1253 #if defined(VERSION_JP) || defined(VERSION_SH)
   1254 void adjust_pos_and_print_period_char(s8 *xMatrix, s16 *linePos) {
   1255     if (*linePos != 0) {
   1256         create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * *xMatrix, 0, 0);
   1257     }
   1258 
   1259     create_dl_translation_matrix(MENU_MTX_PUSH, -2.0f, -5.0f, 0);
   1260     render_generic_char(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN);
   1261 
   1262     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   1263 
   1264     (*linePos)++;
   1265     *xMatrix = 1;
   1266 }
   1267 #endif
   1268 
   1269 #ifdef VERSION_EU
   1270 void render_star_count_dialog_text(struct DialogEntry *dialog, s8 *linePos)
   1271 #else
   1272 void render_star_count_dialog_text(s8 *xMatrix, s16 *linePos)
   1273 #endif
   1274 {
   1275     s8 tensDigit = gDialogVariable / 10;
   1276     s8 onesDigit = gDialogVariable - (tensDigit * 10); // remainder
   1277 
   1278     if (tensDigit != 0) {
   1279 #if defined(VERSION_JP) || defined(VERSION_SH)
   1280         create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * *xMatrix, 0, 0);
   1281         render_generic_char(tensDigit);
   1282 #elif defined(VERSION_US) || defined(VERSION_CN)
   1283         if (*xMatrix != 1) {
   1284             create_dl_translation_matrix(
   1285                 MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * *xMatrix), 0, 0);
   1286         }
   1287 
   1288         render_generic_char(tensDigit);
   1289         create_dl_translation_matrix(MENU_MTX_NOPUSH, (f32) gDialogCharWidths[tensDigit], 0, 0);
   1290         *xMatrix = 1;
   1291         (*linePos)++;
   1292 #elif defined(VERSION_EU)
   1293         render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, tensDigit);
   1294         gDialogX += gDialogCharWidths[tensDigit];
   1295         *linePos = 1;
   1296 #endif
   1297     }
   1298 #ifndef VERSION_EU
   1299     else {
   1300 #if defined(VERSION_JP) || defined(VERSION_SH)
   1301         (*xMatrix)++;
   1302 #endif
   1303     }
   1304 #endif
   1305 
   1306 #ifndef VERSION_EU
   1307 #if defined(VERSION_JP) || defined(VERSION_SH)
   1308     create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * *xMatrix, 0, 0);
   1309     render_generic_char(onesDigit);
   1310 #elif defined(VERSION_US) || defined(VERSION_CN)
   1311     if (*xMatrix != 1) {
   1312         create_dl_translation_matrix(
   1313             MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * (*xMatrix - 1)), 0, 0);
   1314     }
   1315 
   1316     render_generic_char(onesDigit);
   1317     create_dl_translation_matrix(MENU_MTX_NOPUSH, (f32) gDialogCharWidths[onesDigit], 0, 0);
   1318 #endif
   1319     (*linePos)++;
   1320     *xMatrix = 1;
   1321 #else // VERSION_EU
   1322     render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, onesDigit);
   1323     gDialogX += gDialogCharWidths[onesDigit];
   1324     *linePos = 1;
   1325 #endif
   1326 }
   1327 
   1328 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN)
   1329 #ifdef VERSION_EU
   1330 void render_multi_text_string_lines(s8 multiTextId, s8 lineNum, s8 linesPerBox, UNUSED s8 xMatrix, s8 lowerBound, struct DialogEntry *dialog)
   1331 #else
   1332 void render_multi_text_string_lines(s8 multiTextId, s8 lineNum, s16 *linePos, s8 linesPerBox, s8 xMatrix, s8 lowerBound)
   1333 #endif
   1334 {
   1335     s8 i;
   1336     struct MultiTextEntry textLengths[2] = {
   1337         { { 3, TEXT_THE_RAW } },
   1338         { { 3, TEXT_YOU_RAW } },
   1339     };
   1340 
   1341     if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) {
   1342 #ifndef VERSION_EU
   1343         if (*linePos != 0 || xMatrix != 1) {
   1344             create_dl_translation_matrix(
   1345                 MENU_MTX_NOPUSH, (gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1)), 0, 0);
   1346         }
   1347 #endif
   1348         for (i = 0; i < textLengths[multiTextId].str[0]; i++) {
   1349 #ifdef VERSION_EU
   1350             render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, textLengths[multiTextId].str[1 + i]);
   1351             gDialogX += gDialogCharWidths[textLengths[multiTextId].str[1 + i]];
   1352 #else
   1353             render_generic_char(textLengths[multiTextId].str[1 + i]);
   1354             create_dl_translation_matrix(
   1355                 MENU_MTX_NOPUSH, (gDialogCharWidths[textLengths[multiTextId].str[1 + i]]), 0, 0);
   1356 #endif
   1357         }
   1358     }
   1359 
   1360 #ifndef VERSION_EU
   1361     linePos += textLengths[multiTextId].str[0];
   1362 #endif
   1363 }
   1364 #endif
   1365 
   1366 #ifdef VERSION_EU
   1367 void render_dialog_lowercase_diacritic(struct DialogEntry *dialog, u8 chr, u8 diacritic) {
   1368     render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, chr);
   1369     render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, diacritic + 0xE7);
   1370     gDialogX += gDialogCharWidths[chr];
   1371 }
   1372 
   1373 void render_dialog_uppercase_diacritic(struct DialogEntry *dialog, u8 chr, u8 diacritic) {
   1374     render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, chr);
   1375     render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY - 4, diacritic + 0xE3);
   1376     gDialogX += gDialogCharWidths[chr];
   1377 }
   1378 #endif
   1379 
   1380 u32 ensure_nonnegative(s16 value) {
   1381     if (value < 0) {
   1382         value = 0;
   1383     }
   1384 
   1385     return value;
   1386 }
   1387 
   1388 #ifdef VERSION_JP
   1389 void handle_dialog_text_and_pages(s8 colorMode, struct DialogEntry *dialog)
   1390 #else
   1391 void handle_dialog_text_and_pages(s8 colorMode, struct DialogEntry *dialog, s8 lowerBound)
   1392 #endif
   1393 {
   1394     UNUSED u64 filler;
   1395 #ifdef VERSION_EU
   1396     s16 startY = 14;
   1397 #endif
   1398 #ifdef VERSION_CN
   1399     s32 strChar;
   1400 #else
   1401     u8 strChar;
   1402 #endif
   1403     u8 *str = segmented_to_virtual(dialog->str);
   1404     s8 lineNum = 1;
   1405     s8 totalLines;
   1406     s8 pageState = DIALOG_PAGE_STATE_NONE;
   1407     UNUSED s8 mark = DIALOG_MARK_NONE; // only used in JP and SH
   1408     s8 xMatrix = 1;
   1409     s8 linesPerBox = dialog->linesPerBox;
   1410     s16 strIndex;
   1411 #ifndef VERSION_EU
   1412     s16 linePos = 0;
   1413 #endif
   1414 
   1415     if (gMenuState == MENU_STATE_DIALOG_SCROLLING) {
   1416         // If scrolling, consider the number of lines for both
   1417         // the current page and the page being scrolled to.
   1418 #ifdef VERSION_CN
   1419         totalLines = linesPerBox * 2 - 1;
   1420 #else
   1421         totalLines = linesPerBox * 2 + 1;
   1422 #endif
   1423     } else {
   1424 #ifdef VERSION_CN
   1425         totalLines = linesPerBox;
   1426 #else
   1427         totalLines = linesPerBox + 1;
   1428 #endif
   1429     }
   1430 
   1431     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   1432 
   1433     strIndex = gDialogPageStartStrIndex;
   1434 
   1435 #ifdef VERSION_EU
   1436     gDialogX = 0;
   1437     gDialogY = startY;
   1438 #endif
   1439 
   1440     if (gMenuState == MENU_STATE_DIALOG_SCROLLING) {
   1441 #ifdef VERSION_EU
   1442         gDialogY -= gDialogScrollOffsetY;
   1443 #else
   1444         create_dl_translation_matrix(MENU_MTX_NOPUSH, 0, (f32) gDialogScrollOffsetY, 0);
   1445 #endif
   1446     }
   1447 
   1448 #ifndef VERSION_EU
   1449     create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL3, 2 - lineNum * Y_VAL3, 0);
   1450 #endif
   1451 
   1452     while (pageState == DIALOG_PAGE_STATE_NONE) {
   1453         change_and_flash_dialog_text_color_lines(colorMode, lineNum);
   1454 
   1455 #ifdef VERSION_CN
   1456         strChar = str[strIndex] << 8 | str[strIndex + 1];
   1457 #else
   1458         strChar = str[strIndex];
   1459 #endif
   1460 
   1461         switch (strChar) {
   1462             case SPECIAL_CHAR(DIALOG_CHAR_TERMINATOR):
   1463                 pageState = DIALOG_PAGE_STATE_END;
   1464 #ifndef VERSION_EU
   1465                 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   1466 #endif
   1467                 break;
   1468             case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE):
   1469                 lineNum++;
   1470 #ifdef VERSION_EU
   1471                 handle_dialog_scroll_page_state(lineNum, totalLines, &pageState, &xMatrix);
   1472                 gDialogX = 0;
   1473 #else
   1474                 handle_dialog_scroll_page_state(lineNum, totalLines, &pageState, &xMatrix, &linePos);
   1475 #ifdef VERSION_SH
   1476                 mark = DIALOG_MARK_NONE;
   1477 #endif
   1478 #endif
   1479                 break;
   1480 
   1481 #ifdef VERSION_EU
   1482             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_GRAVE):
   1483             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_CIRCUMFLEX):
   1484             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_UMLAUT):
   1485                 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('a'), strChar & 0xF);
   1486                 break;
   1487             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_GRAVE):
   1488             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_CIRCUMFLEX):
   1489             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_UMLAUT):
   1490                 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('A'), strChar & 0xF);
   1491                 break;
   1492             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_GRAVE):
   1493             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_CIRCUMFLEX):
   1494             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_UMLAUT):
   1495             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_ACUTE):
   1496                 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('e'), strChar & 0xF);
   1497                 break;
   1498             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_GRAVE):
   1499             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_CIRCUMFLEX):
   1500             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_UMLAUT):
   1501             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_ACUTE):
   1502                 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('E'), strChar & 0xF);
   1503                 break;
   1504             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_GRAVE):
   1505             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_CIRCUMFLEX):
   1506             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_UMLAUT):
   1507                 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('u'), strChar & 0xF);
   1508                 break;
   1509             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_GRAVE):
   1510             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_CIRCUMFLEX):
   1511             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_UMLAUT):
   1512                 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('U'), strChar & 0xF);
   1513                 break;
   1514             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_CIRCUMFLEX):
   1515             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_UMLAUT):
   1516                 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('o'), strChar & 0xF);
   1517                 break;
   1518             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_O_CIRCUMFLEX):
   1519             case SPECIAL_CHAR(DIALOG_CHAR_UPPER_O_UMLAUT):
   1520                 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('O'), strChar & 0xF);
   1521                 break;
   1522             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_CIRCUMFLEX):
   1523             case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_UMLAUT):
   1524                 render_dialog_lowercase_diacritic(dialog, DIALOG_CHAR_I_NO_DIA, strChar & 0xF);
   1525                 break;
   1526 #else
   1527             case SPECIAL_CHAR(DIALOG_CHAR_DAKUTEN):
   1528 #ifndef VERSION_CN
   1529                 mark = DIALOG_MARK_DAKUTEN;
   1530 #endif
   1531                 break;
   1532             case SPECIAL_CHAR(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN):
   1533 #ifndef VERSION_CN
   1534                 mark = DIALOG_MARK_HANDAKUTEN;
   1535 #endif
   1536                 break;
   1537 #endif
   1538 
   1539 #ifdef VERSION_CN
   1540             case SPECIAL_CHAR(DIALOG_CHAR_STAR_OPEN): // ???
   1541                 xMatrix += 2;
   1542                 linePos += 2;
   1543                 break;
   1544 #endif
   1545 
   1546             case SPECIAL_CHAR(DIALOG_CHAR_SPACE):
   1547 #ifdef VERSION_EU
   1548                 gDialogX += gDialogCharWidths[DIALOG_CHAR_SPACE];
   1549 #else
   1550 #if defined(VERSION_JP) || defined(VERSION_SH)
   1551                 if (linePos != 0) {
   1552 #endif
   1553                     xMatrix++;
   1554 #if defined(VERSION_JP) || defined(VERSION_SH)
   1555                 }
   1556 #endif
   1557                 linePos++;
   1558 #endif
   1559                 break;
   1560 
   1561 #if defined(VERSION_JP) || defined(VERSION_SH)
   1562             case SPECIAL_CHAR(DIALOG_CHAR_PERIOD):
   1563                 adjust_pos_and_print_period_char(&xMatrix, &linePos);
   1564                 break;
   1565 #else
   1566             case SPECIAL_CHAR(DIALOG_CHAR_SLASH):
   1567 #ifdef VERSION_EU
   1568                 gDialogX += gDialogCharWidths[DIALOG_CHAR_SPACE] * 2;
   1569 #else
   1570                 xMatrix += 2;
   1571                 linePos += 2;
   1572 #endif
   1573                 break;
   1574             case SPECIAL_CHAR(DIALOG_CHAR_MULTI_THE):
   1575 #ifdef VERSION_EU
   1576                 render_multi_text_string_lines(STRING_THE, lineNum, linesPerBox, xMatrix, lowerBound, dialog);
   1577 #else
   1578                 render_multi_text_string_lines(STRING_THE, lineNum, &linePos, linesPerBox, xMatrix, lowerBound);
   1579 #endif
   1580                 xMatrix = 1;
   1581                 break;
   1582             case SPECIAL_CHAR(DIALOG_CHAR_MULTI_YOU):
   1583 #ifdef VERSION_EU
   1584                 render_multi_text_string_lines(STRING_YOU, lineNum, linesPerBox, xMatrix, lowerBound, dialog);
   1585 #else
   1586                 render_multi_text_string_lines(STRING_YOU, lineNum, &linePos, linesPerBox, xMatrix, lowerBound);
   1587 #endif
   1588                 xMatrix = 1;
   1589                 break;
   1590 #endif
   1591 
   1592             case SPECIAL_CHAR(DIALOG_CHAR_STAR_COUNT):
   1593 #ifdef VERSION_EU
   1594                 render_star_count_dialog_text(dialog, &xMatrix);
   1595 #else
   1596                 render_star_count_dialog_text(&xMatrix, &linePos);
   1597 #endif
   1598                 break;
   1599 
   1600 #ifdef VERSION_EU
   1601             case SPECIAL_CHAR(DIALOG_CHAR_DOUBLE_LOW_QUOTE):
   1602                 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY + 8, 0xF6);
   1603                 gDialogX += gDialogCharWidths[0xF6];
   1604                 break;
   1605 #endif
   1606 
   1607             default: // any other character
   1608 #ifdef VERSION_CN
   1609                 if (strChar >= 0x0100) {
   1610                     strChar = strChar * 2 - 0x0100;
   1611 
   1612                     if (lineNum < lowerBound || lineNum > (lowerBound + linesPerBox)) {
   1613                         break;
   1614                     }
   1615 
   1616                     if (linePos != 0 || xMatrix != 1) {
   1617                         create_dl_translation_matrix(MENU_MTX_NOPUSH, gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1), 0.0f, 0.0f);
   1618                     }
   1619 
   1620                     render_generic_char_cn(strChar);
   1621                     create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f);
   1622                     render_generic_char_cn(strChar + 1);
   1623                     create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f);
   1624                 } else {
   1625                     if (lineNum < lowerBound || lineNum > (lowerBound + linesPerBox)) {
   1626                         break;
   1627                     }
   1628 
   1629                     if (linePos != 0 || xMatrix != 1) {
   1630                         create_dl_translation_matrix(MENU_MTX_NOPUSH, gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1), 0.0f, 0.0f);
   1631                     }
   1632 
   1633                     render_generic_char(strChar);
   1634                     create_dl_translation_matrix(MENU_MTX_NOPUSH, gDialogCharWidths[strChar], 0.0f, 0.0f);
   1635                 }
   1636 
   1637                 xMatrix = 1;
   1638                 linePos++;
   1639 #elif defined(VERSION_JP) || defined(VERSION_SH)
   1640 #ifdef VERSION_SH
   1641                 if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) {
   1642 #endif
   1643                     if (linePos != 0) {
   1644                         create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * xMatrix, 0, 0);
   1645                     }
   1646 
   1647                     render_generic_char(strChar);
   1648                     xMatrix = 1;
   1649                     linePos++;
   1650 
   1651                     if (mark != DIALOG_MARK_NONE) {
   1652                         create_dl_translation_matrix(MENU_MTX_PUSH, 5.0f, 7.0f, 0);
   1653                         render_generic_char(DIALOG_CHAR_MARK_START + mark);
   1654                         gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   1655                         mark = DIALOG_MARK_NONE;
   1656                     }
   1657 #ifdef VERSION_SH
   1658                 }
   1659 #endif
   1660 #elif defined(VERSION_US)
   1661                 if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) {
   1662                     if (linePos != 0 || xMatrix != 1) {
   1663                         create_dl_translation_matrix(
   1664                             MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1)), 0, 0);
   1665                     }
   1666 
   1667                     render_generic_char(strChar);
   1668                     create_dl_translation_matrix(MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[strChar]), 0, 0);
   1669                     xMatrix = 1;
   1670                     linePos++;
   1671                 }
   1672 #else // VERSION_EU
   1673                 if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) {
   1674                     render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, strChar);
   1675                 }
   1676                 gDialogX += gDialogCharWidths[strChar];
   1677 #endif
   1678         }
   1679 
   1680 #ifdef VERSION_JP
   1681         if (linePos == 12) {
   1682             if (str[strIndex + 1] == DIALOG_CHAR_PERIOD) {
   1683                 adjust_pos_and_print_period_char(&xMatrix, &linePos);
   1684                 strIndex++;
   1685             }
   1686 
   1687             if (str[strIndex + 1] == DIALOG_CHAR_COMMA) {
   1688                 create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * xMatrix, 0, 0);
   1689                 render_generic_char(DIALOG_CHAR_COMMA);
   1690                 strIndex++;
   1691             }
   1692 
   1693             if (str[strIndex + 1] == DIALOG_CHAR_NEWLINE) {
   1694                 strIndex++;
   1695             }
   1696 
   1697             if (str[strIndex + 1] == DIALOG_CHAR_TERMINATOR) {
   1698                 pageState = DIALOG_PAGE_STATE_END;
   1699                 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   1700                 break; // exit loop
   1701             } else {
   1702                 lineNum++;
   1703                 handle_dialog_scroll_page_state(lineNum, totalLines, &pageState, &xMatrix, &linePos);
   1704             }
   1705         }
   1706 #endif
   1707 
   1708 #ifdef VERSION_CN
   1709         strIndex += 2;
   1710 #else
   1711         strIndex++;
   1712 #endif
   1713     }
   1714 
   1715     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   1716 
   1717     if (gMenuState == MENU_STATE_DIALOG_OPEN) {
   1718         if (pageState == DIALOG_PAGE_STATE_END) {
   1719             gNextDialogPageStartStrIndex = -1;
   1720         } else {
   1721             gNextDialogPageStartStrIndex = strIndex;
   1722         }
   1723     }
   1724 
   1725     gLastDialogLineNum = lineNum;
   1726 }
   1727 
   1728 #if defined(VERSION_JP) || defined(VERSION_SH)
   1729     #define X_VAL4_1 50
   1730     #define X_VAL4_2 25
   1731     #define Y_VAL4_1 1
   1732     #define Y_VAL4_2 20
   1733 #elif defined(VERSION_CN)
   1734     #define X_VAL4_1 58
   1735     #define X_VAL4_2 0
   1736     #define Y_VAL4_1 2
   1737     #define Y_VAL4_2 20
   1738 #else
   1739     #define X_VAL4_1 56
   1740     #define X_VAL4_2 9
   1741     #define Y_VAL4_1 2
   1742     #define Y_VAL4_2 16
   1743 #endif
   1744 
   1745 void render_dialog_triangle_choice(void) {
   1746     if (gMenuState == MENU_STATE_DIALOG_OPEN) {
   1747         handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &gMenuLineNum, 1, 2);
   1748     }
   1749 
   1750     create_dl_translation_matrix(MENU_MTX_NOPUSH, ((gMenuLineNum - 1) * X_VAL4_1) + X_VAL4_2, Y_VAL4_1 - (gLastDialogLineNum * Y_VAL4_2), 0);
   1751 
   1752     if (gDialogBoxType == DIALOG_TYPE_ROTATE) { // White Text
   1753         gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
   1754     } else { // Black Text
   1755         gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
   1756     }
   1757 
   1758     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   1759 }
   1760 
   1761 #if defined(VERSION_JP) || defined(VERSION_SH)
   1762     #define X_VAL5 123.0f
   1763     #define Y_VAL5_1 -20
   1764     #define Y_VAL5_2 2
   1765     #define X_Y_VAL6 0.8f
   1766 #elif defined(VERSION_US)
   1767     #define X_VAL5 118.0f
   1768     #define Y_VAL5_1 -16
   1769     #define Y_VAL5_2 5
   1770     #define X_Y_VAL6 0.8f
   1771 #elif defined(VERSION_EU)
   1772     #define X_VAL5 122.0f
   1773     #define Y_VAL5_1 -16
   1774     #define Y_VAL5_2 3
   1775     #define X_Y_VAL6 0.5f
   1776 #elif defined(VERSION_CN)
   1777     #define X_VAL5 118.0f
   1778     #define Y_VAL5_1 -18
   1779     #define Y_VAL5_2 5
   1780     #define X_Y_VAL6 0.8f
   1781 #endif
   1782 
   1783 void render_dialog_triangle_next(s8 linesPerBox) {
   1784     s32 globalTimer = gGlobalTimer;
   1785 
   1786     if (globalTimer & 8) {
   1787         return;
   1788     }
   1789 
   1790     create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL5, (linesPerBox * Y_VAL5_1) + Y_VAL5_2, 0);
   1791     create_dl_scale_matrix(MENU_MTX_NOPUSH, X_Y_VAL6, X_Y_VAL6, 1.0f);
   1792     create_dl_rotation_matrix(MENU_MTX_NOPUSH, -90.0f, 0, 0, 1.0f);
   1793 
   1794     if (gDialogBoxType == DIALOG_TYPE_ROTATE) { // White Text
   1795         gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
   1796     } else { // Black Text
   1797         gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
   1798     }
   1799 
   1800     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   1801     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   1802 }
   1803 
   1804 void handle_special_dialog_text(s16 dialogID) { // dialog ID tables, in order
   1805     // King Bob-omb (Start), Whomp (Start), King Bob-omb (throw him out), Eyerock (Start), Wiggler (Start)
   1806     s16 dialogBossStart[] = { DIALOG_017, DIALOG_114, DIALOG_128, DIALOG_117, DIALOG_150 };
   1807     // Koopa the Quick (BoB), Koopa the Quick (THI), Penguin Race, Fat Penguin Race (120 stars)
   1808     s16 dialogRaceSound[] = { DIALOG_005, DIALOG_009, DIALOG_055, DIALOG_164 };
   1809     // Red Switch, Green Switch, Blue Switch, 100 coins star, Bowser Red Coin Star
   1810     s16 dialogStarSound[] = { DIALOG_010, DIALOG_011, DIALOG_012, DIALOG_013, DIALOG_014 };
   1811     // King Bob-omb (Start), Whomp (Defeated), King Bob-omb (Defeated, missing in JP), Eyerock (Defeated), Wiggler (Defeated)
   1812 #if BUGFIX_KING_BOB_OMB_FADE_MUSIC
   1813     s16 dialogBossStop[] = { DIALOG_017, DIALOG_115, DIALOG_116, DIALOG_118, DIALOG_152 };
   1814 #else
   1815     //! @bug JP misses King Bob-omb defeated DIALOG_116, meaning that the boss music will still
   1816     //! play after King Bob-omb is defeated until BoB loads it's music after the star cutscene
   1817     s16 dialogBossStop[] = { DIALOG_017, DIALOG_115, DIALOG_118, DIALOG_152 };
   1818 #endif
   1819     s16 i;
   1820 
   1821     for (i = 0; i < (s16) ARRAY_COUNT(dialogBossStart); i++) {
   1822         if (dialogBossStart[i] == dialogID) {
   1823             seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60);
   1824             play_music(SEQ_PLAYER_LEVEL, SEQUENCE_ARGS(4, SEQ_EVENT_BOSS), 0);
   1825             return;
   1826         }
   1827     }
   1828 
   1829     for (i = 0; i < (s16) ARRAY_COUNT(dialogRaceSound); i++) {
   1830         if (dialogRaceSound[i] == dialogID && gMenuLineNum == DIALOG_RESPONSE_YES) {
   1831             play_race_fanfare();
   1832             return;
   1833         }
   1834     }
   1835 
   1836     for (i = 0; i < (s16) ARRAY_COUNT(dialogStarSound); i++) {
   1837         if (dialogStarSound[i] == dialogID && gMenuLineNum == DIALOG_RESPONSE_YES) {
   1838             play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
   1839             return;
   1840         }
   1841     }
   1842 
   1843     for (i = 0; i < (s16) ARRAY_COUNT(dialogBossStop); i++) {
   1844         if (dialogBossStop[i] == dialogID) {
   1845             seq_player_fade_out(SEQ_PLAYER_LEVEL, 1);
   1846             return;
   1847         }
   1848     }
   1849 }
   1850 
   1851 s16 gMenuMode = MENU_MODE_NONE;
   1852 
   1853 #ifdef VERSION_CN
   1854 u8 gEndCutsceneStrEn0[] = { TEXT_EMPTY_STRING };
   1855 u8 gEndCutsceneStrEn1[] = { TEXT_EMPTY_STRING };
   1856 u8 gEndCutsceneStrEn2[] = { TEXT_EMPTY_STRING };
   1857 u8 gEndCutsceneStrEn3[] = { TEXT_EMPTY_STRING };
   1858 u8 gEndCutsceneStrEn4[] = { TEXT_EMPTY_STRING };
   1859 u8 gEndCutsceneStrEn5[] = { TEXT_EMPTY_STRING };
   1860 u8 gEndCutsceneStrEn6[] = { TEXT_EMPTY_STRING };
   1861 u8 gEndCutsceneStrEn7[] = { TEXT_EMPTY_STRING };
   1862 u8 gEndCutsceneStrEn8[] = { TEXT_EMPTY_STRING };
   1863 #else
   1864 u8 gEndCutsceneStrEn0[] = { TEXT_FILE_MARIO_EXCLAMATION };
   1865 u8 gEndCutsceneStrEn1[] = { TEXT_POWER_STARS_RESTORED };
   1866 u8 gEndCutsceneStrEn2[] = { TEXT_THANKS_TO_YOU };
   1867 u8 gEndCutsceneStrEn3[] = { TEXT_THANK_YOU_MARIO };
   1868 u8 gEndCutsceneStrEn4[] = { TEXT_SOMETHING_SPECIAL };
   1869 u8 gEndCutsceneStrEn5[] = { TEXT_LISTEN_EVERYBODY };
   1870 u8 gEndCutsceneStrEn6[] = { TEXT_LETS_HAVE_CAKE };
   1871 u8 gEndCutsceneStrEn7[] = { TEXT_FOR_MARIO };
   1872 u8 gEndCutsceneStrEn8[] = { TEXT_FILE_MARIO_QUESTION };
   1873 #endif
   1874 
   1875 u8 *gEndCutsceneStringsEn[] = {
   1876     gEndCutsceneStrEn0,
   1877     gEndCutsceneStrEn1,
   1878     gEndCutsceneStrEn2,
   1879     gEndCutsceneStrEn3,
   1880     gEndCutsceneStrEn4,
   1881     gEndCutsceneStrEn5,
   1882     gEndCutsceneStrEn6,
   1883     gEndCutsceneStrEn7,
   1884     // This [8] string is actually unused. In the cutscene handler, the developers do not
   1885     // set the 8th one, but use the first string again at the very end, so Peach ends up
   1886     // saying "Mario!" twice. It is likely that she was originally meant to say "Mario?" at
   1887     // the end but the developers changed their mind, possibly because the line recorded
   1888     // sounded more like an exclamation than a question.
   1889     gEndCutsceneStrEn8,
   1890     NULL
   1891 };
   1892 
   1893 #ifdef VERSION_EU
   1894 u8 gEndCutsceneStrFr0[] = { TEXT_FILE_MARIO_EXCLAMATION };
   1895 u8 gEndCutsceneStrFr1[] = { TEXT_POWER_STARS_RESTORED_FR };
   1896 u8 gEndCutsceneStrFr2[] = { TEXT_THANKS_TO_YOU_FR };
   1897 u8 gEndCutsceneStrFr3[] = { TEXT_THANK_YOU_MARIO_FR };
   1898 u8 gEndCutsceneStrFr4[] = { TEXT_SOMETHING_SPECIAL_FR };
   1899 u8 gEndCutsceneStrFr5[] = { TEXT_COME_ON_EVERYBODY_FR };
   1900 u8 gEndCutsceneStrFr6[] = { TEXT_LETS_HAVE_CAKE_FR };
   1901 u8 gEndCutsceneStrFr7[] = { TEXT_FOR_MARIO_FR };
   1902 u8 gEndCutsceneStrFr8[] = { TEXT_FILE_MARIO_QUESTION };
   1903 
   1904 u8 *gEndCutsceneStringsFr[] = {
   1905     gEndCutsceneStrFr0,
   1906     gEndCutsceneStrFr1,
   1907     gEndCutsceneStrFr2,
   1908     gEndCutsceneStrFr3,
   1909     gEndCutsceneStrFr4,
   1910     gEndCutsceneStrFr5,
   1911     gEndCutsceneStrFr6,
   1912     gEndCutsceneStrFr7,
   1913     gEndCutsceneStrFr8,
   1914     NULL
   1915 };
   1916 
   1917 u8 gEndCutsceneStrDe0[] = { TEXT_FILE_MARIO_EXCLAMATION };
   1918 u8 gEndCutsceneStrDe1[] = { TEXT_POWER_STARS_RESTORED_DE };
   1919 u8 gEndCutsceneStrDe2[] = { TEXT_THANKS_TO_YOU_DE };
   1920 u8 gEndCutsceneStrDe3[] = { TEXT_THANK_YOU_MARIO_DE };
   1921 u8 gEndCutsceneStrDe4[] = { TEXT_SOMETHING_SPECIAL_DE };
   1922 u8 gEndCutsceneStrDe5[] = { TEXT_COME_ON_EVERYBODY_DE };
   1923 u8 gEndCutsceneStrDe6[] = { TEXT_LETS_HAVE_CAKE_DE };
   1924 u8 gEndCutsceneStrDe7[] = { TEXT_FOR_MARIO_DE };
   1925 u8 gEndCutsceneStrDe8[] = { TEXT_FILE_MARIO_QUESTION };
   1926 
   1927 u8 *gEndCutsceneStringsDe[] = {
   1928     gEndCutsceneStrDe0,
   1929     gEndCutsceneStrDe1,
   1930     gEndCutsceneStrDe2,
   1931     gEndCutsceneStrDe3,
   1932     gEndCutsceneStrDe4,
   1933     gEndCutsceneStrDe5,
   1934     gEndCutsceneStrDe6,
   1935     gEndCutsceneStrDe7,
   1936     gEndCutsceneStrDe8,
   1937     NULL
   1938 };
   1939 #endif
   1940 
   1941 u16 gCutsceneMsgFade = 0;
   1942 s16 gCutsceneMsgIndex = -1;
   1943 s16 gCutsceneMsgDuration = -1;
   1944 s16 gCutsceneMsgTimer = 0;
   1945 s8 gDialogCameraAngleIndex = CAM_SELECTION_MARIO;
   1946 s8 gDialogCourseActNum = 1;
   1947 
   1948 #if defined(VERSION_JP) || defined(VERSION_SH)
   1949     #define DIAG_VAL1 20
   1950     #define DIAG_VAL2 240
   1951     #define DIAG_VAL3 130
   1952     #define DIAG_VAL4 4
   1953 #else
   1954     #define DIAG_VAL1 16
   1955 #if defined(VERSION_US) || defined(VERSION_CN)
   1956     #define DIAG_VAL2 240
   1957 #else
   1958     #define DIAG_VAL2 238
   1959 #endif
   1960     #define DIAG_VAL3 132
   1961     #define DIAG_VAL4 5
   1962 #endif
   1963 
   1964 void render_dialog_entries(void) {
   1965 #ifdef VERSION_EU
   1966     s8 lowerBound;
   1967 #endif
   1968     void **dialogTable;
   1969     struct DialogEntry *dialog;
   1970 #if defined(VERSION_US) || defined(VERSION_SH) || defined(VERSION_CN)
   1971     s8 lowerBound;
   1972 #endif
   1973 
   1974 #ifdef VERSION_EU
   1975     gInGameLanguage = eu_get_language();
   1976     switch (gInGameLanguage) {
   1977         case LANGUAGE_ENGLISH:
   1978             dialogTable = segmented_to_virtual(dialog_table_eu_en);
   1979             break;
   1980         case LANGUAGE_FRENCH:
   1981             dialogTable = segmented_to_virtual(dialog_table_eu_fr);
   1982             break;
   1983         case LANGUAGE_GERMAN:
   1984             dialogTable = segmented_to_virtual(dialog_table_eu_de);
   1985             break;
   1986     }
   1987 #else
   1988     dialogTable = segmented_to_virtual(seg2_dialog_table);
   1989 #endif
   1990     dialog = segmented_to_virtual(dialogTable[gDialogID]);
   1991 
   1992     // if the dialog entry is invalid, set the ID to DIALOG_NONE.
   1993     if (dialog == segmented_to_virtual(NULL)) {
   1994         gDialogID = DIALOG_NONE;
   1995         return;
   1996     }
   1997 
   1998 #ifdef VERSION_EU
   1999     gDialogX = 0;
   2000     gDialogY = 0;
   2001 #endif
   2002 
   2003     switch (gMenuState) {
   2004         case MENU_STATE_DIALOG_OPENING:
   2005             if (gDialogBoxAngle == DIALOG_BOX_ANGLE_DEFAULT) {
   2006                 play_dialog_sound(gDialogID);
   2007                 play_sound(SOUND_MENU_MESSAGE_APPEAR, gGlobalSoundSource);
   2008             }
   2009 
   2010             if (gDialogBoxType == DIALOG_TYPE_ROTATE) {
   2011                 gDialogBoxAngle -= 7.5;
   2012                 gDialogBoxScale -= 1.5;
   2013             } else {
   2014                 gDialogBoxAngle -= 10.0;
   2015                 gDialogBoxScale -= 2.0;
   2016             }
   2017 
   2018             if (gDialogBoxAngle == 0.0f) {
   2019                 gMenuState = MENU_STATE_DIALOG_OPEN;
   2020                 gMenuLineNum = 1;
   2021             }
   2022 #ifndef VERSION_JP
   2023             lowerBound = 1;
   2024 #endif
   2025             break;
   2026 
   2027         case MENU_STATE_DIALOG_OPEN:
   2028             gDialogBoxAngle = 0.0f;
   2029 
   2030             if ((gPlayer3Controller->buttonPressed & A_BUTTON)
   2031              || (gPlayer3Controller->buttonPressed & B_BUTTON)) {
   2032                 if (gNextDialogPageStartStrIndex == -1) {
   2033                     handle_special_dialog_text(gDialogID);
   2034                     gMenuState = MENU_STATE_DIALOG_CLOSING;
   2035                 } else {
   2036                     gMenuState = MENU_STATE_DIALOG_SCROLLING;
   2037                     play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, gGlobalSoundSource);
   2038                 }
   2039             }
   2040 #ifndef VERSION_JP
   2041             lowerBound = 1;
   2042 #endif
   2043             break;
   2044 
   2045         case MENU_STATE_DIALOG_SCROLLING:
   2046             gDialogScrollOffsetY += dialog->linesPerBox * 2;
   2047 
   2048             if (gDialogScrollOffsetY >= dialog->linesPerBox * DIAG_VAL1) {
   2049                 gDialogPageStartStrIndex = gNextDialogPageStartStrIndex;
   2050                 gMenuState = MENU_STATE_DIALOG_OPEN;
   2051                 gDialogScrollOffsetY = 0;
   2052             }
   2053 #ifndef VERSION_JP
   2054             lowerBound = (gDialogScrollOffsetY / DIAG_VAL1) + 1;
   2055 #endif
   2056             break;
   2057 
   2058         case MENU_STATE_DIALOG_CLOSING:
   2059             if (gDialogBoxAngle == 20.0f) {
   2060                 level_set_transition(0, NULL);
   2061                 play_sound(SOUND_MENU_MESSAGE_DISAPPEAR, gGlobalSoundSource);
   2062 
   2063                 if (gDialogBoxType == DIALOG_TYPE_ZOOM) {
   2064                     trigger_cutscene_dialog(2);
   2065                 }
   2066 
   2067                 gDialogResponse = gMenuLineNum;
   2068             }
   2069 
   2070             gDialogBoxAngle += 10.0f;
   2071             gDialogBoxScale += 2.0f;
   2072 
   2073             if (gDialogBoxAngle == DIALOG_BOX_ANGLE_DEFAULT) {
   2074                 gMenuState = MENU_STATE_DEFAULT;
   2075                 gDialogID = DIALOG_NONE;
   2076                 gDialogPageStartStrIndex = 0;
   2077                 gDialogWithChoice = FALSE;
   2078                 gNextDialogPageStartStrIndex = 0;
   2079                 gDialogResponse = DIALOG_RESPONSE_NONE;
   2080             }
   2081 #ifndef VERSION_JP
   2082             lowerBound = 1;
   2083 #endif
   2084             break;
   2085     }
   2086 
   2087     render_dialog_box_type(dialog, dialog->linesPerBox);
   2088 
   2089 #ifdef VERSION_CN
   2090 // This isn't really a diff. The iQue compiler doesn't allow the use of ifdefs inside a macro for some reason, so those were eliminated.
   2091 #ifdef WIDESCREEN
   2092 #define ulx 0
   2093 #define lrx SCREEN_WIDTH
   2094 #else
   2095 #define ulx ensure_nonnegative(dialog->leftOffset)
   2096 #define lrx ensure_nonnegative(dialog->leftOffset + DIAG_VAL3)
   2097 #endif
   2098     gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, ulx, ensure_nonnegative(DIAG_VAL2 - dialog->width),
   2099         lrx, ensure_nonnegative((256 - dialog->width) + (dialog->linesPerBox * 80 / DIAG_VAL4)));
   2100 #undef ulx
   2101 #undef lrx
   2102 #else
   2103     gDPSetScissor(
   2104         gDisplayListHead++, G_SC_NON_INTERLACE,
   2105         // Horizontal scissoring isn't really required and can potentially mess up widescreen enhancements.
   2106 #ifdef WIDESCREEN
   2107         0,
   2108 #else
   2109         ensure_nonnegative(dialog->leftOffset),
   2110 #endif
   2111         ensure_nonnegative(DIAG_VAL2 - dialog->width),
   2112 #ifdef VERSION_EU
   2113 #ifdef WIDESCREEN
   2114         SCREEN_WIDTH,
   2115 #else
   2116         ensure_nonnegative(dialog->leftOffset + (DIAG_VAL3 / gDialogBoxScale)),
   2117 #endif
   2118         ensure_nonnegative((240 - dialog->width) + (dialog->linesPerBox * 80 / DIAG_VAL4 / gDialogBoxScale))
   2119 #else
   2120 #ifdef WIDESCREEN
   2121         SCREEN_WIDTH,
   2122 #else
   2123         ensure_nonnegative(dialog->leftOffset + DIAG_VAL3),
   2124 #endif
   2125         ensure_nonnegative((240 - dialog->width) + (dialog->linesPerBox * 80 / DIAG_VAL4))
   2126 #endif
   2127     );
   2128 #endif
   2129 
   2130 #ifdef VERSION_JP
   2131     handle_dialog_text_and_pages(0, dialog);
   2132 #else
   2133     handle_dialog_text_and_pages(0, dialog, lowerBound);
   2134 #endif
   2135 
   2136     if (gNextDialogPageStartStrIndex == -1 && gDialogWithChoice == TRUE) {
   2137         render_dialog_triangle_choice();
   2138     }
   2139 
   2140     #ifdef VERSION_EU
   2141     #undef BORDER_HEIGHT
   2142     #define BORDER_HEIGHT 8
   2143     #endif
   2144     gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 2, 2, SCREEN_WIDTH - BORDER_HEIGHT / 2, SCREEN_HEIGHT - BORDER_HEIGHT / 2);
   2145     #ifdef VERSION_EU
   2146     #undef BORDER_HEIGHT
   2147     #define BORDER_HEIGHT 1
   2148     #endif
   2149 
   2150     if (gNextDialogPageStartStrIndex != -1 && gMenuState == MENU_STATE_DIALOG_OPEN) {
   2151         render_dialog_triangle_next(dialog->linesPerBox);
   2152     }
   2153 }
   2154 
   2155 // Calls a gMenuMode value defined by render_menus_and_dialogs cases
   2156 void set_menu_mode(s16 mode) {
   2157     if (gMenuMode == MENU_MODE_NONE) {
   2158         gMenuMode = mode;
   2159     }
   2160 }
   2161 
   2162 void reset_cutscene_msg_fade(void) {
   2163     gCutsceneMsgFade = 0;
   2164 }
   2165 
   2166 void dl_rgba16_begin_cutscene_msg_fade(void) {
   2167     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
   2168     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gCutsceneMsgFade);
   2169 }
   2170 
   2171 void dl_rgba16_stop_cutscene_msg_fade(void) {
   2172     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
   2173 
   2174     if (gCutsceneMsgFade < 250) {
   2175         gCutsceneMsgFade += 25;
   2176     } else {
   2177         gCutsceneMsgFade = 255;
   2178     }
   2179 }
   2180 
   2181 u8 ascii_to_credits_char(u8 c) {
   2182     if (c >= 'A' && c <= 'Z') {
   2183         return (c - ('A' - 0xA));
   2184     }
   2185 
   2186     if (c >= 'a' && c <= 'z') { // remap lower to upper case
   2187         return (c - ('a' - 0xA));
   2188     }
   2189 
   2190     if (c == ' ') {
   2191         return GLOBAL_CHAR_SPACE;
   2192     }
   2193 
   2194     if (c == '.') {
   2195         return 0x24;
   2196     }
   2197 
   2198     if (c == '3') {
   2199         return ASCII_TO_DIALOG('3');
   2200     }
   2201 
   2202     if (c == '4') {
   2203         return ASCII_TO_DIALOG('4');
   2204     }
   2205 
   2206     if (c == '6') {
   2207         return ASCII_TO_DIALOG('6');
   2208     }
   2209 
   2210     return GLOBAL_CHAR_SPACE;
   2211 }
   2212 
   2213 void print_credits_str_ascii(s16 x, s16 y, const char *str) {
   2214     s32 pos = 0;
   2215     u8 c = str[pos];
   2216     u8 creditStr[100];
   2217 
   2218     while (c != '\0') {
   2219         creditStr[pos++] = ascii_to_credits_char(c);
   2220         c = str[pos];
   2221     }
   2222 
   2223     creditStr[pos] = GLOBAL_CHAR_TERMINATOR;
   2224 
   2225     print_credits_string(x, y, creditStr);
   2226 }
   2227 
   2228 void set_cutscene_message(s16 xOffset, s16 yOffset, s16 msgIndex, s16 msgDuration) {
   2229     // is message done printing?
   2230     if (gCutsceneMsgIndex == -1) {
   2231         gCutsceneMsgIndex = msgIndex;
   2232         gCutsceneMsgDuration = msgDuration;
   2233         gCutsceneMsgTimer = 0;
   2234         gCutsceneMsgXOffset = xOffset;
   2235         gCutsceneMsgYOffset = yOffset;
   2236         gCutsceneMsgFade = 0;
   2237     }
   2238 }
   2239 
   2240 void do_cutscene_handler(void) {
   2241     s16 x;
   2242 
   2243     // is a cutscene playing? do not perform this handler's actions if so.
   2244     if (gCutsceneMsgIndex == -1) {
   2245         return;
   2246     }
   2247 
   2248     create_dl_ortho_matrix();
   2249 
   2250     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   2251     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gCutsceneMsgFade);
   2252 
   2253 #ifdef VERSION_EU
   2254     switch (eu_get_language()) {
   2255         case LANGUAGE_ENGLISH:
   2256             x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex], 10.0f);
   2257             print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex]);
   2258             break;
   2259         case LANGUAGE_FRENCH:
   2260             x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsFr[gCutsceneMsgIndex], 10.0f);
   2261             print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsFr[gCutsceneMsgIndex]);
   2262             break;
   2263         case LANGUAGE_GERMAN:
   2264             x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsDe[gCutsceneMsgIndex], 10.0f);
   2265             print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsDe[gCutsceneMsgIndex]);
   2266             break;
   2267     }
   2268 #else
   2269     // get the x coordinate of where the cutscene string starts.
   2270     x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex], 10.0f);
   2271     print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex]);
   2272 #endif
   2273 
   2274     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   2275 
   2276     // if the timing variable is less than 5, increment
   2277     // the fade until we are at full opacity.
   2278     if (gCutsceneMsgTimer < 5) {
   2279         gCutsceneMsgFade += 50;
   2280     }
   2281 
   2282     // if the cutscene frame length + the fade-in counter is
   2283     // less than the timer, it means we have exceeded the
   2284     // time that the message is supposed to remain on
   2285     // screen. if (message_duration = 50) and (msg_timer = 55)
   2286     // then after the first 5 frames, the message will remain
   2287     // on screen for another 50 frames until it starts fading.
   2288     if (gCutsceneMsgDuration + 5 < gCutsceneMsgTimer) {
   2289         gCutsceneMsgFade -= 50;
   2290     }
   2291 
   2292     // like the first check, it takes 5 frames to fade out, so
   2293     // perform a + 10 to account for the earlier check (10-5=5).
   2294     if (gCutsceneMsgDuration + 10 < gCutsceneMsgTimer) {
   2295         gCutsceneMsgIndex = -1;
   2296         gCutsceneMsgFade = 0;
   2297         gCutsceneMsgTimer = 0;
   2298         return;
   2299     }
   2300 
   2301     gCutsceneMsgTimer++;
   2302 }
   2303 
   2304 #ifdef VERSION_JP
   2305     #define PEACH_MESSAGE_TIMER 170
   2306 #else
   2307     #define PEACH_MESSAGE_TIMER 250
   2308 #endif
   2309 
   2310 #if defined(VERSION_JP) || defined(VERSION_SH)
   2311     #define STR_X 53
   2312     #define STR_Y 136
   2313 #else
   2314     #define STR_X 38
   2315     #define STR_Y 142
   2316 #endif
   2317 
   2318 // "Dear Mario" message handler
   2319 void print_peach_letter_message(void) {
   2320     void **dialogTable;
   2321     struct DialogEntry *dialog;
   2322     u8 *str;
   2323 
   2324 #ifdef VERSION_EU
   2325     gInGameLanguage = eu_get_language();
   2326     switch (gInGameLanguage) {
   2327         case LANGUAGE_ENGLISH:
   2328             dialogTable = segmented_to_virtual(dialog_table_eu_en);
   2329             break;
   2330         case LANGUAGE_FRENCH:
   2331             dialogTable = segmented_to_virtual(dialog_table_eu_fr);
   2332             break;
   2333         case LANGUAGE_GERMAN:
   2334             dialogTable = segmented_to_virtual(dialog_table_eu_de);
   2335             break;
   2336     }
   2337 #else
   2338     dialogTable = segmented_to_virtual(seg2_dialog_table);
   2339 #endif
   2340     dialog = segmented_to_virtual(dialogTable[gDialogID]);
   2341     str = segmented_to_virtual(dialog->str);
   2342 
   2343     create_dl_translation_matrix(MENU_MTX_PUSH, 97.0f, 118.0f, 0);
   2344 
   2345     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gCutsceneMsgFade);
   2346     gSPDisplayList(gDisplayListHead++, castle_grounds_seg7_dl_0700EA58);
   2347     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2348     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   2349     gDPSetEnvColor(gDisplayListHead++, 20, 20, 20, gCutsceneMsgFade);
   2350 
   2351     print_generic_string(STR_X, STR_Y, str);
   2352 #if defined(VERSION_JP)
   2353     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   2354 #endif
   2355     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
   2356 #ifndef VERSION_JP
   2357     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   2358     gDPSetEnvColor(gDisplayListHead++, 200, 80, 120, gCutsceneMsgFade);
   2359     gSPDisplayList(gDisplayListHead++, castle_grounds_seg7_us_dl_0700F2E8);
   2360 #endif
   2361 
   2362     // at the start/end of message, reset the fade.
   2363     if (gCutsceneMsgTimer == 0) {
   2364         gCutsceneMsgFade = 0;
   2365     }
   2366 
   2367     // we're less than 20 increments, so increase the fade.
   2368     if (gCutsceneMsgTimer < 20) {
   2369         gCutsceneMsgFade += 10;
   2370     }
   2371 
   2372     // we're after PEACH_MESSAGE_TIMER increments, so decrease the fade.
   2373     if (gCutsceneMsgTimer > PEACH_MESSAGE_TIMER) {
   2374         gCutsceneMsgFade -= 10;
   2375     }
   2376 
   2377     // 20 increments after the start of the decrease, we're
   2378     // back where we are, so reset everything at the end.
   2379     if (gCutsceneMsgTimer > (PEACH_MESSAGE_TIMER + 20)) {
   2380         gCutsceneMsgIndex = -1;
   2381         gCutsceneMsgFade = 0; //! uselessly reset since the next execution will just set it to 0 again.
   2382         gDialogID = DIALOG_NONE;
   2383         gCutsceneMsgTimer = 0;
   2384         return; // return to avoid incrementing the timer
   2385     }
   2386 
   2387     gCutsceneMsgTimer++;
   2388 }
   2389 
   2390 /**
   2391  * Renders the cannon reticle when Mario is inside a cannon.
   2392  * Formed by four triangles.
   2393  */
   2394 void render_hud_cannon_reticle(void) {
   2395     create_dl_translation_matrix(MENU_MTX_PUSH, 160.0f, 120.0f, 0);
   2396 
   2397     gDPSetEnvColor(gDisplayListHead++, 50, 50, 50, 180);
   2398     create_dl_translation_matrix(MENU_MTX_PUSH, -20.0f, -8.0f, 0);
   2399     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2400     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2401 
   2402     create_dl_translation_matrix(MENU_MTX_PUSH, 20.0f, 8.0f, 0);
   2403     create_dl_rotation_matrix(MENU_MTX_NOPUSH, 180.0f, 0, 0, 1.0f);
   2404     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2405     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2406 
   2407     create_dl_translation_matrix(MENU_MTX_PUSH, 8.0f, -20.0f, 0);
   2408     create_dl_rotation_matrix(MENU_MTX_NOPUSH, 90.0f, 0, 0, 1.0f);
   2409     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2410     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2411 
   2412     create_dl_translation_matrix(MENU_MTX_PUSH, -8.0f, 20.0f, 0);
   2413     create_dl_rotation_matrix(MENU_MTX_NOPUSH, -90.0f, 0, 0, 1.0f);
   2414     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2415     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2416 
   2417     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2418 }
   2419 
   2420 void reset_red_coins_collected(void) {
   2421     gRedCoinsCollected = 0;
   2422 }
   2423 
   2424 void change_dialog_camera_angle(void) {
   2425     if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO) {
   2426         gDialogCameraAngleIndex = CAM_SELECTION_MARIO;
   2427     } else {
   2428         gDialogCameraAngleIndex = CAM_SELECTION_FIXED;
   2429     }
   2430 }
   2431 
   2432 void shade_screen(void) {
   2433     create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(0), SCREEN_HEIGHT, 0);
   2434 
   2435     // This is a bit weird. It reuses the dialog text box (width 130, height -80),
   2436     // so scale to at least fit the screen.
   2437 #ifdef WIDESCREEN
   2438     create_dl_scale_matrix(MENU_MTX_NOPUSH,
   2439                            GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT / 130.0f, 3.0f, 1.0f);
   2440 #else
   2441     create_dl_scale_matrix(MENU_MTX_NOPUSH, 2.6f, 3.4f, 1.0f);
   2442 #endif
   2443 
   2444     gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 110);
   2445     gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
   2446     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2447 }
   2448 
   2449 void print_animated_red_coin(s16 x, s16 y) {
   2450     u32 globalTimer = gGlobalTimer;
   2451 
   2452     create_dl_translation_matrix(MENU_MTX_PUSH, x, y, 0);
   2453     create_dl_scale_matrix(MENU_MTX_NOPUSH, 0.2f, 0.2f, 1.0f);
   2454     gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2);
   2455 
   2456     switch (globalTimer & 6) {
   2457         case 0:
   2458             gSPDisplayList(gDisplayListHead++, coin_seg3_dl_03007940);
   2459             break;
   2460         case 2:
   2461             gSPDisplayList(gDisplayListHead++, coin_seg3_dl_03007968);
   2462             break;
   2463         case 4:
   2464             gSPDisplayList(gDisplayListHead++, coin_seg3_dl_03007990);
   2465             break;
   2466         case 6:
   2467             gSPDisplayList(gDisplayListHead++, coin_seg3_dl_030079B8);
   2468             break;
   2469     }
   2470 
   2471     gDPSetRenderMode(gDisplayListHead++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2);
   2472     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2473 }
   2474 
   2475 void render_pause_red_coins(void) {
   2476     s8 x;
   2477 
   2478     for (x = 0; x < gRedCoinsCollected; x++) {
   2479         print_animated_red_coin(GFX_DIMENSIONS_FROM_RIGHT_EDGE(30) - x * 20, 16);
   2480     }
   2481 }
   2482 
   2483 #ifdef VERSION_EU
   2484 u8 gTextCourse[][7] = {
   2485     { TEXT_COURSE },
   2486     { TEXT_COURSE_FR },
   2487     { TEXT_COURSE_DE }
   2488 };
   2489 #define textCourse gTextCourse
   2490 #endif
   2491 
   2492 #if defined(VERSION_JP) || defined(VERSION_SH)
   2493     #define CRS_NUM_X1 93
   2494 #elif defined(VERSION_US) || defined(VERSION_CN)
   2495     #define CRS_NUM_X1 100
   2496 #elif defined(VERSION_EU)
   2497     #define CRS_NUM_X1 get_string_width(LANGUAGE_ARRAY(textCourse)) + 51
   2498 #endif
   2499 
   2500 #if defined(VERSION_EU)
   2501     #define TXT_COURSE_X      48
   2502     #define TXT_STAR_X        89
   2503     #define ACT_NAME_X        107
   2504     #define CRS_NAME_START    3
   2505     #define CRS_NAME_X        108
   2506     #define SECRET_CRS_NAME_X get_str_x_pos_from_center(159, &courseName[CRS_NAME_START], 10.0f)
   2507     #define MYSCORE_X         48
   2508 #elif defined(VERSION_CN)
   2509     #define TXT_COURSE_X      63
   2510     #define TXT_STAR_X        98
   2511     #define ACT_NAME_X        112
   2512     #define CRS_NAME_START    6
   2513     #define CRS_NAME_X        117
   2514     #define SECRET_CRS_NAME_X 94
   2515     #define MYSCORE_X         42
   2516 #else
   2517     #define TXT_COURSE_X      63
   2518     #define TXT_STAR_X        98
   2519     #define ACT_NAME_X        116
   2520     #define CRS_NAME_START    3
   2521     #define CRS_NAME_X        117
   2522     #define SECRET_CRS_NAME_X 94
   2523     #define MYSCORE_X         62
   2524 #endif
   2525 
   2526 void render_pause_my_score_coins(void) {
   2527 #ifdef VERSION_EU
   2528     u8 textMyScore[][10] = {
   2529         { TEXT_MY_SCORE },
   2530         { TEXT_MY_SCORE_FR },
   2531         { TEXT_MY_SCORE_DE }
   2532     };
   2533 #else
   2534     u8 textCourse[] = { TEXT_COURSE };
   2535     u8 textMyScore[] = { TEXT_MY_SCORE };
   2536 #endif
   2537     u8 textStar[] = { TEXT_STAR_DIFF };
   2538     u8 textUnfilledStar[] = { TEXT_UNFILLED_STAR };
   2539 
   2540     u8 strCourseNum[4];
   2541     void **courseNameTbl;
   2542     u8 *courseName;
   2543     void **actNameTbl;
   2544     u8 *actName;
   2545     u8 courseIndex;
   2546     u8 starFlags;
   2547 
   2548 #ifndef VERSION_EU
   2549     courseNameTbl = segmented_to_virtual(seg2_course_name_table);
   2550     actNameTbl = segmented_to_virtual(seg2_act_name_table);
   2551 #endif
   2552 
   2553     courseIndex = COURSE_NUM_TO_INDEX(gCurrCourseNum);
   2554     starFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum));
   2555 
   2556 #ifdef VERSION_EU
   2557     switch (gInGameLanguage) {
   2558         case LANGUAGE_ENGLISH:
   2559             actNameTbl = segmented_to_virtual(act_name_table_eu_en);
   2560             courseNameTbl = segmented_to_virtual(course_name_table_eu_en);
   2561             break;
   2562         case LANGUAGE_FRENCH:
   2563             actNameTbl = segmented_to_virtual(act_name_table_eu_fr);
   2564             courseNameTbl = segmented_to_virtual(course_name_table_eu_fr);
   2565             break;
   2566         case LANGUAGE_GERMAN:
   2567             actNameTbl = segmented_to_virtual(act_name_table_eu_de);
   2568             courseNameTbl = segmented_to_virtual(course_name_table_eu_de);
   2569             break;
   2570     }
   2571 #endif
   2572 
   2573     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
   2574     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2575 
   2576     if (courseIndex <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX)) {
   2577         print_hud_my_score_coins(1, gCurrSaveFileNum - 1, courseIndex, 178, 103);
   2578         print_hud_my_score_stars(gCurrSaveFileNum - 1, courseIndex, 118, 103);
   2579     }
   2580 
   2581     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
   2582     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   2583 
   2584     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2585 
   2586     if (courseIndex <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX)
   2587         && save_file_get_course_star_count(gCurrSaveFileNum - 1, courseIndex) != 0) {
   2588         print_generic_string(MYSCORE_X, 121, LANGUAGE_ARRAY(textMyScore));
   2589     }
   2590 
   2591     courseName = segmented_to_virtual(courseNameTbl[courseIndex]);
   2592 
   2593     if (courseIndex <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX)) {
   2594         print_generic_string(TXT_COURSE_X, 157, LANGUAGE_ARRAY(textCourse));
   2595         INT_TO_STR_DIFF(gCurrCourseNum, strCourseNum);
   2596         print_generic_string(CRS_NUM_X1, 157, strCourseNum);
   2597 
   2598         actName = segmented_to_virtual(actNameTbl[COURSE_NUM_TO_INDEX(gCurrCourseNum) * 6 + gDialogCourseActNum - 1]);
   2599 
   2600         if (starFlags & (1 << (gDialogCourseActNum - 1))) {
   2601             print_generic_string(TXT_STAR_X, 140, textStar);
   2602         } else {
   2603             print_generic_string(TXT_STAR_X, 140, textUnfilledStar);
   2604         }
   2605 
   2606         print_generic_string(ACT_NAME_X, 140, actName);
   2607 #ifndef VERSION_JP
   2608         print_generic_string(CRS_NAME_X, 157, &courseName[CRS_NAME_START]);
   2609     } else {
   2610         print_generic_string(SECRET_CRS_NAME_X, 157, &courseName[CRS_NAME_START]);
   2611 #endif
   2612     }
   2613 
   2614 #ifdef VERSION_JP
   2615     print_generic_string(CRS_NAME_X, 157, &courseName[CRS_NAME_START]);
   2616 #endif
   2617 
   2618     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   2619 }
   2620 
   2621 #if defined(VERSION_JP) || defined(VERSION_SH)
   2622     #define TXT1_X 4
   2623     #define TXT2_X 116
   2624     #define Y_VAL7 0
   2625 #else
   2626     #define TXT1_X 3
   2627     #define TXT2_X 119
   2628     #define Y_VAL7 2
   2629 #endif
   2630 
   2631 void render_pause_camera_options(s16 x, s16 y, s8 *index, s16 xIndex) {
   2632     UNUSED_CN u8 textLakituMario[] = { TEXT_LAKITU_MARIO };
   2633     UNUSED_CN u8 textLakituStop[] = { TEXT_LAKITU_STOP };
   2634 #ifdef VERSION_EU
   2635     u8 textNormalUpClose[][20] = {
   2636         { TEXT_NORMAL_UPCLOSE },
   2637         { TEXT_NORMAL_UPCLOSE_FR },
   2638         { TEXT_NORMAL_UPCLOSE_DE }
   2639     };
   2640     u8 textNormalFixed[][17] = {
   2641         { TEXT_NORMAL_FIXED },
   2642         { TEXT_NORMAL_FIXED_FR },
   2643         { TEXT_NORMAL_FIXED_DE },
   2644     };
   2645 #else
   2646     u8 textNormalUpClose[] = { TEXT_NORMAL_UPCLOSE };
   2647     u8 textNormalFixed[] = { TEXT_NORMAL_FIXED };
   2648 #endif
   2649 
   2650     handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, index, 1, 2);
   2651 
   2652     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   2653     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2654 
   2655 #ifdef VERSION_CN
   2656     print_generic_string(x + 14, y + 2, textNormalUpClose);
   2657     print_generic_string(x + 124, y + 2, textNormalFixed);
   2658 #else
   2659     print_generic_string(x + 14, y + 2, textLakituMario);
   2660     print_generic_string(x + TXT1_X, y - 13, LANGUAGE_ARRAY(textNormalUpClose));
   2661     print_generic_string(x + 124, y + 2, textLakituStop);
   2662     print_generic_string(x + TXT2_X, y - 13, LANGUAGE_ARRAY(textNormalFixed));
   2663 #endif
   2664 
   2665     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   2666     create_dl_translation_matrix(MENU_MTX_PUSH, x + ((*index - 1) * xIndex), y + Y_VAL7, 0);
   2667     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2668     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2669     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2670 
   2671     switch (*index) {
   2672         case CAM_SELECTION_MARIO:
   2673             cam_select_alt_mode(CAM_SELECTION_MARIO);
   2674             break;
   2675         case CAM_SELECTION_FIXED:
   2676             cam_select_alt_mode(CAM_SELECTION_FIXED);
   2677             break;
   2678     }
   2679 }
   2680 
   2681 #if defined(VERSION_JP) || defined(VERSION_SH)
   2682     #define X_VAL8 0
   2683     #define Y_VAL8 4
   2684     #define Y_OFFSET1 17
   2685     #define Y_OFFSET2 33
   2686 #elif defined(VERSION_US) || defined(VERSION_EU)
   2687     #define X_VAL8 4
   2688     #define Y_VAL8 2
   2689     #define Y_OFFSET1 17
   2690     #define Y_OFFSET2 33
   2691 #elif defined(VERSION_CN)
   2692     #define X_VAL8 4
   2693     #define Y_VAL8 2
   2694     #define Y_OFFSET1 20
   2695     #define Y_OFFSET2 38
   2696 #endif
   2697 
   2698 void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) {
   2699 #ifdef VERSION_EU
   2700     u8 textContinue[][10] = {
   2701         { TEXT_CONTINUE },
   2702         { TEXT_CONTINUE_FR },
   2703         { TEXT_CONTINUE_DE }
   2704     };
   2705     u8 textExitCourse[][15] = {
   2706         { TEXT_EXIT_COURSE },
   2707         { TEXT_EXIT_COURSE_FR },
   2708         { TEXT_EXIT_COURSE_DE }
   2709     };
   2710     u8 textCameraAngleR[][24] = {
   2711         { TEXT_CAMERA_ANGLE_R },
   2712         { TEXT_CAMERA_ANGLE_R_FR },
   2713         { TEXT_CAMERA_ANGLE_R_DE }
   2714     };
   2715 #else
   2716     u8 textContinue[] = { TEXT_CONTINUE };
   2717     u8 textExitCourse[] = { TEXT_EXIT_COURSE };
   2718     u8 textCameraAngleR[] = { TEXT_CAMERA_ANGLE_R };
   2719 #endif
   2720 
   2721     handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 3);
   2722 
   2723     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   2724     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2725 
   2726     print_generic_string(x + 10, y - 2, LANGUAGE_ARRAY(textContinue));
   2727     print_generic_string(x + 10, y - Y_OFFSET1, LANGUAGE_ARRAY(textExitCourse));
   2728 
   2729     if (*index != MENU_OPT_CAMERA_ANGLE_R) {
   2730         print_generic_string(x + 10, y - Y_OFFSET2, LANGUAGE_ARRAY(textCameraAngleR));
   2731         gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   2732 
   2733         create_dl_translation_matrix(MENU_MTX_PUSH, x - X_VAL8, (y - ((*index - 1) * yIndex)) - Y_VAL8, 0);
   2734 
   2735         gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2736         gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2737         gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2738     }
   2739 
   2740     if (*index == MENU_OPT_CAMERA_ANGLE_R) {
   2741         render_pause_camera_options(x - 42, y - 42, &gDialogCameraAngleIndex, 110);
   2742     }
   2743 }
   2744 
   2745 void render_pause_castle_menu_box(s16 x, s16 y) {
   2746     create_dl_translation_matrix(MENU_MTX_PUSH, x - 78, y - 32, 0);
   2747     create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.2f, 0.8f, 1.0f);
   2748     gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 105);
   2749     gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
   2750     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2751 
   2752     create_dl_translation_matrix(MENU_MTX_PUSH, x + 6, y - 28, 0);
   2753     create_dl_rotation_matrix(MENU_MTX_NOPUSH, 90.0f, 0, 0, 1.0f);
   2754     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2755     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2756     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2757 
   2758     create_dl_translation_matrix(MENU_MTX_PUSH, x - 9, y - 101, 0);
   2759     create_dl_rotation_matrix(MENU_MTX_NOPUSH, 270.0f, 0, 0, 1.0f);
   2760     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   2761     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   2762 }
   2763 
   2764 void highlight_last_course_complete_stars(void) {
   2765     u8 completedCourseIndex;
   2766 
   2767     if (gLastCompletedCourseNum == COURSE_NONE) {
   2768         completedCourseIndex = 0;
   2769     } else {
   2770         completedCourseIndex = COURSE_NUM_TO_INDEX(gLastCompletedCourseNum);
   2771 
   2772         if (completedCourseIndex >= COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES)) {
   2773             completedCourseIndex = COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES);
   2774         }
   2775     }
   2776 
   2777     gMenuLineNum = completedCourseIndex;
   2778 }
   2779 
   2780 #if defined(VERSION_EU)
   2781     #define PAUSE_X get_str_x_pos_from_center_scale(SCREEN_WIDTH / 2, textPause, 12.0f)
   2782 #elif defined(VERSION_CN)
   2783     #define PAUSE_X 113
   2784 #else
   2785     #define PAUSE_X 123
   2786 #endif
   2787 
   2788 void print_hud_pause_colorful_str(void) {
   2789     u8 textPause[] = { TEXT_PAUSE };
   2790 
   2791     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
   2792     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2793 
   2794     print_hud_lut_string(HUD_LUT_GLOBAL, PAUSE_X, 81, textPause);
   2795 
   2796     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
   2797 }
   2798 
   2799 void render_pause_castle_course_stars(s16 x, s16 y, s16 fileIndex, s16 courseIndex) {
   2800     s16 hasStar = 0;
   2801 
   2802 #ifdef VERSION_CN
   2803     u8 str[60];
   2804 #else
   2805     u8 str[30];
   2806 #endif
   2807 
   2808     u8 textStar[] = { TEXT_STAR };
   2809 
   2810     u8 starFlags = save_file_get_star_flags(fileIndex, courseIndex);
   2811     u16 starCount = save_file_get_course_star_count(fileIndex, courseIndex);
   2812 
   2813     u16 nextStar = 0;
   2814 
   2815     if (starFlags & (1 << 6)) {
   2816         starCount--;
   2817         print_generic_string(x + 89, y - 5, textStar);
   2818     }
   2819 
   2820     while (hasStar != starCount) {
   2821         if (starFlags & (1 << nextStar)) {
   2822 #ifdef VERSION_CN
   2823             str[nextStar * 4] = 0x00;
   2824             str[nextStar * 4 + 1] = DIALOG_CHAR_STAR_FILLED;
   2825 #else
   2826             str[nextStar * 2] = DIALOG_CHAR_STAR_FILLED;
   2827 #endif
   2828             hasStar++;
   2829         } else {
   2830 #ifdef VERSION_CN
   2831             str[nextStar * 4] = 0x00;
   2832             str[nextStar * 4 + 1] = DIALOG_CHAR_STAR_OPEN;
   2833 #else
   2834             str[nextStar * 2] = DIALOG_CHAR_STAR_OPEN;
   2835 #endif
   2836         }
   2837 
   2838 #ifdef VERSION_CN
   2839         str[nextStar * 4 + 2] = DIALOG_CHAR_SPECIAL_MODIFIER;
   2840         str[nextStar * 4 + 3] = DIALOG_CHAR_SPACE;
   2841 #else
   2842         str[nextStar * 2 + 1] = DIALOG_CHAR_SPACE;
   2843 #endif
   2844         nextStar++;
   2845     }
   2846 
   2847     if (starCount == nextStar && starCount != 6) {
   2848 #ifdef VERSION_CN
   2849         str[nextStar * 4] = DIALOG_CHAR_SPECIAL_MODIFIER;
   2850         str[nextStar * 4 + 1] = DIALOG_CHAR_SLASH; //! Meant to be DIALOG_CHAR_STAR_OPEN?
   2851         str[nextStar * 4 + 2] = DIALOG_CHAR_SPECIAL_MODIFIER;
   2852         str[nextStar * 4 + 3] = DIALOG_CHAR_SPACE;
   2853 #else
   2854         str[nextStar * 2] = DIALOG_CHAR_STAR_OPEN;
   2855         str[nextStar * 2 + 1] = DIALOG_CHAR_SPACE;
   2856 #endif
   2857         nextStar++;
   2858     }
   2859 
   2860 #ifdef VERSION_CN
   2861     str[nextStar * 4] = DIALOG_CHAR_SPECIAL_MODIFIER;
   2862     str[nextStar * 4 + 1] = DIALOG_CHAR_TERMINATOR;
   2863 #else
   2864     str[nextStar * 2] = DIALOG_CHAR_TERMINATOR;
   2865 #endif
   2866 
   2867     print_generic_string(x + 14, y + 13, str);
   2868 }
   2869 
   2870 void render_pause_castle_main_strings(s16 x, s16 y) {
   2871 #ifdef VERSION_EU
   2872     void **courseNameTbl;
   2873 #else
   2874     void **courseNameTbl = segmented_to_virtual(seg2_course_name_table);
   2875 #endif
   2876 
   2877 #ifdef VERSION_EU
   2878     u8 textCoin[] = { TEXT_COIN };
   2879     u8 textX[] = { TEXT_VARIABLE_X };
   2880 #else
   2881     u8 textCoin[] = { TEXT_COIN_X };
   2882 #endif
   2883 
   2884     void *courseName;
   2885 
   2886     u8 strVal[8];
   2887     s16 prevCourseIndex = gMenuLineNum;
   2888 
   2889 #ifdef VERSION_EU
   2890     switch (gInGameLanguage) {
   2891         case LANGUAGE_ENGLISH:
   2892             courseNameTbl = segmented_to_virtual(course_name_table_eu_en);
   2893             break;
   2894         case LANGUAGE_FRENCH:
   2895             courseNameTbl = segmented_to_virtual(course_name_table_eu_fr);
   2896             break;
   2897         case LANGUAGE_GERMAN:
   2898             courseNameTbl = segmented_to_virtual(course_name_table_eu_de);
   2899             break;
   2900     }
   2901 #endif
   2902 
   2903     handle_menu_scrolling(
   2904         MENU_SCROLL_VERTICAL, &gMenuLineNum,
   2905         COURSE_NUM_TO_INDEX(COURSE_MIN) - 1, COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES) + 1
   2906     );
   2907 
   2908     if (gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES) + 1) {
   2909         gMenuLineNum = COURSE_NUM_TO_INDEX(COURSE_MIN); // Exceeded max, set to min
   2910     }
   2911 
   2912     if (gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_MIN) - 1) {
   2913         gMenuLineNum = COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES); // Exceeded min, set to max
   2914     }
   2915 
   2916     if (gMenuLineNum != COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES)) {
   2917         while (save_file_get_course_star_count(gCurrSaveFileNum - 1, gMenuLineNum) == 0) {
   2918             if (gMenuLineNum >= prevCourseIndex) {
   2919                 gMenuLineNum++;
   2920             } else {
   2921                 gMenuLineNum--;
   2922             }
   2923 
   2924             if (gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX) + 1
   2925                 || gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_MIN) - 1) {
   2926                 gMenuLineNum = COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES);
   2927                 break;
   2928             }
   2929         }
   2930     }
   2931 
   2932     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   2933     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   2934 
   2935     if (gMenuLineNum <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX)) { // Main courses
   2936         courseName = segmented_to_virtual(courseNameTbl[gMenuLineNum]);
   2937         render_pause_castle_course_stars(x, y, gCurrSaveFileNum - 1, gMenuLineNum);
   2938         print_generic_string(x + 34, y - 5, textCoin);
   2939 #ifdef VERSION_EU
   2940         print_generic_string(x + 44, y - 5, textX);
   2941 #endif
   2942         INT_TO_STR_DIFF(save_file_get_course_coin_score(gCurrSaveFileNum - 1, gMenuLineNum), strVal);
   2943         print_generic_string(x + 54, y - 5, strVal);
   2944 #ifdef VERSION_EU
   2945         print_generic_string(x - 17, y + 30, courseName);
   2946 #endif
   2947     } else { // Castle secret stars
   2948         u8 textStarX[] = { TEXT_STAR_X };
   2949         courseName = segmented_to_virtual(courseNameTbl[COURSE_MAX]);
   2950         print_generic_string(x + 40, y + 13, textStarX);
   2951         INT_TO_STR_DIFF(save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_BONUS_STAGES - 1, COURSE_MAX - 1), strVal);
   2952         print_generic_string(x + 60, y + 13, strVal);
   2953 #ifdef VERSION_EU
   2954         print_generic_string(get_str_x_pos_from_center(x + 51, courseName, 10.0f), y + 30, courseName);
   2955 #endif
   2956     }
   2957 
   2958 #ifndef VERSION_EU
   2959     print_generic_string(x - 9, y + 30, courseName);
   2960 #endif
   2961 
   2962     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   2963 }
   2964 
   2965 s8 gCourseCompleteCoinsEqual = FALSE;
   2966 s32 gCourseCompleteScreenTimer = 0;
   2967 s32 gCourseCompleteCoins = 0;
   2968 s8 gHudFlash = 0;
   2969 
   2970 s16 render_pause_screen(void) {
   2971     s16 index;
   2972 
   2973 #ifdef VERSION_EU
   2974     gInGameLanguage = eu_get_language();
   2975 #endif
   2976 
   2977     switch (gMenuState) {
   2978         case MENU_STATE_PAUSE_SCREEN_OPENING:
   2979             gMenuLineNum = MENU_OPT_DEFAULT;
   2980             gMenuTextAlpha = 0;
   2981             level_set_transition(-1, NULL);
   2982             play_sound(SOUND_MENU_PAUSE, gGlobalSoundSource);
   2983 
   2984             if (gCurrCourseNum >= COURSE_MIN && gCurrCourseNum <= COURSE_MAX) {
   2985                 change_dialog_camera_angle();
   2986                 gMenuState = MENU_STATE_PAUSE_SCREEN_COURSE;
   2987             } else {
   2988                 highlight_last_course_complete_stars();
   2989                 gMenuState = MENU_STATE_PAUSE_SCREEN_CASTLE;
   2990             }
   2991             break;
   2992 
   2993         case MENU_STATE_PAUSE_SCREEN_COURSE:
   2994             shade_screen();
   2995             render_pause_my_score_coins();
   2996             render_pause_red_coins();
   2997 
   2998             if (gMarioStates[0].action & ACT_FLAG_PAUSE_EXIT) {
   2999                 render_pause_course_options(99, 93, &gMenuLineNum, 15);
   3000             }
   3001 
   3002 #ifdef VERSION_EU
   3003             if (gPlayer3Controller->buttonPressed & (A_BUTTON | START_BUTTON | Z_TRIG))
   3004 #else
   3005             if ((gPlayer3Controller->buttonPressed & A_BUTTON)
   3006              || (gPlayer3Controller->buttonPressed & START_BUTTON))
   3007 #endif
   3008             {
   3009                 level_set_transition(0, NULL);
   3010                 play_sound(SOUND_MENU_PAUSE_2, gGlobalSoundSource);
   3011                 gMenuState = MENU_STATE_DEFAULT;
   3012                 gMenuMode = MENU_MODE_NONE;
   3013 
   3014                 if (gMenuLineNum == MENU_OPT_EXIT_COURSE) {
   3015                     index = gMenuLineNum;
   3016                 } else { // MENU_OPT_CONTINUE or MENU_OPT_CAMERA_ANGLE_R
   3017                     index = MENU_OPT_DEFAULT;
   3018                 }
   3019 
   3020                 return index;
   3021             }
   3022             break;
   3023 
   3024         case MENU_STATE_PAUSE_SCREEN_CASTLE:
   3025             shade_screen();
   3026             print_hud_pause_colorful_str();
   3027             render_pause_castle_menu_box(160, 143);
   3028             render_pause_castle_main_strings(104, 60);
   3029 
   3030 #ifdef VERSION_EU
   3031             if (gPlayer3Controller->buttonPressed & (A_BUTTON | START_BUTTON | Z_TRIG))
   3032 #else
   3033             if ((gPlayer3Controller->buttonPressed & A_BUTTON)
   3034              || (gPlayer3Controller->buttonPressed & START_BUTTON))
   3035 #endif
   3036             {
   3037                 level_set_transition(0, NULL);
   3038                 play_sound(SOUND_MENU_PAUSE_2, gGlobalSoundSource);
   3039                 gMenuMode = MENU_MODE_NONE;
   3040                 gMenuState = MENU_STATE_DEFAULT;
   3041 
   3042                 return MENU_OPT_DEFAULT;
   3043             }
   3044             break;
   3045     }
   3046 
   3047     if (gMenuTextAlpha < 250) {
   3048         gMenuTextAlpha += 25;
   3049     }
   3050 
   3051     return MENU_OPT_NONE;
   3052 }
   3053 
   3054 #if defined(VERSION_JP)
   3055     #define TXT_HISCORE_X 112
   3056     #define TXT_HISCORE_Y 48
   3057     #define TXT_CONGRATS_X 60
   3058     #define TXT_CONGRATS_Y 67
   3059 #elif defined(VERSION_US)
   3060     #define TXT_HISCORE_X 109
   3061     #define TXT_HISCORE_Y 36
   3062     #define TXT_CONGRATS_X 70
   3063     #define TXT_CONGRATS_Y 67
   3064 #elif defined(VERSION_EU)
   3065     #define TXT_HISCORE_X get_str_x_pos_from_center_scale(160, LANGUAGE_ARRAY(textHiScore), 12.0f)
   3066     #define TXT_HISCORE_Y 36
   3067     #define TXT_CONGRATS_X get_str_x_pos_from_center_scale(160, LANGUAGE_ARRAY(textCongratulations), 12.0f)
   3068     #define TXT_CONGRATS_Y 67
   3069 #elif defined(VERSION_SH)
   3070     #define TXT_HISCORE_X 118
   3071     #define TXT_HISCORE_Y 48
   3072     #define TXT_CONGRATS_X 70
   3073     #define TXT_CONGRATS_Y 67
   3074 #elif defined(VERSION_CN)
   3075     #define TXT_HISCORE_X 109
   3076     #define TXT_HISCORE_Y 26
   3077     #define TXT_CONGRATS_X 100
   3078     #define TXT_CONGRATS_Y 62
   3079 #endif
   3080 
   3081 #define HUD_PRINT_HISCORE         0
   3082 #define HUD_PRINT_CONGRATULATIONS 1
   3083 
   3084 void print_hud_course_complete_string(s8 str) {
   3085 #ifdef VERSION_EU
   3086     u8 textHiScore[][15] = {
   3087         { TEXT_HUD_HI_SCORE },
   3088         { TEXT_HUD_HI_SCORE_FR },
   3089         { TEXT_HUD_HI_SCORE_DE }
   3090     };
   3091     u8 textCongratulations[][16] = {
   3092         { TEXT_HUD_CONGRATULATIONS },
   3093         { TEXT_HUD_CONGRATULATIONS_FR },
   3094         { TEXT_HUD_CONGRATULATIONS_DE }
   3095     };
   3096 #else
   3097     u8 textHiScore[] = { TEXT_HUD_HI_SCORE };
   3098     u8 textCongratulations[] = { TEXT_HUD_CONGRATULATIONS };
   3099 #endif
   3100 
   3101     u8 color = sins(gMenuTextColorTransTimer) * 50.0f + 200.0f;
   3102 
   3103     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
   3104     gDPSetEnvColor(gDisplayListHead++, color, color, color, 255);
   3105 
   3106     if (str == HUD_PRINT_HISCORE) {
   3107         print_hud_lut_string(HUD_LUT_GLOBAL, TXT_HISCORE_X, TXT_HISCORE_Y, LANGUAGE_ARRAY(textHiScore));
   3108     } else { // HUD_PRINT_CONGRATULATIONS
   3109         print_hud_lut_string(HUD_LUT_GLOBAL, TXT_CONGRATS_X, TXT_CONGRATS_Y, LANGUAGE_ARRAY(textCongratulations));
   3110     }
   3111 
   3112     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
   3113 }
   3114 
   3115 void print_hud_course_complete_coins(s16 x, s16 y) {
   3116     u8 courseCompleteCoinsStr[4];
   3117     u8 hudTextSymCoin[] = { GLYPH_COIN, GLYPH_SPACE };
   3118     u8 hudTextSymX[] = { GLYPH_MULTIPLY, GLYPH_SPACE };
   3119 
   3120     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
   3121     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
   3122 
   3123     print_hud_lut_string(HUD_LUT_GLOBAL, x, y, hudTextSymCoin);
   3124     print_hud_lut_string(HUD_LUT_GLOBAL, x + 16, y, hudTextSymX);
   3125 
   3126     int_to_str(gCourseCompleteCoins, courseCompleteCoinsStr);
   3127     print_hud_lut_string(HUD_LUT_GLOBAL, x + 32, y, courseCompleteCoinsStr);
   3128 
   3129     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
   3130 
   3131     if (gCourseCompleteCoins >= gHudDisplay.coins) {
   3132         gCourseCompleteCoinsEqual = TRUE;
   3133         gCourseCompleteCoins = gHudDisplay.coins;
   3134 
   3135         if (gGotFileCoinHiScore) {
   3136             print_hud_course_complete_string(HUD_PRINT_HISCORE);
   3137         }
   3138     } else {
   3139         if ((gCourseCompleteScreenTimer & 1) || gHudDisplay.coins > 70) {
   3140             gCourseCompleteCoins++;
   3141             play_sound(SOUND_MENU_YOSHI_GAIN_LIVES, gGlobalSoundSource);
   3142 
   3143             if (gCourseCompleteCoins == 50 || gCourseCompleteCoins == 100 || gCourseCompleteCoins == 150) {
   3144                 play_sound(SOUND_GENERAL_COLLECT_1UP, gGlobalSoundSource);
   3145                 gMarioState->numLives++;
   3146             }
   3147         }
   3148 
   3149         if (gCourseCompleteCoins == gHudDisplay.coins && gGotFileCoinHiScore) {
   3150             play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gGlobalSoundSource);
   3151         }
   3152     }
   3153 }
   3154 
   3155 void play_star_fanfare_and_flash_hud(s32 arg, u8 starFlag) {
   3156     if (gCourseCompleteCoins == gHudDisplay.coins && !(gCurrCourseStarFlags & starFlag) && gHudFlash == 0) {
   3157         play_star_fanfare();
   3158         gHudFlash = arg;
   3159     }
   3160 }
   3161 
   3162 #ifdef VERSION_EU
   3163     #define TXT_NAME_X1 centerX
   3164     #define TXT_NAME_X2 centerX - 1
   3165 #else
   3166     #define TXT_NAME_X1 71
   3167     #define TXT_NAME_X2 TXT_NAME_X1 - 2
   3168 #endif
   3169 
   3170 #if defined(VERSION_JP) || defined(VERSION_SH)
   3171     #define CRS_NUM_X2 95
   3172     #define CRS_NUM_X3 CRS_NUM_X2 - 2
   3173     #define TXT_CLEAR_X1 205
   3174     #define TXT_CLEAR_X2 TXT_CLEAR_X1 - 2
   3175 #else
   3176     #define CRS_NUM_X2 104
   3177     #define CRS_NUM_X3 CRS_NUM_X2 - 2
   3178     #define TXT_CLEAR_X1 get_string_width(name) + 81
   3179     #define TXT_CLEAR_X2 TXT_CLEAR_X1 - 2
   3180 #endif
   3181 
   3182 void render_course_complete_lvl_info_and_hud_str(void) {
   3183 #if defined(VERSION_JP)
   3184     u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE };
   3185     u8 textCourse[] = { TEXT_COURSE };
   3186     u8 textCatch[] = { TEXT_CATCH };
   3187     u8 textClear[] = { TEXT_CLEAR };
   3188 #elif defined(VERSION_EU)
   3189     UNUSED u8 textCatch[] = { TEXT_CATCH }; // unused in EU
   3190     u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE };
   3191 #else
   3192     u8 textCourse[] = { TEXT_COURSE };
   3193     UNUSED u8 textCatch[] = { TEXT_CATCH }; // unused in US and iQue
   3194     u8 textClear[] = { TEXT_CLEAR };
   3195     u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE };
   3196 #endif
   3197 
   3198     void **actNameTbl;
   3199     void **courseNameTbl;
   3200     u8 *name;
   3201 
   3202     u8 strCourseNum[4];
   3203 
   3204 #ifdef VERSION_EU
   3205     s16 centerX;
   3206     switch (gInGameLanguage) {
   3207         case LANGUAGE_ENGLISH:
   3208             actNameTbl = segmented_to_virtual(act_name_table_eu_en);
   3209             courseNameTbl = segmented_to_virtual(course_name_table_eu_en);
   3210             break;
   3211         case LANGUAGE_FRENCH:
   3212             actNameTbl = segmented_to_virtual(act_name_table_eu_fr);
   3213             courseNameTbl = segmented_to_virtual(course_name_table_eu_fr);
   3214             break;
   3215         case LANGUAGE_GERMAN:
   3216             actNameTbl = segmented_to_virtual(act_name_table_eu_de);
   3217             courseNameTbl = segmented_to_virtual(course_name_table_eu_de);
   3218             break;
   3219     }
   3220 #else
   3221     actNameTbl = segmented_to_virtual(seg2_act_name_table);
   3222     courseNameTbl = segmented_to_virtual(seg2_course_name_table);
   3223 #endif
   3224 
   3225     if (gLastCompletedCourseNum <= COURSE_STAGES_MAX) { // Main courses
   3226         print_hud_course_complete_coins(118, 103);
   3227         play_star_fanfare_and_flash_hud(1, 1 << (gLastCompletedStarNum - 1));
   3228 
   3229         if (gLastCompletedStarNum == 7) {
   3230             name = segmented_to_virtual(actNameTbl[COURSE_STAGES_MAX * 6 + 1]);
   3231         } else {
   3232             name = segmented_to_virtual(actNameTbl[COURSE_NUM_TO_INDEX(gLastCompletedCourseNum) * 6 + gLastCompletedStarNum - 1]);
   3233         }
   3234 
   3235         // Print course number
   3236         gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   3237 
   3238         INT_TO_STR_DIFF(gLastCompletedCourseNum, strCourseNum);
   3239 
   3240         gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, gMenuTextAlpha);
   3241         print_generic_string(65, 165, LANGUAGE_ARRAY(textCourse));
   3242         print_generic_string(CRS_NUM_X2, 165, strCourseNum);
   3243 
   3244         gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   3245         print_generic_string(63, 167, LANGUAGE_ARRAY(textCourse));
   3246         print_generic_string(CRS_NUM_X3, 167, strCourseNum);
   3247 
   3248         gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   3249     } else if (gLastCompletedCourseNum == COURSE_BITDW || gLastCompletedCourseNum == COURSE_BITFS) { // Bowser courses
   3250         name = segmented_to_virtual(courseNameTbl[COURSE_NUM_TO_INDEX(gLastCompletedCourseNum)]);
   3251 
   3252         // Print course name and clear text
   3253         gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   3254 
   3255         gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, gMenuTextAlpha);
   3256 #ifdef VERSION_EU
   3257         centerX = get_str_x_pos_from_center(153, name, 12.0f);
   3258 #endif
   3259         print_generic_string(TXT_NAME_X1, 130, name);
   3260 #ifndef VERSION_EU
   3261         print_generic_string(TXT_CLEAR_X1, 130, textClear);
   3262 #endif
   3263 
   3264         gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   3265         print_generic_string(TXT_NAME_X2, 132, name);
   3266 #ifndef VERSION_EU
   3267         print_generic_string(TXT_CLEAR_X2, 132, textClear);
   3268 #endif
   3269 
   3270         gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   3271 
   3272         print_hud_course_complete_string(HUD_PRINT_CONGRATULATIONS);
   3273         print_hud_course_complete_coins(118, 111);
   3274         play_star_fanfare_and_flash_hud(2, 0); //! 2 isn't defined, originally for key hud?
   3275 
   3276         return;
   3277     } else { // Castle secret stars
   3278         name = segmented_to_virtual(actNameTbl[COURSE_STAGES_MAX * 6]);
   3279 
   3280         print_hud_course_complete_coins(118, 103);
   3281         play_star_fanfare_and_flash_hud(1, 1 << (gLastCompletedStarNum - 1));
   3282     }
   3283 
   3284     // Print star glyph
   3285     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
   3286 
   3287     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   3288     print_hud_lut_string(HUD_LUT_GLOBAL, 55, 77, textSymStar);
   3289 
   3290     gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
   3291 
   3292     // Print act name and catch text
   3293     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   3294 
   3295     gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, gMenuTextAlpha);
   3296     print_generic_string(76, 145, name);
   3297 #if defined(VERSION_JP) || defined(VERSION_SH)
   3298     print_generic_string(220, 145, textCatch);
   3299 #endif
   3300 
   3301     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   3302     print_generic_string(74, 147, name);
   3303 #if defined(VERSION_JP) || defined(VERSION_SH)
   3304     print_generic_string(218, 147, textCatch);
   3305 #endif
   3306 
   3307     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   3308 }
   3309 
   3310 #if defined(VERSION_JP) || defined(VERSION_SH)
   3311     #define X_VAL9 x
   3312     #define TXT_SAVEOPTIONS_X x + 10
   3313     #define TXT_SAVECONT_Y 2
   3314     #define TXT_SAVEQUIT_Y 18
   3315     #define TXT_CONTNOSAVE_Y 38
   3316 #else
   3317 #ifdef VERSION_EU
   3318     #define X_VAL9 xOffset - 12
   3319     #define TXT_SAVEOPTIONS_X xOffset
   3320 #else
   3321     #define X_VAL9 x
   3322     #define TXT_SAVEOPTIONS_X x + 12
   3323 #endif
   3324     #define TXT_SAVECONT_Y 0
   3325     #define TXT_SAVEQUIT_Y 20
   3326     #define TXT_CONTNOSAVE_Y 40
   3327 #endif
   3328 
   3329 #ifdef VERSION_EU
   3330 void render_save_confirmation(s16 y, s8 *index, s16 yOffset)
   3331 #else
   3332 void render_save_confirmation(s16 x, s16 y, s8 *index, s16 yOffset)
   3333 #endif
   3334 {
   3335 #ifdef VERSION_EU
   3336     u8 textSaveAndContinue[][24] = {
   3337         { TEXT_SAVE_AND_CONTINUE },
   3338         { TEXT_SAVE_AND_CONTINUE_FR },
   3339         { TEXT_SAVE_AND_CONTINUE_DE }
   3340     };
   3341     u8 textSaveAndQuit[][22] = {
   3342         { TEXT_SAVE_AND_QUIT },
   3343         { TEXT_SAVE_AND_QUIT_FR },
   3344         { TEXT_SAVE_AND_QUIT_DE }
   3345     };
   3346     u8 textContinueWithoutSave[][27] = {
   3347         { TEXT_CONTINUE_WITHOUT_SAVING },
   3348         { TEXT_CONTINUE_WITHOUT_SAVING_FR },
   3349         { TEXT_CONTINUE_WITHOUT_SAVING_DE }
   3350     };
   3351     s16 xOffset = get_str_x_pos_from_center(160, LANGUAGE_ARRAY(textContinueWithoutSave), 12.0f);
   3352 #else
   3353     u8 textSaveAndContinue[] = { TEXT_SAVE_AND_CONTINUE };
   3354     u8 textSaveAndQuit[] = { TEXT_SAVE_AND_QUIT };
   3355     u8 textContinueWithoutSave[] = { TEXT_CONTINUE_WITHOUT_SAVING };
   3356 #endif
   3357 
   3358     handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 3);
   3359 
   3360     gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
   3361     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   3362 
   3363     print_generic_string(TXT_SAVEOPTIONS_X, y + TXT_SAVECONT_Y, LANGUAGE_ARRAY(textSaveAndContinue));
   3364     print_generic_string(TXT_SAVEOPTIONS_X, y - TXT_SAVEQUIT_Y, LANGUAGE_ARRAY(textSaveAndQuit));
   3365     print_generic_string(TXT_SAVEOPTIONS_X, y - TXT_CONTNOSAVE_Y, LANGUAGE_ARRAY(textContinueWithoutSave));
   3366 
   3367     gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
   3368 
   3369     create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL9, y - ((*index - 1) * yOffset), 0);
   3370 
   3371     gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha);
   3372     gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
   3373 
   3374     gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
   3375 }
   3376 
   3377 s16 render_course_complete_screen(void) {
   3378     s16 index;
   3379 #ifdef VERSION_EU
   3380     gInGameLanguage = eu_get_language();
   3381 #endif
   3382 
   3383     switch (gMenuState) {
   3384         case MENU_STATE_COURSE_COMPLETE_SCREEN_OPENING:
   3385             render_course_complete_lvl_info_and_hud_str();
   3386             if (gCourseCompleteScreenTimer > 100 && gCourseCompleteCoinsEqual == TRUE) {
   3387                 gMenuState = MENU_STATE_COURSE_COMPLETE_SCREEN_OPEN;
   3388                 level_set_transition(-1, NULL);
   3389                 gMenuTextAlpha = 0;
   3390                 gMenuLineNum = MENU_OPT_DEFAULT;
   3391             }
   3392             break;
   3393 
   3394         case MENU_STATE_COURSE_COMPLETE_SCREEN_OPEN:
   3395             shade_screen();
   3396             render_course_complete_lvl_info_and_hud_str();
   3397 #ifdef VERSION_EU
   3398             render_save_confirmation(86, &gMenuLineNum, 20);
   3399 #else
   3400             render_save_confirmation(100, 86, &gMenuLineNum, 20);
   3401 #endif
   3402 
   3403             if (gCourseCompleteScreenTimer > 110
   3404                 && ((gPlayer3Controller->buttonPressed & A_BUTTON)
   3405                  || (gPlayer3Controller->buttonPressed & START_BUTTON)
   3406 #ifdef VERSION_EU
   3407                  || (gPlayer3Controller->buttonPressed & Z_TRIG)
   3408 #endif
   3409                 )) {
   3410                 level_set_transition(0, NULL);
   3411                 play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
   3412                 gMenuState = MENU_STATE_DEFAULT;
   3413                 gMenuMode = MENU_MODE_NONE;
   3414                 index = gMenuLineNum;
   3415                 gCourseCompleteScreenTimer = 0;
   3416                 gCourseCompleteCoins = 0;
   3417                 gCourseCompleteCoinsEqual = FALSE;
   3418                 gHudFlash = 0;
   3419 
   3420                 return index;
   3421             }
   3422             break;
   3423     }
   3424 
   3425     if (gMenuTextAlpha < 250) {
   3426         gMenuTextAlpha += 25;
   3427     }
   3428 
   3429     gCourseCompleteScreenTimer++;
   3430 
   3431     return MENU_OPT_NONE;
   3432 }
   3433 
   3434 s16 render_menus_and_dialogs(void) {
   3435     s16 index = MENU_OPT_NONE;
   3436 
   3437     create_dl_ortho_matrix();
   3438 
   3439     if (gMenuMode != MENU_MODE_NONE) {
   3440         switch (gMenuMode) {
   3441             case MENU_MODE_UNUSED_0:
   3442                 index = render_pause_screen();
   3443                 break;
   3444             case MENU_MODE_RENDER_PAUSE_SCREEN:
   3445                 index = render_pause_screen();
   3446                 break;
   3447             case MENU_MODE_RENDER_COURSE_COMPLETE_SCREEN:
   3448                 index = render_course_complete_screen();
   3449                 break;
   3450             case MENU_MODE_UNUSED_3:
   3451                 index = render_course_complete_screen();
   3452                 break;
   3453         }
   3454 
   3455         gMenuTextColorTransTimer = (s16) gMenuTextColorTransTimer + 0x1000;
   3456     } else if (gDialogID != DIALOG_NONE) {
   3457         // The Peach "Dear Mario" message needs to be repositioned separately
   3458         if (gDialogID == DIALOG_020) {
   3459             print_peach_letter_message();
   3460             return index;
   3461         }
   3462 
   3463         render_dialog_entries();
   3464         gMenuTextColorTransTimer = (s16) gMenuTextColorTransTimer + 0x1000;
   3465     }
   3466 
   3467     return index;
   3468 }