From 5f8e2c94756af1ab5eee09b2fb03148fd2feb5bd Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 23 Oct 2025 03:41:51 -0400 Subject: [PATCH 1/2] fix the size parameter of snprintf and vsnprintf These functions accept a size parameter that *includes* the null terminator, so tweak function calls accordingly. --- code/cfile/cfilesystem.cpp | 2 +- code/fireball/fireballs.cpp | 18 +++++++++--------- code/graphics/opengl/gropengl.cpp | 4 ++-- code/graphics/software/font.cpp | 8 ++++---- code/hud/hud.cpp | 4 ++-- code/hud/hudmessage.cpp | 6 +++--- code/lab/dialogs/lab_ui_helpers.cpp | 6 +++--- code/network/multiui.cpp | 4 ++-- code/network/stand_gui.cpp | 18 +++++++++--------- code/osapi/osregistry.cpp | 2 +- code/popup/popup.cpp | 6 +++--- code/ship/shipfx.cpp | 4 ++-- 12 files changed, 41 insertions(+), 41 deletions(-) diff --git a/code/cfile/cfilesystem.cpp b/code/cfile/cfilesystem.cpp index 2fce560458a..c23b5bdf46b 100644 --- a/code/cfile/cfilesystem.cpp +++ b/code/cfile/cfilesystem.cpp @@ -2480,7 +2480,7 @@ void cfile_spew_pack_file_crcs() my_time = time(NULL); memset( datetime, 0, sizeof(datetime) ); - snprintf(datetime, sizeof(datetime)-1, "%s", ctime(&my_time)); + snprintf(datetime, sizeof(datetime), "%s", ctime(&my_time)); // ctime() adds a newline char, so we have to strip it off datetime[strlen(datetime)-1] = '\0'; diff --git a/code/fireball/fireballs.cpp b/code/fireball/fireballs.cpp index 92f5d96b1d5..c786b57dcb2 100644 --- a/code/fireball/fireballs.cpp +++ b/code/fireball/fireballs.cpp @@ -107,7 +107,7 @@ void fireball_play_warphole_close_sound(fireball *fb) snd_play_3d(gamesnd_get_game_sound(sound_index), &fireball_objp->pos, &Eye_position, fireball_objp->radius, NULL, 0, 1.0F, SND_PRIORITY_SINGLE_INSTANCE, NULL, fb->warp_sound_range_multiplier); // play warp sound effect } -static void fireball_generate_unique_id(char *unique_id, int buffer_len, int fireball_index) +static void fireball_generate_unique_id(char *unique_id, size_t buffer_size, int fireball_index) { Assertion(SCP_vector_inbounds(Fireball_info, fireball_index), "fireball_index is out of bounds!"); @@ -115,37 +115,37 @@ static void fireball_generate_unique_id(char *unique_id, int buffer_len, int fir { // use sensible names for the fireball.tbl default entries case FIREBALL_EXPLOSION_MEDIUM: - strncpy(unique_id, "Medium Explosion", buffer_len); + strncpy(unique_id, "Medium Explosion", buffer_size-1); break; case FIREBALL_WARP: - strncpy(unique_id, "Warp Effect", buffer_len); + strncpy(unique_id, "Warp Effect", buffer_size-1); break; case FIREBALL_KNOSSOS: - strncpy(unique_id, "Knossos Effect", buffer_len); + strncpy(unique_id, "Knossos Effect", buffer_size-1); break; case FIREBALL_ASTEROID: - strncpy(unique_id, "Asteroid Explosion", buffer_len); + strncpy(unique_id, "Asteroid Explosion", buffer_size-1); break; case FIREBALL_EXPLOSION_LARGE1: - strncpy(unique_id, "Large Explosion 1", buffer_len); + strncpy(unique_id, "Large Explosion 1", buffer_size-1); break; case FIREBALL_EXPLOSION_LARGE2: - strncpy(unique_id, "Large Explosion 2", buffer_len); + strncpy(unique_id, "Large Explosion 2", buffer_size-1); break; // base the id on the index default: - snprintf(unique_id, buffer_len, "Custom Fireball %d", fireball_index - NUM_DEFAULT_FIREBALLS + 1); + snprintf(unique_id, buffer_size, "Custom Fireball %d", fireball_index - NUM_DEFAULT_FIREBALLS + 1); break; } // null-terminate - unique_id[buffer_len - 1] = '\0'; + unique_id[buffer_size-1] = '\0'; } /** diff --git a/code/graphics/opengl/gropengl.cpp b/code/graphics/opengl/gropengl.cpp index 4db80fb0cd8..632000ff7e8 100644 --- a/code/graphics/opengl/gropengl.cpp +++ b/code/graphics/opengl/gropengl.cpp @@ -276,7 +276,7 @@ void gr_opengl_print_screen(const char *filename) GLuint pbo = 0; // save to a "screenshots" directory and tack on the filename - snprintf(tmp, MAX_PATH_LEN-1, "screenshots/%s.png", filename); + snprintf(tmp, MAX_PATH_LEN, "screenshots/%s.png", filename); _mkdir(os_get_config_path("screenshots").c_str()); @@ -436,7 +436,7 @@ void gr_opengl_dump_envmap(const char* filename) glBindTexture(sphere_tex->texture_target, sphere_tex->texture_id); pixels = (GLubyte*)vm_malloc(sphere_width * sphere_height * 4, memory::quiet_alloc); glGetTexImage(sphere_tex->texture_target, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - snprintf(tmp, MAX_PATH_LEN - 1, "envmaps/%s.png", filename); + snprintf(tmp, MAX_PATH_LEN, "envmaps/%s.png", filename); if (!png_write_bitmap(os_get_config_path(tmp).c_str(), 4 * width, 2 * height, true, pixels)) { ReleaseWarning(LOCATION, "Failed to write envmap to \"%s\".", os_get_config_path(tmp).c_str()); } diff --git a/code/graphics/software/font.cpp b/code/graphics/software/font.cpp index 3ce1380c4e3..592bc36b4b6 100644 --- a/code/graphics/software/font.cpp +++ b/code/graphics/software/font.cpp @@ -777,7 +777,7 @@ void gr_printf(int x, int y, const char * format, ...) if (!FontManager::isReady()) return; va_start(args, format); - vsnprintf(grx_printf_text, sizeof(grx_printf_text) - 1, format, args); + vsnprintf(grx_printf_text, sizeof(grx_printf_text), format, args); va_end(args); grx_printf_text[sizeof(grx_printf_text) - 1] = '\0'; @@ -791,7 +791,7 @@ void gr_printf_menu(int x, int y, const char * format, ...) if (!FontManager::isReady()) return; va_start(args, format); - vsnprintf(grx_printf_text, sizeof(grx_printf_text) - 1, format, args); + vsnprintf(grx_printf_text, sizeof(grx_printf_text), format, args); va_end(args); grx_printf_text[sizeof(grx_printf_text) - 1] = '\0'; @@ -805,7 +805,7 @@ void gr_printf_menu_zoomed(int x, int y, const char * format, ...) if (!FontManager::isReady()) return; va_start(args, format); - vsnprintf(grx_printf_text, sizeof(grx_printf_text) - 1, format, args); + vsnprintf(grx_printf_text, sizeof(grx_printf_text), format, args); va_end(args); grx_printf_text[sizeof(grx_printf_text) - 1] = '\0'; @@ -819,7 +819,7 @@ void gr_printf_no_resize(int x, int y, const char * format, ...) if (!FontManager::isReady()) return; va_start(args, format); - vsnprintf(grx_printf_text, sizeof(grx_printf_text) - 1, format, args); + vsnprintf(grx_printf_text, sizeof(grx_printf_text), format, args); va_end(args); grx_printf_text[sizeof(grx_printf_text) - 1] = '\0'; diff --git a/code/hud/hud.cpp b/code/hud/hud.cpp index b6b66877d0b..a3b1ec613a6 100644 --- a/code/hud/hud.cpp +++ b/code/hud/hud.cpp @@ -992,7 +992,7 @@ void HudGauge::renderPrintf(int x, int y, float scale, bool config, const char* // format the text va_start(args, format); - vsnprintf(tmp, sizeof(tmp)-1, format, args); + vsnprintf(tmp, sizeof(tmp), format, args); va_end(args); tmp[sizeof(tmp)-1] = '\0'; @@ -1006,7 +1006,7 @@ void HudGauge::renderPrintfWithGauge(int x, int y, int gauge_id, float scale, bo // format the text va_start(args, format); - vsnprintf(tmp, sizeof(tmp)-1, format, args); + vsnprintf(tmp, sizeof(tmp), format, args); va_end(args); tmp[sizeof(tmp)-1] = '\0'; diff --git a/code/hud/hudmessage.cpp b/code/hud/hudmessage.cpp index fc1a58003c7..47fb58e42bc 100644 --- a/code/hud/hudmessage.cpp +++ b/code/hud/hudmessage.cpp @@ -497,7 +497,7 @@ void HUD_fixed_printf(float duration, color col, const char *format, ...) } va_start(args, format); - vsnprintf(tmp, sizeof(tmp)-1, format, args); + vsnprintf(tmp, sizeof(tmp), format, args); va_end(args); tmp[sizeof(tmp)-1] = '\0'; @@ -553,7 +553,7 @@ void HUD_printf(const char *format, ...) } va_start(args, format); - vsnprintf(tmp, sizeof(tmp)-1, format, args); + vsnprintf(tmp, sizeof(tmp), format, args); va_end(args); tmp[sizeof(tmp)-1] = '\0'; @@ -579,7 +579,7 @@ void HUD_sourced_printf(int source, const char *format, ...) } va_start(args, format); - vsnprintf(tmp, sizeof(tmp)-1, format, args); + vsnprintf(tmp, sizeof(tmp), format, args); va_end(args); tmp[sizeof(tmp)-1] = '\0'; diff --git a/code/lab/dialogs/lab_ui_helpers.cpp b/code/lab/dialogs/lab_ui_helpers.cpp index 9bb45c57bca..6e3a8b051dc 100644 --- a/code/lab/dialogs/lab_ui_helpers.cpp +++ b/code/lab/dialogs/lab_ui_helpers.cpp @@ -143,7 +143,7 @@ SCP_string get_ship_table_text(ship_info* sip) if (!stricmp(line2 + i, sip->name)) { memset(file_text, 0, sizeof(file_text)); snprintf(file_text, - sizeof(file_text) - 1, + sizeof(file_text), "-- %s -------------------------------\r\n", tbl_file_names[n].c_str()); result += file_text; @@ -269,7 +269,7 @@ SCP_string get_weapon_table_text(weapon_info* wip) if (!stricmp(line2 + i, wip->name)) { memset(file_text, 0, sizeof(file_text)); snprintf(file_text, - sizeof(file_text) - 1, + sizeof(file_text), "-- %s -------------------------------\r\n", tbl_file_names[n].c_str()); result += file_text; @@ -391,7 +391,7 @@ SCP_string get_asteroid_table_text(const asteroid_info* aip) if (!stricmp(line2 + i, aip->name)) { memset(file_text, 0, sizeof(file_text)); snprintf(file_text, - sizeof(file_text) - 1, + sizeof(file_text), "-- %s -------------------------------\r\n", tbl_file_names[n].c_str()); result += file_text; diff --git a/code/network/multiui.cpp b/code/network/multiui.cpp index 4e63cd7f7cf..d32e759f3be 100644 --- a/code/network/multiui.cpp +++ b/code/network/multiui.cpp @@ -4457,7 +4457,7 @@ void multi_create_list_load_missions() Multi_create_mission_list.clear(); memset( wild_card, 0, sizeof(wild_card) ); - snprintf(wild_card, sizeof(wild_card) - 1, "*%s", FS_MISSION_FILE_EXT); + snprintf(wild_card, sizeof(wild_card), "*%s", FS_MISSION_FILE_EXT); file_list = (char**) vm_malloc( sizeof(char*) * 1024 ); @@ -4550,7 +4550,7 @@ void multi_create_list_load_campaigns() Multi_create_campaign_list.clear(); memset( wild_card, 0, sizeof(wild_card) ); - snprintf(wild_card, sizeof(wild_card) - 1, "*%s", FS_CAMPAIGN_FILE_EXT); + snprintf(wild_card, sizeof(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT); file_list = (char**) vm_malloc( sizeof(char*) * 1024 ); diff --git a/code/network/stand_gui.cpp b/code/network/stand_gui.cpp index 970fa16d2b2..1dc21075a23 100644 --- a/code/network/stand_gui.cpp +++ b/code/network/stand_gui.cpp @@ -1052,7 +1052,7 @@ static HWND Player_stats[MAX_PLAYER_STAT_FIELDS]; // text boxes for player allt static HWND Player_mstats[MAX_PLAYER_STAT_FIELDS]; // text boxes for player mission statistics info // sprintf and set window text to the passed int -#define STD_ADDSTRING(hwnd,val) { snprintf(txt,sizeof(txt)-1,"%d",(int)val); SetWindowText(hwnd,txt); } +#define STD_ADDSTRING(hwnd,val) { snprintf(txt,sizeof(txt),"%d",sz2i(val)); SetWindowText(hwnd,txt); } // intialize all the controls in the player info tab void std_pinfo_init_player_info_controls(); @@ -2036,17 +2036,17 @@ void std_build_title_string(char *str) memset(ver_str, 0, sizeof(ver_str)); if (FS_VERSION_BUILD == 0 && FS_VERSION_HAS_REVIS == 0) { //-V547 - snprintf(ver_str, sizeof(ver_str)-1, "%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR); + snprintf(ver_str, sizeof(ver_str), "%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR); } else if (FS_VERSION_HAS_REVIS == 0) { - snprintf(ver_str, sizeof(ver_str)-1, "%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD); + snprintf(ver_str, sizeof(ver_str), "%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD); } else { - snprintf(ver_str, sizeof(ver_str)-1, "%i.%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS); + snprintf(ver_str, sizeof(ver_str), "%i.%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS); } // now build the title memset(temp, 0, 256); - snprintf(temp, sizeof(temp)-1, "%s %s", XSTR("FreeSpace Standalone", 935), ver_str); + snprintf(temp, sizeof(temp), "%s %s", XSTR("FreeSpace Standalone", 935), ver_str); // output first part strcpy(str, temp); @@ -2105,22 +2105,22 @@ static HMENU std_create_systray_menu() HMENU stdPopup = CreatePopupMenu(); // Type of connection: - snprintf(tstr, sizeof(tstr)-1, "Connection Type: %s", MULTI_IS_TRACKER_GAME ? "PXO" : "Local/IP"); + snprintf(tstr, sizeof(tstr), "Connection Type: %s", MULTI_IS_TRACKER_GAME ? "PXO" : "Local/IP"); AppendMenu(stdPopup, MF_STRING | MF_GRAYED, 0, tstr); // ---------------------------------------------- AppendMenu(stdPopup, MF_SEPARATOR, 0, NULL); // Game name: - snprintf(tstr, sizeof(tstr)-1, "Name: %s", Netgame.name); + snprintf(tstr, sizeof(tstr), "Name: %s", Netgame.name); AppendMenu(stdPopup, MF_STRING | MF_GRAYED, 0, tstr); // Mission name: - snprintf(tstr, sizeof(tstr)-1, "Mission: %s", strlen(Netgame.mission_name) ? Netgame.mission_name : ""); + snprintf(tstr, sizeof(tstr), "Mission: %s", strlen(Netgame.mission_name) ? Netgame.mission_name : ""); AppendMenu(stdPopup, MF_STRING | MF_GRAYED, 0, tstr); // Number of players: - snprintf(tstr, sizeof(tstr)-1, "Num Players: %d", multi_num_players()); + snprintf(tstr, sizeof(tstr), "Num Players: %d", multi_num_players()); AppendMenu(stdPopup, MF_STRING | MF_GRAYED, 0, tstr); // ---------------------------------------------- diff --git a/code/osapi/osregistry.cpp b/code/osapi/osregistry.cpp index 9a466cb22c9..f039a464e85 100644 --- a/code/osapi/osregistry.cpp +++ b/code/osapi/osregistry.cpp @@ -1108,7 +1108,7 @@ void os_config_write_uint(const char* section, const char* name, unsigned int va char buf[21]; - snprintf(buf, 20, "%u", value); + snprintf(buf, 21, "%u", value); profile = profile_update(profile, section, name, buf); profile_save(profile, file); diff --git a/code/popup/popup.cpp b/code/popup/popup.cpp index b5e0c26b980..455e32d481d 100644 --- a/code/popup/popup.cpp +++ b/code/popup/popup.cpp @@ -1145,7 +1145,7 @@ int popup(int flags, int nchoices, ... ) // get msg text format = va_arg( args, char * ); - vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text)-1, format, args); + vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text), format, args); va_end(args); Popup_info.raw_text[sizeof(Popup_info.raw_text)-1] = '\0'; @@ -1202,7 +1202,7 @@ int popup_till_condition(int (*condition)(), ...) // get msg text format = va_arg( args, char * ); - vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text)-1, format, args); + vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text), format, args); va_end(args); Popup_info.raw_text[sizeof(Popup_info.raw_text)-1] = '\0'; @@ -1333,7 +1333,7 @@ bool popup_conditional_create(int flags, ...) // get msg text format = va_arg( args, char * ); - vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text)-1, format, args); + vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text), format, args); Popup_info.raw_text[sizeof(Popup_info.raw_text)-1] = '\0'; va_end(args); diff --git a/code/ship/shipfx.cpp b/code/ship/shipfx.cpp index ea3aac21d30..fc86bc9b190 100644 --- a/code/ship/shipfx.cpp +++ b/code/ship/shipfx.cpp @@ -2870,7 +2870,7 @@ class CombinedVariable int getInt(int *output); //Returns handle, or < 0 on failure/wrong type gamesnd_id getSound(); - //Returns 1 if buffer was successfully written to + //Returns 1 if buffer was successfully written to (output_max includes the null terminator) int getString(char *output, size_t output_max); //Returns true if TYPE_NONE @@ -3065,7 +3065,7 @@ void parse_combined_variable_list(CombinedVariable *dest, flag_def_list *src, si sp = &src[i]; dp = &dest[i]; - snprintf(buf, sizeof(buf)-1, "+%s:", sp->name); + snprintf(buf, sizeof(buf), "+%s:", sp->name); if(optional_string(buf)) { switch(sp->var) From 041dd2b9dc1f714f64772f9a494c391172e50403 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Fri, 24 Oct 2025 00:49:27 -0400 Subject: [PATCH 2/2] several utility enhancements These were coded for the `split_str` refactor, but are generally useful elsewhere as well. 1. Use SCP_string in some utility functions where appropriate 2. Avoid copying C-strings in some utility functions where it isn't necessary 3. Add concat functions to `SCP_vector` 4. Change return type `int` to `bool` on some common parselo functions 5. Add `find_white_space` and `find_gray_space` parselo functions --- code/globalincs/vmallocator.h | 10 +++++ code/hud/hud.cpp | 14 +++---- code/hud/hudmessage.cpp | 70 ++++++++++++++++++----------------- code/hud/hudmessage.h | 4 +- code/parse/parselo.cpp | 26 +++++++++++-- code/parse/parselo.h | 8 ++-- code/utils/unicode.h | 6 ++- code/weapon/emp.cpp | 6 +-- 8 files changed, 89 insertions(+), 55 deletions(-) diff --git a/code/globalincs/vmallocator.h b/code/globalincs/vmallocator.h index 191e0771007..6b7f71f0067 100644 --- a/code/globalincs/vmallocator.h +++ b/code/globalincs/vmallocator.h @@ -32,6 +32,16 @@ class SCP_vector : public std::vector> return std::find(this->begin(), this->end(), item) != this->end(); } + void concat(SCP_vector&& other) + { + insert(this->end(), std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); + } + + void concat(const SCP_vector& other) + { + insert(this->end(), other.begin(), other.end()); + } + bool in_bounds(int idx) const { return (idx >= 0) && (static_cast(idx) < this->size()); diff --git a/code/hud/hud.cpp b/code/hud/hud.cpp index a3b1ec613a6..ba5b8de7668 100644 --- a/code/hud/hud.cpp +++ b/code/hud/hud.cpp @@ -987,30 +987,28 @@ void HudGauge::renderStringAlignCenter(int x, int y, int area_width, const char void HudGauge::renderPrintf(int x, int y, float scale, bool config, const char* format, ...) { - char tmp[256] = ""; + SCP_string tmp; va_list args; // format the text va_start(args, format); - vsnprintf(tmp, sizeof(tmp), format, args); + vsprintf(tmp, format, args); va_end(args); - tmp[sizeof(tmp)-1] = '\0'; - renderString(x, y, tmp, scale, config); + renderString(x, y, tmp.c_str(), scale, config); } void HudGauge::renderPrintfWithGauge(int x, int y, int gauge_id, float scale, bool config, const char* format, ...) { - char tmp[256] = ""; + SCP_string tmp; va_list args; // format the text va_start(args, format); - vsnprintf(tmp, sizeof(tmp), format, args); + vsprintf(tmp, format, args); va_end(args); - tmp[sizeof(tmp)-1] = '\0'; - renderString(x, y, gauge_id, tmp, scale, config); + renderString(x, y, gauge_id, tmp.c_str(), scale, config); } void HudGauge::renderBitmapColor(int frame, int x, int y, float scale, bool config) const diff --git a/code/hud/hudmessage.cpp b/code/hud/hudmessage.cpp index 47fb58e42bc..05377d9f009 100644 --- a/code/hud/hudmessage.cpp +++ b/code/hud/hudmessage.cpp @@ -140,7 +140,7 @@ static SCP_vector Msg_scrollback_lines; typedef struct HUD_ft { int end_time; // Timestamp at which this message will go away. - char text[MAX_HUD_LINE_LEN]; // Text to display. + char text[MAX_HUD_LINE_BUF]; // Text to display. int color; // 0rgb color, 8 bit fields. } HUD_ft; @@ -487,8 +487,6 @@ void HudGaugeMessages::render(float /*frametime*/, bool config) void HUD_fixed_printf(float duration, color col, const char *format, ...) { va_list args; - char tmp[HUD_MSG_LENGTH_MAX]; - size_t msg_length; // make sure we only print these messages if we're in the correct state if((Game_mode & GM_MULTIPLAYER) && (Netgame.game_state != NETGAME_STATE_IN_MISSION)){ @@ -497,22 +495,9 @@ void HUD_fixed_printf(float duration, color col, const char *format, ...) } va_start(args, format); - vsnprintf(tmp, sizeof(tmp), format, args); + vsnprintf(HUD_fixed_text[0].text, MAX_HUD_LINE_BUF, format, args); va_end(args); - tmp[sizeof(tmp)-1] = '\0'; - - msg_length = strlen(tmp); - - if ( !msg_length ) { - nprintf(("Warning", "HUD_fixed_printf ==> attempt to print a 0 length string in msg window\n")); - return; - - } else if (msg_length > MAX_HUD_LINE_LEN - 1){ - nprintf(("Warning", "HUD_fixed_printf ==> Following string truncated to %d chars: %s\n", MAX_HUD_LINE_LEN - 1, tmp)); - tmp[MAX_HUD_LINE_LEN-1] = '\0'; - } - - strcpy_s(HUD_fixed_text[0].text, tmp); + HUD_fixed_text[0].text[MAX_HUD_LINE_BUF-1] = '\0'; if (duration == 0.0f){ HUD_fixed_text[0].end_time = timestamp(-1); @@ -544,7 +529,7 @@ int HUD_source_get_team(int source) void HUD_printf(const char *format, ...) { va_list args; - char tmp[HUD_MSG_LENGTH_MAX]; + SCP_string tmp; // make sure we only print these messages if we're in the correct state if((Game_mode & GM_MULTIPLAYER) && (Net_player->state != NETPLAYER_STATE_IN_MISSION)){ @@ -553,9 +538,8 @@ void HUD_printf(const char *format, ...) } va_start(args, format); - vsnprintf(tmp, sizeof(tmp), format, args); + vsprintf(tmp, format, args); va_end(args); - tmp[sizeof(tmp)-1] = '\0'; hud_sourced_print(HUD_SOURCE_COMPUTER, tmp); } @@ -570,7 +554,7 @@ void HUD_printf(const char *format, ...) void HUD_sourced_printf(int source, const char *format, ...) { va_list args; - char tmp[HUD_MSG_LENGTH_MAX]; + SCP_string tmp; // make sure we only print these messages if we're in the correct state if((Game_mode & GM_MULTIPLAYER) && (Net_player->state != NETPLAYER_STATE_IN_MISSION)){ @@ -579,16 +563,40 @@ void HUD_sourced_printf(int source, const char *format, ...) } va_start(args, format); - vsnprintf(tmp, sizeof(tmp), format, args); + vsprintf(tmp, format, args); va_end(args); - tmp[sizeof(tmp)-1] = '\0'; hud_sourced_print(source, tmp); } +void hud_sourced_print(int source, const SCP_string &msg) +{ + if ( msg.empty() ) { + nprintf(("Warning", "HUD ==> attempt to print a 0 length string in msg window\n")); + return; + } + + // add message to the scrollback log first + hud_add_msg_to_scrollback(msg.c_str(), source, Missiontime); + + HUD_message_data new_msg; + + new_msg.text = msg; + new_msg.source = source; + new_msg.x = 0; + + HUD_msg_buffer.push_back(new_msg); + + // Invoke the scripting hook + if (OnHudMessageReceivedHook->isActive()) { + OnHudMessageReceivedHook->run(scripting::hook_param_list(scripting::hook_param("Text", 's', msg.c_str()), + scripting::hook_param("SourceType", 'i', source))); + } +} + void hud_sourced_print(int source, const char *msg) { - if ( !strlen(msg) ) { + if ( !*msg ) { nprintf(("Warning", "HUD ==> attempt to print a 0 length string in msg window\n")); return; } @@ -643,21 +651,15 @@ void HUD_add_to_scrollback(const char *text, int source) void hud_add_msg_to_scrollback(const char *text, int source, int t) { - size_t msg_len = strlen(text); - if (msg_len == 0) + if (!*text) return; - - Assert(msg_len < HUD_MSG_LENGTH_MAX); - - char buf[HUD_MSG_LENGTH_MAX], *ptr; - strcpy_s(buf, text); - ptr = strstr(buf, NOX(": ")); int w = 0; // determine the length of the sender's name for underlining + auto ptr = strstr(text, NOX(": ")); if (ptr) { - gr_get_string_size(&w, nullptr, buf, 1.0f, (ptr - buf)); + gr_get_string_size(&w, nullptr, text, 1.0f, (ptr - text)); } // create the new node for the vector diff --git a/code/hud/hudmessage.h b/code/hud/hudmessage.h index 4599cf3a045..ebfbef29d46 100644 --- a/code/hud/hudmessage.h +++ b/code/hud/hudmessage.h @@ -16,7 +16,7 @@ #include "graphics/generic.h" #include "hud/hud.h" -#define MAX_HUD_LINE_LEN 256 // maximum number of characters for a HUD message +#define MAX_HUD_LINE_BUF 256 // maximum size for a HUD message // If these are changed, the lua 'addMessageToScrollback' method in mission.cpp should be updated. #define HUD_SOURCE_COMPUTER 0 @@ -67,12 +67,12 @@ int HUD_team_get_source(int team); int HUD_source_get_team(int team); void HUD_printf(SCP_FORMAT_STRING const char *format, ...) SCP_FORMAT_STRING_ARGS(1, 2); void hud_sourced_print(int source, const char *msg); +void hud_sourced_print(int source, const SCP_string &msg); void HUD_sourced_printf(int source, SCP_FORMAT_STRING const char *format, ...) SCP_FORMAT_STRING_ARGS(2, 3); // send hud message from specified source void HUD_fixed_printf(float duration, color col, SCP_FORMAT_STRING const char *format, ...) SCP_FORMAT_STRING_ARGS(3, 4); // Display a single message for duration seconds. void HUD_init_fixed_text(); // Clear all pending fixed text. void HUD_add_to_scrollback(const char *text, int source); -void hud_add_line_to_scrollback(const char *text, int source, int t, int x, int y, int w); void hud_add_msg_to_scrollback(const char *text, int source, int t); class HudGaugeMessages: public HudGauge // HUD_MESSAGE_LINES diff --git a/code/parse/parselo.cpp b/code/parse/parselo.cpp index 729838a305c..609a5ae2708 100644 --- a/code/parse/parselo.cpp +++ b/code/parse/parselo.cpp @@ -68,25 +68,43 @@ static const SCP_unordered_map retail_hashes = { // Return true if this character is white space, else false. -int is_white_space(char ch) +bool is_white_space(char ch) { return ((ch == ' ') || (ch == '\t') || (ch == EOLN) || (ch == CARRIAGE_RETURN)); } -int is_white_space(unicode::codepoint_t cp) + +// Return true if this character is white space, else false. +bool is_white_space(unicode::codepoint_t cp) { return ((cp == UNICODE_CHAR(' ')) || (cp == UNICODE_CHAR('\t')) || (cp == (unicode::codepoint_t)EOLN) || (cp == (unicode::codepoint_t)CARRIAGE_RETURN)); } +// Returns the length of the string up to but excluding any white space. This could be the entire string if the string contains no white space. +// Equivalently, returns the position of the first white space character, or the string length if no white space is found. +size_t find_white_space(const char *str) +{ + return strcspn(str, " \t\n\r"); +} + // Returns true if this character is gray space, else false (gray space is white space except for EOLN). -int is_gray_space(char ch) +bool is_gray_space(char ch) { return ((ch == ' ') || (ch == '\t')); } -bool is_gray_space(unicode::codepoint_t cp) { +// Returns true if this character is gray space, else false (gray space is white space except for EOLN). +bool is_gray_space(unicode::codepoint_t cp) +{ return cp == UNICODE_CHAR(' ') || cp == UNICODE_CHAR('\t'); } +// Returns the length of the string up to but excluding any white space. This could be the entire string if the string contains no white space. +// Equivalently, returns the position of the first white space character, or the string length if no white space is found. +size_t find_gray_space(const char *str) +{ + return strcspn(str, " \t"); +} + bool is_parenthesis(char ch) { return ((ch == '(') || (ch == ')')); diff --git a/code/parse/parselo.h b/code/parse/parselo.h index f446e690645..50493800a0b 100644 --- a/code/parse/parselo.h +++ b/code/parse/parselo.h @@ -89,8 +89,9 @@ extern void consolidate_double_characters(char *str, char ch); char *three_dot_truncate(char *buffer, const char *source, size_t buffer_size); // white space -extern int is_white_space(char ch); -extern int is_white_space(unicode::codepoint_t cp); +extern bool is_white_space(char ch); +extern bool is_white_space(unicode::codepoint_t cp); +extern size_t find_white_space(const char *str); extern void ignore_white_space(const char **pp = nullptr); extern void drop_trailing_white_space(char *str); extern void drop_leading_white_space(char *str); @@ -102,8 +103,9 @@ extern void drop_leading_white_space(SCP_string &str); extern void drop_white_space(SCP_string &str); // gray space -extern int is_gray_space(char ch); +extern bool is_gray_space(char ch); extern bool is_gray_space(unicode::codepoint_t cp); +extern size_t find_gray_space(const char *str); extern void ignore_gray_space(const char **pp = nullptr); // other diff --git a/code/utils/unicode.h b/code/utils/unicode.h index 9272b317e1f..aa0ce7953ab 100644 --- a/code/utils/unicode.h +++ b/code/utils/unicode.h @@ -42,7 +42,11 @@ class text_iterator { public: explicit text_iterator(const char* current_byte, const char* range_start_byte, const char* range_end_byte = nullptr); - typedef codepoint_t value_type; + using difference_type = std::ptrdiff_t; + using value_type = codepoint_t; + using pointer = text_iterator*; + using reference = text_iterator&; + using iterator_category = std::bidirectional_iterator_tag; const char* pos() const; diff --git a/code/weapon/emp.cpp b/code/weapon/emp.cpp index 4c72b35b3ee..086ea5cde9f 100644 --- a/code/weapon/emp.cpp +++ b/code/weapon/emp.cpp @@ -495,11 +495,11 @@ void emp_hud_string(int x, int y, int gauge_id, const char *str, int resize_mode if (!*str) return; - // copy the string - strcpy_s(tmp, str); - // if the emp effect is not active, don't even bother messing with the text if(emp_active_local()){ + // use a copied string rather than the original + strcpy_s(tmp, str); + emp_maybe_reformat_text(tmp, 256, gauge_id); // jitter the coords