Browse Source

Overhaul translation fetching

1. Do not modify the map after loading. Instead, return string views
   (guaranteed to be null-terminated) from look up functions and return
   the key directly if not found.
2. Use an `unorded_map` instead of `map` where available (C++20).
   Saves a bit of RAM (~50 KiB) and improves lookup performance.
pull/4703/head
Gleb Mazovetskiy 4 years ago
parent
commit
779ccaca17
  1. 16
      Source/DiabloUI/dialogs.cpp
  2. 5
      Source/DiabloUI/dialogs.h
  3. 2
      Source/DiabloUI/mainmenu.cpp
  4. 8
      Source/DiabloUI/selconn.cpp
  5. 48
      Source/DiabloUI/selgame.cpp
  6. 40
      Source/DiabloUI/selhero.cpp
  7. 2
      Source/DiabloUI/title.cpp
  8. 27
      Source/appfat.cpp
  9. 7
      Source/appfat.h
  10. 6
      Source/automap.cpp
  11. 17
      Source/control.cpp
  12. 3
      Source/control.h
  13. 7
      Source/controls/modifier_hints.cpp
  14. 2
      Source/controls/plrctrls.cpp
  15. 8
      Source/discord/discord.cpp
  16. 4
      Source/dvlnet/tcp_client.cpp
  17. 4
      Source/error.cpp
  18. 3
      Source/error.h
  19. 10
      Source/gamemenu.cpp
  20. 4
      Source/init.cpp
  21. 4
      Source/inv.cpp
  22. 26
      Source/items.cpp
  23. 3
      Source/items.h
  24. 10
      Source/loadsave.cpp
  25. 2
      Source/menu.cpp
  26. 24
      Source/monster.cpp
  27. 21
      Source/msg.cpp
  28. 6
      Source/panels/charpanel.cpp
  29. 4
      Source/panels/spell_list.cpp
  30. 14
      Source/pfile.cpp
  31. 4
      Source/plrmsg.cpp
  32. 6
      Source/qol/chatlog.cpp
  33. 2
      Source/qol/chatlog.h
  34. 7
      Source/qol/monhealthbar.cpp
  35. 4
      Source/qol/stash.cpp
  36. 9
      Source/qol/xpbar.cpp
  37. 18
      Source/stores.cpp
  38. 64
      Source/utils/language.cpp
  39. 26
      Source/utils/language.h
  40. 40
      Source/utils/log.hpp
  41. 11
      Source/utils/stdcompat/string_view.hpp
  42. 94
      Source/utils/string_or_view.hpp
  43. 2
      Source/utils/ui_fwd.h

16
Source/DiabloUI/dialogs.cpp

@ -277,11 +277,11 @@ void DialogLoop(const std::vector<std::unique_ptr<UiItemBase>> &items, const std
} while (!dialogEnd);
}
void UiOkDialog(const char *caption, const char *text, bool error, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind)
void UiOkDialog(string_view caption, string_view text, bool error, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind)
{
static bool inDialog = false;
if (caption != nullptr) {
if (!caption.empty()) {
LogError("{}\n{}", caption, text);
} else {
LogError("{}", text);
@ -291,7 +291,9 @@ void UiOkDialog(const char *caption, const char *text, bool error, const std::ve
if (!gbQuietMode) {
if (SDL_ShowCursor(SDL_ENABLE) <= -1)
Log("{}", SDL_GetError());
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, nullptr) <= -1) {
std::string captionStr = std::string(caption);
std::string textStr = std::string(caption);
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, captionStr.c_str(), textStr.c_str(), nullptr) <= -1) {
Log("{}", SDL_GetError());
}
}
@ -313,19 +315,19 @@ void UiOkDialog(const char *caption, const char *text, bool error, const std::ve
} // namespace
void UiErrorOkDialog(const char *caption, const char *text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind)
void UiErrorOkDialog(string_view caption, string_view text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind)
{
UiOkDialog(caption, text, /*error=*/true, renderBehind);
}
void UiErrorOkDialog(const char *caption, const char *text, bool error)
void UiErrorOkDialog(string_view caption, string_view text, bool error)
{
UiOkDialog(caption, text, error, vecNULL);
}
void UiErrorOkDialog(const char *text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind)
void UiErrorOkDialog(string_view text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind)
{
UiErrorOkDialog(nullptr, text, renderBehind);
UiErrorOkDialog({}, text, renderBehind);
}
} // namespace devilution

5
Source/DiabloUI/dialogs.h

@ -3,10 +3,11 @@
#include <cstddef>
#include "DiabloUI/ui_item.h"
#include "utils/stdcompat/string_view.hpp"
namespace devilution {
void UiErrorOkDialog(const char *text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind);
void UiErrorOkDialog(const char *caption, const char *text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind);
void UiErrorOkDialog(string_view text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind);
void UiErrorOkDialog(string_view caption, string_view text, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind);
} // namespace devilution

2
Source/DiabloUI/mainmenu.cpp

@ -60,7 +60,7 @@ void MainmenuLoad(const char *name, void (*fnSound)(const char *file))
if (gbIsSpawn && gbIsHellfire) {
SDL_Rect rect1 = { (Sint16)(uiPosition.x), (Sint16)(uiPosition.y + 145), 640, 30 };
vecMainMenuDialog.push_back(std::make_unique<UiArtText>(_("Shareware").c_str(), rect1, UiFlags::FontSize30 | UiFlags::ColorUiSilver | UiFlags::AlignCenter, 8));
vecMainMenuDialog.push_back(std::make_unique<UiArtText>(_("Shareware").data(), rect1, UiFlags::FontSize30 | UiFlags::ColorUiSilver | UiFlags::AlignCenter, 8));
}
vecMainMenuDialog.push_back(std::make_unique<UiList>(vecMenuItems, vecMenuItems.size(), uiPosition.x + 64, (uiPosition.y + 192), 510, 43, UiFlags::FontSize42 | UiFlags::ColorUiGold | UiFlags::AlignCenter, 5));

8
Source/DiabloUI/selconn.cpp

@ -53,25 +53,25 @@ void SelconnLoad()
const Point uiPosition = GetUIRectangle().position;
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 24), (Sint16)(Sint16)(uiPosition.y + 161), 590, 35 };
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("Multi Player Game").c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("Multi Player Game").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 218), DESCRIPTION_WIDTH, 21 };
vecSelConnDlg.push_back(std::make_unique<UiArtText>(selconn_MaxPlayers, rect2, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 256), DESCRIPTION_WIDTH, 21 };
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("Requirements:").c_str(), rect3, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("Requirements:").data(), rect3, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 275), DESCRIPTION_WIDTH, 66 };
vecSelConnDlg.push_back(std::make_unique<UiArtText>(selconn_Description, rect4, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark, 1, 16));
SDL_Rect rect5 = { (Sint16)(uiPosition.x + 30), (Sint16)(uiPosition.y + 356), 220, 31 };
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("no gateway needed").c_str(), rect5, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiSilver, 0));
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("no gateway needed").data(), rect5, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiSilver, 0));
SDL_Rect rect6 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 393), DESCRIPTION_WIDTH, 21 };
vecSelConnDlg.push_back(std::make_unique<UiArtText>(selconn_Gateway, rect6, UiFlags::AlignCenter | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect7 = { (Sint16)(uiPosition.x + 300), (Sint16)(uiPosition.y + 211), 295, 33 };
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("Select Connection").c_str(), rect7, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelConnDlg.push_back(std::make_unique<UiArtText>(_("Select Connection").data(), rect7, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect8 = { (Sint16)(uiPosition.x + 16), (Sint16)(uiPosition.y + 427), 250, 35 };
vecSelConnDlg.push_back(std::make_unique<UiArtTextButton>(_("Change Gateway"), nullptr, rect8, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold | UiFlags::ElementHidden));

48
Source/DiabloUI/selgame.cpp

@ -77,7 +77,7 @@ bool IsGameCompatible(const GameData &data)
static std::string GetErrorMessageIncompatibility(const GameData &data)
{
if (data.programid != GAME_ID) {
std::string gameMode;
string_view gameMode;
switch (data.programid) {
case GameIdDiabloFull:
gameMode = _("Diablo");
@ -92,7 +92,7 @@ static std::string GetErrorMessageIncompatibility(const GameData &data)
gameMode = _("Hellfire Shareware");
break;
default:
return _("The host is running a different game than you.");
return std::string(_("The host is running a different game than you."));
}
return fmt::format(_("The host is running a different game mode ({:s}) than you."), gameMode);
} else {
@ -128,16 +128,16 @@ void UiInitGameSelectionList(string_view search)
vecSelGameDialog.push_back(std::make_unique<UiScrollbar>(PcxSprite { *ArtScrollBarBackground }, PcxSprite { *ArtScrollBarThumb }, PcxSpriteSheet { *ArtScrollBarArrow }, rectScrollbar));
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 24), (Sint16)(uiPosition.y + 161), 590, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_(ConnectionNames[provider]).c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_(ConnectionNames[provider]).data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 211), 205, 192 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Description:").c_str(), rect2, UiFlags::FontSize24 | UiFlags::ColorUiSilver));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Description:").data(), rect2, UiFlags::FontSize24 | UiFlags::ColorUiSilver));
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 256), DESCRIPTION_WIDTH, 192 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(selgame_Description, rect3, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark, 1, 16));
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 300), (Sint16)(uiPosition.y + 211), 295, 33 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Action").c_str(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Action").data(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
#ifdef PACKET_ENCRYPTION
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Create Game"), 0, UiFlags::ColorUiGold));
@ -220,7 +220,7 @@ void selgame_GameSelection_Focus(int value)
break;
default:
const auto &gameInfo = Gamelist[vecSelGameDlgItems[value]->m_value - 3];
std::string infoString = _("Join the public game already in progress.");
std::string infoString = std::string(_("Join the public game already in progress."));
infoString.append("\n\n");
if (IsGameCompatible(gameInfo.gameData)) {
string_view difficulty;
@ -239,16 +239,16 @@ void selgame_GameSelection_Focus(int value)
infoString.append("\n");
switch (gameInfo.gameData.nTickRate) {
case 20:
infoString.append(_("Speed: Normal"));
AppendStrView(infoString, _("Speed: Normal"));
break;
case 30:
infoString.append(_("Speed: Fast"));
AppendStrView(infoString, _("Speed: Fast"));
break;
case 40:
infoString.append(_("Speed: Faster"));
AppendStrView(infoString, _("Speed: Faster"));
break;
case 50:
infoString.append(_("Speed: Fastest"));
AppendStrView(infoString, _("Speed: Fastest"));
break;
default:
// This should not occure, so no translations is needed
@ -256,7 +256,7 @@ void selgame_GameSelection_Focus(int value)
break;
}
infoString.append("\n");
infoString.append(_("Players: "));
AppendStrView(infoString, _("Players: "));
for (auto &playerName : gameInfo.players) {
infoString.append(playerName);
infoString.append(" ");
@ -315,10 +315,10 @@ void selgame_GameSelection_Select(int value)
switch (value) {
case 0:
case 1: {
title = _("Create Game").c_str();
title = _("Create Game").data();
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 299), (Sint16)(uiPosition.y + 211), 295, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Difficulty").c_str(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Difficulty").data(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Normal"), DIFF_NORMAL));
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Nightmare"), DIFF_NIGHTMARE));
@ -341,9 +341,9 @@ void selgame_GameSelection_Select(int value)
const char *inputHint;
if (provider == SELCONN_ZT) {
inputHint = _("Enter Game ID").c_str();
inputHint = _("Enter Game ID").data();
} else {
inputHint = _("Enter address").c_str();
inputHint = _("Enter address").data();
}
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 305), (Sint16)(uiPosition.y + 211), 285, 33 };
@ -405,9 +405,9 @@ bool IsDifficultyAllowed(int value)
selgame_Free();
if (value == 1)
UiSelOkDialog(title, _("Your character must reach level 20 before you can enter a multiplayer game of Nightmare difficulty.").c_str(), false);
UiSelOkDialog(title, _("Your character must reach level 20 before you can enter a multiplayer game of Nightmare difficulty.").data(), false);
if (value == 2)
UiSelOkDialog(title, _("Your character must reach level 30 before you can enter a multiplayer game of Hell difficulty.").c_str(), false);
UiSelOkDialog(title, _("Your character must reach level 30 before you can enter a multiplayer game of Hell difficulty.").data(), false);
selgame_Init();
@ -476,7 +476,7 @@ void selgame_GameSpeedSelection()
const Point uiPosition = GetUIRectangle().position;
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 24), (Sint16)(uiPosition.y + 161), 590, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Create Game").c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Create Game").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 34), (Sint16)(uiPosition.y + 211), 205, 33 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(selgame_Label, rect2, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
@ -485,7 +485,7 @@ void selgame_GameSpeedSelection()
vecSelGameDialog.push_back(std::make_unique<UiArtText>(selgame_Description, rect3, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark, 1, 16));
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 299), (Sint16)(uiPosition.y + 211), 295, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Game Speed").c_str(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Game Speed").data(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Normal"), 20));
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Fast"), 30));
@ -555,16 +555,16 @@ void selgame_Password_Init(int /*value*/)
const Point uiPosition = GetUIRectangle().position;
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 24), (Sint16)(uiPosition.y + 161), 590, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_(ConnectionNames[provider]).c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_(ConnectionNames[provider]).data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 211), 205, 192 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Description:").c_str(), rect2, UiFlags::FontSize24 | UiFlags::ColorUiSilver));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Description:").data(), rect2, UiFlags::FontSize24 | UiFlags::ColorUiSilver));
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 35), (Sint16)(uiPosition.y + 256), DESCRIPTION_WIDTH, 192 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(selgame_Description, rect3, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark, 1, 16));
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 305), (Sint16)(uiPosition.y + 211), 285, 33 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Enter Password").c_str(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Enter Password").data(), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
// Allow password to be empty only when joining games
bool allowEmpty = selgame_selectedGame == 2;
@ -629,7 +629,7 @@ void selgame_Password_Select(int /*value*/)
std::string error = SDL_GetError();
if (error.empty())
error = "Unknown network error";
UiSelOkDialog(_("Multi Player Game").c_str(), error.c_str(), false);
UiSelOkDialog(_("Multi Player Game").data(), error.c_str(), false);
selgame_Init();
selgame_Password_Init(selgame_selectedGame);
}
@ -650,7 +650,7 @@ void selgame_Password_Select(int /*value*/)
std::string error = SDL_GetError();
if (error.empty())
error = "Unknown network error";
UiSelOkDialog(_("Multi Player Game").c_str(), error.c_str(), false);
UiSelOkDialog(_("Multi Player Game").data(), error.c_str(), false);
selgame_Init();
selgame_Password_Init(0);
}

40
Source/DiabloUI/selhero.cpp

@ -131,7 +131,7 @@ void SelheroListSelect(int value)
vecSelDlgItems.clear();
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Choose Class").c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Choose Class").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelHeroDlgItems.clear();
int itemH = 33;
@ -162,7 +162,7 @@ void SelheroListSelect(int value)
memset(&selhero_heroInfo.name, 0, sizeof(selhero_heroInfo.name));
selhero_heroInfo.saveNumber = pfile_ui_get_first_unused_save_num();
SelheroSetStats();
title = selhero_isMultiPlayer ? _("New Multi Player Hero").c_str() : _("New Single Player Hero").c_str();
title = selhero_isMultiPlayer ? _("New Multi Player Hero").data() : _("New Single Player Hero").data();
return;
}
@ -170,7 +170,7 @@ void SelheroListSelect(int value)
vecSelDlgItems.clear();
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Save File Exists").c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Save File Exists").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelHeroDlgItems.clear();
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Load Game"), 0));
@ -184,7 +184,7 @@ void SelheroListSelect(int value)
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("Cancel"), &UiFocusNavigationEsc, rect3, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
UiInitList(SelheroLoadFocus, SelheroLoadSelect, selhero_List_Init, vecSelDlgItems, true);
title = _("Single Player Characters").c_str();
title = _("Single Player Characters").data();
return;
}
@ -230,7 +230,7 @@ void SelheroClassSelectorSelect(int value)
auto hClass = static_cast<HeroClass>(vecSelHeroDlgItems[value]->m_value);
if (gbIsSpawn && (hClass == HeroClass::Rogue || hClass == HeroClass::Sorcerer || (hClass == HeroClass::Bard && !hfbard_mpq))) {
ArtBackground = std::nullopt;
UiSelOkDialog(nullptr, _("The Rogue and Sorcerer are only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase.").c_str(), false);
UiSelOkDialog(nullptr, _("The Rogue and Sorcerer are only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase.").data(), false);
LoadBackgroundArt("ui_art\\selhero.pcx");
SelheroListSelect(selhero_SaveCount);
return;
@ -238,13 +238,13 @@ void SelheroClassSelectorSelect(int value)
const Point uiPosition = GetUIRectangle().position;
title = selhero_isMultiPlayer ? _("New Multi Player Hero").c_str() : _("New Single Player Hero").c_str();
title = selhero_isMultiPlayer ? _("New Multi Player Hero").data() : _("New Single Player Hero").data();
memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name));
if (ShouldPrefillHeroName())
strcpy(selhero_heroInfo.name, SelheroGenerateName(selhero_heroInfo.heroclass));
vecSelDlgItems.clear();
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Enter Name").c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Enter Name").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 265), (Sint16)(uiPosition.y + 317), 320, 33 };
vecSelDlgItems.push_back(std::make_unique<UiEdit>(_("Enter Name"), selhero_heroInfo.name, 15, false, rect2, UiFlags::FontSize24 | UiFlags::ColorUiGold));
@ -276,14 +276,14 @@ void SelheroNameSelect(int /*value*/)
// only check names in multiplayer, we don't care about them in single
if (selhero_isMultiPlayer && !UiValidPlayerName(selhero_heroInfo.name)) {
ArtBackground = std::nullopt;
UiSelOkDialog(title, _("Invalid name. A name cannot contain spaces, reserved characters, or reserved words.\n").c_str(), false);
UiSelOkDialog(title, _("Invalid name. A name cannot contain spaces, reserved characters, or reserved words.\n").data(), false);
LoadBackgroundArt("ui_art\\selhero.pcx");
} else {
if (gfnHeroCreate(&selhero_heroInfo)) {
SelheroLoadSelect(1);
return;
}
UiErrorOkDialog(_(/* TRANSLATORS: Error Message */ "Unable to create character.").c_str(), vecSelDlgItems);
UiErrorOkDialog(_(/* TRANSLATORS: Error Message */ "Unable to create character."), vecSelDlgItems);
}
memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name));
@ -439,36 +439,36 @@ void selhero_Init()
vecSelHeroDialog.push_back(std::move(heroImg));
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 39), (Sint16)(uiPosition.y + 323), 110, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Level:").c_str(), rect3, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Level:").data(), rect3, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 39), (Sint16)(uiPosition.y + 323), 110, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Level:").c_str(), rect4, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Level:").data(), rect4, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect5 = { (Sint16)(uiPosition.x + 159), (Sint16)(uiPosition.y + 323), 40, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(textStats[0], rect5, UiFlags::AlignCenter | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect6 = { (Sint16)(uiPosition.x + 39), (Sint16)(uiPosition.y + 358), 110, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Strength:").c_str(), rect6, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Strength:").data(), rect6, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect7 = { (Sint16)(uiPosition.x + 159), (Sint16)(uiPosition.y + 358), 40, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(textStats[1], rect7, UiFlags::AlignCenter | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect8 = { (Sint16)(uiPosition.x + 39), (Sint16)(uiPosition.y + 380), 110, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Magic:").c_str(), rect8, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Magic:").data(), rect8, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect9 = { (Sint16)(uiPosition.x + 159), (Sint16)(uiPosition.y + 380), 40, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(textStats[2], rect9, UiFlags::AlignCenter | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect10 = { (Sint16)(uiPosition.x + 39), (Sint16)(uiPosition.y + 401), 110, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Dexterity:").c_str(), rect10, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Dexterity:").data(), rect10, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect11 = { (Sint16)(uiPosition.x + 159), (Sint16)(uiPosition.y + 401), 40, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(textStats[3], rect11, UiFlags::AlignCenter | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect12 = { (Sint16)(uiPosition.x + 39), (Sint16)(uiPosition.y + 422), 110, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Vitality:").c_str(), rect12, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Vitality:").data(), rect12, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect13 = { (Sint16)(uiPosition.x + 159), (Sint16)(uiPosition.y + 422), 40, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(textStats[4], rect13, UiFlags::AlignCenter | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
#ifdef _DEBUG
SDL_Rect rect14 = { (Sint16)(uiPosition.x + 39), (Sint16)(uiPosition.y + 443), 110, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Savegame:").c_str(), rect14, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(_("Savegame:").data(), rect14, UiFlags::AlignRight | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
SDL_Rect rect15 = { (Sint16)(uiPosition.x + 159), (Sint16)(uiPosition.y + 443), 40, 21 };
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(textStats[5], rect15, UiFlags::AlignCenter | UiFlags::FontSize12 | UiFlags::ColorUiSilverDark));
#endif
@ -482,7 +482,7 @@ void selhero_List_Init()
vecSelDlgItems.clear();
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Select Hero").c_str(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Select Hero").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
vecSelHeroDlgItems.clear();
for (std::size_t i = 0; i < selhero_SaveCount; i++) {
@ -490,7 +490,7 @@ void selhero_List_Init()
if (selhero_heros[i].saveNumber == selhero_heroInfo.saveNumber)
selectedItem = i;
}
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("New Hero").c_str(), static_cast<int>(selhero_SaveCount)));
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("New Hero").data(), static_cast<int>(selhero_SaveCount)));
vecSelDlgItems.push_back(std::make_unique<UiList>(vecSelHeroDlgItems, 6, uiPosition.x + 265, (uiPosition.y + 256), 320, 26, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold));
@ -510,9 +510,9 @@ void selhero_List_Init()
UiInitList(SelheroListFocus, SelheroListSelect, SelheroListEsc, vecSelDlgItems, false, nullptr, SelheroListDeleteYesNo, selectedItem);
if (selhero_isMultiPlayer) {
title = _("Multi Player Characters").c_str();
title = _("Multi Player Characters").data();
} else {
title = _("Single Player Characters").c_str();
title = _("Single Player Characters").data();
}
}

2
Source/DiabloUI/title.cpp

@ -48,7 +48,7 @@ void UiTitleDialog()
UiAddLogo(&vecTitleScreen, LOGO_BIG, 182);
SDL_Rect rect = { (Sint16)(uiPosition.x), (Sint16)(uiPosition.y + 410), 640, 26 };
vecTitleScreen.push_back(std::make_unique<UiArtText>(_("Copyright © 1996-2001 Blizzard Entertainment").c_str(), rect, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiSilver));
vecTitleScreen.push_back(std::make_unique<UiArtText>(_("Copyright © 1996-2001 Blizzard Entertainment").data(), rect, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiSilver));
}
bool endMenu = false;

27
Source/appfat.cpp

@ -35,7 +35,7 @@ void MsgBox(const char *pszFmt, va_list va)
vsnprintf(text, sizeof(text), pszFmt, va);
UiErrorOkDialog(_("Error").c_str(), text);
UiErrorOkDialog(_("Error"), text);
}
/**
@ -59,6 +59,13 @@ void FreeDlg()
} // namespace
void app_fatal(string_view str)
{
FreeDlg();
UiErrorOkDialog(_("Error"), str);
diablo_quit(1);
}
void app_fatal(const char *pszFmt, ...)
{
va_list va;
@ -74,18 +81,6 @@ void app_fatal(const char *pszFmt, ...)
diablo_quit(1);
}
void DrawDlg(const char *pszFmt, ...)
{
char text[256];
va_list va;
va_start(va, pszFmt);
vsnprintf(text, sizeof(text), pszFmt, va);
va_end(va);
UiErrorOkDialog(PROJECT_NAME, text, false);
}
#ifdef _DEBUG
void assert_fail(int nLineNo, const char *pszFile, const char *pszFail)
{
@ -99,7 +94,7 @@ void ErrDlg(const char *title, string_view error, string_view logFilePath, int l
std::string text = fmt::format(_(/* TRANSLATORS: Error message that displays relevant information for bug report */ "{:s}\n\nThe error occurred at: {:s} line {:d}"), error, logFilePath, logLineNr);
UiErrorOkDialog(title, text.c_str());
UiErrorOkDialog(title, text);
app_fatal(nullptr);
}
@ -111,7 +106,7 @@ void InsertCDDlg(string_view archiveName)
"Make sure that it is in the game folder."),
archiveName);
UiErrorOkDialog(_("Data File Error").c_str(), text.c_str());
UiErrorOkDialog(_("Data File Error"), text);
app_fatal(nullptr);
}
@ -119,7 +114,7 @@ void DirErrorDlg(string_view error)
{
std::string text = fmt::format(_(/* TRANSLATORS: Error when Program is not allowed to write data */ "Unable to write to location:\n{:s}"), error);
UiErrorOkDialog(_("Read-Only Directory Error").c_str(), text.c_str());
UiErrorOkDialog(_("Read-Only Directory Error"), text);
app_fatal(nullptr);
}

7
Source/appfat.h

@ -31,13 +31,8 @@ namespace devilution {
* @param ... (see printf)
*/
[[noreturn]] void app_fatal(const char *pszFmt, ...) DVL_PRINTF_ATTRIBUTE(1, 2);
[[noreturn]] void app_fatal(string_view str);
/**
* @brief Displays a warning message box based on the given formatted error message.
* @param pszFmt Error message format
* @param ... Additional parameters for message format
*/
void DrawDlg(const char *pszFmt, ...) DVL_PRINTF_ATTRIBUTE(1, 2);
#ifdef _DEBUG
/**
* @brief Show an error and exit the application.

6
Source/automap.cpp

@ -480,7 +480,7 @@ void DrawAutomapText(const Surface &out)
if (gbIsMultiplayer) {
if (strcasecmp("0.0.0.0", szPlayerName) != 0) {
std::string description = _("Game: ");
std::string description = std::string(_("Game: "));
description.append(szPlayerName);
DrawString(out, description, linePosition);
linePosition.y += 15;
@ -488,10 +488,10 @@ void DrawAutomapText(const Surface &out)
std::string description;
if (!PublicGame) {
description = _("Password: ");
description = std::string(_("Password: "));
description.append(szPlayerDescript);
} else {
description = _("Public Game");
description = std::string(_("Public Game"));
}
DrawString(out, description, linePosition);
linePosition.y += 15;

17
Source/control.cpp

@ -45,6 +45,7 @@
#include "utils/language.h"
#include "utils/sdl_geometry.h"
#include "utils/stdcompat/optional.hpp"
#include "utils/string_or_view.hpp"
#include "utils/utf8.hpp"
#ifdef _DEBUG
@ -74,7 +75,7 @@ bool talkflag;
bool sbookflag;
bool chrflag;
bool drawbtnflag;
std::string InfoString;
StringOrView InfoString;
bool panelflag;
int initialDropGoldValue;
bool panbtndown;
@ -579,7 +580,7 @@ void InitControlPan()
buttonEnabled = false;
chrbtnactive = false;
pDurIcons = LoadCel("Items\\DurIcons.CEL", 32);
InfoString.clear();
InfoString = {};
ClearPanel();
drawhpflag = true;
drawmanaflag = true;
@ -867,7 +868,7 @@ void DrawInfoBox(const Surface &out)
{
DrawPanelBox(out, { 177, 62, 288, 63 }, GetMainPanel().position + Displacement { 177, 46 });
if (!panelflag && !trigflag && pcursinvitem == -1 && pcursstashitem == uint16_t(-1) && !spselflag) {
InfoString.clear();
InfoString = {};
InfoColor = UiFlags::ColorWhite;
ClearPanel();
}
@ -882,9 +883,9 @@ void DrawInfoBox(const Surface &out)
InfoString = _("Requirements not met");
} else {
if (myPlayer.HoldItem._iIdentified)
InfoString = myPlayer.HoldItem._iIName;
InfoString = string_view(myPlayer.HoldItem._iIName);
else
InfoString = myPlayer.HoldItem._iName;
InfoString = string_view(myPlayer.HoldItem._iName);
InfoColor = myPlayer.HoldItem.getTextColor();
}
} else {
@ -896,7 +897,7 @@ void DrawInfoBox(const Surface &out)
if (leveltype != DTYPE_TOWN) {
const auto &monster = Monsters[pcursmonst];
InfoColor = UiFlags::ColorWhite;
InfoString = monster.mName;
InfoString = string_view(monster.mName);
ClearPanel();
if (monster._uniqtype != 0) {
InfoColor = UiFlags::ColorWhitegold;
@ -905,13 +906,13 @@ void DrawInfoBox(const Surface &out)
PrintMonstHistory(monster.MType->mtype);
}
} else if (pcursitem == -1) {
InfoString = std::string(Towners[pcursmonst].name);
InfoString = string_view(Towners[pcursmonst].name);
}
}
if (pcursplr != -1) {
InfoColor = UiFlags::ColorWhitegold;
auto &target = Players[pcursplr];
InfoString = target._pName;
InfoString = string_view(target._pName);
ClearPanel();
AddPanelString(fmt::format(_("{:s}, Level: {:d}"), _(ClassStrTbl[static_cast<std::size_t>(target._pClass)]), target._pLevel));
AddPanelString(fmt::format(_("Hit Points {:d} of {:d}"), target._pHitPoints >> 6, target._pMaxHP >> 6));

3
Source/control.h

@ -19,6 +19,7 @@
#include "utils/attributes.h"
#include "utils/stdcompat/optional.hpp"
#include "utils/stdcompat/string_view.hpp"
#include "utils/string_or_view.hpp"
#include "utils/ui_fwd.h"
namespace devilution {
@ -41,7 +42,7 @@ extern bool talkflag;
extern bool sbookflag;
extern bool chrflag;
extern bool drawbtnflag;
extern std::string InfoString;
extern StringOrView InfoString;
extern bool panelflag;
extern int initialDropGoldValue;
extern bool panbtndown;

7
Source/controls/modifier_hints.cpp

@ -176,10 +176,9 @@ void InitModifierHints()
LoadMaskedArt("data\\hinticons.pcx", &hintIcons, 6, 1);
if (hintBox.surface == nullptr || hintBoxBackground.surface == nullptr) {
app_fatal("%s", _("Failed to load UI resources.\n"
"\n"
"Make sure devilutionx.mpq is in the game folder and that it is up to date.")
.c_str());
app_fatal(_("Failed to load UI resources.\n"
"\n"
"Make sure devilutionx.mpq is in the game folder and that it is up to date."));
}
}

2
Source/controls/plrctrls.cpp

@ -1645,7 +1645,7 @@ void plrctrls_after_check_curs_move()
return;
}
if (!invflag) {
InfoString.clear();
InfoString = {};
ClearPanel();
FindActor();
FindItemOrObject();

8
Source/discord/discord.cpp

@ -59,7 +59,7 @@ std::string GetLocationString()
{
// Quest Level Name
if (setlevel) {
return _(QuestLevelNames[setlvlnum]);
return std::string(_(QuestLevelNames[setlvlnum]));
}
// Dungeon Name
@ -86,7 +86,7 @@ std::string GetLocationString()
std::string GetCharacterString()
{
const std::string &charClassStr = _(ClassStrTbl[static_cast<int>(MyPlayer->_pClass)]);
const string_view charClassStr = _(ClassStrTbl[static_cast<int>(MyPlayer->_pClass)]);
return fmt::format(_(/* TRANSLATORS: Discord character, i.e. "Lv 6 Warrior" */ "Lv {} {}"), tracked_data.playerLevel, charClassStr);
}
@ -98,7 +98,7 @@ std::string GetDetailString()
std::string GetStateString()
{
constexpr std::array<const char *, 3> DifficultyStrs = { N_("Normal"), N_("Nightmare"), N_("Hell") };
std::string difficultyStr = _(DifficultyStrs[sgGameInitInfo.nDifficulty]);
const string_view difficultyStr = _(DifficultyStrs[sgGameInitInfo.nDifficulty]);
return fmt::format(_(/* TRANSLATORS: Discord state i.e. "Nightmare difficulty" */ "{} difficulty"), difficultyStr);
}
@ -179,7 +179,7 @@ void UpdateMenu(bool forced)
discord::Activity activity = {};
activity.SetName(PROJECT_NAME);
activity.SetState(_(/* TRANSLATORS: Discord activity, not in game */ "In Menu").c_str());
activity.SetState(_(/* TRANSLATORS: Discord activity, not in game */ "In Menu").data());
activity.GetTimestamps().SetStart(start_time);

4
Source/dvlnet/tcp_client.cpp

@ -62,7 +62,7 @@ int tcp_client::join(std::string addrstr)
}
}
if (plr_self == PLR_BROADCAST) {
SDL_SetError("%s", _("Unable to connect").c_str());
SDL_SetError("%s", _("Unable to connect").data());
return -1;
}
@ -88,7 +88,7 @@ void tcp_client::HandleReceive(const asio::error_code &error, size_t bytesRead)
return;
}
if (bytesRead == 0) {
throw std::runtime_error(_("error: read 0 bytes from server"));
throw std::runtime_error(_("error: read 0 bytes from server").data());
}
recv_buffer.resize(bytesRead);
recv_queue.Write(std::move(recv_buffer));

4
Source/error.cpp

@ -111,7 +111,7 @@ void InitDiabloMsg(diablo_message e)
InitDiabloMsg(LanguageTranslate(MsgStrings[e]));
}
void InitDiabloMsg(const std::string &msg)
void InitDiabloMsg(string_view msg)
{
if (DiabloMessages.size() >= MAX_SEND_STR_LEN)
return;
@ -119,7 +119,7 @@ void InitDiabloMsg(const std::string &msg)
if (std::find(DiabloMessages.begin(), DiabloMessages.end(), msg) != DiabloMessages.end())
return;
DiabloMessages.push_back(msg);
DiabloMessages.push_back(std::string(msg));
if (DiabloMessages.size() == 1)
InitNextLines();
}

3
Source/error.h

@ -9,6 +9,7 @@
#include <string>
#include "engine.h"
#include "utils/stdcompat/string_view.hpp"
namespace devilution {
@ -71,7 +72,7 @@ enum diablo_message : uint8_t {
};
void InitDiabloMsg(diablo_message e);
void InitDiabloMsg(const std::string &msg);
void InitDiabloMsg(string_view msg);
bool IsDiabloMsgAvailable();
void CancelCurrentDiabloMsg();
void ClrDiabloMsg();

10
Source/gamemenu.cpp

@ -153,19 +153,19 @@ void GamemenuGetSpeed()
if (gbIsMultiplayer) {
sgOptionsMenu[3].dwFlags &= ~(GMENU_ENABLED | GMENU_SLIDER);
if (sgGameInitInfo.nTickRate >= 50)
sgOptionsMenu[3].pszStr = _("Speed: Fastest").c_str();
sgOptionsMenu[3].pszStr = _("Speed: Fastest").data();
else if (sgGameInitInfo.nTickRate >= 40)
sgOptionsMenu[3].pszStr = _("Speed: Faster").c_str();
sgOptionsMenu[3].pszStr = _("Speed: Faster").data();
else if (sgGameInitInfo.nTickRate >= 30)
sgOptionsMenu[3].pszStr = _("Speed: Fast").c_str();
sgOptionsMenu[3].pszStr = _("Speed: Fast").data();
else if (sgGameInitInfo.nTickRate == 20)
sgOptionsMenu[3].pszStr = _("Speed: Normal").c_str();
sgOptionsMenu[3].pszStr = _("Speed: Normal").data();
return;
}
sgOptionsMenu[3].dwFlags |= GMENU_ENABLED | GMENU_SLIDER;
sgOptionsMenu[3].pszStr = _("Speed").c_str();
sgOptionsMenu[3].pszStr = _("Speed").data();
gmenu_slider_steps(&sgOptionsMenu[3], 46);
gmenu_slider_set(&sgOptionsMenu[3], 20, 50, sgGameInitInfo.nTickRate);
}

4
Source/init.cpp

@ -212,7 +212,7 @@ void LoadGameArchives()
hfvoice_mpq = LoadMPQ(paths, "hfvoice.mpq");
if (gbIsHellfire && (!hfmonk_mpq || !hfmusic_mpq || !hfvoice_mpq)) {
UiErrorOkDialog(_("Some Hellfire MPQs are missing").c_str(), _("Not all Hellfire MPQs were found.\nPlease copy all the hf*.mpq files.").c_str());
UiErrorOkDialog(_("Some Hellfire MPQs are missing"), _("Not all Hellfire MPQs were found.\nPlease copy all the hf*.mpq files."));
app_fatal(nullptr);
}
}
@ -220,7 +220,7 @@ void LoadGameArchives()
void init_create_window()
{
if (!SpawnWindow(PROJECT_NAME))
app_fatal("%s", _("Unable to create main window").c_str());
app_fatal(_("Unable to create main window"));
dx_init();
gbActive = true;
#ifndef USE_SDL1

4
Source/inv.cpp

@ -1932,10 +1932,10 @@ int8_t CheckInvHLight()
} else {
InfoColor = pi->getTextColor();
if (pi->_iIdentified) {
InfoString = pi->_iIName;
InfoString = string_view(pi->_iIName);
PrintItemDetails(*pi);
} else {
InfoString = pi->_iName;
InfoString = string_view(pi->_iName);
PrintItemDur(*pi);
}
}

26
Source/items.cpp

@ -631,9 +631,11 @@ void GetBookSpell(Item &item, int lvl)
if (s == maxSpells)
s = 1;
}
std::string spellName = pgettext("spell", spelldata[bs].sNameText);
CopyUtf8(item._iName, std::string(item._iName + spellName), sizeof(item._iIName));
CopyUtf8(item._iIName, std::string(item._iIName + spellName), sizeof(item._iIName));
const string_view spellName = pgettext("spell", spelldata[bs].sNameText);
const size_t iNameLen = string_view(item._iName).size();
const size_t iINameLen = string_view(item._iIName).size();
CopyUtf8(item._iName + iNameLen, spellName, sizeof(item._iName) - iNameLen);
CopyUtf8(item._iIName + iINameLen, spellName, sizeof(item._iIName) - iINameLen);
item._iSpell = bs;
item._iMinMag = spelldata[bs].sMinInt;
item._ivalue += spelldata[bs].sBookCost;
@ -1847,7 +1849,7 @@ void PrintItemInfo(const Item &item)
uint8_t dex = item._iMinDex;
uint8_t mag = item._iMinMag;
if (str != 0 || mag != 0 || dex != 0) {
std::string text = _("Required:");
std::string text = std::string(_("Required:"));
if (str != 0)
text.append(fmt::format(_(" {:d} Str"), str));
if (mag != 0)
@ -3443,9 +3445,9 @@ void GetItemStr(Item &item)
{
if (item._itype != ItemType::Gold) {
if (item._iIdentified)
InfoString = item._iIName;
InfoString = string_view(item._iIName);
else
InfoString = item._iName;
InfoString = string_view(item._iName);
InfoColor = item.getTextColor();
} else {
@ -3511,7 +3513,7 @@ bool DoOil(Player &player, int cii)
return true;
}
[[nodiscard]] std::string PrintItemPower(char plidx, const Item &item)
[[nodiscard]] StringOrView PrintItemPower(char plidx, const Item &item)
{
switch (plidx) {
case IPL_TOHIT:
@ -3646,13 +3648,13 @@ bool DoOil(Player &player, int cii)
return _(/*xgettext:no-c-format*/ "hit steals 3% mana");
if (HasAnyOf(item._iFlags, ItemSpecialEffect::StealMana5))
return _(/*xgettext:no-c-format*/ "hit steals 5% mana");
return "";
return {};
case IPL_STEALLIFE:
if (HasAnyOf(item._iFlags, ItemSpecialEffect::StealLife3))
return _(/*xgettext:no-c-format*/ "hit steals 3% life");
if (HasAnyOf(item._iFlags, ItemSpecialEffect::StealLife5))
return _(/*xgettext:no-c-format*/ "hit steals 5% life");
return "";
return {};
case IPL_TARGAC:
return _("penetrates target's armor");
case IPL_FASTATTACK:
@ -3696,7 +3698,7 @@ bool DoOil(Player &player, int cii)
case IPL_INFRAVISION:
return _("see with infravision");
case IPL_INVCURS:
return " ";
return { string_view(" ") };
case IPL_ADDACLIFE:
if (item._iFMinDam == item._iFMaxDam)
return fmt::format(_("lightning damage: {:d}"), item._iFMinDam);
@ -3708,7 +3710,7 @@ bool DoOil(Player &player, int cii)
if (item._iPLFR > 0)
return fmt::format(_("Resist Fire: {:+d}%"), item._iPLFR);
else
return " ";
return { string_view(" ") };
case IPL_DEVASTATION:
return _("occasional triple damage");
case IPL_DECAY:
@ -3716,7 +3718,7 @@ bool DoOil(Player &player, int cii)
case IPL_PERIL:
return _("2x dmg to monst, 1x to you");
case IPL_JESTERS:
return _(/*xgettext:no-c-format*/ "Random 0 - 500% damage");
return std::string(_(/*xgettext:no-c-format*/ "Random 0 - 500% damage"));
case IPL_CRYSTALLINE:
return fmt::format(_(/*xgettext:no-c-format*/ "low dur, {:+d}% damage"), item._iPLDam);
case IPL_DOPPELGANGER:

3
Source/items.h

@ -14,6 +14,7 @@
#include "itemdat.h"
#include "monster.h"
#include "utils/stdcompat/optional.hpp"
#include "utils/string_or_view.hpp"
namespace devilution {
@ -495,7 +496,7 @@ void CheckIdentify(Player &player, int cii);
void DoRepair(Player &player, int cii);
void DoRecharge(Player &player, int cii);
bool DoOil(Player &player, int cii);
[[nodiscard]] std::string PrintItemPower(char plidx, const Item &item);
[[nodiscard]] StringOrView PrintItemPower(char plidx, const Item &item);
void DrawUniqueInfo(const Surface &out);
void PrintItemDetails(const Item &item);
void PrintItemDur(const Item &item);

10
Source/loadsave.cpp

@ -1958,10 +1958,10 @@ void LoadGame(bool firstflag)
LoadHelper file(OpenSaveArchive(gSaveNumber), "game");
if (!file.IsValid())
app_fatal("%s", _("Unable to open save file archive").c_str());
app_fatal(_("Unable to open save file archive"));
if (!IsHeaderValid(file.NextLE<uint32_t>()))
app_fatal("%s", _("Invalid save file").c_str());
app_fatal(_("Invalid save file"));
if (gbIsHellfireSaveGame) {
giNumberOfLevels = 25;
@ -1992,7 +1992,7 @@ void LoadGame(bool firstflag)
int tmpNobjects = file.NextBE<int32_t>();
if (!gbIsHellfire && IsAnyOf(leveltype, DTYPE_NEST, DTYPE_CRYPT))
app_fatal("%s", _("Player is on a Hellfire only level").c_str());
app_fatal(_("Player is on a Hellfire only level"));
for (uint8_t i = 0; i < giNumberOfLevels; i++) {
glSeedTbl[i] = file.NextBE<uint32_t>();
@ -2238,7 +2238,7 @@ void SaveGameData()
else if (!gbIsSpawn && !gbIsHellfire)
file.WriteLE<uint32_t>(LoadLE32("RETL"));
else
app_fatal("%s", _("Invalid game state").c_str());
app_fatal(_("Invalid game state"));
if (gbIsHellfire) {
giNumberOfLevels = 25;
@ -2478,7 +2478,7 @@ void LoadLevel()
GetPermLevelNames(szName);
LoadHelper file(OpenSaveArchive(gSaveNumber), szName);
if (!file.IsValid())
app_fatal("%s", _("Unable to open save file archive").c_str());
app_fatal(_("Unable to open save file archive"));
if (leveltype != DTYPE_TOWN) {
for (int j = 0; j < MAXDUNY; j++) {

2
Source/menu.cpp

@ -151,7 +151,7 @@ void mainmenu_loop()
if (demo::IsRunning())
menu = MAINMENU_SINGLE_PLAYER;
else if (!UiMainMenuDialog(gszProductName, &menu, effects_play_sound, 30))
app_fatal("%s", _("Unable to display mainmenu").c_str());
app_fatal(_("Unable to display mainmenu"));
switch (menu) {
case MAINMENU_NONE:

24
Source/monster.cpp

@ -195,7 +195,7 @@ void InitMonster(Monster &monster, Direction rd, int mtype, Point position)
monster._mmode = MonsterMode::Stand;
monster.MType = &LevelMonsterTypes[mtype];
monster.MData = monster.MType->MData;
monster.mName = pgettext("monster", monster.MData->mName).c_str();
monster.mName = pgettext("monster", monster.MData->mName).data();
monster.AnimInfo = {};
monster.ChangeAnimationData(MonsterGraphic::Stand);
monster.AnimInfo.TickCounterOfCurrentFrame = GenerateRnd(monster.AnimInfo.TicksPerFrame - 1);
@ -3490,7 +3490,7 @@ void PrepareUniqueMonst(Monster &monster, int uniqindex, int miniontype, int bos
}
monster.mExp *= 2;
monster.mName = pgettext("monster", uniqueMonsterData.mName).c_str();
monster.mName = pgettext("monster", uniqueMonsterData.mName).data();
monster._mmaxhp = uniqueMonsterData.mmaxhp << 6;
if (!gbIsMultiplayer)
@ -4523,9 +4523,9 @@ void SyncMonsterAnim(Monster &monster)
#endif
monster.MData = LevelMonsterTypes[monster._mMTidx].MData;
if (monster._uniqtype != 0)
monster.mName = pgettext("monster", UniqueMonstersData[monster._uniqtype - 1].mName).c_str();
monster.mName = pgettext("monster", UniqueMonstersData[monster._uniqtype - 1].mName).data();
else
monster.mName = pgettext("monster", monster.MData->mName).c_str();
monster.mName = pgettext("monster", monster.MData->mName).data();
if (monster._uniqtype != 0)
InitTRNForUniqueMonster(monster);
@ -4657,23 +4657,23 @@ void PrintMonstHistory(int mt)
AddPanelString(_("No magic resistance"));
} else {
if ((res & (RESIST_MAGIC | RESIST_FIRE | RESIST_LIGHTNING)) != 0) {
std::string resists = _("Resists:");
std::string resists = std::string(_("Resists:"));
if ((res & RESIST_MAGIC) != 0)
resists.append(_(" Magic"));
AppendStrView(resists, _(" Magic"));
if ((res & RESIST_FIRE) != 0)
resists.append(_(" Fire"));
AppendStrView(resists, _(" Fire"));
if ((res & RESIST_LIGHTNING) != 0)
resists.append(_(" Lightning"));
AppendStrView(resists, _(" Lightning"));
AddPanelString(resists);
}
if ((res & (IMMUNE_MAGIC | IMMUNE_FIRE | IMMUNE_LIGHTNING)) != 0) {
std::string immune = _("Immune:");
std::string immune = std::string(_("Immune:"));
if ((res & IMMUNE_MAGIC) != 0)
immune.append(_(" Magic"));
AppendStrView(immune, _(" Magic"));
if ((res & IMMUNE_FIRE) != 0)
immune.append(_(" Fire"));
AppendStrView(immune, _(" Fire"));
if ((res & IMMUNE_LIGHTNING) != 0)
immune.append(_(" Lightning"));
AppendStrView(immune, _(" Lightning"));
AddPanelString(immune);
}
}

21
Source/msg.cpp

@ -11,6 +11,7 @@
#include "DiabloUI/diabloui.h"
#include "automap.h"
#include "config.h"
#include "control.h"
#include "dead.h"
#include "drlg_l1.h"
@ -534,7 +535,7 @@ void DeltaPutItem(const TCmdPItem &message, Point position, uint8_t bLevel)
&& item.dwSeed == message.dwSeed) {
if (item.bCmd == TCmdPItem::DroppedItem)
return;
app_fatal("%s", _("Trying to drop a floor item?").c_str());
app_fatal(_("Trying to drop a floor item?"));
}
}
@ -1036,7 +1037,7 @@ DWORD OnSpellWall(const TCmd *pCmd, Player &player)
auto spell = static_cast<spell_id>(message.wParam1);
if (leveltype == DTYPE_TOWN && !spelldata[spell].sTownSpell) {
LogError(_("{:s} has cast an illegal spell.").c_str(), player._pName);
LogError(_("{:s} has cast an illegal spell."), player._pName);
return sizeof(message);
}
@ -1071,7 +1072,7 @@ DWORD OnSpellTile(const TCmd *pCmd, Player &player)
auto spell = static_cast<spell_id>(message.wParam1);
if (leveltype == DTYPE_TOWN && !spelldata[spell].sTownSpell) {
LogError(_("{:s} has cast an illegal spell.").c_str(), player._pName);
LogError(_("{:s} has cast an illegal spell."), player._pName);
return sizeof(message);
}
@ -1102,7 +1103,7 @@ DWORD OnTargetSpellTile(const TCmd *pCmd, Player &player)
auto spell = static_cast<spell_id>(message.wParam1);
if (leveltype == DTYPE_TOWN && !spelldata[spell].sTownSpell) {
LogError(_("{:s} has cast an illegal spell.").c_str(), player._pName);
LogError(_("{:s} has cast an illegal spell."), player._pName);
return sizeof(message);
}
@ -1229,7 +1230,7 @@ DWORD OnSpellMonster(const TCmd *pCmd, Player &player)
auto spell = static_cast<spell_id>(message.wParam2);
if (leveltype == DTYPE_TOWN && !spelldata[spell].sTownSpell) {
LogError(_("{:s} has cast an illegal spell.").c_str(), player._pName);
LogError(_("{:s} has cast an illegal spell."), player._pName);
return sizeof(message);
}
@ -1261,7 +1262,7 @@ DWORD OnSpellPlayer(const TCmd *pCmd, Player &player)
auto spell = static_cast<spell_id>(message.wParam2);
if (leveltype == DTYPE_TOWN && !spelldata[spell].sTownSpell) {
LogError(_("{:s} has cast an illegal spell.").c_str(), player._pName);
LogError(_("{:s} has cast an illegal spell."), player._pName);
return sizeof(message);
}
@ -1293,7 +1294,7 @@ DWORD OnTargetSpellMonster(const TCmd *pCmd, Player &player)
auto spell = static_cast<spell_id>(message.wParam2);
if (leveltype == DTYPE_TOWN && !spelldata[spell].sTownSpell) {
LogError(_("{:s} has cast an illegal spell.").c_str(), player._pName);
LogError(_("{:s} has cast an illegal spell."), player._pName);
return sizeof(message);
}
@ -1323,7 +1324,7 @@ DWORD OnTargetSpellPlayer(const TCmd *pCmd, Player &player)
auto spell = static_cast<spell_id>(message.wParam2);
if (leveltype == DTYPE_TOWN && !spelldata[spell].sTownSpell) {
LogError(_("{:s} has cast an illegal spell.").c_str(), player._pName);
LogError(_("{:s} has cast an illegal spell."), player._pName);
return sizeof(message);
}
@ -2053,13 +2054,13 @@ bool msg_wait_resync()
}
if (gbGameDestroyed) {
DrawDlg("%s", _("The game ended").c_str());
UiErrorOkDialog(PROJECT_NAME, _("The game ended"), /*error=*/false);
FreePackets();
return false;
}
if (sgbDeltaChunks != MAX_CHUNKS) {
DrawDlg("%s", _("Unable to get level data").c_str());
UiErrorOkDialog(PROJECT_NAME, _("Unable to get level data"), /*error=*/false);
FreePackets();
return false;
}

6
Source/panels/charpanel.cpp

@ -125,7 +125,7 @@ PanelEntry panelEntries[] = {
{ "", { 9, 14 }, 150, 0,
[]() { return StyledText { UiFlags::ColorWhite, MyPlayer->_pName }; } },
{ "", { 161, 14 }, 149, 0,
[]() { return StyledText { UiFlags::ColorWhite, _(ClassStrTbl[static_cast<std::size_t>(MyPlayer->_pClass)]) }; } },
[]() { return StyledText { UiFlags::ColorWhite, std::string(_(ClassStrTbl[static_cast<std::size_t>(MyPlayer->_pClass)])) }; } },
{ N_("Level"), { 57, 52 }, 57, 45,
[]() { return StyledText { UiFlags::ColorWhite, fmt::format("{:d}", MyPlayer->_pLevel) }; } },
@ -134,7 +134,7 @@ PanelEntry panelEntries[] = {
{ N_("Next level"), { TopRightLabelX, 80 }, 99, 198,
[]() {
if (MyPlayer->_pLevel == MAXCHARLEVEL) {
return StyledText { UiFlags::ColorWhitegold, _("None") };
return StyledText { UiFlags::ColorWhitegold, std::string(_("None")) };
}
return StyledText { UiFlags::ColorWhite, fmt::format("{:d}", MyPlayer->_pNextExper) };
} },
@ -219,7 +219,7 @@ void DrawShadowString(const Surface &out, const PanelEntry &entry)
return;
constexpr int Spacing = 0;
const std::string &textStr = LanguageTranslate(entry.label.c_str());
const string_view textStr = LanguageTranslate(entry.label);
string_view text;
std::string wrapped;
if (entry.labelLength > 0) {

4
Source/panels/spell_list.cpp

@ -20,7 +20,7 @@ namespace devilution {
namespace {
void PrintSBookSpellType(const Surface &out, Point position, const std::string &text, uint8_t rectColorIndex)
void PrintSBookSpellType(const Surface &out, Point position, string_view text, uint8_t rectColorIndex)
{
Point rect { position };
rect += Displacement { 0, -SPLICONLENGTH + 1 };
@ -124,7 +124,7 @@ void DrawSpell(const Surface &out)
void DrawSpellList(const Surface &out)
{
InfoString.clear();
InfoString = {};
ClearPanel();
Player &myPlayer = *MyPlayer;

14
Source/pfile.cpp

@ -303,7 +303,7 @@ PFileScopedArchiveWriter::PFileScopedArchiveWriter(bool clearTables)
, clear_tables_(clearTables)
{
if (!OpenArchive(save_num_))
app_fatal("%s", _("Failed to open player archive for writing.").c_str());
app_fatal(_("Failed to open player archive for writing."));
}
PFileScopedArchiveWriter::~PFileScopedArchiveWriter()
@ -370,7 +370,7 @@ void sfile_write_stash()
return;
if (!StashWriter.Open(GetStashSavePath().c_str()))
app_fatal("%s", _("Failed to open stash archive for writing.").c_str());
app_fatal(_("Failed to open stash archive for writing."));
SaveStash();
@ -481,9 +481,9 @@ void pfile_read_player_from_save(uint32_t saveNum, Player &player)
{
std::optional<MpqArchive> archive = OpenSaveArchive(saveNum);
if (!archive)
app_fatal("%s", _("Unable to open archive").c_str());
app_fatal(_("Unable to open archive"));
if (!ReadHero(*archive, &pkplr))
app_fatal("%s", _("Unable to load character").c_str());
app_fatal(_("Unable to load character"));
gbValidSaveFile = ArchiveContainsGame(*archive);
if (gbValidSaveFile)
@ -507,7 +507,7 @@ bool LevelFileExists()
uint32_t saveNum = gSaveNumber;
if (!OpenArchive(saveNum))
app_fatal("%s", _("Unable to read to save file archive").c_str());
app_fatal(_("Unable to read to save file archive"));
bool hasFile = SaveWriter.HasFile(szName);
SaveWriter.Close();
@ -527,7 +527,7 @@ void GetPermLevelNames(char *szPerm)
uint32_t saveNum = gSaveNumber;
GetTempLevelNames(szPerm);
if (!OpenArchive(saveNum))
app_fatal("%s", _("Unable to read to save file archive").c_str());
app_fatal(_("Unable to read to save file archive"));
bool hasFile = SaveWriter.HasFile(szPerm);
SaveWriter.Close();
@ -546,7 +546,7 @@ void pfile_remove_temp_files()
uint32_t saveNum = gSaveNumber;
if (!OpenArchive(saveNum))
app_fatal("%s", _("Unable to write to save file archive").c_str());
app_fatal(_("Unable to write to save file archive"));
SaveWriter.RemoveHashEntries(GetTempSaveNames);
SaveWriter.Close();
}

4
Source/plrmsg.cpp

@ -73,7 +73,7 @@ void EventPlrMsg(string_view text, UiFlags style)
message.text = std::string(text);
message.from = string_view(message.text.data(), 0);
message.lineHeight = GetLineHeight(message.text, GameFont12) + 3;
AddMessageToChatLog(std::string(text));
AddMessageToChatLog(text);
}
void SendPlrMsg(Player &player, string_view text)
@ -87,7 +87,7 @@ void SendPlrMsg(Player &player, string_view text)
message.text = from + std::string(text);
message.from = string_view(message.text.data(), from.size());
message.lineHeight = GetLineHeight(message.text, GameFont12) + 3;
AddMessageToChatLog(std::string(text), &player);
AddMessageToChatLog(text, &player);
}
void InitPlrMsg()

6
Source/qol/chatlog.cpp

@ -113,7 +113,7 @@ void ToggleChatLog()
}
}
void AddMessageToChatLog(const std::string &message, Player *player, UiFlags flags)
void AddMessageToChatLog(string_view message, Player *player, UiFlags flags)
{
MessageCounter++;
time_t tm = time(nullptr);
@ -121,10 +121,10 @@ void AddMessageToChatLog(const std::string &message, Player *player, UiFlags fla
int oldSize = ChatLogLines.size();
ChatLogLines.emplace_back(MultiColoredText { "", { {} } });
if (player == nullptr) {
ChatLogLines.emplace_back(MultiColoredText { "{0} {1}", { { timestamp, UiFlags::ColorRed }, { message, flags } } });
ChatLogLines.emplace_back(MultiColoredText { "{0} {1}", { { timestamp, UiFlags::ColorRed }, { std::string(message), flags } } });
} else {
std::string playerInfo = fmt::format(_("{:s} (lvl {:d}): "), player->_pName, player->_pLevel);
ChatLogLines.emplace_back(MultiColoredText { message, { {} }, 20 });
ChatLogLines.emplace_back(MultiColoredText { std::string(message), { {} }, 20 });
UiFlags nameColor = player == MyPlayer ? UiFlags::ColorWhitegold : UiFlags::ColorBlue;
ChatLogLines.emplace_back(MultiColoredText { "{0} - {1}", { { timestamp, UiFlags::ColorRed }, { playerInfo, nameColor } } });
}

2
Source/qol/chatlog.h

@ -13,7 +13,7 @@ namespace devilution {
extern bool ChatLogFlag;
void ToggleChatLog();
void AddMessageToChatLog(const std::string &message, Player *player = nullptr, UiFlags flags = UiFlags::ColorWhite);
void AddMessageToChatLog(string_view message, Player *player = nullptr, UiFlags flags = UiFlags::ColorWhite);
void DrawChatLog(const Surface &out);
void ChatLogScrollUp();
void ChatLogScrollDown();

7
Source/qol/monhealthbar.cpp

@ -41,10 +41,9 @@ void InitMonsterHealthBar()
if ((healthBox.surface == nullptr)
|| (health.surface == nullptr)
|| (resistance.surface == nullptr)) {
app_fatal("%s", _("Failed to load UI resources.\n"
"\n"
"Make sure devilutionx.mpq is in the game folder and that it is up to date.")
.c_str());
app_fatal(_("Failed to load UI resources.\n"
"\n"
"Make sure devilutionx.mpq is in the game folder and that it is up to date."));
}
}

4
Source/qol/stash.cpp

@ -425,10 +425,10 @@ uint16_t CheckStashHLight(Point mousePosition)
InfoColor = item.getTextColor();
if (item._iIdentified) {
InfoString = item._iIName;
InfoString = string_view(item._iIName);
PrintItemDetails(item);
} else {
InfoString = item._iName;
InfoString = string_view(item._iName);
PrintItemDur(item);
}

9
Source/qol/xpbar.cpp

@ -58,7 +58,7 @@ std::string PrintWithSeparator(int n)
mlength = 3;
out.append(number.substr(0, mlength));
for (int i = mlength; i < length; i += 3) {
out.append(_(/* TRANSLATORS: Thousands separator */ ","));
AppendStrView(out, _(/* TRANSLATORS: Thousands separator */ ","));
out.append(number.substr(i, 3));
}
@ -73,10 +73,9 @@ void InitXPBar()
LoadMaskedArt("data\\xpbar.pcx", &xpbarArt, 1, 1);
if (xpbarArt.surface == nullptr) {
app_fatal("%s", _("Failed to load UI resources.\n"
"\n"
"Make sure devilutionx.mpq is in the game folder and that it is up to date.")
.c_str());
app_fatal(_("Failed to load UI resources.\n"
"\n"
"Make sure devilutionx.mpq is in the game folder and that it is up to date."));
}
}
}

18
Source/stores.cpp

@ -241,29 +241,29 @@ void AddItemListBackButton(bool selectable = false)
void PrintStoreItem(const Item &item, int l, UiFlags flags)
{
std::string productLine = "";
std::string productLine;
if (item._iIdentified) {
if (item._iMagical != ITEM_QUALITY_UNIQUE) {
if (item._iPrePower != -1) {
productLine.append(PrintItemPower(item._iPrePower, item));
AppendStrView(productLine, PrintItemPower(item._iPrePower, item));
}
}
if (item._iSufPower != -1) {
if (!productLine.empty())
productLine.append(_(", "));
productLine.append(PrintItemPower(item._iSufPower, item));
AppendStrView(productLine, _(", "));
AppendStrView(productLine, PrintItemPower(item._iSufPower, item));
}
}
if (item._iMiscId == IMISC_STAFF && item._iMaxCharges != 0) {
if (!productLine.empty())
productLine.append(_(", "));
AppendStrView(productLine, _(", "));
productLine.append(fmt::format(_("Charges: {:d}/{:d}"), item._iCharges, item._iMaxCharges));
}
if (!productLine.empty()) {
AddSText(40, l, productLine, flags, false);
l++;
productLine = "";
productLine.clear();
}
if (item._itype != ItemType::Misc) {
@ -274,7 +274,7 @@ void PrintStoreItem(const Item &item, int l, UiFlags flags)
if (item._iMaxDur != DUR_INDESTRUCTIBLE && item._iMaxDur != 0)
productLine += fmt::format(_("Dur: {:d}/{:d}, "), item._iDurability, item._iMaxDur);
else
productLine += _("Indestructible, ");
AppendStrView(productLine, _("Indestructible, "));
}
int8_t str = item._iMinStr;
@ -282,9 +282,9 @@ void PrintStoreItem(const Item &item, int l, UiFlags flags)
int8_t dex = item._iMinDex;
if (str == 0 && mag == 0 && dex == 0) {
productLine.append(_("No required attributes"));
AppendStrView(productLine, _("No required attributes"));
} else {
productLine.append(_("Required:"));
AppendStrView(productLine, _("Required:"));
if (str != 0)
productLine.append(fmt::format(_(" {:d} Str"), str));
if (mag != 0)

64
Source/utils/language.cpp

@ -1,10 +1,13 @@
#include "utils/language.h"
#include <functional>
#include <map>
#include <memory>
#include <vector>
#if __cplusplus >= 202002L
#include <version>
#endif
#include "engine/assets.hpp"
#include "options.h"
#include "utils/file_util.h"
@ -12,19 +15,46 @@
#include "utils/paths.h"
#include "utils/stdcompat/string_view.hpp"
using namespace devilution;
#define MO_MAGIC 0x950412de
#if defined(__cpp_lib_generic_unordered_lookup)
#include <unordered_map>
namespace {
struct CStringCmp {
bool operator()(const char *s1, const char *s2) const
using namespace devilution;
struct StringHash {
using is_transparent = void;
auto operator()(const char *str) const noexcept
{
return strcmp(s1, s2) < 0;
return std::hash<string_view> {}(str);
}
auto operator()(string_view str) const noexcept
{
return std::hash<string_view> {}(str);
}
auto operator()(const std::string &str) const noexcept
{
return std::hash<string_view> {}(str);
}
};
std::vector<std::unordered_map<std::string, std::string, StringHash, std::equal_to<>>> translation = { {}, {} };
} // namespace
#else
#include <map>
namespace {
using namespace devilution;
std::vector<std::map<std::string, std::string, std::less<>>> translation = { {}, {} };
} // namespace
#endif
namespace {
struct MoHead {
uint32_t magic;
@ -254,42 +284,42 @@ bool ReadEntry(SDL_RWops *rw, MoEntry *e, std::vector<char> &result)
} // namespace
const std::string &LanguageParticularTranslate(const char *context, const char *message)
string_view LanguageParticularTranslate(string_view context, string_view message)
{
constexpr const char *glue = "\004";
constexpr const char Glue = '\004';
std::string key = context;
key += glue;
key += message;
std::string key = std::string(context);
key.reserve(key.size() + 1 + message.size());
key += Glue;
AppendStrView(key, message);
auto it = translation[0].find(key);
if (it == translation[0].end()) {
it = translation[0].insert({ key, message }).first;
return message;
}
return it->second;
}
const std::string &LanguagePluralTranslate(const char *singular, const char *plural, int count)
string_view LanguagePluralTranslate(string_view singular, string_view plural, int count)
{
int n = GetLocalPluralId(count);
auto it = translation[n].find(singular);
if (it == translation[n].end()) {
if (count != 1)
it = translation[1].insert({ singular, plural }).first;
else
it = translation[0].insert({ singular, singular }).first;
return plural;
return singular;
}
return it->second;
}
const std::string &LanguageTranslate(const char *key)
string_view LanguageTranslate(string_view key)
{
auto it = translation[0].find(key);
if (it == translation[0].end()) {
it = translation[0].insert({ key, key }).first;
return key;
}
return it->second;

26
Source/utils/language.h

@ -2,6 +2,8 @@
#include <string>
#include "utils/stdcompat/string_view.hpp"
#define _(x) LanguageTranslate(x)
#define ngettext(x, y, z) LanguagePluralTranslate(x, y, z)
#define pgettext(context, x) LanguageParticularTranslate(context, x)
@ -10,9 +12,27 @@
bool HasTranslation(const std::string &locale);
void LanguageInitialize();
const std::string &LanguageParticularTranslate(const char *context, const char *message);
const std::string &LanguagePluralTranslate(const char *singular, const char *plural, int count);
const std::string &LanguageTranslate(const char *key);
/**
* @brief Returns the translation for the given key.
*
* @return guaranteed to be null-terminated if `key` is.
*/
devilution::string_view LanguageTranslate(devilution::string_view key);
/**
* @brief Returns a singular or plural translation for the given keys and count.
*
* @return guaranteed to be null-terminated if `key` is.
*/
devilution::string_view LanguagePluralTranslate(devilution::string_view singular, devilution::string_view plural, int count);
/**
* @brief Returns the translation for the given key and context identifier.
*
* @return guaranteed to be null-terminated if `key` is.
*/
devilution::string_view LanguageParticularTranslate(devilution::string_view context, devilution::string_view message);
// Chinese and Japanese, and Korean small font is 16px instead of a 12px one for readability.
bool IsSmallFontTall();

40
Source/utils/log.hpp

@ -4,6 +4,8 @@
#include <fmt/core.h>
#include <fmt/ranges.h>
#include "utils/stdcompat/string_view.hpp"
#ifdef USE_SDL1
#include "sdl2_to_1_2_backports.h"
#endif
@ -11,7 +13,7 @@
namespace devilution {
// Local definition to fix compilation issue due to header conflict.
[[noreturn]] void app_fatal(const char *pszFmt, ...);
[[noreturn]] void app_fatal(string_view);
enum class LogCategory {
Application = SDL_LOG_CATEGORY_APPLICATION,
@ -39,7 +41,7 @@ enum class LogPriority {
namespace detail {
template <typename... Args>
std::string format(const char *fmt, Args &&...args)
std::string format(string_view fmt, Args &&...args)
{
FMT_TRY
{
@ -47,108 +49,108 @@ std::string format(const char *fmt, Args &&...args)
}
FMT_CATCH(const fmt::format_error &e)
{
auto error = fmt::format("Format error, fmt: {}, error: {}", fmt ? fmt : "nullptr", e.what());
std::string error = fmt::format("Format error, fmt: {}, error: {}", fmt, e.what());
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", error.c_str());
app_fatal("%s", error.c_str());
app_fatal(error);
}
}
} // namespace detail
template <typename... Args>
void Log(const char *fmt, Args &&...args)
void Log(string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_Log("%s", str.c_str());
}
template <typename... Args>
void LogVerbose(LogCategory category, const char *fmt, Args &&...args)
void LogVerbose(LogCategory category, string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogVerbose(static_cast<int>(category), "%s", str.c_str());
}
template <typename... Args>
void LogVerbose(const char *fmt, Args &&...args)
void LogVerbose(string_view fmt, Args &&...args)
{
LogVerbose(defaultCategory, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void LogDebug(LogCategory category, const char *fmt, Args &&...args)
void LogDebug(LogCategory category, string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogDebug(static_cast<int>(category), "%s", str.c_str());
}
template <typename... Args>
void LogDebug(const char *fmt, Args &&...args)
void LogDebug(string_view fmt, Args &&...args)
{
LogDebug(defaultCategory, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void LogInfo(LogCategory category, const char *fmt, Args &&...args)
void LogInfo(LogCategory category, string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogInfo(static_cast<int>(category), "%s", str.c_str());
}
template <typename... Args>
void LogInfo(const char *fmt, Args &&...args)
void LogInfo(string_view fmt, Args &&...args)
{
LogInfo(defaultCategory, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void LogWarn(LogCategory category, const char *fmt, Args &&...args)
void LogWarn(LogCategory category, string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogWarn(static_cast<int>(category), "%s", str.c_str());
}
template <typename... Args>
void LogWarn(const char *fmt, Args &&...args)
void LogWarn(string_view fmt, Args &&...args)
{
LogWarn(defaultCategory, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void LogError(LogCategory category, const char *fmt, Args &&...args)
void LogError(LogCategory category, string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogError(static_cast<int>(category), "%s", str.c_str());
}
template <typename... Args>
void LogError(const char *fmt, Args &&...args)
void LogError(string_view fmt, Args &&...args)
{
LogError(defaultCategory, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void LogCritical(LogCategory category, const char *fmt, Args &&...args)
void LogCritical(LogCategory category, string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogCritical(static_cast<int>(category), "%s", str.c_str());
}
template <typename... Args>
void LogCritical(const char *fmt, Args &&...args)
void LogCritical(string_view fmt, Args &&...args)
{
LogCritical(defaultCategory, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void LogMessageV(LogCategory category, LogPriority priority, const char *fmt, Args &&...args)
void LogMessageV(LogCategory category, LogPriority priority, string_view fmt, Args &&...args)
{
auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogMessageV(static_cast<int>(category), static_cast<SDL_LogPriority>(priority), "%s", str.c_str());
}
template <typename... Args>
void LogMessageV(const char *fmt, Args &&...args)
void LogMessageV(string_view fmt, Args &&...args)
{
LogMessageV(defaultCategory, fmt, std::forward<Args>(args)...);
}

11
Source/utils/stdcompat/string_view.hpp

@ -3,16 +3,27 @@
#ifdef __has_include
#if defined(__cplusplus) && (__cplusplus >= 201703L || _MSC_VER >= 1930) && __has_include(<string_view>) // should be 201606L, but STL headers disagree
#include <string>
#include <string_view> // IWYU pragma: export
namespace devilution {
using string_view = std::string_view;
inline void AppendStrView(std::string &out, string_view str)
{
out.append(str);
}
} // namespace devilution
#elif __has_include(<experimental/string_view>)
#include <experimental/string_view> // IWYU pragma: export
namespace devilution {
using string_view = std::experimental::string_view;
inline void AppendStrView(std::string &out, string_view str)
{
out.append(str.data(), str.size());
}
} // namespace devilution
#else
#error "Missing support for <string_view> or <experimental/string_view>"

94
Source/utils/string_or_view.hpp

@ -0,0 +1,94 @@
#pragma once
#include <string>
#include <utility>
#include "utils/stdcompat/string_view.hpp"
namespace devilution {
class StringOrView {
public:
StringOrView()
: owned_(false)
, view_()
{
}
StringOrView(std::string &&str)
: owned_(true)
, str_(std::move(str))
{
}
StringOrView(string_view str)
: owned_(false)
, view_(str)
{
}
StringOrView(StringOrView &&other) noexcept
: owned_(other.owned_)
{
if (other.owned_) {
new (&str_) std::string(std::move(other.str_));
} else {
new (&view_) string_view(other.view_);
}
}
StringOrView &operator=(StringOrView &&other) noexcept
{
if (owned_) {
if (other.owned_) {
str_ = std::move(other.str_);
} else {
str_.~basic_string();
owned_ = false;
new (&view_) string_view(other.view_);
}
} else {
if (other.owned_) {
view_.~string_view();
owned_ = true;
new (&str_) std::string(std::move(other.str_));
} else {
view_ = other.view_;
}
}
return *this;
}
~StringOrView()
{
if (owned_) {
str_.~basic_string();
} else {
view_.~string_view();
}
}
bool empty() const
{
return owned_ ? str_.empty() : view_.empty();
}
string_view str() const
{
return owned_ ? str_ : view_;
}
operator string_view() const
{
return str();
}
private:
bool owned_;
union {
std::string str_;
string_view view_;
};
};
} // namespace devilution

2
Source/utils/ui_fwd.h

@ -30,6 +30,6 @@ void ReinitializeIntegerScale();
#endif
void ReinitializeRenderer();
void ResizeWindow();
void UiErrorOkDialog(const char *caption, const char *text, bool error = true);
void UiErrorOkDialog(string_view caption, string_view text, bool error = true);
} // namespace devilution

Loading…
Cancel
Save