print.c (14303B)
1 #include <PR/ultratypes.h> 2 #include <PR/gbi.h> 3 4 #include "config.h" 5 #include "gfx_dimensions.h" 6 #include "game_init.h" 7 #include "memory.h" 8 #include "print.h" 9 #include "segment2.h" 10 11 /** 12 * This file handles printing and formatting the colorful text that 13 * appears when printing things such as "PRESS START". 14 */ 15 16 struct TextLabel { 17 u32 x; 18 u32 y; 19 s16 length; 20 char buffer[50]; 21 }; 22 23 /** 24 * Stores the text to be rendered on screen 25 * and how they are to be rendered. 26 */ 27 FORCE_BSS struct TextLabel *sTextLabels[52]; 28 s16 sTextLabelsCount = 0; 29 30 /** 31 * Returns n to the exponent power, only for non-negative powers. 32 */ 33 s32 int_pow(s32 n, s32 exponent) { 34 s32 result = 1; 35 s32 i; 36 37 for (i = 0; i < exponent; i++) { 38 result = n * result; 39 } 40 41 return result; 42 } 43 44 /** 45 * Formats an integer n for print by fitting it to width, prefixing with a negative, 46 * and converting the base. 47 */ 48 void format_integer(s32 n, s32 base, char *dest, s32 *totalLength, u8 width, s8 zeroPad) { 49 u32 powBase; 50 s32 numDigits = 0; 51 s32 i; 52 s32 len = 0; 53 s8 digit; 54 s8 negative = FALSE; 55 char pad; 56 57 if (zeroPad == TRUE) { 58 pad = '0'; 59 } else { 60 pad = -1; 61 } 62 63 if (n != 0) { 64 // Formats a negative number for negative prefix. 65 if (n < 0) { 66 n = -n; 67 negative = TRUE; 68 } 69 70 // Increments the number of digits until length is long enough. 71 while (TRUE) { 72 powBase = int_pow(base, numDigits); 73 74 if (powBase > (u32) n) { 75 break; 76 } 77 78 numDigits++; 79 } 80 81 // Add leading pad to fit width. 82 if (width > numDigits) { 83 for (len = 0; len < width - numDigits; len++) dest[len] = pad; 84 85 // Needs 1 length to print negative prefix. 86 if (negative == TRUE) { 87 len--; 88 } 89 } 90 91 // Use 'M' prefix to indicate negative numbers. 92 if (negative == TRUE) { 93 dest[len] = 'M'; 94 len++; 95 } 96 97 // Transfer the digits into the proper base. 98 for (i = numDigits - 1; i >= 0; i--) { 99 powBase = int_pow(base, i); 100 digit = n / powBase; 101 102 if (digit < 10) { 103 *(dest + len + (numDigits - 1) - i) = digit + '0'; 104 } else { 105 *(dest + len + (numDigits - 1) - i) = digit + '7'; 106 } 107 108 n -= digit * powBase; 109 } 110 } else { // n is zero. 111 numDigits = 1; 112 if (width > numDigits) { 113 for (len = 0; len < width - numDigits; len++) dest[len] = pad; 114 } 115 dest[len] = '0'; 116 } 117 118 *totalLength += numDigits + len; 119 } 120 121 /** 122 * Determines the width of the number for printing, writing to 'width'. 123 * Additionally, this determines if a number should be zero-padded, 124 * writing to 'zeroPad'. 125 */ 126 void parse_width_field(const char *str, s32 *srcIndex, u8 *width, s8 *zeroPad) { 127 s8 digits[12]; // unknown length 128 s8 digitsLen = 0; 129 s16 i; 130 131 // If first character is 0, then the string should be zero padded. 132 if (str[*srcIndex] == '0') { 133 *zeroPad = TRUE; 134 } 135 136 // Read width digits up until the 'd' or 'x' format specifier. 137 while (str[*srcIndex] != 'd' && str[*srcIndex] != 'x') { 138 digits[digitsLen] = str[*srcIndex] - '0'; 139 140 if (digits[digitsLen] < 0 || digits[digitsLen] >= 10) { // not a valid digit 141 *width = 0; 142 return; 143 } 144 145 digitsLen++; 146 (*srcIndex)++; 147 } 148 149 // No digits 150 if (digitsLen == 0) { 151 return; 152 } 153 154 // Sum the digits to calculate the total width. 155 for (i = 0; i < digitsLen - 1; i++) { 156 *width = *width + ((digitsLen - i - 1) * 10) * digits[i]; 157 } 158 159 *width = *width + digits[digitsLen - 1]; 160 } 161 162 /** 163 * Takes a number, finds the intended base, formats the number, and prints it 164 * at the given X & Y coordinates. 165 * 166 * Warning: this fails on too large numbers, because format_integer has bugs 167 * related to overflow. For romhacks, prefer sprintf + print_text. 168 */ 169 void print_text_fmt_int(s32 x, s32 y, const char *str, s32 n) { 170 char c = 0; 171 s8 zeroPad = FALSE; 172 u8 width = 0; 173 s32 base = 0; 174 s32 len = 0; 175 s32 srcIndex = 0; 176 177 // Don't continue if there is no memory to do so. 178 if ((sTextLabels[sTextLabelsCount] = mem_pool_alloc(gEffectsMemoryPool, 179 sizeof(struct TextLabel))) == NULL) { 180 return; 181 } 182 183 sTextLabels[sTextLabelsCount]->x = x; 184 sTextLabels[sTextLabelsCount]->y = y; 185 186 c = str[srcIndex]; 187 188 while (c != '\0') { 189 if (c == '%') { 190 srcIndex++; 191 192 parse_width_field(str, &srcIndex, &width, &zeroPad); 193 194 if (str[srcIndex] != 'd' && str[srcIndex] != 'x') { 195 break; 196 } 197 if (str[srcIndex] == 'd') { 198 base = 10; 199 } 200 if (str[srcIndex] == 'x') { 201 base = 16; 202 } 203 204 srcIndex++; 205 206 format_integer(n, base, sTextLabels[sTextLabelsCount]->buffer + len, &len, width, zeroPad); 207 } else { // straight copy 208 sTextLabels[sTextLabelsCount]->buffer[len] = c; 209 len++; 210 srcIndex++; 211 } 212 c = str[srcIndex]; 213 } 214 215 sTextLabels[sTextLabelsCount]->length = len; 216 sTextLabelsCount++; 217 } 218 219 /** 220 * Prints text in the colorful lettering at given X, Y coordinates. 221 */ 222 void print_text(s32 x, s32 y, const char *str) { 223 char c = 0; 224 s32 length = 0; 225 s32 srcIndex = 0; 226 227 // Don't continue if there is no memory to do so. 228 if ((sTextLabels[sTextLabelsCount] = mem_pool_alloc(gEffectsMemoryPool, 229 sizeof(struct TextLabel))) == NULL) { 230 return; 231 } 232 233 sTextLabels[sTextLabelsCount]->x = x; 234 sTextLabels[sTextLabelsCount]->y = y; 235 236 c = str[srcIndex]; 237 238 // Set the array with the text to print while finding length. 239 while (c != '\0') { 240 sTextLabels[sTextLabelsCount]->buffer[length] = c; 241 length++; 242 srcIndex++; 243 c = str[srcIndex]; 244 } 245 246 sTextLabels[sTextLabelsCount]->length = length; 247 sTextLabelsCount++; 248 } 249 250 /** 251 * Prints text in the colorful lettering centered at given X, Y coordinates. 252 */ 253 void print_text_centered(s32 x, s32 y, const char *str) { 254 char c = 0; 255 UNUSED s8 unused1 = 0; 256 UNUSED s32 unused2 = 0; 257 s32 length = 0; 258 s32 srcIndex = 0; 259 #ifdef VERSION_CN 260 s32 width = 0; 261 #endif 262 263 // Don't continue if there is no memory to do so. 264 if ((sTextLabels[sTextLabelsCount] = mem_pool_alloc(gEffectsMemoryPool, 265 sizeof(struct TextLabel))) == NULL) { 266 return; 267 } 268 269 c = str[srcIndex]; 270 271 // Set the array with the text to print while finding length. 272 while (c != '\0') { 273 #ifdef VERSION_CN 274 if ((u8) c == 0xB0 || (u8) c == 0xC0) { 275 width = 16; 276 } else { 277 width = 12; 278 } 279 #endif 280 sTextLabels[sTextLabelsCount]->buffer[length] = c; 281 length++; 282 srcIndex++; 283 c = str[srcIndex]; 284 } 285 286 sTextLabels[sTextLabelsCount]->length = length; 287 #ifdef VERSION_CN 288 sTextLabels[sTextLabelsCount]->x = x - width * length / 2; 289 #else 290 sTextLabels[sTextLabelsCount]->x = x - 12 * length / 2; 291 #endif 292 sTextLabels[sTextLabelsCount]->y = y; 293 sTextLabelsCount++; 294 } 295 296 /** 297 * Converts a char into the proper colorful glyph for the char. 298 */ 299 s8 char_to_glyph_index(char c) { 300 if (c >= 'A' && c <= 'Z') { 301 return c - 55; 302 } 303 304 if (c >= 'a' && c <= 'z') { 305 return c - 87; 306 } 307 308 if (c >= '0' && c <= '9') { 309 return c - 48; 310 } 311 312 if (c == ' ') { 313 return GLYPH_SPACE; 314 } 315 316 if (c == '!') { 317 return GLYPH_EXCLAMATION_PNT; // !, JP only 318 } 319 320 if (c == '#') { 321 return GLYPH_TWO_EXCLAMATION; // !!, JP only 322 } 323 324 if (c == '?') { 325 return GLYPH_QUESTION_MARK; // ?, JP only 326 } 327 328 if (c == '&') { 329 return GLYPH_AMPERSAND; // &, JP only 330 } 331 332 if (c == '%') { 333 return GLYPH_PERCENT; // %, JP only 334 } 335 336 if (c == '*') { 337 return GLYPH_MULTIPLY; // x 338 } 339 340 if (c == '+') { 341 return GLYPH_COIN; // coin 342 } 343 344 if (c == ',') { 345 return GLYPH_MARIO_HEAD; // Imagine I drew Mario's head 346 } 347 348 if (c == '-') { 349 return GLYPH_STAR; // star 350 } 351 352 if (c == '.') { 353 return GLYPH_PERIOD; // large shaded dot, JP only 354 } 355 356 if (c == '/') { 357 return GLYPH_BETA_KEY; // beta key, JP only. Reused for Ü in EU. 358 } 359 360 return GLYPH_SPACE; 361 } 362 363 /** 364 * Adds an individual glyph to be rendered. 365 */ 366 void add_glyph_texture(s8 glyphIndex) { 367 const u8 *const *glyphs = segmented_to_virtual(main_hud_lut); 368 369 gDPPipeSync(gDisplayListHead++); 370 #ifdef VERSION_CN 371 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, glyphs[(u8) glyphIndex]); 372 #else 373 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, glyphs[glyphIndex]); 374 #endif 375 gSPDisplayList(gDisplayListHead++, dl_hud_img_load_tex_block); 376 } 377 378 #ifndef WIDESCREEN 379 /** 380 * Clips textrect into the boundaries defined. 381 */ 382 void clip_to_bounds(s32 *x, s32 *y) { 383 if (*x < TEXRECT_MIN_X) { 384 *x = TEXRECT_MIN_X; 385 } 386 387 if (*x > TEXRECT_MAX_X) { 388 *x = TEXRECT_MAX_X; 389 } 390 391 if (*y < TEXRECT_MIN_Y) { 392 *y = TEXRECT_MIN_Y; 393 } 394 395 if (*y > TEXRECT_MAX_Y) { 396 *y = TEXRECT_MAX_Y; 397 } 398 } 399 #endif 400 401 /** 402 * Renders the glyph that's set at the given position. 403 */ 404 #ifdef VERSION_CN 405 void render_textrect(s32 x, s32 y, s32 pos, s32 width) { 406 s32 rectBaseX = x + pos * width; 407 #else 408 void render_textrect(s32 x, s32 y, s32 pos) { 409 s32 rectBaseX = x + pos * 12; 410 #endif 411 s32 rectBaseY = 224 - y; 412 s32 rectX; 413 s32 rectY; 414 415 #ifndef WIDESCREEN 416 // For widescreen we must allow drawing outside the usual area 417 clip_to_bounds(&rectBaseX, &rectBaseY); 418 #endif 419 rectX = rectBaseX; 420 rectY = rectBaseY; 421 gSPTextureRectangle(gDisplayListHead++, rectX << 2, rectY << 2, (rectX + 15) << 2, 422 (rectY + 15) << 2, G_TX_RENDERTILE, 0, 0, 4 << 10, 1 << 10); 423 } 424 425 /** 426 * Renders the text in sTextLabels on screen at the proper locations by iterating 427 * a for loop. 428 */ 429 void render_text_labels(void) { 430 s32 i; 431 s32 j; 432 s8 glyphIndex; 433 Mtx *mtx; 434 435 if (sTextLabelsCount == 0) { 436 return; 437 } 438 439 mtx = alloc_display_list(sizeof(*mtx)); 440 441 if (mtx == NULL) { 442 sTextLabelsCount = 0; 443 return; 444 } 445 446 guOrtho(mtx, 0.0f, SCREEN_WIDTH, 0.0f, SCREEN_HEIGHT, -10.0f, 10.0f, 1.0f); 447 gSPPerspNormalize((Gfx *) (gDisplayListHead++), 0xFFFF); 448 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); 449 gSPDisplayList(gDisplayListHead++, dl_hud_img_begin); 450 451 for (i = 0; i < sTextLabelsCount; i++) { 452 for (j = 0; j < sTextLabels[i]->length; j++) { 453 #ifdef VERSION_CN 454 if ((u8) sTextLabels[i]->buffer[j] < 0xA0) { 455 glyphIndex = char_to_glyph_index(sTextLabels[i]->buffer[j]); 456 } else if ((u8) sTextLabels[i]->buffer[j] == 0xB0) { 457 glyphIndex = 0xB0; 458 } else if ((u8) sTextLabels[i]->buffer[j] == 0xC0) { 459 glyphIndex = 0xC0; 460 } else { 461 glyphIndex = GLYPH_SPACE; 462 } 463 #else 464 glyphIndex = char_to_glyph_index(sTextLabels[i]->buffer[j]); 465 #endif 466 467 if (glyphIndex != GLYPH_SPACE) { 468 #ifdef VERSION_EU 469 // Beta Key was removed by EU, so glyph slot reused. 470 // This produces a colorful Ü. 471 if (glyphIndex == GLYPH_BETA_KEY) { 472 add_glyph_texture(GLYPH_U); 473 render_textrect(sTextLabels[i]->x, sTextLabels[i]->y, j); 474 475 add_glyph_texture(GLYPH_UMLAUT); 476 render_textrect(sTextLabels[i]->x, sTextLabels[i]->y + 3, j); 477 } else { 478 add_glyph_texture(glyphIndex); 479 render_textrect(sTextLabels[i]->x, sTextLabels[i]->y, j); 480 } 481 #elif defined(VERSION_CN) 482 if ((u8) glyphIndex == 0xB0) { 483 add_glyph_texture(0x92); 484 render_textrect(45, 50, 0, 16); 485 add_glyph_texture(0x93); 486 render_textrect(45, 50, 1, 16); 487 add_glyph_texture(0x94); 488 render_textrect(45, 34, 0, 16); 489 add_glyph_texture(0x95); 490 render_textrect(45, 34, 1, 16); 491 } else if ((u8) glyphIndex == 0xC0) { 492 add_glyph_texture(0xAE); 493 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 193, 0, 16); 494 add_glyph_texture(0xAF); 495 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 193, 1, 16); 496 add_glyph_texture(0xB0); 497 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 177, 0, 16); 498 add_glyph_texture(0xB1); 499 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 177, 1, 16); 500 add_glyph_texture(0xB2); 501 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 193, 2, 16); 502 add_glyph_texture(0xB3); 503 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 193, 3, 16); 504 add_glyph_texture(0xB4); 505 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 177, 2, 16); 506 add_glyph_texture(0xB5); 507 render_textrect(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 177, 3, 16); 508 } else { 509 add_glyph_texture(glyphIndex); 510 render_textrect(sTextLabels[i]->x, sTextLabels[i]->y, j, 12); 511 } 512 #else 513 add_glyph_texture(glyphIndex); 514 render_textrect(sTextLabels[i]->x, sTextLabels[i]->y, j); 515 #endif 516 } 517 } 518 519 mem_pool_free(gEffectsMemoryPool, sTextLabels[i]); 520 } 521 522 gSPDisplayList(gDisplayListHead++, dl_hud_img_end); 523 524 sTextLabelsCount = 0; 525 }