diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py index 46b521710..409cd8427 100644 --- a/autogen/convert_constants.py +++ b/autogen/convert_constants.py @@ -535,6 +535,8 @@ def def_constant(fname, processed_constant, skip_constant): continue if '"' in c[1]: s += '\n--- @type string\n' + elif "." in c[1]: + s += '\n--- @type number\n' else: s += '\n--- @type integer\n' s += '%s = %s\n' % (c[0], c[1]) diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index 8846db56f..0ec22b12c 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -1342,7 +1342,7 @@ def doc_function(fname, function): s += '- None\n' s += '\n### Returns\n' - if rtype != None: + if len(rvalues) > 0: for _, ptype, plink in rvalues: if plink: s += '- [%s](%s)\n' % (ptype, plink) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index fef72a27c..8e160c5d4 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -2760,6 +2760,42 @@ CONSOLE_MESSAGE_ERROR = 2 --- @type ConsoleMessageLevel --- | `CONSOLE_MESSAGE_WARNING` --- | `CONSOLE_MESSAGE_ERROR` +--- @type number +ROTATION_PIVOT_X_LEFT = 0.0 + +--- @type number +ROTATION_PIVOT_X_CENTER = 0.5 + +--- @type number +ROTATION_PIVOT_X_RIGHT = 1.0 + +--- @type number +ROTATION_PIVOT_Y_TOP = 0.0 + +--- @type number +ROTATION_PIVOT_Y_CENTER = 0.5 + +--- @type number +ROTATION_PIVOT_Y_BOTTOM = 1.0 + +--- @type number +TEXT_HALIGN_LEFT = 0.0 + +--- @type number +TEXT_HALIGN_CENTER = 0.5 + +--- @type number +TEXT_HALIGN_RIGHT = 1.0 + +--- @type number +TEXT_VALIGN_TOP = 0.0 + +--- @type number +TEXT_VALIGN_CENTER = 0.5 + +--- @type number +TEXT_VALIGN_BOTTOM = 1.0 + RESOLUTION_DJUI = 0 --- @type HudUtilsResolution RESOLUTION_N64 = 1 --- @type HudUtilsResolution RESOLUTION_COUNT = 2 --- @type HudUtilsResolution @@ -2778,16 +2814,18 @@ FILTER_COUNT = 2 --- @type HudUtilsFilter --- | `FILTER_LINEAR` --- | `FILTER_COUNT` -FONT_NORMAL = 0 --- @type DjuiFontType -FONT_MENU = 1 --- @type DjuiFontType -FONT_HUD = 2 --- @type DjuiFontType -FONT_ALIASED = 3 --- @type DjuiFontType -FONT_CUSTOM_HUD = 4 --- @type DjuiFontType -FONT_RECOLOR_HUD = 5 --- @type DjuiFontType -FONT_SPECIAL = 6 --- @type DjuiFontType -FONT_COUNT = 7 --- @type DjuiFontType +FONT_LEGACY = -1 --- @type DjuiFontType +FONT_NORMAL = 0 --- @type DjuiFontType +FONT_MENU = 1 --- @type DjuiFontType +FONT_HUD = 2 --- @type DjuiFontType +FONT_ALIASED = 3 --- @type DjuiFontType +FONT_CUSTOM_HUD = 4 --- @type DjuiFontType +FONT_RECOLOR_HUD = 5 --- @type DjuiFontType +FONT_SPECIAL = 6 --- @type DjuiFontType +FONT_COUNT = 7 --- @type DjuiFontType --- @alias DjuiFontType +--- | `FONT_LEGACY` --- | `FONT_NORMAL` --- | `FONT_MENU` --- | `FONT_HUD` diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index f7b0aa63d..746a8f4c4 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -3856,7 +3856,9 @@ function djui_hud_reset_color() -- ... end ---- @return HudUtilsRotation +--- @return integer rotation +--- @return number pivotX +--- @return number pivotY --- Gets the current DJUI HUD rotation function djui_hud_get_rotation() -- ... @@ -3881,6 +3883,29 @@ function djui_hud_set_rotation_interpolated(prevRotation, prevPivotX, prevPivotY -- ... end +--- @return number textHAlign +--- @return number textVAlign +--- Gets the current DJUI HUD text alignment +function djui_hud_get_text_alignment() + -- ... +end + +--- @param textHAlign number +--- @param textVAlign number +--- Sets the current DJUI HUD text alignment +function djui_hud_set_text_alignment(textHAlign, textVAlign) + -- ... +end + +--- @param prevTextHAlign number +--- @param prevTextVAlign number +--- @param textHAlign number +--- @param textVAlign number +--- Sets the current DJUI HUD text alignment interpolated +function djui_hud_set_text_alignment_interpolated(prevTextHAlign, prevTextVAlign, textHAlign, textVAlign) + -- ... +end + --- @return integer --- Gets the screen width in the current DJUI HUD resolution function djui_hud_get_screen_width() diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index c971ce2ff..5441e7424 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -955,14 +955,6 @@ --- @field public translation Vec3s --- @field public rotation Vec3s ---- @class HudUtilsRotation ---- @field public rotation number ---- @field public rotationDiff number ---- @field public prevPivotX number ---- @field public prevPivotY number ---- @field public pivotX number ---- @field public pivotY number - --- @class InstantWarp --- @field public id integer --- @field public area integer diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 8f49768aa..0c92edac9 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -1149,6 +1149,18 @@
## [djui_hud_utils.h](#djui_hud_utils.h) +- ROTATION_PIVOT_X_LEFT +- ROTATION_PIVOT_X_CENTER +- ROTATION_PIVOT_X_RIGHT +- ROTATION_PIVOT_Y_TOP +- ROTATION_PIVOT_Y_CENTER +- ROTATION_PIVOT_Y_BOTTOM +- TEXT_HALIGN_LEFT +- TEXT_HALIGN_CENTER +- TEXT_HALIGN_RIGHT +- TEXT_VALIGN_TOP +- TEXT_VALIGN_CENTER +- TEXT_VALIGN_BOTTOM ### [enum HudUtilsResolution](#HudUtilsResolution) | Identifier | Value | @@ -1167,6 +1179,7 @@ ### [enum DjuiFontType](#DjuiFontType) | Identifier | Value | | :--------- | :---- | +| FONT_LEGACY | -1 | | FONT_NORMAL | 0 | | FONT_MENU | 1 | | FONT_HUD | 2 | diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md index 7b1704e71..8e4f8c230 100644 --- a/docs/lua/functions-3.md +++ b/docs/lua/functions-3.md @@ -1312,7 +1312,8 @@ Calculates and returns the pitch and yaw angles from one 3D position (`from`) to | to | [Vec3f](structs.md#Vec3f) | ### Returns -- None +- `integer` +- `integer` ### C Prototype `void calculate_angles(Vec3f from, Vec3f to, RET s16 *pitch, RET s16 *yaw);` @@ -1585,7 +1586,7 @@ Applies a roll-based shake effect to the camera. Simulates rotational disturbanc | roll | `integer` | ### Returns -- None +- `integer` ### C Prototype `void shake_camera_roll(INOUT s16 *roll);` @@ -2825,7 +2826,7 @@ Gets the current DJUI HUD font - `integer` ### C Prototype -`u8 djui_hud_get_font(void);` +`s8 djui_hud_get_font(void);` [:arrow_up_small:](#) @@ -2928,16 +2929,18 @@ Resets the current DJUI HUD color Gets the current DJUI HUD rotation ### Lua Example -`local hudUtilsRotationValue = djui_hud_get_rotation()` +`local rotation, pivotX, pivotY = djui_hud_get_rotation()` ### Parameters - None ### Returns -- [HudUtilsRotation](structs.md#HudUtilsRotation) +- `integer` +- `number` +- `number` ### C Prototype -`struct HudUtilsRotation* djui_hud_get_rotation(void);` +`void djui_hud_get_rotation(RET s16 *rotation, RET f32 *pivotX, RET f32 *pivotY);` [:arrow_up_small:](#) @@ -2990,7 +2993,79 @@ Sets the current DJUI HUD rotation interpolated - None ### C Prototype -`void djui_hud_set_rotation_interpolated(s32 prevRotation, f32 prevPivotX, f32 prevPivotY, s32 rotation, f32 pivotX, f32 pivotY);` +`void djui_hud_set_rotation_interpolated(s16 prevRotation, f32 prevPivotX, f32 prevPivotY, s16 rotation, f32 pivotX, f32 pivotY);` + +[:arrow_up_small:](#) + +
+ +## [djui_hud_get_text_alignment](#djui_hud_get_text_alignment) + +### Description +Gets the current DJUI HUD text alignment + +### Lua Example +`local textHAlign, textVAlign = djui_hud_get_text_alignment()` + +### Parameters +- None + +### Returns +- `number` +- `number` + +### C Prototype +`void djui_hud_get_text_alignment(RET f32 *textHAlign, RET f32 *textVAlign);` + +[:arrow_up_small:](#) + +
+ +## [djui_hud_set_text_alignment](#djui_hud_set_text_alignment) + +### Description +Sets the current DJUI HUD text alignment + +### Lua Example +`djui_hud_set_text_alignment(textHAlign, textVAlign)` + +### Parameters +| Field | Type | +| ----- | ---- | +| textHAlign | `number` | +| textVAlign | `number` | + +### Returns +- None + +### C Prototype +`void djui_hud_set_text_alignment(f32 textHAlign, f32 textVAlign);` + +[:arrow_up_small:](#) + +
+ +## [djui_hud_set_text_alignment_interpolated](#djui_hud_set_text_alignment_interpolated) + +### Description +Sets the current DJUI HUD text alignment interpolated + +### Lua Example +`djui_hud_set_text_alignment_interpolated(prevTextHAlign, prevTextVAlign, textHAlign, textVAlign)` + +### Parameters +| Field | Type | +| ----- | ---- | +| prevTextHAlign | `number` | +| prevTextVAlign | `number` | +| textHAlign | `number` | +| textVAlign | `number` | + +### Returns +- None + +### C Prototype +`void djui_hud_set_text_alignment_interpolated(f32 prevTextHAlign, f32 prevTextVAlign, f32 textHAlign, f32 textVAlign);` [:arrow_up_small:](#) diff --git a/docs/lua/functions-4.md b/docs/lua/functions-4.md index 6c8cb8c8b..4bd956272 100644 --- a/docs/lua/functions-4.md +++ b/docs/lua/functions-4.md @@ -5134,7 +5134,9 @@ Calculates the distance between two points in 3D space (`from` and `to`), as wel | to | [Vec3f](structs.md#Vec3f) | ### Returns -- None +- `number` +- `integer` +- `integer` ### C Prototype `void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, RET f32 *dist, RET s16 *pitch, RET s16 *yaw);` diff --git a/docs/lua/functions-5.md b/docs/lua/functions-5.md index 507dd7401..91636f399 100644 --- a/docs/lua/functions-5.md +++ b/docs/lua/functions-5.md @@ -2945,7 +2945,7 @@ Determines an object's forward speed multiplier. | floor_nY | `number` | ### Returns -- None +- `number` ### C Prototype `void calc_obj_friction(RET f32 *objFriction, f32 floor_nY);` @@ -4419,7 +4419,7 @@ Begin by increasing the current object's scale by `scaleVel`, and slowly decreas | blinkLength | `integer` | ### Returns -- None +- `integer` ### C Prototype `void obj_update_blinking(INOUT s32 *blinkTimer, s16 baseCycleLength, s16 cycleLengthRange, s16 blinkLength);` @@ -4743,7 +4743,8 @@ Treats far home as Mario. Returns the distance and angle to the nearest player | threshold | `number` | ### Returns -- None +- `integer` +- `integer` ### C Prototype `void treat_far_home_as_mario(f32 threshold, RET s32* distanceToPlayer, RET s32* angleToPlayer);` diff --git a/docs/lua/functions-6.md b/docs/lua/functions-6.md index afd96447e..41b220737 100644 --- a/docs/lua/functions-6.md +++ b/docs/lua/functions-6.md @@ -1963,7 +1963,7 @@ Marks an object to be unloaded at the end of the frame | dragStrength | `number` | ### Returns -- None +- `number` ### C Prototype `void apply_drag_to_value(INOUT f32 *value, f32 dragStrength);` diff --git a/docs/lua/functions.md b/docs/lua/functions.md index e2f94b46b..4076375df 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -763,6 +763,9 @@ - [djui_hud_get_rotation](functions-3.md#djui_hud_get_rotation) - [djui_hud_set_rotation](functions-3.md#djui_hud_set_rotation) - [djui_hud_set_rotation_interpolated](functions-3.md#djui_hud_set_rotation_interpolated) + - [djui_hud_get_text_alignment](functions-3.md#djui_hud_get_text_alignment) + - [djui_hud_set_text_alignment](functions-3.md#djui_hud_set_text_alignment) + - [djui_hud_set_text_alignment_interpolated](functions-3.md#djui_hud_set_text_alignment_interpolated) - [djui_hud_get_screen_width](functions-3.md#djui_hud_get_screen_width) - [djui_hud_get_screen_height](functions-3.md#djui_hud_get_screen_height) - [djui_hud_get_mouse_x](functions-3.md#djui_hud_get_mouse_x) diff --git a/docs/lua/structs.md b/docs/lua/structs.md index f189b286d..79c6d19ff 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -53,7 +53,6 @@ - [GraphNodeSwitchCase](#GraphNodeSwitchCase) - [GraphNodeTranslation](#GraphNodeTranslation) - [GraphNodeTranslationRotation](#GraphNodeTranslationRotation) -- [HudUtilsRotation](#HudUtilsRotation) - [InstantWarp](#InstantWarp) - [LakituState](#LakituState) - [LevelValues](#LevelValues) @@ -1427,21 +1426,6 @@
-## [HudUtilsRotation](#HudUtilsRotation) - -| Field | Type | Access | -| ----- | ---- | ------ | -| rotation | `number` | | -| rotationDiff | `number` | | -| prevPivotX | `number` | | -| prevPivotY | `number` | | -| pivotX | `number` | | -| pivotY | `number` | | - -[:arrow_up_small:](#) - -
- ## [InstantWarp](#InstantWarp) | Field | Type | Access | diff --git a/src/pc/crash_handler.c b/src/pc/crash_handler.c index 23301f0f2..470f4e2e6 100644 --- a/src/pc/crash_handler.c +++ b/src/pc/crash_handler.c @@ -235,6 +235,8 @@ static void crash_handler_produce_one_frame_callback(void) { // render the line f32 addX = 0; char* c = text->s; + + font->render_begin(); while (*c != '\0') { f32 charWidth = 0.4f; @@ -253,6 +255,7 @@ static void crash_handler_produce_one_frame_callback(void) { create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth, 0, 0); c = djui_unicode_next_char(c); } + font->render_end(); // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); diff --git a/src/pc/discord/discord_activity.c b/src/pc/discord/discord_activity.c index 510ac440e..36745701c 100644 --- a/src/pc/discord/discord_activity.c +++ b/src/pc/discord/discord_activity.c @@ -122,11 +122,9 @@ void discord_activity_update(void) { // HACK: give the detail population more space than the Discord details can fit so it gets truncated without cutting off the largest strings char details[512] = { 0 }; - discord_populate_details(details, 512); - char* detailsNoColor = str_remove_color_codes(details); - - snprintf(sCurActivity.details, 128, "%s", detailsNoColor); - free(detailsNoColor); + discord_populate_details(details, ARRAY_COUNT(details)); + djui_text_remove_colors(details); + snprintf(sCurActivity.details, 128, "%s", details); if (!app.activities) { LOG_INFO("no activities"); diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c index 5c79ff3c6..3bd9fc8b1 100644 --- a/src/pc/djui/djui.c +++ b/src/pc/djui/djui.c @@ -168,7 +168,8 @@ void djui_lua_error_clear(void) { void djui_reset_hud_params(void) { djui_hud_set_resolution(RESOLUTION_DJUI); djui_hud_set_font(FONT_NORMAL); - djui_hud_set_rotation(0, 0, 0); + djui_hud_set_rotation(0, ROTATION_PIVOT_X_LEFT, ROTATION_PIVOT_Y_TOP); + djui_hud_set_text_alignment(TEXT_HALIGN_LEFT, TEXT_VALIGN_TOP); djui_hud_reset_color(); djui_hud_set_filter(FILTER_NEAREST); djui_hud_reset_viewport(); diff --git a/src/pc/djui/djui_font.c b/src/pc/djui/djui_font.c index f2d88cc88..4b649d784 100644 --- a/src/pc/djui/djui_font.c +++ b/src/pc/djui/djui_font.c @@ -7,7 +7,7 @@ // font 0 (built-in normal font) // /////////////////////////////////// -static void djui_font_normal_render_char(char* c) { +static void djui_font_normal_render_char(const char* c) { // replace undisplayable characters if (*c == ' ') { return; } @@ -18,16 +18,16 @@ static void djui_font_normal_render_char(char* c) { u32 tx = index % 64; u32 ty = index / 64; extern ALIGNED8 const Texture texture_font_jp[]; - djui_gfx_render_texture_tile(texture_font_jp, 512, 1024, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16, false, true); + djui_gfx_render_texture_tile_font(texture_font_jp, 512, 1024, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16); } else { u32 tx = index % 32; u32 ty = index / 32; extern ALIGNED8 const Texture texture_font_normal[]; - djui_gfx_render_texture_tile(texture_font_normal, 256, 128, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16, false, true); + djui_gfx_render_texture_tile_font(texture_font_normal, 256, 128, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16); } } -static f32 djui_font_normal_char_width(char* c) { +static f32 djui_font_normal_char_width(const char* c) { if (*c == ' ') { return configExCoopTheme ? 6 / 32.0f : 0.30f; } extern const f32 font_normal_widths[]; return djui_unicode_get_sprite_width(c, font_normal_widths, 32.0f); @@ -41,7 +41,9 @@ static const struct DjuiFont sDjuiFontNormal = { .yOffset = 0.0f, .defaultFontScale = 32.0f, .textBeginDisplayList = NULL, + .render_begin = djui_gfx_render_texture_tile_font_begin, .render_char = djui_font_normal_render_char, + .render_end = djui_gfx_render_texture_tile_font_end, .char_width = djui_font_normal_char_width, }; @@ -49,7 +51,7 @@ static const struct DjuiFont sDjuiFontNormal = { // font 1 (custom title font) // //////////////////////////////// -static void djui_font_title_render_char(char* c) { +static void djui_font_title_render_char(const char* c) { // replace undisplayable characters if (*c == ' ') { return; } @@ -64,10 +66,10 @@ static void djui_font_title_render_char(char* c) { u32 ty = index / 16; extern ALIGNED8 const Texture texture_font_title[]; - djui_gfx_render_texture_tile(texture_font_title, 1024, 512, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 64, ty * 64, 64, 64, false, true); + djui_gfx_render_texture_tile_font(texture_font_title, 1024, 512, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 64, ty * 64, 64, 64); } -static f32 djui_font_title_char_width(char* text) { +static f32 djui_font_title_char_width(const char* text) { char c = *text; if (c == ' ') { return 0.30f; } c = djui_unicode_get_base_char(text); @@ -83,7 +85,9 @@ static const struct DjuiFont sDjuiFontTitle = { .yOffset = 0.0f, .defaultFontScale = 64.0f, .textBeginDisplayList = NULL, + .render_begin = djui_gfx_render_texture_tile_font_begin, .render_char = djui_font_title_render_char, + .render_end = djui_gfx_render_texture_tile_font_end, .char_width = djui_font_title_char_width, }; @@ -124,27 +128,29 @@ static u8 djui_font_hud_index(char c) { return c; } -static void djui_font_hud_render_char(char* text) { +static void djui_font_hud_render_char(const char* text) { char c = *text; if (c == ' ') { return; } c = djui_unicode_get_base_char(text); u8 index = djui_font_hud_index(c); - djui_gfx_render_texture(main_hud_lut[index], 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b, djui_hud_get_filter()); + djui_gfx_render_texture_font(main_hud_lut[index], 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b); } -static f32 djui_font_hud_char_width(UNUSED char* text) { +static f32 djui_font_hud_char_width(UNUSED const char* text) { return 0.75f; } static const struct DjuiFont sDjuiFontHud = { .charWidth = 1.0f, .charHeight = 0.9f, - .lineHeight = 0.7f, + .lineHeight = 1.25f, .xOffset = 0.0f, .yOffset = 0.0f, .defaultFontScale = 16.0f, .textBeginDisplayList = NULL, + .render_begin = djui_gfx_render_texture_font_begin, .render_char = djui_font_hud_render_char, + .render_end = djui_gfx_render_texture_font_end, .char_width = djui_font_hud_char_width, }; @@ -152,7 +158,7 @@ static const struct DjuiFont sDjuiFontHud = { // font 3 (DJ's aliased font) // //////////////////////////////// -static void djui_font_aliased_render_char(char* c) { +static void djui_font_aliased_render_char(const char* c) { // replace undisplayable characters if (*c == ' ') { return; } @@ -163,16 +169,16 @@ static void djui_font_aliased_render_char(char* c) { u32 tx = index % 64; u32 ty = index / 64; extern ALIGNED8 const Texture texture_font_jp_aliased[]; - djui_gfx_render_texture_tile(texture_font_jp_aliased, 1024, 2048, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 16, ty * 32, 16, 32, false, true); + djui_gfx_render_texture_tile_font(texture_font_jp_aliased, 1024, 2048, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 16, ty * 32, 16, 32); } else { u32 tx = index % 32; u32 ty = index / 32; extern ALIGNED8 const Texture texture_font_aliased[]; - djui_gfx_render_texture_tile(texture_font_aliased, 512, 256, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 16, ty * 32, 16, 32, false, true); + djui_gfx_render_texture_tile_font(texture_font_aliased, 512, 256, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 16, ty * 32, 16, 32); } } -static f32 djui_font_aliased_char_width(char* c) { +static f32 djui_font_aliased_char_width(const char* c) { if (*c == ' ') { return 6 / 32.0f; } extern const f32 font_aliased_widths[]; return djui_unicode_get_sprite_width(c, font_aliased_widths, 1.0f) / 32.0f; @@ -186,7 +192,9 @@ static const struct DjuiFont sDjuiFontAliased = { .lineHeight = 0.8125f, .defaultFontScale = 32.0f, .textBeginDisplayList = NULL, + .render_begin = djui_gfx_render_texture_tile_font_begin, .render_char = djui_font_aliased_render_char, + .render_end = djui_gfx_render_texture_tile_font_end, .char_width = djui_font_aliased_char_width, }; @@ -194,7 +202,7 @@ static const struct DjuiFont sDjuiFontAliased = { // font 4/5 (custom hud font/recolor) // //////////////////////////////////////// -static void djui_font_custom_hud_render_char(char* c) { +static void djui_font_custom_hud_render_char(const char* c) { // replace undisplayable characters if (*c == ' ') { return; } @@ -204,10 +212,10 @@ static void djui_font_custom_hud_render_char(char* c) { u32 ty = index / 16; extern ALIGNED8 const Texture texture_font_hud[]; - djui_gfx_render_texture_tile(texture_font_hud, 512, 512, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 32, ty * 32, 32, 32, false, true); + djui_gfx_render_texture_tile_font(texture_font_hud, 512, 512, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 32, ty * 32, 32, 32); } -static void djui_font_custom_hud_recolor_render_char(char* c) { +static void djui_font_custom_hud_recolor_render_char(const char* c) { // replace undisplayable characters if (*c == ' ') { return; } @@ -217,10 +225,10 @@ static void djui_font_custom_hud_recolor_render_char(char* c) { u32 ty = index / 16; extern ALIGNED8 const Texture texture_font_hud_recolor[]; - djui_gfx_render_texture_tile(texture_font_hud_recolor, 512, 512, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 32, ty * 32, 32, 32, false, true); + djui_gfx_render_texture_tile_font(texture_font_hud_recolor, 512, 512, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 32, ty * 32, 32, 32); } -static f32 djui_font_custom_hud_char_width(char* text) { +static f32 djui_font_custom_hud_char_width(const char* text) { char c = *text; if (c == ' ') { return 0.3750f; } c = djui_unicode_get_base_char(text); @@ -236,7 +244,9 @@ static const struct DjuiFont sDjuiFontCustomHud = { .yOffset = -10.25f, .defaultFontScale = 32.0f, .textBeginDisplayList = NULL, + .render_begin = djui_gfx_render_texture_tile_font_begin, .render_char = djui_font_custom_hud_render_char, + .render_end = djui_gfx_render_texture_tile_font_end, .char_width = djui_font_custom_hud_char_width, }; @@ -248,7 +258,9 @@ static const struct DjuiFont sDjuiFontCustomHudRecolor = { .yOffset = -10.25f, .defaultFontScale = 32.0f, .textBeginDisplayList = NULL, + .render_begin = djui_gfx_render_texture_tile_font_begin, .render_char = djui_font_custom_hud_recolor_render_char, + .render_end = djui_gfx_render_texture_tile_font_end, .char_width = djui_font_custom_hud_char_width, }; @@ -256,7 +268,7 @@ static const struct DjuiFont sDjuiFontCustomHudRecolor = { // font 6 (special font) // /////////////////////////// -static void djui_font_special_render_char(char* c) { +static void djui_font_special_render_char(const char* c) { // replace undisplayable characters if (*c == ' ') { return; } @@ -266,17 +278,17 @@ static void djui_font_special_render_char(char* c) { u32 tx = index % 64; u32 ty = index / 64; extern ALIGNED8 const Texture texture_font_jp[]; - djui_gfx_render_texture_tile(texture_font_jp, 512, 1024, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16, false, true); + djui_gfx_render_texture_tile_font(texture_font_jp, 512, 1024, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16); } else { u32 tx = index % 32; u32 ty = index / 32; extern ALIGNED8 const Texture texture_font_special[]; - djui_gfx_render_texture_tile(texture_font_special, 256, 128, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16, false, true); + djui_gfx_render_texture_tile_font(texture_font_special, 256, 128, G_IM_FMT_RGBA, G_IM_SIZ_32b, tx * 8, ty * 16, 8, 16); } } -static f32 djui_font_special_char_width(char* c) { +static f32 djui_font_special_char_width(const char* c) { if (*c == ' ') { return 0.5f; } extern const f32 font_special_widths[]; return djui_unicode_get_sprite_width(c, font_special_widths, 32.0f); @@ -290,7 +302,9 @@ static const struct DjuiFont sDjuiFontSpecial = { .yOffset = 0.0f, .defaultFontScale = 32.0f, .textBeginDisplayList = NULL, + .render_begin = djui_gfx_render_texture_tile_font_begin, .render_char = djui_font_special_render_char, + .render_end = djui_gfx_render_texture_tile_font_end, .char_width = djui_font_special_char_width, }; diff --git a/src/pc/djui/djui_font.h b/src/pc/djui/djui_font.h index 02d3eef8a..7acf72a28 100644 --- a/src/pc/djui/djui_font.h +++ b/src/pc/djui/djui_font.h @@ -9,8 +9,10 @@ struct DjuiFont { f32 yOffset; f32 defaultFontScale; const Gfx* textBeginDisplayList; - void (*render_char)(char*); - f32 (*char_width)(char*); + void (*render_begin)(); + void (*render_char)(const char*); + void (*render_end)(); + f32 (*char_width)(const char*); }; extern const struct DjuiFont* gDjuiFonts[]; diff --git a/src/pc/djui/djui_gfx.c b/src/pc/djui/djui_gfx.c index 8ebd6c488..4319782c3 100644 --- a/src/pc/djui/djui_gfx.c +++ b/src/pc/djui/djui_gfx.c @@ -1,6 +1,7 @@ #include #include "sm64.h" #include "djui.h" +#include "djui_hud_utils.h" #include "game/ingame_menu.h" #include "game/segment2.h" #include "pc/pc_main.h" @@ -118,7 +119,93 @@ void djui_gfx_render_texture(const Texture* texture, u32 w, u32 h, u8 fmt, u8 si gSPDisplayList(gDisplayListHead++, dl_djui_image); } -void djui_gfx_render_texture_tile(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz, u32 tileX, u32 tileY, u32 tileW, u32 tileH, bool filter, bool font) { +void djui_gfx_render_texture_tile(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz, u32 tileX, u32 tileY, u32 tileW, u32 tileH, bool filter) { + if (!gDisplayListHead) { + LOG_ERROR("Retrieved a null displaylist head"); + return; + } + + if (!texture) { + LOG_ERROR("Attempted to render null texture"); + return; + } + + Vtx *vtx = alloc_display_list(sizeof(Vtx) * 4); + if (!vtx) { + LOG_ERROR("Failed to allocate vertices"); + return; + } + + f32 aspect = tileH ? ((f32)tileW / (f32)tileH) : 1; + + vtx[0] = (Vtx) {{{ 0, -1, 0 }, 0, { ( tileX * 2048.0f) / (f32)w + 1, ((tileY + tileH) * 2048.0f) / (f32)h + 1 }, { 0xff, 0xff, 0xff, 0xff }}}; + vtx[2] = (Vtx) {{{ 1 * aspect, 0, 0 }, 0, { ((tileX + tileW) * 2048.0f) / (f32)w + 1, ( tileY * 2048.0f) / (f32)h + 1 }, { 0xff, 0xff, 0xff, 0xff }}}; + vtx[1] = (Vtx) {{{ 1 * aspect, -1, 0 }, 0, { ((tileX + tileW) * 2048.0f) / (f32)w + 1, ((tileY + tileH) * 2048.0f) / (f32)h + 1 }, { 0xff, 0xff, 0xff, 0xff }}}; + vtx[3] = (Vtx) {{{ 0, 0, 0 }, 0, { ( tileX * 2048.0f) / (f32)w + 1, ( tileY * 2048.0f) / (f32)h + 1 }, { 0xff, 0xff, 0xff, 0xff }}}; + + gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BOTH); + gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_FADEA); + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + gDPSetTextureFilter(gDisplayListHead++, filter ? G_TF_BILERP : G_TF_POINT); + + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + + gDPSetTextureOverrideDjui(gDisplayListHead++, texture, djui_gfx_power_of_two(w), djui_gfx_power_of_two(h), fmt, siz); + gDPLoadTextureBlockWithoutTexture(gDisplayListHead++, NULL, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 64, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); + + *(gDisplayListHead++) = (Gfx) gsSPExecuteDjui(G_TEXOVERRIDE_DJUI); + + gSPVertexNonGlobal(gDisplayListHead++, vtx, 4, 0); + *(gDisplayListHead++) = (Gfx) gsSPExecuteDjui(G_TEXCLIP_DJUI); + gSP2TrianglesDjui(gDisplayListHead++, 0, 1, 2, 0x0, 0, 2, 3, 0x0); + + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); + gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); + gSPSetGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BACK); +} + +void djui_gfx_render_texture_font_begin() { + gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BOTH); + gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_FADEA); + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + gDPSetTextureFilter(gDisplayListHead++, djui_hud_get_filter() ? G_TF_BILERP : G_TF_POINT); + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + gDPLoadTextureBlockWithoutTexture(gDisplayListHead++, NULL, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 64, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); +} + +void djui_gfx_render_texture_font(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz) { + if (!gDisplayListHead) { + LOG_ERROR("Retrieved a null displaylist head"); + return; + } + + if (!texture) { + LOG_ERROR("Attempted to render null texture"); + return; + } + + gDPSetTextureOverrideDjui(gDisplayListHead++, texture, djui_gfx_power_of_two(w), djui_gfx_power_of_two(h), fmt, siz); + *(gDisplayListHead++) = (Gfx) gsSPExecuteDjui(G_TEXOVERRIDE_DJUI); + gSPVertexNonGlobal(gDisplayListHead++, vertex_djui_image, 4, 0); + gSP2Triangles(gDisplayListHead++, 0, 1, 2, 0x0, 0, 2, 3, 0x0); +} + +void djui_gfx_render_texture_font_end() { + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); + gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); + gSPSetGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BACK); +} + +void djui_gfx_render_texture_tile_font_begin() { + gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BOTH); + gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_FADEA); + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + gDPSetTextureFilter(gDisplayListHead++, G_TF_POINT); + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + gDPLoadTextureBlockWithoutTexture(gDisplayListHead++, NULL, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 64, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); +} + +void djui_gfx_render_texture_tile_font(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { if (!gDisplayListHead) { LOG_ERROR("Retrieved a null displaylist head"); return; @@ -139,29 +226,21 @@ void djui_gfx_render_texture_tile(const Texture* texture, u32 w, u32 h, u8 fmt, // I don't know why adding 1 to all of the UVs seems to fix rendering, but it does... // this should be tested carefully. it definitely fixes some stuff, but what does it break? - f32 offsetX = (font ? -1024.0f / (f32)w : 0) + 1; - f32 offsetY = (font ? -1024.0f / (f32)h : 0) + 1; + f32 offsetX = (-1024.0f / (f32)w) + 1; + f32 offsetY = (-1024.0f / (f32)h) + 1; vtx[0] = (Vtx) {{{ 0, -1, 0 }, 0, { ( tileX * 2048.0f) / (f32)w + offsetX, ((tileY + tileH) * 2048.0f) / (f32)h + offsetY }, { 0xff, 0xff, 0xff, 0xff }}}; vtx[2] = (Vtx) {{{ 1 * aspect, 0, 0 }, 0, { ((tileX + tileW) * 2048.0f) / (f32)w + offsetX, ( tileY * 2048.0f) / (f32)h + offsetY }, { 0xff, 0xff, 0xff, 0xff }}}; vtx[1] = (Vtx) {{{ 1 * aspect, -1, 0 }, 0, { ((tileX + tileW) * 2048.0f) / (f32)w + offsetX, ((tileY + tileH) * 2048.0f) / (f32)h + offsetY }, { 0xff, 0xff, 0xff, 0xff }}}; vtx[3] = (Vtx) {{{ 0, 0, 0 }, 0, { ( tileX * 2048.0f) / (f32)w + offsetX, ( tileY * 2048.0f) / (f32)h + offsetY }, { 0xff, 0xff, 0xff, 0xff }}}; - gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BOTH); - gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_FADEA); - gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); - gDPSetTextureFilter(gDisplayListHead++, filter ? G_TF_BILERP : G_TF_POINT); - - gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); - gDPSetTextureOverrideDjui(gDisplayListHead++, texture, djui_gfx_power_of_two(w), djui_gfx_power_of_two(h), fmt, siz); - gDPLoadTextureBlockWithoutTexture(gDisplayListHead++, NULL, G_IM_FMT_RGBA, G_IM_SIZ_16b, 64, 64, 0, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0); - *(gDisplayListHead++) = (Gfx) gsSPExecuteDjui(G_TEXOVERRIDE_DJUI); - gSPVertexNonGlobal(gDisplayListHead++, vtx, 4, 0); *(gDisplayListHead++) = (Gfx) gsSPExecuteDjui(G_TEXCLIP_DJUI); gSP2TrianglesDjui(gDisplayListHead++, 0, 1, 2, 0x0, 0, 2, 3, 0x0); +} +void djui_gfx_render_texture_tile_font_end() { gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); gSPSetGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BACK); diff --git a/src/pc/djui/djui_gfx.h b/src/pc/djui/djui_gfx.h index 8da361e3f..743ca4460 100644 --- a/src/pc/djui/djui_gfx.h +++ b/src/pc/djui/djui_gfx.h @@ -16,7 +16,15 @@ void djui_gfx_displaylist_end(void); f32 djui_gfx_get_scale(void); void djui_gfx_render_texture(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz, bool filter); -void djui_gfx_render_texture_tile(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz, u32 tileX, u32 tileY, u32 tileW, u32 tileH, bool filter, bool font); +void djui_gfx_render_texture_tile(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz, u32 tileX, u32 tileY, u32 tileW, u32 tileH, bool filter); + +void djui_gfx_render_texture_font_begin(); +void djui_gfx_render_texture_font(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz); +void djui_gfx_render_texture_font_end(); + +void djui_gfx_render_texture_tile_font_begin(); +void djui_gfx_render_texture_tile_font(const Texture* texture, u32 w, u32 h, u8 fmt, u8 siz, u32 tileX, u32 tileY, u32 tileW, u32 tileH); +void djui_gfx_render_texture_tile_font_end(); void gfx_get_dimensions(u32* width, u32* height); diff --git a/src/pc/djui/djui_hud_utils.c b/src/pc/djui/djui_hud_utils.c index 5d7a06d5b..8ad0a5b15 100644 --- a/src/pc/djui/djui_hud_utils.c +++ b/src/pc/djui/djui_hud_utils.c @@ -24,13 +24,45 @@ #include "engine/math_util.h" -static enum HudUtilsResolution sResolution = RESOLUTION_DJUI; -static enum HudUtilsFilter sFilter = FILTER_NEAREST; -static enum DjuiFontType sFont = FONT_NORMAL; -static struct HudUtilsRotation sRotation = { 0, 0, 0, 0, 0, 0 }; -static struct DjuiColor sColor = { 255, 255, 255, 255 }; +#define INTERP_INIT(v) {v, v} + +typedef struct { + f32 prev, curr; +} InterpFieldF32; + +struct HudUtilsState { + enum HudUtilsResolution resolution; + enum HudUtilsFilter filter; + enum DjuiFontType font; + struct DjuiColor color; + struct { + InterpFieldF32 degrees; + InterpFieldF32 pivotX; + InterpFieldF32 pivotY; + } rotation; + struct { + InterpFieldF32 h; + InterpFieldF32 v; + } textAlignment; +}; + +static struct HudUtilsState sHudUtilsState = { + .resolution = RESOLUTION_DJUI, + .filter = FILTER_NEAREST, + .font = FONT_NORMAL, + .color = { 255, 255, 255, 255 }, + .rotation = { + .degrees = INTERP_INIT(0), + .pivotX = INTERP_INIT(ROTATION_PIVOT_X_LEFT), + .pivotY = INTERP_INIT(ROTATION_PIVOT_Y_TOP), + }, + .textAlignment = { + .h = INTERP_INIT(TEXT_HALIGN_LEFT), + .v = INTERP_INIT(TEXT_VALIGN_TOP), + }, +}; + static struct DjuiColor sRefColor = { 255, 255, 255, 255 }; -static bool sLegacy = false; f32 gDjuiHudUtilsZ = 0; bool gDjuiHudLockMouse = false; @@ -67,8 +99,16 @@ struct GlobalTextures gGlobalTextures = { .wario_head = { .texture = texture_hud_char_wario_head, "texture_hud_char_wario_head", .width = 16, .height = 16, .format = G_IM_FMT_RGBA, .size = G_IM_SIZ_16b } }; +static inline const struct DjuiFont *djui_hud_get_text_font() { + return gDjuiFonts[sHudUtilsState.font < 0 ? FONT_NORMAL : sHudUtilsState.font]; +} + +static inline bool djui_hud_text_font_is_legacy() { + return sHudUtilsState.font < 0; +} + static void djui_hud_position_translate(f32* x, f32* y) { - if (sResolution == RESOLUTION_DJUI) { + if (sHudUtilsState.resolution == RESOLUTION_DJUI) { djui_gfx_position_translate(x, y); } else { *x = GFX_DIMENSIONS_FROM_LEFT_EDGE(0) + *x; @@ -77,7 +117,7 @@ static void djui_hud_position_translate(f32* x, f32* y) { } static void djui_hud_size_translate(f32* size) { - if (sResolution == RESOLUTION_DJUI) { + if (sHudUtilsState.resolution == RESOLUTION_DJUI) { djui_gfx_size_translate(size); } } @@ -90,7 +130,7 @@ static void djui_hud_translate_positions(f32 *outX, f32 *outY, f32 *outW, f32 *o *outY -= SCREEN_HEIGHT; // translate scale - if (sResolution == RESOLUTION_DJUI) { + if (sHudUtilsState.resolution == RESOLUTION_DJUI) { u32 windowWidth, windowHeight; gfx_get_dimensions(&windowWidth, &windowHeight); f32 screenWidth = (f32) windowWidth / djui_gfx_get_scale(); @@ -105,22 +145,31 @@ static void djui_hud_translate_positions(f32 *outX, f32 *outY, f32 *outW, f32 *o //////////// #define MAX_INTERP_HUD 512 -struct InterpHud { - Gfx* headPos; - f32 z; - f32 prevX; - f32 prevY; - f32 x; - f32 y; - f32 prevScaleW; - f32 prevScaleH; - f32 scaleW; - f32 scaleH; - f32 width; - f32 height; - enum HudUtilsResolution resolution; - struct HudUtilsRotation rotation; + +enum InterpHudType { + INTERP_HUD_TRANSLATION, + INTERP_HUD_ROTATION, + INTERP_HUD_SCALE, + INTERP_HUD_HALIGN, + INTERP_HUD_VALIGN, + INTERP_HUD_NEW_LINE, }; + +typedef struct { + enum InterpHudType type; + Gfx *pos; + f32 params[1]; // we don't need more for now +} InterpHudGfx; + +struct InterpHud { + f32 z; + InterpFieldF32 posX, posY; + InterpFieldF32 scaleX, scaleY; + f32 width, height; + struct HudUtilsState state; + struct GrowingArray *gfx; +}; + static struct InterpHud sInterpHuds[MAX_INTERP_HUD] = { 0 }; static u16 sInterpHudCount = 0; static u8 sColorAltered = FALSE; @@ -132,140 +181,211 @@ void patch_djui_hud_before(void) { void patch_djui_hud(f32 delta) { f32 savedZ = gDjuiHudUtilsZ; Gfx* savedHeadPos = gDisplayListHead; - enum HudUtilsResolution savedResolution = sResolution; - struct HudUtilsRotation savedRotation = sRotation; + struct HudUtilsState savedState = sHudUtilsState; + for (u16 i = 0; i < sInterpHudCount; i++) { struct InterpHud* interp = &sInterpHuds[i]; - f32 x = delta_interpolate_f32(interp->prevX, interp->x, delta); - f32 y = delta_interpolate_f32(interp->prevY, interp->y, delta); - f32 scaleW = delta_interpolate_f32(interp->prevScaleW, interp->scaleW, delta); - f32 scaleH = delta_interpolate_f32(interp->prevScaleH, interp->scaleH, delta); - sResolution = interp->resolution; - sRotation = interp->rotation; + f32 x = delta_interpolate_f32(interp->posX.prev, interp->posX.curr, delta); + f32 y = delta_interpolate_f32(interp->posY.prev, interp->posY.curr, delta); + f32 scaleW = delta_interpolate_f32(interp->scaleX.prev, interp->scaleX.curr, delta); + f32 scaleH = delta_interpolate_f32(interp->scaleY.prev, interp->scaleY.curr, delta); + sHudUtilsState = interp->state; gDjuiHudUtilsZ = interp->z; - gDisplayListHead = interp->headPos; - // translate position - f32 translatedX = x; - f32 translatedY = y; - djui_hud_position_translate(&translatedX, &translatedY); - create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); + for (u32 j = 0; j != interp->gfx->count; ++j) { + const InterpHudGfx *gfx = interp->gfx->buffer[j]; + gDisplayListHead = gfx->pos; - // rotate - f32 translatedW = scaleW; - f32 translatedH = scaleH; - djui_hud_size_translate(&translatedW); - djui_hud_size_translate(&translatedH); - if (sRotation.rotationDiff != 0 || sRotation.rotation != 0) { - s32 rotation = delta_interpolate_s32(sRotation.rotation - sRotation.rotationDiff, sRotation.rotation, delta); - f32 pivotX = delta_interpolate_f32(sRotation.prevPivotX, sRotation.pivotX, delta); - f32 pivotY = delta_interpolate_f32(sRotation.prevPivotY, sRotation.pivotY, delta); - f32 pivotTranslationX = interp->width * translatedW * pivotX; - f32 pivotTranslationY = interp->height * translatedH * pivotY; - create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); - create_dl_rotation_matrix(DJUI_MTX_NOPUSH, rotation, 0, 0, 1); - create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); + switch (gfx->type) { + case INTERP_HUD_TRANSLATION: { + f32 translatedX = x; + f32 translatedY = y; + djui_hud_position_translate(&translatedX, &translatedY); + create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); + } break; + + case INTERP_HUD_ROTATION: { + if (sHudUtilsState.rotation.degrees.prev != 0 || sHudUtilsState.rotation.degrees.curr != 0) { + f32 translatedW = scaleW; + f32 translatedH = scaleH; + djui_hud_size_translate(&translatedW); + djui_hud_size_translate(&translatedH); + s16 rotPrev = degrees_to_sm64(sHudUtilsState.rotation.degrees.prev); + s16 rotCurr = degrees_to_sm64(sHudUtilsState.rotation.degrees.curr); + s32 normalizedDiff = (((s32) rotCurr - (s32) rotPrev + 0x8000) & 0xFFFF) - 0x8000; // Fix modular overflow/underflow + s32 rotation = delta_interpolate_s32(rotCurr - normalizedDiff, rotCurr, delta); + f32 pivotX = delta_interpolate_f32(sHudUtilsState.rotation.pivotX.prev, sHudUtilsState.rotation.pivotX.curr, delta); + f32 pivotY = delta_interpolate_f32(sHudUtilsState.rotation.pivotY.prev, sHudUtilsState.rotation.pivotY.curr, delta); + f32 pivotTranslationX = interp->width * translatedW * pivotX; + f32 pivotTranslationY = interp->height * translatedH * pivotY; + create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); + create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sm64_to_degrees(rotation), 0, 0, 1); + create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); + } + } break; + + case INTERP_HUD_SCALE: { + f32 translatedW = scaleW; + f32 translatedH = scaleH; + djui_hud_size_translate(&translatedW); + djui_hud_size_translate(&translatedH); + create_dl_scale_matrix(DJUI_MTX_NOPUSH, interp->width * translatedW, interp->height * translatedH, 1.0f); + } break; + + case INTERP_HUD_HALIGN: { + f32 textHAlign = delta_interpolate_f32(sHudUtilsState.textAlignment.h.prev, sHudUtilsState.textAlignment.h.curr, delta); + f32 lineWidth = gfx->params[0]; + create_dl_translation_matrix(DJUI_MTX_NOPUSH, -lineWidth * textHAlign, 0, 0); + } break; + + case INTERP_HUD_VALIGN: { + f32 textVAlign = delta_interpolate_f32(sHudUtilsState.textAlignment.v.prev, sHudUtilsState.textAlignment.v.curr, delta); + f32 textHeight = gfx->params[0]; + create_dl_translation_matrix(DJUI_MTX_NOPUSH, 0, textHeight * textVAlign, 0); + } break; + + case INTERP_HUD_NEW_LINE: { + const struct DjuiFont *font = djui_hud_get_text_font(); + f32 textHAlign = delta_interpolate_f32(sHudUtilsState.textAlignment.h.prev, sHudUtilsState.textAlignment.h.curr, delta); + f32 lineWidth = gfx->params[0]; + create_dl_translation_matrix(DJUI_MTX_NOPUSH, -lineWidth * (1.f - textHAlign), -font->lineHeight, 0); + } break; + } } - - // scale - create_dl_scale_matrix(DJUI_MTX_NOPUSH, interp->width * translatedW, interp->height * translatedH, 1.0f); } - sResolution = savedResolution; - sRotation = savedRotation; + + sHudUtilsState = savedState; gDisplayListHead = savedHeadPos; gDjuiHudUtilsZ = savedZ; } +struct InterpHud *djui_hud_create_interp() { + if (sInterpHudCount >= MAX_INTERP_HUD) { return NULL; } + + struct InterpHud *interp = &sInterpHuds[sInterpHudCount++]; + interp->z = gDjuiHudUtilsZ; + interp->state = sHudUtilsState; + if (!interp->gfx) { + interp->gfx = growing_array_init(NULL, 8, malloc, free); + } else { + interp->gfx->count = 0; + } + + return interp; +} + +InterpHudGfx *djui_hud_create_interp_gfx(struct InterpHud *interp, enum InterpHudType type) { + if (!interp) { return NULL; } + + InterpHudGfx *gfx = growing_array_alloc(interp->gfx, sizeof(InterpHudGfx)); + gfx->type = type; + gfx->pos = gDisplayListHead; + return gfx; +} + //////////// // others // //////////// u8 djui_hud_get_resolution(void) { - return sResolution; + return sHudUtilsState.resolution; } void djui_hud_set_resolution(enum HudUtilsResolution resolutionType) { if (resolutionType >= RESOLUTION_COUNT) { return; } - sResolution = resolutionType; + sHudUtilsState.resolution = resolutionType; } u8 djui_hud_get_filter(void) { - return sFilter; + return sHudUtilsState.filter; } void djui_hud_set_filter(enum HudUtilsFilter filterType) { if (filterType >= FILTER_COUNT) { return; } - sFilter = filterType; + sHudUtilsState.filter = filterType; } -u8 djui_hud_get_font(void) { - return sFont; +s8 djui_hud_get_font(void) { + return sHudUtilsState.font; } void djui_hud_set_font(s8 fontType) { if (fontType >= FONT_COUNT || fontType < -1) { return; } - sLegacy = fontType == -1; - if (sLegacy) { fontType = 0; } - sFont = fontType; + sHudUtilsState.font = fontType; } struct DjuiColor* djui_hud_get_color(void) { - sRefColor.r = sColor.r; - sRefColor.g = sColor.g; - sRefColor.b = sColor.b; - sRefColor.a = sColor.a; + sRefColor.r = sHudUtilsState.color.r; + sRefColor.g = sHudUtilsState.color.g; + sRefColor.b = sHudUtilsState.color.b; + sRefColor.a = sHudUtilsState.color.a; return &sRefColor; } void djui_hud_set_color(u8 r, u8 g, u8 b, u8 a) { - sColor.r = r; - sColor.g = g; - sColor.b = b; - sColor.a = a; + sHudUtilsState.color.r = r; + sHudUtilsState.color.g = g; + sHudUtilsState.color.b = b; + sHudUtilsState.color.a = a; sColorAltered = TRUE; gDPSetEnvColor(gDisplayListHead++, r, g, b, a); } void djui_hud_reset_color(void) { if (sColorAltered) { - sColor.r = 255; - sColor.g = 255; - sColor.b = 255; - sColor.a = 255; + sHudUtilsState.color.r = 255; + sHudUtilsState.color.g = 255; + sHudUtilsState.color.b = 255; + sHudUtilsState.color.a = 255; sColorAltered = FALSE; gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); } } -struct HudUtilsRotation* djui_hud_get_rotation(void) { - return &sRotation; +void djui_hud_get_rotation(RET s16 *rotation, RET f32 *pivotX, RET f32 *pivotY) { + *rotation = degrees_to_sm64(sHudUtilsState.rotation.degrees.curr); + *pivotX = sHudUtilsState.rotation.pivotX.curr; + *pivotY = sHudUtilsState.rotation.pivotY.curr; } void djui_hud_set_rotation(s16 rotation, f32 pivotX, f32 pivotY) { - sRotation.rotationDiff = 0; - sRotation.prevPivotX = pivotX; - sRotation.prevPivotY = pivotY; - sRotation.rotation = (rotation * 180.f) / 0x8000; - sRotation.pivotX = pivotX; - sRotation.pivotY = pivotY; + sHudUtilsState.rotation.degrees.prev = sHudUtilsState.rotation.degrees.curr = sm64_to_degrees(rotation); + sHudUtilsState.rotation.pivotX.prev = sHudUtilsState.rotation.pivotX.curr = pivotX; + sHudUtilsState.rotation.pivotY.prev = sHudUtilsState.rotation.pivotY.curr = pivotY; } -void djui_hud_set_rotation_interpolated(s32 prevRotation, f32 prevPivotX, f32 prevPivotY, s32 rotation, f32 pivotX, f32 pivotY) { - f32 normalizedDiff = ((rotation - prevRotation + 0x8000) & 0xFFFF) - 0x8000; // Fix modular overflow/underflow - sRotation.rotationDiff = (normalizedDiff * 180.f) / 0x8000; - sRotation.prevPivotX = prevPivotX; - sRotation.prevPivotY = prevPivotY; - sRotation.rotation = (rotation * 180.f) / 0x8000; - sRotation.pivotX = pivotX; - sRotation.pivotY = pivotY; +void djui_hud_set_rotation_interpolated(s16 prevRotation, f32 prevPivotX, f32 prevPivotY, s16 rotation, f32 pivotX, f32 pivotY) { + sHudUtilsState.rotation.degrees.prev = sm64_to_degrees(prevRotation); + sHudUtilsState.rotation.degrees.curr = sm64_to_degrees(rotation); + sHudUtilsState.rotation.pivotX.prev = prevPivotX; + sHudUtilsState.rotation.pivotX.curr = pivotX; + sHudUtilsState.rotation.pivotY.prev = prevPivotY; + sHudUtilsState.rotation.pivotY.curr = pivotY; +} + +void djui_hud_get_text_alignment(RET f32 *textHAlign, RET f32 *textVAlign) { + *textHAlign = sHudUtilsState.textAlignment.h.curr; + *textVAlign = sHudUtilsState.textAlignment.v.curr; +} + +void djui_hud_set_text_alignment(f32 textHAlign, f32 textVAlign) { + sHudUtilsState.textAlignment.h.prev = sHudUtilsState.textAlignment.h.curr = textHAlign; + sHudUtilsState.textAlignment.v.prev = sHudUtilsState.textAlignment.v.curr = textVAlign; +} + +void djui_hud_set_text_alignment_interpolated(f32 prevTextHAlign, f32 prevTextVAlign, f32 textHAlign, f32 textVAlign) { + sHudUtilsState.textAlignment.h.prev = prevTextHAlign; + sHudUtilsState.textAlignment.h.curr = textHAlign; + sHudUtilsState.textAlignment.v.prev = prevTextVAlign; + sHudUtilsState.textAlignment.v.curr = textVAlign; } u32 djui_hud_get_screen_width(void) { u32 windowWidth, windowHeight; gfx_get_dimensions(&windowWidth, &windowHeight); - return (sResolution == RESOLUTION_N64) + return (sHudUtilsState.resolution == RESOLUTION_N64) ? GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT : (windowWidth / djui_gfx_get_scale()); } @@ -274,7 +394,7 @@ u32 djui_hud_get_screen_height(void) { u32 windowWidth, windowHeight; gfx_get_dimensions(&windowWidth, &windowHeight); - return (sResolution == RESOLUTION_N64) + return (sHudUtilsState.resolution == RESOLUTION_N64) ? SCREEN_HEIGHT : (windowHeight / djui_gfx_get_scale()); } @@ -364,23 +484,58 @@ void djui_hud_reset_scissor(void) { f32 djui_hud_measure_text(const char* message) { if (message == NULL) { return 0; } - const struct DjuiFont* font = gDjuiFonts[sFont]; - f32 width = 0; - const char* c = message; - while(*c != '\0') { - width += font->char_width((char*)c) * (sLegacy ? 0.5f : 1.0f); - c = djui_unicode_next_char((char*)c); + const struct DjuiFont* font = djui_hud_get_text_font(); + f32 width = 0, maxWidth = 0; + char *c = (char *) message; + const char *end = message + strlen(message); + while (*c != '\0') { + + // check color code + if (djui_text_parse_color(c, end, false, NULL, &c, NULL)) { + continue; + } + + // new line + if (*c == '\n') { + maxWidth = max(width, maxWidth); + width = 0; + } + + // tab: align to the next (4 x space width) + else if (*c == '\t') { + f32 tabWidth = 4 * font->char_width(" ") * (djui_hud_text_font_is_legacy() ? 0.5f : 1.0f); + width += tabWidth - fmodf(width, tabWidth); + } + + // unprintable chars + else if (!djui_text_is_printable(c)) { + // treat them as empty + } + + // regular chars + else { + width += font->char_width(c) * (djui_hud_text_font_is_legacy() ? 0.5f : 1.0f); + } + + c = djui_unicode_next_char(c); } - return width * font->defaultFontScale; + return max(width, maxWidth) * font->defaultFontScale; } -void djui_hud_print_text(const char* message, f32 x, f32 y, f32 scale) { +static Mtx *allocate_dl_translation_matrix() { + Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); + if (matrix == NULL) { return NULL; } + gSPMatrix(gDisplayListHead++, matrix, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); + return matrix; +} + +static void djui_hud_print_text_internal(const char* message, f32 x, f32 y, f32 scale, struct InterpHud *interp) { if (message == NULL) { return; } gDjuiHudUtilsZ += 0.001f; - if (sLegacy) { scale *= 0.5f; } + if (djui_hud_text_font_is_legacy()) { scale *= 0.5f; } - const struct DjuiFont* font = gDjuiFonts[sFont]; + const struct DjuiFont* font = djui_hud_get_text_font(); f32 fontScale = font->defaultFontScale * scale; // setup display list @@ -389,117 +544,147 @@ void djui_hud_print_text(const char* message, f32 x, f32 y, f32 scale) { } // translate position + djui_hud_create_interp_gfx(interp, INTERP_HUD_TRANSLATION); f32 translatedX = x + (font->xOffset * scale); f32 translatedY = y + (font->yOffset * scale); djui_hud_position_translate(&translatedX, &translatedY); create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); - // compute font size + // rotate f32 translatedFontSize = fontScale; djui_hud_size_translate(&translatedFontSize); + if (sHudUtilsState.rotation.degrees.prev != 0 || sHudUtilsState.rotation.degrees.curr != 0) { + djui_hud_create_interp_gfx(interp, INTERP_HUD_ROTATION); + f32 pivotTranslationX = font->defaultFontScale * translatedFontSize * sHudUtilsState.rotation.pivotX.curr; + f32 pivotTranslationY = font->defaultFontScale * translatedFontSize * sHudUtilsState.rotation.pivotY.curr; + create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); + create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sHudUtilsState.rotation.degrees.curr, 0, 0, 1); + create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); + } + + // compute font size + djui_hud_create_interp_gfx(interp, INTERP_HUD_SCALE); create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedFontSize, translatedFontSize, 1.0f); - // render the line - f32 addX = 0; - char* c = (char*)message; - while (*c != '\0') { - f32 charWidth = font->char_width(c); + // allocate the translation matrix for the vertical alignment + InterpHudGfx *valignGfx = djui_hud_create_interp_gfx(interp, INTERP_HUD_VALIGN); + Mtx *valignMatrix = allocate_dl_translation_matrix(); + if (valignMatrix == NULL) { return; } - if (*c == '\n' && *c == ' ') { - addX += charWidth; - c++; + // allocate the translation matrix for the horizontal alignment + InterpHudGfx *halignGfx = djui_hud_create_interp_gfx(interp, INTERP_HUD_HALIGN); + Mtx *halignMatrix = allocate_dl_translation_matrix(); + if (halignMatrix == NULL) { return; } + + // render the line + char* c = (char*)message; + const char *end = message + strlen(message); + f32 lineWidth = 0; + f32 textHeight = font->lineHeight; + + font->render_begin(); + while (*c != '\0') { + + // check color code + struct DjuiColor parsedColor; + if (djui_text_parse_color(c, end, false, &sHudUtilsState.color, &c, &parsedColor)) { + gDPSetEnvColor(gDisplayListHead++, parsedColor.r, parsedColor.g, parsedColor.b, parsedColor.a); + continue; + } + + // new line + if (*c == '\n') { + + // compute the horizontal alignment matrix for the current line + guTranslate(halignMatrix, -lineWidth * sHudUtilsState.textAlignment.h.curr, 0, 0); + if (halignGfx) { halignGfx->params[0] = lineWidth; } + + // allocate a new translation matrix for the next line + halignGfx = djui_hud_create_interp_gfx(interp, INTERP_HUD_HALIGN); + halignMatrix = allocate_dl_translation_matrix(); + if (halignMatrix == NULL) { return; } + + // cancel out the line translation and move to the next line + // this is needed because otherwise the text would be rendered in a staircase way + InterpHudGfx *newlineGfx = djui_hud_create_interp_gfx(interp, INTERP_HUD_NEW_LINE); + create_dl_translation_matrix(DJUI_MTX_NOPUSH, -lineWidth * (1.f - sHudUtilsState.textAlignment.h.curr), -font->lineHeight, 0); + if (newlineGfx) { newlineGfx->params[0] = lineWidth; } + lineWidth = 0; + textHeight += font->lineHeight; + c = djui_unicode_next_char(c); + continue; + } + + // tab: align to the next (4 x space width) + else if (*c == '\t') { + f32 tabWidth = 4 * font->char_width(" "); + f32 newLineWidth = lineWidth + tabWidth - fmodf(lineWidth, tabWidth); + create_dl_translation_matrix(DJUI_MTX_NOPUSH, newLineWidth - lineWidth, 0, 0); + lineWidth = newLineWidth; + c = djui_unicode_next_char(c); + continue; + } + + // unprintable chars + if (!djui_text_is_printable(c)) { + c = djui_unicode_next_char(c); continue; } // render + f32 charWidth = font->char_width(c); font->render_char(c); - create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth + addX, 0, 0); - addX = 0; + create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth, 0, 0); + lineWidth += charWidth; c = djui_unicode_next_char(c); } + font->render_end(); + + // compute the horizontal alignment matrix for the last line + guTranslate(halignMatrix, -lineWidth * sHudUtilsState.textAlignment.h.curr, 0, 0); + if (halignGfx) { halignGfx->params[0] = lineWidth; } + + // compute the vertical alignment matrix + guTranslate(valignMatrix, 0, textHeight * sHudUtilsState.textAlignment.v.curr, 0); + if (valignGfx) { valignGfx->params[0] = textHeight; } // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } +void djui_hud_print_text(const char* message, f32 x, f32 y, f32 scale) { + djui_hud_print_text_internal(message, x, y, scale, NULL); +} + void djui_hud_print_text_interpolated(const char* message, f32 prevX, f32 prevY, f32 prevScale, f32 x, f32 y, f32 scale) { if (message == NULL) { return; } - f32 savedZ = gDjuiHudUtilsZ; - gDjuiHudUtilsZ += 0.001f; - if (sLegacy) { - prevScale *= 0.5f; - scale *= 0.5f; + if (djui_hud_text_font_is_legacy()) { prevScale *= 0.5f; } + + struct InterpHud *interp = djui_hud_create_interp(); + if (interp) { + const struct DjuiFont* font = djui_hud_get_text_font(); + interp->posX.prev = prevX; + interp->posY.prev = prevY; + interp->posX.curr = x; + interp->posY.curr = y; + interp->scaleX.prev = prevScale; + interp->scaleY.prev = prevScale; + interp->scaleX.curr = scale; + interp->scaleY.curr = scale; + interp->width = font->defaultFontScale; + interp->height = font->defaultFontScale; } - const struct DjuiFont* font = gDjuiFonts[sFont]; - f32 fontScale = font->defaultFontScale * scale; - - // setup display list - if (font->textBeginDisplayList != NULL) { - gSPDisplayList(gDisplayListHead++, font->textBeginDisplayList); - } - - Gfx* savedHeadPos = gDisplayListHead; - - // translate position - f32 translatedX = x + (font->xOffset * scale); - f32 translatedY = y + (font->yOffset * scale); - djui_hud_position_translate(&translatedX, &translatedY); - create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); - - // compute font size - f32 translatedFontSize = fontScale; - djui_hud_size_translate(&translatedFontSize); - create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedFontSize, translatedFontSize, 1.0f); - - // render the line - f32 addX = 0; - char* c = (char*)message; - while (*c != '\0') { - f32 charWidth = font->char_width(c); - - if (*c == '\n' && *c == ' ') { - addX += charWidth; - c++; - continue; - } - - // render - font->render_char(c); - create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth + addX, 0, 0); - addX = 0; - - c = djui_unicode_next_char(c); - } - - // pop - gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); - - if (sInterpHudCount >= MAX_INTERP_HUD) { return; } - struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; - interp->headPos = savedHeadPos; - interp->prevX = prevX; - interp->prevY = prevY; - interp->prevScaleW = prevScale; - interp->prevScaleH = prevScale; - interp->x = x; - interp->y = y; - interp->scaleW = scale; - interp->scaleH = scale; - interp->width = font->defaultFontScale; - interp->height = font->defaultFontScale; - interp->z = savedZ; - interp->resolution = sResolution; - interp->rotation = sRotation; + djui_hud_print_text_internal(message, x, y, scale, interp); } static inline bool is_power_of_two(u32 n) { return (n > 0) && ((n & (n - 1)) == 0); } -static void djui_hud_render_texture_raw(const Texture* texture, u32 width, u32 height, u8 fmt, u8 siz, f32 x, f32 y, f32 scaleW, f32 scaleH) { +static void djui_hud_render_texture_raw(const Texture* texture, u32 width, u32 height, u8 fmt, u8 siz, f32 x, f32 y, f32 scaleW, f32 scaleH, struct InterpHud *interp) { if (!is_power_of_two(width) || !is_power_of_two(height)) { LOG_LUA_LINE("Tried to render DJUI HUD texture with NPOT width or height"); return; @@ -510,6 +695,7 @@ static void djui_hud_render_texture_raw(const Texture* texture, u32 width, u32 h gDjuiHudUtilsZ += 0.001f; // translate position + djui_hud_create_interp_gfx(interp, INTERP_HUD_TRANSLATION); f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); @@ -520,25 +706,27 @@ static void djui_hud_render_texture_raw(const Texture* texture, u32 width, u32 h f32 translatedH = scaleH; djui_hud_size_translate(&translatedW); djui_hud_size_translate(&translatedH); - if (sRotation.rotation != 0) { - f32 pivotTranslationX = width * translatedW * sRotation.pivotX; - f32 pivotTranslationY = height * translatedH * sRotation.pivotY; + if (sHudUtilsState.rotation.degrees.prev != 0 || sHudUtilsState.rotation.degrees.curr != 0) { + djui_hud_create_interp_gfx(interp, INTERP_HUD_ROTATION); + f32 pivotTranslationX = width * translatedW * sHudUtilsState.rotation.pivotX.curr; + f32 pivotTranslationY = height * translatedH * sHudUtilsState.rotation.pivotY.curr; create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); - create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sRotation.rotation, 0, 0, 1); + create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sHudUtilsState.rotation.degrees.curr, 0, 0, 1); create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); } // translate scale + djui_hud_create_interp_gfx(interp, INTERP_HUD_SCALE); create_dl_scale_matrix(DJUI_MTX_NOPUSH, width * translatedW, height * translatedH, 1.0f); // render - djui_gfx_render_texture(texture, width, height, fmt, siz, sFilter); + djui_gfx_render_texture(texture, width, height, fmt, siz, sHudUtilsState.filter); // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } -static void djui_hud_render_texture_tile_raw(const Texture* texture, u32 width, u32 height, u8 fmt, u8 siz, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { +static void djui_hud_render_texture_tile_raw(const Texture* texture, u32 width, u32 height, u8 fmt, u8 siz, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH, struct InterpHud *interp) { if (!texture) { return; } gDjuiHudUtilsZ += 0.001f; @@ -546,6 +734,7 @@ static void djui_hud_render_texture_tile_raw(const Texture* texture, u32 width, if (height != 0) { scaleH *= (f32) tileH / (f32) height; } // translate position + djui_hud_create_interp_gfx(interp, INTERP_HUD_TRANSLATION); f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); @@ -556,20 +745,22 @@ static void djui_hud_render_texture_tile_raw(const Texture* texture, u32 width, f32 translatedH = scaleH; djui_hud_size_translate(&translatedW); djui_hud_size_translate(&translatedH); - if (sRotation.rotation != 0) { + if (sHudUtilsState.rotation.degrees.prev != 0 || sHudUtilsState.rotation.degrees.curr != 0) { + djui_hud_create_interp_gfx(interp, INTERP_HUD_ROTATION); f32 aspect = tileH ? ((f32) tileW / (f32) tileH) : 1.f; - f32 pivotTranslationX = width * translatedW * aspect * sRotation.pivotX; - f32 pivotTranslationY = height * translatedH * sRotation.pivotY; + f32 pivotTranslationX = width * translatedW * aspect * sHudUtilsState.rotation.pivotX.curr; + f32 pivotTranslationY = height * translatedH * sHudUtilsState.rotation.pivotY.curr; create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); - create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sRotation.rotation, 0, 0, 1); + create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sHudUtilsState.rotation.degrees.curr, 0, 0, 1); create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); } // translate scale + djui_hud_create_interp_gfx(interp, INTERP_HUD_SCALE); create_dl_scale_matrix(DJUI_MTX_NOPUSH, width * translatedW, height * translatedH, 1.0f); // render - djui_gfx_render_texture_tile(texture, width, height, fmt, siz, tileX, tileY, tileW, tileH, sFilter, false); + djui_gfx_render_texture_tile(texture, width, height, fmt, siz, tileX, tileY, tileW, tileH, sHudUtilsState.filter); // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); @@ -577,44 +768,35 @@ static void djui_hud_render_texture_tile_raw(const Texture* texture, u32 width, void djui_hud_render_texture(struct TextureInfo* texInfo, f32 x, f32 y, f32 scaleW, f32 scaleH) { if (!texInfo) { return; } - djui_hud_render_texture_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, x, y, scaleW, scaleH); + djui_hud_render_texture_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, x, y, scaleW, scaleH, NULL); } void djui_hud_render_texture_tile(struct TextureInfo* texInfo, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { if (!texInfo) { return; } - djui_hud_render_texture_tile_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, x, y, scaleW, scaleH, tileX, tileY, tileW, tileH); + djui_hud_render_texture_tile_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, x, y, scaleW, scaleH, tileX, tileY, tileW, tileH, NULL); } void djui_hud_render_texture_interpolated(struct TextureInfo* texInfo, f32 prevX, f32 prevY, f32 prevScaleW, f32 prevScaleH, f32 x, f32 y, f32 scaleW, f32 scaleH) { - Gfx* savedHeadPos = gDisplayListHead; - f32 savedZ = gDjuiHudUtilsZ; - if (!texInfo) { return; } - djui_hud_render_texture_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, prevX, prevY, prevScaleW, prevScaleH); + struct InterpHud *interp = djui_hud_create_interp(); + if (interp) { + interp->posX.prev = prevX; + interp->posY.prev = prevY; + interp->posX.curr = x; + interp->posY.curr = y; + interp->scaleX.prev = prevScaleW; + interp->scaleY.prev = prevScaleH; + interp->scaleX.curr = scaleW; + interp->scaleY.curr = scaleH; + interp->width = texInfo->width; + interp->height = texInfo->height; + } - if (sInterpHudCount >= MAX_INTERP_HUD) { return; } - struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; - interp->headPos = savedHeadPos; - interp->prevX = prevX; - interp->prevY = prevY; - interp->prevScaleW = prevScaleW; - interp->prevScaleH = prevScaleH; - interp->x = x; - interp->y = y; - interp->scaleW = scaleW; - interp->scaleH = scaleH; - interp->width = texInfo->width; - interp->height = texInfo->height; - interp->z = savedZ; - interp->resolution = sResolution; - interp->rotation = sRotation; + djui_hud_render_texture_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, prevX, prevY, prevScaleW, prevScaleH, interp); } void djui_hud_render_texture_tile_interpolated(struct TextureInfo* texInfo, f32 prevX, f32 prevY, f32 prevScaleW, f32 prevScaleH, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { - Gfx* savedHeadPos = gDisplayListHead; - f32 savedZ = gDjuiHudUtilsZ; - if (!texInfo) { return; } // apply scale correction for tiles @@ -627,30 +809,28 @@ void djui_hud_render_texture_tile_interpolated(struct TextureInfo* texInfo, f32 prevScaleH *= ((f32)tileH / (f32)texInfo->height); } - djui_hud_render_texture_tile_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, prevX, prevY, prevScaleW, prevScaleH, tileX, tileY, tileW, tileH); + struct InterpHud *interp = djui_hud_create_interp(); + if (interp) { + interp->posX.prev = prevX; + interp->posY.prev = prevY; + interp->posX.curr = x; + interp->posY.curr = y; + interp->scaleX.prev = prevScaleW; + interp->scaleY.prev = prevScaleH; + interp->scaleX.curr = scaleW; + interp->scaleY.curr = scaleH; + interp->width = texInfo->width; + interp->height = texInfo->height; + } - if (sInterpHudCount >= MAX_INTERP_HUD) { return; } - struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; - interp->headPos = savedHeadPos; - interp->prevX = prevX; - interp->prevY = prevY; - interp->prevScaleW = prevScaleW; - interp->prevScaleH = prevScaleH; - interp->x = x; - interp->y = y; - interp->scaleW = scaleW; - interp->scaleH = scaleH; - interp->width = texInfo->width; - interp->height = texInfo->height; - interp->z = savedZ; - interp->resolution = sResolution; - interp->rotation = sRotation; + djui_hud_render_texture_tile_raw(texInfo->texture, texInfo->width, texInfo->height, texInfo->format, texInfo->size, prevX, prevY, prevScaleW, prevScaleH, tileX, tileY, tileW, tileH, interp); } -void djui_hud_render_rect(f32 x, f32 y, f32 width, f32 height) { +static void djui_hud_render_rect_internal(f32 x, f32 y, f32 width, f32 height, struct InterpHud *interp) { gDjuiHudUtilsZ += 0.001f; // translate position + djui_hud_create_interp_gfx(interp, INTERP_HUD_TRANSLATION); f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); @@ -661,15 +841,17 @@ void djui_hud_render_rect(f32 x, f32 y, f32 width, f32 height) { f32 translatedH = height; djui_hud_size_translate(&translatedW); djui_hud_size_translate(&translatedH); - if (sRotation.rotation != 0) { - f32 pivotTranslationX = translatedW * sRotation.pivotX; - f32 pivotTranslationY = translatedH * sRotation.pivotY; + if (sHudUtilsState.rotation.degrees.prev != 0 || sHudUtilsState.rotation.degrees.curr != 0) { + djui_hud_create_interp_gfx(interp, INTERP_HUD_ROTATION); + f32 pivotTranslationX = translatedW * sHudUtilsState.rotation.pivotX.curr; + f32 pivotTranslationY = translatedH * sHudUtilsState.rotation.pivotY.curr; create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); - create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sRotation.rotation, 0, 0, 1); + create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sHudUtilsState.rotation.degrees.curr, 0, 0, 1); create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); } // translate scale + djui_hud_create_interp_gfx(interp, INTERP_HUD_SCALE); create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedW, translatedH, 1.0f); // render @@ -679,28 +861,26 @@ void djui_hud_render_rect(f32 x, f32 y, f32 width, f32 height) { gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } +void djui_hud_render_rect(f32 x, f32 y, f32 width, f32 height) { + djui_hud_render_rect_internal(x, y, width, height, NULL); +} + void djui_hud_render_rect_interpolated(f32 prevX, f32 prevY, f32 prevWidth, f32 prevHeight, f32 x, f32 y, f32 width, f32 height) { - Gfx* savedHeadPos = gDisplayListHead; - f32 savedZ = gDjuiHudUtilsZ; + struct InterpHud *interp = djui_hud_create_interp(); + if (interp) { + interp->posX.prev = prevX; + interp->posY.prev = prevY; + interp->posX.curr = x; + interp->posY.curr = y; + interp->scaleX.prev = prevWidth; + interp->scaleY.prev = prevHeight; + interp->scaleX.curr = width; + interp->scaleY.curr = height; + interp->width = 1; + interp->height = 1; + } - djui_hud_render_rect(prevX, prevY, prevWidth, prevHeight); - - if (sInterpHudCount >= MAX_INTERP_HUD) { return; } - struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; - interp->headPos = savedHeadPos; - interp->prevX = prevX; - interp->prevY = prevY; - interp->prevScaleW = prevWidth; - interp->prevScaleH = prevHeight; - interp->x = x; - interp->y = y; - interp->scaleW = width; - interp->scaleH = height; - interp->width = 1; - interp->height = 1; - interp->z = savedZ; - interp->resolution = sResolution; - interp->rotation = sRotation; + djui_hud_render_rect_internal(prevX, prevY, prevWidth, prevHeight, interp); } void djui_hud_render_line(f32 p1X, f32 p1Y, f32 p2X, f32 p2Y, f32 size) { @@ -752,7 +932,7 @@ bool djui_hud_world_pos_to_screen_pos(Vec3f pos, VEC_OUT Vec3f out) { out[1] *= fovCoeff; f32 screenWidth, screenHeight; - if (sResolution == RESOLUTION_N64) { + if (sHudUtilsState.resolution == RESOLUTION_N64) { screenWidth = GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT; screenHeight = SCREEN_HEIGHT; } else { diff --git a/src/pc/djui/djui_hud_utils.h b/src/pc/djui/djui_hud_utils.h index b4e1b9d08..353ee3511 100644 --- a/src/pc/djui/djui_hud_utils.h +++ b/src/pc/djui/djui_hud_utils.h @@ -1,6 +1,22 @@ #ifndef DJUI_HUD_UTILS_H #define DJUI_HUD_UTILS_H +// Common pivot values for rotation +#define ROTATION_PIVOT_X_LEFT 0.0 +#define ROTATION_PIVOT_X_CENTER 0.5 +#define ROTATION_PIVOT_X_RIGHT 1.0 +#define ROTATION_PIVOT_Y_TOP 0.0 +#define ROTATION_PIVOT_Y_CENTER 0.5 +#define ROTATION_PIVOT_Y_BOTTOM 1.0 + +// Common alignment values for text alignment +#define TEXT_HALIGN_LEFT 0.0 +#define TEXT_HALIGN_CENTER 0.5 +#define TEXT_HALIGN_RIGHT 1.0 +#define TEXT_VALIGN_TOP 0.0 +#define TEXT_VALIGN_CENTER 0.5 +#define TEXT_VALIGN_BOTTOM 1.0 + enum HudUtilsResolution { RESOLUTION_DJUI, RESOLUTION_N64, @@ -14,7 +30,8 @@ enum HudUtilsFilter { }; enum DjuiFontType { - FONT_NORMAL, + FONT_LEGACY = -1, + FONT_NORMAL = 0, FONT_MENU, FONT_HUD, FONT_ALIASED, @@ -24,15 +41,6 @@ enum DjuiFontType { FONT_COUNT, }; -struct HudUtilsRotation { - f32 rotation; - f32 rotationDiff; - f32 prevPivotX; - f32 prevPivotY; - f32 pivotX; - f32 pivotY; -}; - struct GlobalTextures { struct TextureInfo camera; struct TextureInfo lakitu; @@ -63,7 +71,7 @@ u8 djui_hud_get_filter(void); /* |description|Sets the current DJUI HUD texture filter|descriptionEnd| */ void djui_hud_set_filter(enum HudUtilsFilter filterType); /* |description|Gets the current DJUI HUD font|descriptionEnd| */ -u8 djui_hud_get_font(void); +s8 djui_hud_get_font(void); /* |description|Sets the current DJUI HUD font|descriptionEnd| */ void djui_hud_set_font(s8 fontType); /* |description|Gets the current DJUI HUD color|descriptionEnd| */ @@ -73,11 +81,17 @@ void djui_hud_set_color(u8 r, u8 g, u8 b, u8 a); /* |description|Resets the current DJUI HUD color|descriptionEnd| */ void djui_hud_reset_color(void); /* |description|Gets the current DJUI HUD rotation|descriptionEnd| */ -struct HudUtilsRotation* djui_hud_get_rotation(void); +void djui_hud_get_rotation(RET s16 *rotation, RET f32 *pivotX, RET f32 *pivotY); /* |description|Sets the current DJUI HUD rotation|descriptionEnd| */ void djui_hud_set_rotation(s16 rotation, f32 pivotX, f32 pivotY); /* |description|Sets the current DJUI HUD rotation interpolated|descriptionEnd| */ -void djui_hud_set_rotation_interpolated(s32 prevRotation, f32 prevPivotX, f32 prevPivotY, s32 rotation, f32 pivotX, f32 pivotY); +void djui_hud_set_rotation_interpolated(s16 prevRotation, f32 prevPivotX, f32 prevPivotY, s16 rotation, f32 pivotX, f32 pivotY); +/* |description|Gets the current DJUI HUD text alignment|descriptionEnd| */ +void djui_hud_get_text_alignment(RET f32 *textHAlign, RET f32 *textVAlign); +/* |description|Sets the current DJUI HUD text alignment|descriptionEnd| */ +void djui_hud_set_text_alignment(f32 textHAlign, f32 textVAlign); +/* |description|Sets the current DJUI HUD text alignment interpolated|descriptionEnd| */ +void djui_hud_set_text_alignment_interpolated(f32 prevTextHAlign, f32 prevTextVAlign, f32 textHAlign, f32 textVAlign); /* |description|Gets the screen width in the current DJUI HUD resolution|descriptionEnd| */ u32 djui_hud_get_screen_width(void); diff --git a/src/pc/djui/djui_inputbox.c b/src/pc/djui/djui_inputbox.c index 878a3a94d..c0a911130 100644 --- a/src/pc/djui/djui_inputbox.c +++ b/src/pc/djui/djui_inputbox.c @@ -589,6 +589,8 @@ static bool djui_inputbox_render(struct DjuiBase* base) { f32 drawX = inputbox->viewX; f32 additionalShift = 0; bool wasInsideSelection = false; + + font->render_begin(); for (u16 i = 0; i < inputbox->bufferSize; i++) { //render composition text @@ -617,6 +619,7 @@ static bool djui_inputbox_render(struct DjuiBase* base) { djui_inputbox_render_char(inputbox, c, &drawX, &additionalShift); c = djui_unicode_next_char(c); } + font->render_end(); gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); gSPDisplayList(gDisplayListHead++, dl_ia_text_end); diff --git a/src/pc/djui/djui_panel_mod_menu.c b/src/pc/djui/djui_panel_mod_menu.c index 782fcddee..36437b2c6 100644 --- a/src/pc/djui/djui_panel_mod_menu.c +++ b/src/pc/djui/djui_panel_mod_menu.c @@ -113,7 +113,8 @@ void djui_panel_mod_menu_mod_create(struct DjuiBase* caller) { } if (mod == NULL) { return; } - struct DjuiThreePanel* panel = djui_panel_menu_create(to_uppercase(mod->name), false); + char *modNameUppercase = to_uppercase(mod->name); + struct DjuiThreePanel* panel = djui_panel_menu_create(modNameUppercase, false); struct DjuiBase* body = djui_three_panel_get_body(panel); { struct DjuiPaginated* paginated = djui_paginated_create(body, 8); @@ -130,6 +131,7 @@ void djui_panel_mod_menu_mod_create(struct DjuiBase* caller) { } djui_panel_add(caller, panel, NULL); + free(modNameUppercase); } void djui_panel_mod_menu_create(struct DjuiBase* caller) { diff --git a/src/pc/djui/djui_text.c b/src/pc/djui/djui_text.c index 48f362aac..26fd4ea9c 100644 --- a/src/pc/djui/djui_text.c +++ b/src/pc/djui/djui_text.c @@ -4,10 +4,146 @@ #include "djui_hud_utils.h" #include "game/segment2.h" -static u8 sSavedR = 0; -static u8 sSavedG = 0; -static u8 sSavedB = 0; -static u8 sSavedA = 0; + /////////// + // color // +/////////// + +static const struct DjuiColor sDjuiTextDefaultColor = { 220, 220, 220, 255 }; +static struct DjuiColor sDjuiTextCurrentColor; + +bool djui_text_parse_color(char *begin, const char *end, bool ignoreAlpha, const struct DjuiColor *baseColor, char **nextChar, struct DjuiColor *parsedColor) { + char *c = begin; + + // Not an escape + if (*c != '\\') { + return false; + } + c = djui_unicode_next_char(c); + + // Not a color + if (*c != '#') { + return false; + } + c = djui_unicode_next_char(c); + + // Parse color + u32 color = 0; + u8 length = 0; + while (c < end) { + if (*c == '\\') { + break; + } + + u8 colorPiece = 0; + if (*c >= '0' && *c <= '9') { colorPiece = *c - '0'; } + else if (*c >= 'a' && *c <= 'f') { colorPiece = 10 + *c - 'a'; } + else if (*c >= 'A' && *c <= 'F') { colorPiece = 10 + *c - 'A'; } + else { // Not a valid color piece + return false; + } + color = (color << 4) | colorPiece; + length++; + + c = djui_unicode_next_char(c); + } + + // Unterminated color code + if (c == end) { + return false; + } + + switch (length) { + + // reset to base color + case 0: { + if (baseColor && parsedColor) { + *parsedColor = *baseColor; + } + } break; + + // #rgb + case 3: { + if (parsedColor) { + u32 r = (color >> 8) & 0xF; + u32 g = (color >> 4) & 0xF; + u32 b = (color >> 0) & 0xF; + parsedColor->r = (r << 4) | r; + parsedColor->g = (g << 4) | g; + parsedColor->b = (b << 4) | b; + parsedColor->a = 0xFF; + } + } break; + + // #rgba + case 4: { + if (parsedColor) { + u32 r = (color >> 12) & 0xF; + u32 g = (color >> 8) & 0xF; + u32 b = (color >> 4) & 0xF; + u32 a = (color >> 0) & 0xF; + parsedColor->r = (r << 4) | r; + parsedColor->g = (g << 4) | g; + parsedColor->b = (b << 4) | b; + parsedColor->a = ignoreAlpha ? 0xFF : ((a << 4) | a); + } + } break; + + // #rrggbb + case 6: { + if (parsedColor) { + parsedColor->r = ((color >> 16) & 0xFF); + parsedColor->g = ((color >> 8) & 0xFF); + parsedColor->b = ((color >> 0) & 0xFF); + parsedColor->a = 0xFF; + } + } break; + + // #rrggbbaa + case 8: { + if (parsedColor) { + parsedColor->r = ((color >> 24) & 0xFF); + parsedColor->g = ((color >> 16) & 0xFF); + parsedColor->b = ((color >> 8) & 0xFF); + parsedColor->a = ignoreAlpha ? 0xFF : ((color >> 0) & 0xFF); + } + } break; + + // Invalid color + default: return false; + } + + if (nextChar) { + *nextChar = djui_unicode_next_char(c); + } + return true; +} + +void djui_text_remove_colors(char *str) { + if (!str) { return; } + char *colorStart = str; + const char *strEnd = str + strlen(str); + while ((colorStart = strstr(colorStart, "\\#"))) { + char *colorEnd; + if (djui_text_parse_color(colorStart, strEnd, false, NULL, &colorEnd, NULL) && colorEnd > colorStart) { + memmove(colorStart, colorEnd, strlen(colorEnd) + 1); + } else { + colorStart++; + } + } +} + +char *djui_text_get_uncolored_string(char *dest, size_t length, const char *str) { + if (!dest) { + dest = malloc(length * sizeof(char)); + if (!dest) { + return NULL; + } + } + strncpy(dest, str, length - 1); + dest[length - 1] = 0; + djui_text_remove_colors(dest); + return dest; +} //////////////// // properties // @@ -59,6 +195,10 @@ static f32 sTextRenderY = 0; static f32 sTextRenderLastX = 0; static f32 sTextRenderLastY = 0; +bool djui_text_is_printable(const char *c) { + return c != NULL && (!iscntrl(*c) || *c == 0x7F); // the star +} + static void djui_text_translate(f32 x, f32 y) { sTextRenderX += x; sTextRenderY += y; @@ -90,7 +230,7 @@ static void djui_text_render_char(struct DjuiText* text, char* c) { sTextRenderY += 1.0f / text->fontScale; gDPSetEnvColor(gDisplayListHead++, text->dropShadow.r, text->dropShadow.g, text->dropShadow.b, text->dropShadow.a); djui_text_render_single_char(text, c); - gDPSetEnvColor(gDisplayListHead++, sSavedR, sSavedG, sSavedB, sSavedA); + gDPSetEnvColor(gDisplayListHead++, sDjuiTextCurrentColor.r, sDjuiTextCurrentColor.g, sDjuiTextCurrentColor.b, sDjuiTextCurrentColor.a); sTextRenderX -= 1.0f / text->fontScale; sTextRenderY -= 1.0f / text->fontScale; } @@ -99,16 +239,21 @@ static void djui_text_render_char(struct DjuiText* text, char* c) { static f32 djui_text_measure_word_width(struct DjuiText* text, char* message) { f32 width = 0; - bool skipping = false; char* c = message; + const char *end = message + strlen(message); while (*c != '\0') { - if (*c == ' ') { return width; } - if (*c == '\n') { return width; } - if (*c == '\0') { return width; } - if (*c == '\\') { skipping = !skipping; } - if (!skipping) { - width += text->font->char_width(c); + + // color code + if (djui_text_parse_color(c, end, true, NULL, &c, NULL)) { + continue; } + + // end of word due to unprintable chars or space + if (!djui_text_is_printable(c) || *c == ' ') { + return width; + } + + width += text->font->char_width(c); c = djui_unicode_next_char(c); } return width; @@ -122,17 +267,18 @@ static void djui_text_read_line(struct DjuiText* text, char** message, f32* line u16 lastSafeEllipsesIndex = *index; u16 lastSafeEllipsesLineWidth = *lineWidth + ellipsesWidth;*/ - bool skipping = false; char* c = *message; + const char *end = *message + strlen(*message); while (*c != '\0') { f32 charWidth = text->font->char_width(c); - // check for special escape sequences - if (*c == '\\') { skipping = !skipping; } - if (skipping || *c == '\\') { + // check for color code + if (*c == '\\') { lastC = c; - c = djui_unicode_next_char(c); - continue; + if (djui_text_parse_color(c, end, true, NULL, &c, NULL)) { + lastC = c; + continue; + } } // check for newline @@ -212,59 +358,6 @@ f32 djui_text_find_width(struct DjuiText* text, u16 maxLines) { return largestWidth * text->fontScale; } -static char* djui_text_render_line_parse_escape(char* c1, char* c2) { - bool parsingColor = (c1[1] == '#'); - char* c = parsingColor ? (c1 + 2) : (c1 + 1); - - u32 color = 0; - u8 colorPieces = 0; - while (c < c2) { - if (*c == '\\') { break; } - if (parsingColor) { - u8 colorPiece = 0; - if (*c >= '0' && *c <= '9') { colorPiece = *c - '0'; } - else if (*c >= 'a' && *c <= 'f') { colorPiece = 10 + *c - 'a'; } - else if (*c >= 'A' && *c <= 'F') { colorPiece = 10 + *c - 'A'; } - color = (color << 4) | colorPiece; - colorPieces++; - } - c = djui_unicode_next_char(c); - } - - if (parsingColor) { - if (colorPieces == 3) { - u32 r = (color >> 8) & 0xF; - u32 g = (color >> 4) & 0xF; - u32 b = (color >> 0) & 0xF; - sSavedR = (r << 4) | r; - sSavedG = (g << 4) | g; - sSavedB = (b << 4) | b; - /*} else if (colorPieces == 4) { - u32 r = (color >> 12) & 0xF; - u32 g = (color >> 8) & 0xF; - u32 b = (color >> 4) & 0xF; - u32 a = (color >> 0) & 0xF; - sSavedR = (r << 4) | r; - sSavedG = (g << 4) | g; - sSavedB = (b << 4) | b; - sSavedA = (a << 4) | a;*/ - } else if (colorPieces == 6) { - sSavedR = ((color >> 16) & 0xFF); - sSavedG = ((color >> 8) & 0xFF); - sSavedB = ((color >> 0) & 0xFF); - }/*else if (colorPieces == 8) { - sSavedR = ((color >> 24) & 0xFF); - sSavedG = ((color >> 16) & 0xFF); - sSavedB = ((color >> 8) & 0xFF); - sSavedA = ((color >> 0) & 0xFF); - }*/ - gDPSetEnvColor(gDisplayListHead++, sSavedR, sSavedG, sSavedB, sSavedA); - } - - c = djui_unicode_next_char(c); - return c; -} - static void djui_text_render_line(struct DjuiText* text, char* c1, char* c2, f32 lineWidth, bool ellipses) { struct DjuiBase* base = &text->base; struct DjuiBaseRect* comp = &base->comp; @@ -284,9 +377,13 @@ static void djui_text_render_line(struct DjuiText* text, char* c1, char* c2, f32 } // render the line + text->font->render_begin(); + for (char* c = c1; c < c2;) { - if (*c == '\\') { - c = djui_text_render_line_parse_escape(c, c2); + struct DjuiColor parsedColor; + if (djui_text_parse_color(c, c2, true, &sDjuiTextDefaultColor, &c, &parsedColor)) { + gDPSetEnvColor(gDisplayListHead++, parsedColor.r, parsedColor.g, parsedColor.b, parsedColor.a); + sDjuiTextCurrentColor = parsedColor; continue; } @@ -312,6 +409,8 @@ static void djui_text_render_line(struct DjuiText* text, char* c1, char* c2, f32 } } + text->font->render_end(); + // reset translation matrix djui_text_translate(-curWidth, text->font->lineHeight); } @@ -352,10 +451,7 @@ static bool djui_text_render(struct DjuiBase* base) { // set color gDPSetEnvColor(gDisplayListHead++, base->color.r, base->color.g, base->color.b, base->color.a); - sSavedR = base->color.r; - sSavedG = base->color.g; - sSavedB = base->color.b; - sSavedA = base->color.a; + sDjuiTextCurrentColor = base->color; // count lines u16 maxLines = comp->height / ((f32)text->font->lineHeight * text->fontScale); diff --git a/src/pc/djui/djui_text.h b/src/pc/djui/djui_text.h index 2d8f84ee5..89e6da0cb 100644 --- a/src/pc/djui/djui_text.h +++ b/src/pc/djui/djui_text.h @@ -11,12 +11,17 @@ struct DjuiText { enum DjuiVAlign textVAlign; }; +bool djui_text_parse_color(char *begin, const char *end, bool ignoreAlpha, const struct DjuiColor *baseColor, char **nextChar, struct DjuiColor *parsedColor); +void djui_text_remove_colors(char *str); +char *djui_text_get_uncolored_string(char *dest, size_t length, const char *str); + void djui_text_set_text(struct DjuiText* text, const char* message); void djui_text_set_font(struct DjuiText* text, const struct DjuiFont* font); void djui_text_set_font_scale(struct DjuiText* text, f32 fontScale); void djui_text_set_drop_shadow(struct DjuiText* text, f32 r, f32 g, f32 b, f32 a); void djui_text_set_alignment(struct DjuiText* text, enum DjuiHAlign hAlign, enum DjuiVAlign vAlign); +bool djui_text_is_printable(const char *c); int djui_text_count_lines(struct DjuiText* text, u16 maxLines); f32 djui_text_find_width(struct DjuiText* text, u16 maxLines); diff --git a/src/pc/djui/djui_unicode.c b/src/pc/djui/djui_unicode.c index e97cd8551..4714be653 100644 --- a/src/pc/djui/djui_unicode.c +++ b/src/pc/djui/djui_unicode.c @@ -204,7 +204,7 @@ struct SmCodeGlyph sSmCodeDuplicateGlyphs[] = { static void* sCharMap = NULL; -static s32 count_bytes_for_char(char* text) { +static s32 count_bytes_for_char(const char* text) { s32 bytes = 0; u8 mask = (1 << 7); while (*text & mask) { @@ -214,7 +214,7 @@ static s32 count_bytes_for_char(char* text) { return bytes ? bytes : 1; } -static u64 convert_unicode_char_to_u64(char* text) { +static u64 convert_unicode_char_to_u64(const char* text) { s32 bytes = count_bytes_for_char(text); u64 value = (u8)*text; @@ -273,7 +273,7 @@ void djui_unicode_init(void) { } } -u32 djui_unicode_get_sprite_index(char* text) { +u32 djui_unicode_get_sprite_index(const char* text) { // check for ASCI if ((u8)*text < 128) { // make sure it's in the valid range @@ -298,7 +298,7 @@ u32 djui_unicode_get_sprite_index(char* text) { return (u8)'?' - SPRITE_INDEX_START_CHAR; } -f32 djui_unicode_get_sprite_width(char* text, const f32 font_widths[], f32 unicodeScale) { +f32 djui_unicode_get_sprite_width(const char* text, const f32 font_widths[], f32 unicodeScale) { if (!text) { return 0; } // check for ASCII @@ -358,7 +358,7 @@ size_t djui_unicode_len(char* text) { return len; } -bool djui_unicode_valid_char(char* text) { +bool djui_unicode_valid_char(const char* text) { if ((u8)*text < 128) { return ((u8)*text >= ' '); } @@ -397,7 +397,7 @@ void djui_unicode_cleanup_end(char* text) { } } -char djui_unicode_get_base_char(char* text) { +char djui_unicode_get_base_char(const char* text) { if ((u8)*text < ' ') { return '?'; } if ((u8)*text < 128) { return *text; } if (!sCharMap) { return '?'; } @@ -406,7 +406,7 @@ char djui_unicode_get_base_char(char* text) { return (glyph == NULL) ? '?' : glyph->base; } -void djui_unicode_get_char(char* text, char* output) { +void djui_unicode_get_char(const char* text, char* output) { s32 bytes = count_bytes_for_char(text); while (bytes-- > 0) { *output = *text; diff --git a/src/pc/djui/djui_unicode.h b/src/pc/djui/djui_unicode.h index 59d138162..f99735e6f 100644 --- a/src/pc/djui/djui_unicode.h +++ b/src/pc/djui/djui_unicode.h @@ -4,12 +4,12 @@ #include void djui_unicode_init(void); -u32 djui_unicode_get_sprite_index(char* text); -f32 djui_unicode_get_sprite_width(char* text, const f32 font_widths[], f32 unicodeScale); +u32 djui_unicode_get_sprite_index(const char* text); +f32 djui_unicode_get_sprite_width(const char* text, const f32 font_widths[], f32 unicodeScale); char* djui_unicode_next_char(char* text); char* djui_unicode_at_index(char* text, s32 index); size_t djui_unicode_len(char* text); -bool djui_unicode_valid_char(char* text); +bool djui_unicode_valid_char(const char* text); void djui_unicode_cleanup_end(char* text); -char djui_unicode_get_base_char(char* text); -void djui_unicode_get_char(char* text, char* output); +char djui_unicode_get_base_char(const char* text); +void djui_unicode_get_char(const char* text, char* output); diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index e2cbf87ea..35a3ce52d 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -1228,16 +1228,6 @@ static struct LuaObjectField sGraphNodeTranslationRotationFields[LUA_GRAPH_NODE_ { "translation", LVT_COBJECT, offsetof(struct GraphNodeTranslationRotation, translation), true, LOT_VEC3S, 1, sizeof(Vec3s) }, }; -#define LUA_HUD_UTILS_ROTATION_FIELD_COUNT 6 -static struct LuaObjectField sHudUtilsRotationFields[LUA_HUD_UTILS_ROTATION_FIELD_COUNT] = { - { "pivotX", LVT_F32, offsetof(struct HudUtilsRotation, pivotX), false, LOT_NONE, 1, sizeof(f32) }, - { "pivotY", LVT_F32, offsetof(struct HudUtilsRotation, pivotY), false, LOT_NONE, 1, sizeof(f32) }, - { "prevPivotX", LVT_F32, offsetof(struct HudUtilsRotation, prevPivotX), false, LOT_NONE, 1, sizeof(f32) }, - { "prevPivotY", LVT_F32, offsetof(struct HudUtilsRotation, prevPivotY), false, LOT_NONE, 1, sizeof(f32) }, - { "rotation", LVT_F32, offsetof(struct HudUtilsRotation, rotation), false, LOT_NONE, 1, sizeof(f32) }, - { "rotationDiff", LVT_F32, offsetof(struct HudUtilsRotation, rotationDiff), false, LOT_NONE, 1, sizeof(f32) }, -}; - #define LUA_INSTANT_WARP_FIELD_COUNT 3 static struct LuaObjectField sInstantWarpFields[LUA_INSTANT_WARP_FIELD_COUNT] = { { "area", LVT_U8, offsetof(struct InstantWarp, area), false, LOT_NONE, 1, sizeof(u8) }, @@ -1484,7 +1474,7 @@ static struct LuaObjectField sModFields[LUA_MOD_FIELD_COUNT] = { { "incompatible", LVT_STRING_P, offsetof(struct Mod, incompatible), true, LOT_NONE, 1, sizeof(char*) }, { "index", LVT_S32, offsetof(struct Mod, index), true, LOT_NONE, 1, sizeof(s32) }, { "isDirectory", LVT_BOOL, offsetof(struct Mod, isDirectory), true, LOT_NONE, 1, sizeof(bool) }, - { "name", LVT_STRING_P, offsetof(struct Mod, name), true, LOT_NONE, 1, sizeof(char*) }, + { "name", LVT_STRING, offsetof(struct Mod, name), true, LOT_NONE, 1, sizeof(char) }, { "pausable", LVT_BOOL, offsetof(struct Mod, pausable), true, LOT_NONE, 1, sizeof(bool) }, { "relativePath", LVT_STRING, offsetof(struct Mod, relativePath), true, LOT_NONE, 1, sizeof(char) }, { "renderBehindHud", LVT_BOOL, offsetof(struct Mod, renderBehindHud), true, LOT_NONE, 1, sizeof(bool) }, @@ -2734,7 +2724,6 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN] { LOT_GRAPHNODESWITCHCASE, sGraphNodeSwitchCaseFields, LUA_GRAPH_NODE_SWITCH_CASE_FIELD_COUNT }, { LOT_GRAPHNODETRANSLATION, sGraphNodeTranslationFields, LUA_GRAPH_NODE_TRANSLATION_FIELD_COUNT }, { LOT_GRAPHNODETRANSLATIONROTATION, sGraphNodeTranslationRotationFields, LUA_GRAPH_NODE_TRANSLATION_ROTATION_FIELD_COUNT }, - { LOT_HUDUTILSROTATION, sHudUtilsRotationFields, LUA_HUD_UTILS_ROTATION_FIELD_COUNT }, { LOT_INSTANTWARP, sInstantWarpFields, LUA_INSTANT_WARP_FIELD_COUNT }, { LOT_LAKITUSTATE, sLakituStateFields, LUA_LAKITU_STATE_FIELD_COUNT }, { LOT_LEVELVALUES, sLevelValuesFields, LUA_LEVEL_VALUES_FIELD_COUNT }, @@ -2840,7 +2829,6 @@ const char *sLuaLotNames[] = { [LOT_GRAPHNODESWITCHCASE] = "GraphNodeSwitchCase", [LOT_GRAPHNODETRANSLATION] = "GraphNodeTranslation", [LOT_GRAPHNODETRANSLATIONROTATION] = "GraphNodeTranslationRotation", - [LOT_HUDUTILSROTATION] = "HudUtilsRotation", [LOT_INSTANTWARP] = "InstantWarp", [LOT_LAKITUSTATE] = "LakituState", [LOT_LEVELVALUES] = "LevelValues", diff --git a/src/pc/lua/smlua_cobject_autogen.h b/src/pc/lua/smlua_cobject_autogen.h index 291e0f3ee..d034ce9f7 100644 --- a/src/pc/lua/smlua_cobject_autogen.h +++ b/src/pc/lua/smlua_cobject_autogen.h @@ -74,7 +74,6 @@ enum LuaObjectAutogenType { LOT_GRAPHNODESWITCHCASE, LOT_GRAPHNODETRANSLATION, LOT_GRAPHNODETRANSLATIONROTATION, - LOT_HUDUTILSROTATION, LOT_INSTANTWARP, LOT_LAKITUSTATE, LOT_LEVELVALUES, diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index bc67f2ae0..d7ca5a882 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1488,12 +1488,25 @@ char gSmluaConstants[] = "" "CONSOLE_MESSAGE_INFO=0\n" "CONSOLE_MESSAGE_WARNING=1\n" "CONSOLE_MESSAGE_ERROR=2\n" +"ROTATION_PIVOT_X_LEFT=0.0\n" +"ROTATION_PIVOT_X_CENTER=0.5\n" +"ROTATION_PIVOT_X_RIGHT=1.0\n" +"ROTATION_PIVOT_Y_TOP=0.0\n" +"ROTATION_PIVOT_Y_CENTER=0.5\n" +"ROTATION_PIVOT_Y_BOTTOM=1.0\n" +"TEXT_HALIGN_LEFT=0.0\n" +"TEXT_HALIGN_CENTER=0.5\n" +"TEXT_HALIGN_RIGHT=1.0\n" +"TEXT_VALIGN_TOP=0.0\n" +"TEXT_VALIGN_CENTER=0.5\n" +"TEXT_VALIGN_BOTTOM=1.0\n" "RESOLUTION_DJUI=0\n" "RESOLUTION_N64=1\n" "RESOLUTION_COUNT=2\n" "FILTER_NEAREST=0\n" "FILTER_LINEAR=1\n" "FILTER_COUNT=2\n" +"FONT_LEGACY=-1\n" "FONT_NORMAL=0\n" "FONT_MENU=1\n" "FONT_HUD=2\n" diff --git a/src/pc/lua/smlua_functions.c b/src/pc/lua/smlua_functions.c index cdbd868c0..4054a3ebd 100644 --- a/src/pc/lua/smlua_functions.c +++ b/src/pc/lua/smlua_functions.c @@ -945,7 +945,7 @@ int smlua_func_get_uncolored_string(lua_State* L) { const char *str = smlua_to_string(L, 1); if (!gSmLuaConvertSuccess) { LOG_LUA("get_uncolored_string: Failed to convert parameter 1"); return 0; } - char *strNoColor = str_remove_color_codes(str); + char *strNoColor = djui_text_get_uncolored_string(NULL, strlen(str) + 1, str); lua_pushstring(L, strNoColor); free(strNoColor); diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index c4b6b4f0e..e227a54aa 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -12318,7 +12318,7 @@ int smlua_func_djui_hud_reset_color(UNUSED lua_State* L) { return 1; } -int smlua_func_djui_hud_get_rotation(UNUSED lua_State* L) { +int smlua_func_djui_hud_get_rotation(lua_State* L) { if (L == NULL) { return 0; } int top = lua_gettop(L); @@ -12328,9 +12328,17 @@ int smlua_func_djui_hud_get_rotation(UNUSED lua_State* L) { } - smlua_push_object(L, LOT_HUDUTILSROTATION, djui_hud_get_rotation(), NULL); + s16 rotation; + f32 pivotX; + f32 pivotY; - return 1; + djui_hud_get_rotation(&rotation, &pivotX, &pivotY); + + lua_pushinteger(L, rotation); + lua_pushnumber(L, pivotX); + lua_pushnumber(L, pivotY); + + return 3; } int smlua_func_djui_hud_set_rotation(lua_State* L) { @@ -12363,13 +12371,13 @@ int smlua_func_djui_hud_set_rotation_interpolated(lua_State* L) { return 0; } - s32 prevRotation = smlua_to_integer(L, 1); + s16 prevRotation = smlua_to_integer(L, 1); if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "djui_hud_set_rotation_interpolated"); return 0; } f32 prevPivotX = smlua_to_number(L, 2); if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "djui_hud_set_rotation_interpolated"); return 0; } f32 prevPivotY = smlua_to_number(L, 3); if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "djui_hud_set_rotation_interpolated"); return 0; } - s32 rotation = smlua_to_integer(L, 4); + s16 rotation = smlua_to_integer(L, 4); if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 4, "djui_hud_set_rotation_interpolated"); return 0; } f32 pivotX = smlua_to_number(L, 5); if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 5, "djui_hud_set_rotation_interpolated"); return 0; } @@ -12381,6 +12389,69 @@ int smlua_func_djui_hud_set_rotation_interpolated(lua_State* L) { return 1; } +int smlua_func_djui_hud_get_text_alignment(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "djui_hud_get_text_alignment", 0, top); + return 0; + } + + + f32 textHAlign; + f32 textVAlign; + + djui_hud_get_text_alignment(&textHAlign, &textVAlign); + + lua_pushnumber(L, textHAlign); + lua_pushnumber(L, textVAlign); + + return 2; +} + +int smlua_func_djui_hud_set_text_alignment(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "djui_hud_set_text_alignment", 2, top); + return 0; + } + + f32 textHAlign = smlua_to_number(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "djui_hud_set_text_alignment"); return 0; } + f32 textVAlign = smlua_to_number(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "djui_hud_set_text_alignment"); return 0; } + + djui_hud_set_text_alignment(textHAlign, textVAlign); + + return 1; +} + +int smlua_func_djui_hud_set_text_alignment_interpolated(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 4) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "djui_hud_set_text_alignment_interpolated", 4, top); + return 0; + } + + f32 prevTextHAlign = smlua_to_number(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "djui_hud_set_text_alignment_interpolated"); return 0; } + f32 prevTextVAlign = smlua_to_number(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "djui_hud_set_text_alignment_interpolated"); return 0; } + f32 textHAlign = smlua_to_number(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "djui_hud_set_text_alignment_interpolated"); return 0; } + f32 textVAlign = smlua_to_number(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 4, "djui_hud_set_text_alignment_interpolated"); return 0; } + + djui_hud_set_text_alignment_interpolated(prevTextHAlign, prevTextVAlign, textHAlign, textVAlign); + + return 1; +} + int smlua_func_djui_hud_get_screen_width(UNUSED lua_State* L) { if (L == NULL) { return 0; } @@ -37389,6 +37460,9 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "djui_hud_get_rotation", smlua_func_djui_hud_get_rotation); smlua_bind_function(L, "djui_hud_set_rotation", smlua_func_djui_hud_set_rotation); smlua_bind_function(L, "djui_hud_set_rotation_interpolated", smlua_func_djui_hud_set_rotation_interpolated); + smlua_bind_function(L, "djui_hud_get_text_alignment", smlua_func_djui_hud_get_text_alignment); + smlua_bind_function(L, "djui_hud_set_text_alignment", smlua_func_djui_hud_set_text_alignment); + smlua_bind_function(L, "djui_hud_set_text_alignment_interpolated", smlua_func_djui_hud_set_text_alignment_interpolated); smlua_bind_function(L, "djui_hud_get_screen_width", smlua_func_djui_hud_get_screen_width); smlua_bind_function(L, "djui_hud_get_screen_height", smlua_func_djui_hud_get_screen_height); smlua_bind_function(L, "djui_hud_get_mouse_x", smlua_func_djui_hud_get_mouse_x); diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c index 9bfc6dceb..fc1dafcae 100644 --- a/src/pc/lua/smlua_hooks.c +++ b/src/pc/lua/smlua_hooks.c @@ -952,7 +952,7 @@ char** smlua_get_chat_subcommands_list(const char* maincommand) { for (s32 i = 0; i < sHookedChatCommandsCount; i++) { struct LuaHookedChatCommand* hook = &sHookedChatCommands[i]; if (strcmp(hook->command, maincommand) == 0) { - char* noColorsDesc = str_remove_color_codes(hook->description); + char* noColorsDesc = djui_text_get_uncolored_string(NULL, strlen(hook->description) + 1, hook->description); char* startSubcommands = strstr(noColorsDesc, "["); char* endSubcommands = strstr(noColorsDesc, "]"); diff --git a/src/pc/mods/mod.c b/src/pc/mods/mod.c index aca944c85..03f63ce39 100644 --- a/src/pc/mods/mod.c +++ b/src/pc/mods/mod.c @@ -197,11 +197,6 @@ void mod_clear(struct Mod* mod) { } } - if (mod->name != NULL) { - free(mod->name); - mod->name = NULL; - } - if (mod->incompatible != NULL) { free(mod->incompatible); mod->incompatible = NULL; @@ -449,7 +444,7 @@ static void mod_extract_fields(struct Mod* mod) { fseek(f, 0, SEEK_SET); // default to null - mod->name = NULL; + mod->name[0] = 0; mod->incompatible = NULL; mod->category = NULL; mod->description = NULL; @@ -457,7 +452,7 @@ static void mod_extract_fields(struct Mod* mod) { mod->ignoreScriptWarnings = false; // read line-by-line - #define BUFFER_SIZE MAX(MAX(MOD_NAME_MAX_LENGTH, MOD_INCOMPATIBLE_MAX_LENGTH), MOD_DESCRIPTION_MAX_LENGTH) + #define BUFFER_SIZE MAX(MAX(MOD_NAME_SIZE, MOD_INCOMPATIBLE_SIZE), MOD_DESCRIPTION_SIZE) char buffer[BUFFER_SIZE] = { 0 }; while (!feof(f)) { file_get_line(buffer, BUFFER_SIZE, f); @@ -470,24 +465,23 @@ static void mod_extract_fields(struct Mod* mod) { // extract the field char* extracted = NULL; - if (mod->name == NULL && (extracted = extract_lua_field("-- name:", buffer))) { - mod->name = calloc(MOD_NAME_MAX_LENGTH + 1, sizeof(char)); - if (snprintf(mod->name, MOD_NAME_MAX_LENGTH, "%s", extracted) < 0) { + if (!mod->name[0] && (extracted = extract_lua_field("-- name:", buffer))) { + if (snprintf(mod->name, MOD_NAME_SIZE, "%s", extracted) < 0) { LOG_INFO("Truncated mod name field '%s'", mod->name); } } else if (mod->incompatible == NULL && (extracted = extract_lua_field("-- incompatible:", buffer))) { - mod->incompatible = calloc(MOD_INCOMPATIBLE_MAX_LENGTH + 1, sizeof(char)); - if (snprintf(mod->incompatible, MOD_INCOMPATIBLE_MAX_LENGTH, "%s", extracted) < 0) { + mod->incompatible = calloc(MOD_INCOMPATIBLE_SIZE, sizeof(char)); + if (snprintf(mod->incompatible, MOD_INCOMPATIBLE_SIZE, "%s", extracted) < 0) { LOG_INFO("Truncated mod incompatible field '%s'", mod->incompatible); } } else if (mod->category == NULL && (extracted = extract_lua_field("-- category:", buffer))) { - mod->category = calloc(MOD_CATEGORY_MAX_LENGTH + 1, sizeof(char)); - if (snprintf(mod->category, MOD_CATEGORY_MAX_LENGTH, "%s", extracted) < 0) { + mod->category = calloc(MOD_CATEGORY_SIZE, sizeof(char)); + if (snprintf(mod->category, MOD_CATEGORY_SIZE, "%s", extracted) < 0) { LOG_INFO("Truncated mod category field '%s'", mod->category); } } else if (mod->description == NULL && (extracted = extract_lua_field("-- description:", buffer))) { - mod->description = calloc(MOD_DESCRIPTION_MAX_LENGTH + 1, sizeof(char)); - if (snprintf(mod->description, MOD_DESCRIPTION_MAX_LENGTH, "%s", extracted) < 0) { + mod->description = calloc(MOD_DESCRIPTION_SIZE, sizeof(char)); + if (snprintf(mod->description, MOD_DESCRIPTION_SIZE, "%s", extracted) < 0) { LOG_INFO("Truncated mod description field '%s'", mod->description); } } else if ((extracted = extract_lua_field("-- pausable:", buffer))) { @@ -632,17 +626,19 @@ bool mod_load(struct Mods* mods, char* basePath, char* modName) { mod_extract_fields(mod); // set name - if (mod->name == NULL) { - mod->name = strdup(modName); + if (!mod->name[0]) { + if (snprintf(mod->name, MOD_NAME_SIZE, "%s", modName) < 0) { + LOG_INFO("Truncated mod name field '%s'", mod->name); + } } // set category if (mod->category == NULL) { - char *modNameNoColor = str_remove_color_codes(mod->name); + char modNameNoColor[MOD_NAME_SIZE]; + djui_text_get_uncolored_string(modNameNoColor, MOD_NAME_SIZE, mod->name); if (strstr(modNameNoColor, "[CS]") == modNameNoColor) { mod->category = strdup("cs"); } - free(modNameNoColor); } // print diff --git a/src/pc/mods/mod.h b/src/pc/mods/mod.h index ea1e6c72a..9f14f361e 100644 --- a/src/pc/mods/mod.h +++ b/src/pc/mods/mod.h @@ -10,6 +10,11 @@ #define MOD_CATEGORY_MAX_LENGTH 64 #define MOD_DESCRIPTION_MAX_LENGTH 800 +#define MOD_NAME_SIZE (MOD_NAME_MAX_LENGTH + 1) +#define MOD_INCOMPATIBLE_SIZE (MOD_INCOMPATIBLE_MAX_LENGTH + 1) +#define MOD_CATEGORY_SIZE (MOD_CATEGORY_MAX_LENGTH + 1) +#define MOD_DESCRIPTION_SIZE (MOD_DESCRIPTION_MAX_LENGTH + 1) + struct Mods; struct ModFile { @@ -25,7 +30,7 @@ struct ModFile { }; struct Mod { - char* name; + char name[MOD_NAME_SIZE]; char* incompatible; char* category; char* description; diff --git a/src/pc/mods/mods.c b/src/pc/mods/mods.c index 9af7ab2b3..84b8fb497 100644 --- a/src/pc/mods/mods.c +++ b/src/pc/mods/mods.c @@ -184,19 +184,20 @@ static void mods_sort(struct Mods* mods) { } // By default, this is the alphabetical order on name + char modNameNoColor_i[MOD_NAME_SIZE]; + char modNameNoColor_j[MOD_NAME_SIZE]; for (s32 i = 1; i < mods->entryCount; ++i) { - struct Mod* mod = mods->entries[i]; + struct Mod* mod_i = mods->entries[i]; + djui_text_get_uncolored_string(modNameNoColor_i, MOD_NAME_SIZE, mod_i->name); for (s32 j = 0; j < i; ++j) { - struct Mod* mod2 = mods->entries[j]; - char* name = str_remove_color_codes(mod->name); - char* name2 = str_remove_color_codes(mod2->name); - if (strcmp(name, name2) < 0) { - mods->entries[i] = mod2; - mods->entries[j] = mod; - mod = mods->entries[i]; + struct Mod* mod_j = mods->entries[j]; + djui_text_get_uncolored_string(modNameNoColor_j, MOD_NAME_SIZE, mod_j->name); + if (strcmp(modNameNoColor_i, modNameNoColor_j) < 0) { + mods->entries[i] = mod_j; + mods->entries[j] = mod_i; + mod_i = mod_j; + memcpy(modNameNoColor_i, modNameNoColor_j, MOD_NAME_SIZE * sizeof(char)); } - free(name); - free(name2); } } } diff --git a/src/pc/network/packets/packet_download.c b/src/pc/network/packets/packet_download.c index ab5aa4917..3d26a7a30 100644 --- a/src/pc/network/packets/packet_download.c +++ b/src/pc/network/packets/packet_download.c @@ -318,13 +318,13 @@ after_filled:; // Cache any mod that doesn't have "(wip)" or "[wip]" in its name (case-insensitive) static bool should_cache_mod(struct Mod *mod) { - char *modName = sys_strdup(mod->name); - sys_strlwr(modName); + char modNameLowercase[MOD_NAME_SIZE]; + memcpy(modNameLowercase, mod->name, MOD_NAME_SIZE * sizeof(char)); + sys_strlwr(modNameLowercase); bool shouldCache = ( - !strstr(modName, "(wip)") && - !strstr(modName, "[wip]") + !strstr(modNameLowercase, "(wip)") && + !strstr(modNameLowercase, "[wip]") ); - free(modName); return shouldCache; } diff --git a/src/pc/network/packets/packet_mod_list.c b/src/pc/network/packets/packet_mod_list.c index 2a4a7baf8..23a4411d2 100644 --- a/src/pc/network/packets/packet_mod_list.c +++ b/src/pc/network/packets/packet_mod_list.c @@ -197,9 +197,8 @@ void network_receive_mod_list_entry(struct Packet* p) { } // get name - char name[MOD_NAME_MAX_LENGTH + 1] = { 0 }; - packet_read(p, name, nameLength * sizeof(u8)); - mod->name = strdup(name); + packet_read(p, mod->name, nameLength * sizeof(u8)); + mod->name[nameLength] = 0; // get incompatible length u16 incompatibleLength = 0; @@ -211,7 +210,7 @@ void network_receive_mod_list_entry(struct Packet* p) { // get incompatible if (incompatibleLength > 0) { - char incompatible[MOD_INCOMPATIBLE_MAX_LENGTH + 1] = { 0 }; + char incompatible[MOD_INCOMPATIBLE_SIZE] = { 0 }; packet_read(p, incompatible, incompatibleLength * sizeof(u8)); mod->incompatible = strdup(incompatible); } else { diff --git a/src/pc/utils/misc.c b/src/pc/utils/misc.c index 34e70241a..e358b942d 100644 --- a/src/pc/utils/misc.c +++ b/src/pc/utils/misc.c @@ -594,18 +594,3 @@ void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, } } } - -char *str_remove_color_codes(const char *str) { - char *output = strdup(str); - char *startColor; - while ((startColor = strstr(output, "\\#"))) { - char *endColor = strchr(startColor + 2, '\\'); - if (endColor) { - memmove(startColor, endColor + 1, strlen(endColor + 1) + 1); - } else { - *startColor = 0; - break; - } - } - return output; -} diff --git a/src/pc/utils/misc.h b/src/pc/utils/misc.h index 0efabc869..01d813fa1 100644 --- a/src/pc/utils/misc.h +++ b/src/pc/utils/misc.h @@ -37,6 +37,5 @@ void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta); void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx); void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, int num_strings, char* seperator); -char *str_remove_color_codes(const char *str); #endif \ No newline at end of file diff --git a/textures/custom_font/custom_font_hud_recolor.rgba32.png b/textures/custom_font/custom_font_hud_recolor.rgba32.png index 1ad909523..3f1bf60be 100644 Binary files a/textures/custom_font/custom_font_hud_recolor.rgba32.png and b/textures/custom_font/custom_font_hud_recolor.rgba32.png differ