diff --git a/CMakeLists.txt b/CMakeLists.txt index 900c59de1e..f56bf60438 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,6 +257,7 @@ add_library(${PROJECT_NAME} src/shake.h src/shinonome_gothic.h src/shinonome_mincho.h + src/span.h src/sprite_airshipshadow.cpp src/sprite_airshipshadow.h src/sprite_battler.cpp @@ -276,6 +277,7 @@ add_library(${PROJECT_NAME} src/state.cpp src/state.h src/std_clock.h + src/string_view.h src/system.h src/teleport_target.h src/text.cpp diff --git a/Makefile.am b/Makefile.am index a8ca7f1d11..07f46e0d73 100644 --- a/Makefile.am +++ b/Makefile.am @@ -256,6 +256,7 @@ libeasyrpg_player_a_SOURCES = \ src/sdl_ui.h \ src/shinonome_gothic.h \ src/shinonome_mincho.h \ + src/span.h \ src/sprite.cpp \ src/sprite.h \ src/sprite_airshipshadow.h \ @@ -275,6 +276,7 @@ libeasyrpg_player_a_SOURCES = \ src/state.cpp \ src/state.h \ src/std_clock.h \ + src/string_view.h \ src/system.h \ src/teleport_target.h \ src/text.cpp \ diff --git a/bench/utils.cpp b/bench/utils.cpp new file mode 100644 index 0000000000..2b98f3f3ce --- /dev/null +++ b/bench/utils.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include +#include +#include + +static void BM_ReplacePlaceholders(benchmark::State& state) { + for (auto _: state) { + Utils::ReplacePlaceholders("One night is %V %U", + Utils::MakeArray('V', 'U'), + Utils::MakeSvArray("Rest", "Do not Rest")); + } +} + +BENCHMARK(BM_ReplacePlaceholders); + +BENCHMARK_MAIN(); diff --git a/src/async_handler.cpp b/src/async_handler.cpp index 362842fa54..dbff61c5f2 100644 --- a/src/async_handler.cpp +++ b/src/async_handler.cpp @@ -94,7 +94,7 @@ void AsyncHandler::CreateRequestMapping(const std::string& file) { #endif } -FileRequestAsync* AsyncHandler::RequestFile(const std::string& folder_name, const std::string& file_name) { +FileRequestAsync* AsyncHandler::RequestFile(StringView folder_name, StringView file_name) { auto path = FileFinder::MakePath(folder_name, file_name); auto* request = GetRequest(path); @@ -105,10 +105,10 @@ FileRequestAsync* AsyncHandler::RequestFile(const std::string& folder_name, cons //Output::Debug("Waiting for {}", path); - return RegisterRequest(std::move(path), folder_name, file_name); + return RegisterRequest(std::move(path), std::string(folder_name), std::string(file_name)); } -FileRequestAsync* AsyncHandler::RequestFile(const std::string& file_name) { +FileRequestAsync* AsyncHandler::RequestFile(StringView file_name) { return RequestFile(".", file_name); } diff --git a/src/async_handler.h b/src/async_handler.h index c2d8fa7613..02b06e17d5 100644 --- a/src/async_handler.h +++ b/src/async_handler.h @@ -22,6 +22,7 @@ #include #include #include +#include "string_view.h" class FileRequestAsync; struct FileRequestResult; @@ -48,7 +49,7 @@ namespace AsyncHandler { * @param file_name Name of the requested file requested. * @return The async request. */ - FileRequestAsync* RequestFile(const std::string& folder_name, const std::string& file_name); + FileRequestAsync* RequestFile(StringView folder_name, StringView file_name); /** * Creates a request to a file. @@ -60,7 +61,7 @@ namespace AsyncHandler { * @param file_name Name of the requested file requested. * @return The async request. */ - FileRequestAsync* RequestFile(const std::string& file_name); + FileRequestAsync* RequestFile(StringView file_name); /** * Checks if any file with important-flag hasn't finished downloading yet. diff --git a/src/bitmap.cpp b/src/bitmap.cpp index 940af8e914..dfed87e643 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -309,7 +309,7 @@ void Bitmap::HueChangeBlit(int x, int y, Bitmap const& src, Rect const& src_rect Blit(dst_rect.x, dst_rect.y, bmp, bmp.GetRect(), Opacity::Opaque()); } -void Bitmap::TextDraw(Rect const& rect, int color, std::string const& text, Text::Alignment align) { +void Bitmap::TextDraw(Rect const& rect, int color, StringView text, Text::Alignment align) { FontRef font = Font::Default(); Rect text_rect = font->GetSize(text); int dx = text_rect.width - rect.width; @@ -328,13 +328,13 @@ void Bitmap::TextDraw(Rect const& rect, int color, std::string const& text, Text } } -void Bitmap::TextDraw(int x, int y, int color, std::string const& text, Text::Alignment align) { +void Bitmap::TextDraw(int x, int y, int color, StringView text, Text::Alignment align) { auto font = Font::Default(); auto system = Cache::SystemOrBlack(); Text::Draw(*this, x, y, *font, *system, color, text, align); } -void Bitmap::TextDraw(Rect const& rect, Color color, std::string const& text, Text::Alignment align) { +void Bitmap::TextDraw(Rect const& rect, Color color, StringView text, Text::Alignment align) { FontRef font = Font::Default(); Rect text_rect = font->GetSize(text); int dx = text_rect.width - rect.width; @@ -353,7 +353,7 @@ void Bitmap::TextDraw(Rect const& rect, Color color, std::string const& text, Te } } -void Bitmap::TextDraw(int x, int y, Color color, std::string const& text) { +void Bitmap::TextDraw(int x, int y, Color color, StringView text) { auto font = Font::Default(); Text::Draw(*this, x, y, *font, color, text); } diff --git a/src/bitmap.h b/src/bitmap.h index a9cba07973..e0fcc38f5c 100644 --- a/src/bitmap.h +++ b/src/bitmap.h @@ -34,6 +34,7 @@ #include "pixman_image_ptr.h" #include "opacity.h" #include "filesystem_stream.h" +#include "string_view.h" struct Transform; @@ -207,7 +208,7 @@ class Bitmap { * @param text text to draw. * @param align text alignment. */ - void TextDraw(int x, int y, int color, std::string const& text, Text::Alignment align = Text::AlignLeft); + void TextDraw(int x, int y, int color, StringView text, Text::Alignment align = Text::AlignLeft); /** * Draws text to bitmap using the Font::Default() font. @@ -217,7 +218,7 @@ class Bitmap { * @param text text to draw. * @param align text alignment inside bounding rectangle. */ - void TextDraw(Rect const& rect, int color, std::string const& text, Text::Alignment align = Text::AlignLeft); + void TextDraw(Rect const& rect, int color, StringView text, Text::Alignment align = Text::AlignLeft); /** * Draws text to bitmap using the Font::Default() font. @@ -227,7 +228,7 @@ class Bitmap { * @param color text color. * @param text text to draw. */ - void TextDraw(int x, int y, Color color, std::string const& text); + void TextDraw(int x, int y, Color color, StringView text); /** * Draws text to bitmap using the Font::Default() font. @@ -237,7 +238,7 @@ class Bitmap { * @param text text to draw. * @param align text alignment inside bounding rectangle. */ - void TextDraw(Rect const& rect, Color color, std::string const& text, Text::Alignment align = Text::AlignLeft); + void TextDraw(Rect const& rect, Color color, StringView, Text::Alignment align = Text::AlignLeft); /** * Blits source bitmap to this one. diff --git a/src/cache.cpp b/src/cache.cpp index 84646c78d7..bcfdd5172d 100644 --- a/src/cache.cpp +++ b/src/cache.cpp @@ -39,21 +39,21 @@ using namespace std::chrono_literals; namespace { - std::string MakeHashKey(const std::string& folder_name, const std::string& filename, bool transparent) { - return folder_name + ":" + filename + ":" + (transparent ? "T" : " "); + std::string MakeHashKey(StringView folder_name, StringView filename, bool transparent) { + return ToString(folder_name) + ":" + ToString(filename) + ":" + (transparent ? "T" : " "); } - std::string MakeTileHashKey(const std::string& chipset_name, int id) { + std::string MakeTileHashKey(StringView chipset_name, int id) { std::string key; key.reserve(chipset_name.size() + sizeof(int) + 2); key.append(reinterpret_cast(&id), sizeof(id)); key.append(1, ':'); - key.append(chipset_name); + key.append(chipset_name.begin(), chipset_name.end()); return key; } - int IdFromTileHash(const std::string& key) { + int IdFromTileHash(StringView key) { int id = 0; if (key.size() > sizeof(id)) { std::memcpy(&id, key.data(), sizeof(id)); @@ -61,7 +61,7 @@ namespace { return id; } - const char* NameFromTileHash(const std::string& key) { + const char* NameFromTileHash(StringView key) { int offset = sizeof(int) + 1; if (static_cast(key.size()) < offset) { return ""; @@ -132,14 +132,15 @@ namespace { return (cache[key] = {bmp, Game_Clock::GetFrameTime()}).bitmap; } - BitmapRef LoadBitmap(const std::string& folder_name, const std::string& filename, + BitmapRef LoadBitmap(StringView folder_name, StringView filename, bool transparent, const uint32_t flags) { const auto key = MakeHashKey(folder_name, filename, transparent); auto it = cache.find(key); if (it == cache.end()) { - const std::string path = FileFinder::FindImage(folder_name, filename); + // FIXME: STRING_VIEW string copies here + const std::string path = FileFinder::FindImage(ToString(folder_name), ToString(filename)); BitmapRef bmp = BitmapRef(); @@ -249,7 +250,7 @@ namespace { } template - BitmapRef LoadDummyBitmap(const std::string& folder_name, const std::string& filename, bool transparent) { + BitmapRef LoadDummyBitmap(StringView folder_name, StringView filename, bool transparent) { static_assert(Material::REND < T && T < Material::END, "Invalid material."); const Spec& s = spec[T]; @@ -271,7 +272,7 @@ namespace { } template - BitmapRef LoadBitmap(const std::string& f, bool transparent) { + BitmapRef LoadBitmap(StringView f, bool transparent) { static_assert(Material::REND < T && T < Material::END, "Invalid material."); const Spec& s = spec[T]; @@ -320,7 +321,7 @@ namespace { } template - BitmapRef LoadBitmap(const std::string& f) { + BitmapRef LoadBitmap(StringView f) { static_assert(Material::REND < T && T < Material::END, "Invalid material."); const Spec& s = spec[T]; @@ -331,67 +332,67 @@ namespace { std::vector Cache::exfont_custom; -BitmapRef Cache::Backdrop(const std::string& file) { +BitmapRef Cache::Backdrop(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Battle(const std::string& file) { +BitmapRef Cache::Battle(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Battle2(const std::string& file) { +BitmapRef Cache::Battle2(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Battlecharset(const std::string& file) { +BitmapRef Cache::Battlecharset(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Battleweapon(const std::string& file) { +BitmapRef Cache::Battleweapon(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Charset(const std::string& file) { +BitmapRef Cache::Charset(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Chipset(const std::string& file) { +BitmapRef Cache::Chipset(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Faceset(const std::string& file) { +BitmapRef Cache::Faceset(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Frame(const std::string& file, bool transparent) { +BitmapRef Cache::Frame(StringView file, bool transparent) { return LoadBitmap(file, transparent); } -BitmapRef Cache::Gameover(const std::string& file) { +BitmapRef Cache::Gameover(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Monster(const std::string& file) { +BitmapRef Cache::Monster(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Panorama(const std::string& file) { +BitmapRef Cache::Panorama(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Picture(const std::string& file, bool transparent) { +BitmapRef Cache::Picture(StringView file, bool transparent) { return LoadBitmap(file, transparent); } -BitmapRef Cache::System2(const std::string& file) { +BitmapRef Cache::System2(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::Title(const std::string& file) { +BitmapRef Cache::Title(StringView file) { return LoadBitmap(file); } -BitmapRef Cache::System(const std::string& file) { +BitmapRef Cache::System(StringView file) { return LoadBitmap(file); } @@ -419,7 +420,7 @@ BitmapRef Cache::Exfont() { } } -BitmapRef Cache::Tile(const std::string& filename, int tile_id) { +BitmapRef Cache::Tile(StringView filename, int tile_id) { const auto key = MakeTileHashKey(filename, tile_id); auto it = cache_tiles.find(key); @@ -519,12 +520,12 @@ void Cache::Clear() { cache_tiles.clear(); } -void Cache::SetSystemName(std::string const& filename) { - system_name = filename; +void Cache::SetSystemName(std::string filename) { + system_name = std::move(filename); } -void Cache::SetSystem2Name(std::string const& filename) { - system2_name = filename; +void Cache::SetSystem2Name(std::string filename) { + system2_name = std::move(filename); } BitmapRef Cache::System() { diff --git a/src/cache.h b/src/cache.h index 786767a3ba..64802f9dba 100644 --- a/src/cache.h +++ b/src/cache.h @@ -24,6 +24,7 @@ #include "system.h" #include "memory_management.h" +#include "string_view.h" #define CACHE_DEFAULT_BITMAP "\x01" @@ -35,25 +36,25 @@ class Tone; * Cache namespace. */ namespace Cache { - BitmapRef Backdrop(const std::string& filename); - BitmapRef Battle(const std::string& filename); - BitmapRef Battle2(const std::string& filename); - BitmapRef Battlecharset(const std::string& filename); - BitmapRef Battleweapon(const std::string& filename); - BitmapRef Charset(const std::string& filename); + BitmapRef Backdrop(StringView filename); + BitmapRef Battle(StringView filename); + BitmapRef Battle2(StringView filename); + BitmapRef Battlecharset(StringView filename); + BitmapRef Battleweapon(StringView filename); + BitmapRef Charset(StringView filename); BitmapRef Exfont(); - BitmapRef Faceset(const std::string& filename); - BitmapRef Frame(const std::string& filename, bool transparent = true); - BitmapRef Gameover(const std::string& filename); - BitmapRef Monster(const std::string& filename); - BitmapRef Panorama(const std::string& filename); - BitmapRef Picture(const std::string& filename, bool transparent); - BitmapRef Chipset(const std::string& filename); - BitmapRef Title(const std::string& filename); - BitmapRef System(const std::string& filename); - BitmapRef System2(const std::string& filename); + BitmapRef Faceset(StringView filename); + BitmapRef Frame(StringView filename, bool transparent = true); + BitmapRef Gameover(StringView filename); + BitmapRef Monster(StringView filename); + BitmapRef Panorama(StringView filename); + BitmapRef Picture(StringView filename, bool transparent); + BitmapRef Chipset(StringView filename); + BitmapRef Title(StringView filename); + BitmapRef System(StringView filename); + BitmapRef System2(StringView filename); - BitmapRef Tile(const std::string& filename, int tile_id); + BitmapRef Tile(StringView filename, int tile_id); BitmapRef SpriteEffect(const BitmapRef& src_bitmap, const Rect& rect, bool flip_x, bool flip_y, const Tone& tone, const Color& blend); void Clear(); @@ -70,8 +71,8 @@ namespace Cache { /** @return the configured system2 bitmap, or nullptr if there is no system2 */ BitmapRef System2(); - void SetSystemName(std::string const& filename); - void SetSystem2Name(std::string const& filename); + void SetSystemName(std::string filename); + void SetSystem2Name(std::string filename); extern std::vector exfont_custom; } diff --git a/src/filefinder.cpp b/src/filefinder.cpp index 6c09846d7e..cf0103dd1d 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -319,8 +319,8 @@ std::shared_ptr FileFinder::CreateDirectoryTree(const return tree; } -std::string FileFinder::MakePath(const std::string& dir, const std::string& name) { - std::string str = dir.empty()? name : dir + "/" + name; +std::string FileFinder::MakePath(StringView dir, StringView name) { + std::string str = dir.empty()? std::string(name) : std::string(dir) + "/" + std::string(name); #ifdef _WIN32 std::replace(str.begin(), str.end(), '/', '\\'); #else @@ -372,7 +372,7 @@ std::vector FileFinder::SplitPath(const std::string& path) { } std::string FileFinder::GetPathInsidePath(const std::string& path_to, const std::string& path_in) { - if (!Utils::StartsWith(path_in, path_to)) { + if (!ToStringView(path_in).starts_with(path_to)) { return path_in; } @@ -888,7 +888,7 @@ bool FileFinder::IsMajorUpdatedTree() { string_map mem = tree->sub_members["music"]; for (auto& i : mem) { std::string file = mem[i.first]; - if (Utils::EndsWith(Utils::LowerCase(file), ".mp3")) { + if (ToStringView(Utils::LowerCase(file)).ends_with(".mp3")) { Output::Debug("MP3 file ({}) found", file); return true; } diff --git a/src/filefinder.h b/src/filefinder.h index c4f6710045..175302049f 100644 --- a/src/filefinder.h +++ b/src/filefinder.h @@ -21,6 +21,7 @@ // Headers #include "system.h" #include "filesystem_stream.h" +#include "string_view.h" #include #include @@ -203,7 +204,7 @@ namespace FileFinder { * @param name file name to be appended to dir. * @return combined path */ - std::string MakePath(const std::string& dir, const std::string& name); + std::string MakePath(StringView dir, StringView name); /** * Converts a path to the canonical equivalent. diff --git a/src/font.cpp b/src/font.cpp index 382044af4f..ca912a0ef4 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -101,7 +101,7 @@ namespace { BitmapFont(const std::string& name, function_type func); - Rect GetSize(std::string const& txt) const override; + Rect GetSize(StringView txt) const override; Rect GetSize(char32_t ch) const override; GlyphRet Glyph(char32_t code) override; @@ -130,7 +130,7 @@ namespace { struct FTFont : public Font { FTFont(const std::string& name, int size, bool bold, bool italic); - Rect GetSize(std::string const& txt) const override; + Rect GetSize(StringView txt) const override; Rect GetSize(char32_t ch) const override; GlyphRet Glyph(char32_t code) override; @@ -165,7 +165,7 @@ namespace { public: enum { HEIGHT = 12, WIDTH = 12 }; ExFont(); - Rect GetSize(std::string const& txt) const override; + Rect GetSize(StringView txt) const override; Rect GetSize(char32_t ch) const override; GlyphRet Glyph(char32_t code) override; private: @@ -186,7 +186,7 @@ Rect BitmapFont::GetSize(char32_t ch) const { return Rect(0, 0, units * HALF_WIDTH, HEIGHT); } -Rect BitmapFont::GetSize(std::string const& txt) const { +Rect BitmapFont::GetSize(StringView txt) const { size_t units = 0; const auto* iter = txt.data(); const auto* end = txt.data() + txt.size(); @@ -228,7 +228,7 @@ std::weak_ptr::type> FTFont::library_checker_; FTFont::FTFont(const std::string& name, int size, bool bold, bool italic) : Font(name, size, bold, italic), current_size_(0) {} -Rect FTFont::GetSize(std::string const& txt) const { +Rect FTFont::GetSize(StringView txt) const { int const s = Font::Default()->GetSize(txt).width; if (s == -1) { @@ -443,7 +443,7 @@ Font::GlyphRet ExFont::Glyph(char32_t code) { return { bm, Rect(0, 0, WIDTH, HEIGHT) }; } -Rect ExFont::GetSize(std::string const&) const { +Rect ExFont::GetSize(StringView) const { return Rect(0, 0, 12, 12); } diff --git a/src/font.h b/src/font.h index 1f5244d544..4ff32f9dae 100644 --- a/src/font.h +++ b/src/font.h @@ -22,6 +22,7 @@ #include "system.h" #include "memory_management.h" #include "rect.h" +#include "string_view.h" #include class Color; @@ -40,7 +41,7 @@ class Font { * @param txt the string to measure * @return Rect describing the rendered string boundary */ - virtual Rect GetSize(std::string const& txt) const = 0; + virtual Rect GetSize(StringView txt) const = 0; /** * Returns the size of the rendered utf32 character, not including shadows. * diff --git a/src/game_actor.cpp b/src/game_actor.cpp index ef046191f9..3110cf6adf 100644 --- a/src/game_actor.cpp +++ b/src/game_actor.cpp @@ -719,8 +719,8 @@ std::string Game_Actor::GetLevelUpMessage(int new_level) const { ss << new_level; return Utils::ReplacePlaceholders( lcf::Data::terms.level_up, - {'S', 'V', 'U'}, - {GetData().name, ss.str(), lcf::Data::terms.level} + Utils::MakeArray('S', 'V', 'U'), + Utils::MakeSvArray(GetData().name, ss.str(), lcf::Data::terms.level) ); } else { std::string particle, space = ""; @@ -742,8 +742,8 @@ std::string Game_Actor::GetLearningMessage(const lcf::rpg::Skill& skill) const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.skill_learned, - {'S', 'O'}, - {GetData().name, skill.name} + Utils::MakeArray('S', 'O'), + Utils::MakeSvArray(GetData().name, skill.name) ); } diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index d485768d90..c54f57ceae 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -364,8 +364,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetDeathMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S'}, - {GetTarget()->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(GetTarget()->GetName()) ); } else { @@ -381,8 +381,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetAttackFailureMessage(const s if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S', 'O'}, - {GetSource()->GetName(), GetTarget()->GetName()} + Utils::MakeArray('S', 'O'), + Utils::MakeSvArray(GetSource()->GetName(), GetTarget()->GetName()) ); } else { @@ -394,8 +394,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetHpSpRecoveredMessage(int val if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.hp_recovery, - {'S', 'V', 'U'}, - {GetTarget()->GetName(), std::to_string(value), points} + Utils::MakeArray('S', 'V', 'U'), + Utils::MakeSvArray(GetTarget()->GetName(), std::to_string(value), points) ); } else { @@ -427,8 +427,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetUndamagedMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S'}, - {GetTarget()->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(GetTarget()->GetName()) ); } else { @@ -446,8 +446,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetCriticalHitMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S', 'O'}, - {GetSource()->GetName(), GetTarget()->GetName()} + Utils::MakeArray('S', 'O'), + Utils::MakeSvArray(GetSource()->GetName(), GetTarget()->GetName()) ); } else { @@ -465,8 +465,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetHpSpAbsorbedMessage(int valu if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S', 'O', 'V', 'U'}, - {GetSource()->GetName(), GetTarget()->GetName(), std::to_string(value), points} + Utils::MakeArray('S', 'O', 'V', 'U'), + Utils::MakeSvArray(GetSource()->GetName(), GetTarget()->GetName(), std::to_string(value), points) ); } else { @@ -500,8 +500,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetDamagedMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S', 'V', 'U'}, - {GetTarget()->GetName(), std::to_string(value), lcf::Data::terms.health_points} + Utils::MakeArray('S', 'V', 'U'), + Utils::MakeSvArray(GetTarget()->GetName(), std::to_string(value), lcf::Data::terms.health_points) ); } else { @@ -528,8 +528,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetParameterChangeMessage(bool if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S', 'V', 'U'}, - {GetTarget()->GetName(), std::to_string(value), points} + Utils::MakeArray('S', 'V', 'U'), + Utils::MakeSvArray(GetTarget()->GetName(), std::to_string(value), points) ); } else { @@ -556,8 +556,8 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetStateMessage(const std::stri if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( message, - {'S'}, - {GetTarget()->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(GetTarget()->GetName()) ); } else { @@ -572,11 +572,10 @@ std::string Game_BattleAlgorithm::AlgorithmBase::GetAttributeShiftMessage( const std::stringstream ss; if (Player::IsRPG2kE()) { - ss << attribute; return Utils::ReplacePlaceholders( message, - { 'S', 'O' }, - { GetTarget()->GetName(), ss.str() } + Utils::MakeArray('S', 'O'), + Utils::MakeSvArray(GetTarget()->GetName(), attribute) ); } else { @@ -1098,8 +1097,8 @@ std::string Game_BattleAlgorithm::Normal::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.attacking, - {'S'}, - {source->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(source->GetName()) ); } else { @@ -1401,8 +1400,8 @@ std::string Game_BattleAlgorithm::Skill::GetStartMessage() const { auto* target = GetTarget(); return Utils::ReplacePlaceholders( skill.using_message1, - {'S', 'O', 'U'}, - {GetSource()->GetName(), (target ? target->GetName() : "???"), skill.name} + Utils::MakeArray('S', 'O', 'U'), + Utils::MakeSvArray(GetSource()->GetName(), (target ? target->GetName() : "???"), skill.name) ); } else { @@ -1427,8 +1426,8 @@ std::string Game_BattleAlgorithm::Skill::GetSecondStartMessage() const { auto* target = GetTarget(); return Utils::ReplacePlaceholders( skill.using_message2, - { 'S', 'O', 'U' }, - {GetSource()->GetName(), (target ? target->GetName() : "???"), skill.name} + Utils::MakeArray('S', 'O', 'U'), + Utils::MakeSvArray(GetSource()->GetName(), (target ? target->GetName() : "???"), skill.name) ); } else { @@ -1653,8 +1652,8 @@ std::string Game_BattleAlgorithm::Item::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.use_item, - {'S', 'O'}, - {source->GetName(), item.name} + Utils::MakeArray('S', 'O'), + Utils::MakeSvArray(source->GetName(), item.name) ); } else if (Player::IsRPG2k()) { @@ -1696,8 +1695,8 @@ std::string Game_BattleAlgorithm::Defend::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.defending, - {'S'}, - {source->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(source->GetName()) ); } else if (Player::IsRPG2k()) { @@ -1730,8 +1729,8 @@ std::string Game_BattleAlgorithm::Observe::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.observing, - {'S'}, - {source->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(source->GetName()) ); } else if (Player::IsRPG2k()) { @@ -1757,8 +1756,8 @@ std::string Game_BattleAlgorithm::Charge::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.focus, - {'S'}, - {source->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(source->GetName()) ); } else if (Player::IsRPG2k()) { @@ -1788,8 +1787,8 @@ std::string Game_BattleAlgorithm::SelfDestruct::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.autodestruction, - {'S'}, - {source->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(source->GetName()) ); } else if (Player::IsRPG2k()) { @@ -1867,8 +1866,8 @@ std::string Game_BattleAlgorithm::Escape::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.enemy_escape, - {'S'}, - {source->GetName()} + Utils::MakeArray('S'), + Utils::MakeSvArray(source->GetName()) ); } else if (Player::IsRPG2k()) { @@ -1920,8 +1919,8 @@ std::string Game_BattleAlgorithm::Transform::GetStartMessage() const { if (Player::IsRPG2kE()) { return Utils::ReplacePlaceholders( lcf::Data::terms.enemy_transform, - {'S', 'O'}, - {source->GetName(), lcf::ReaderUtil::GetElement(lcf::Data::enemies, new_monster_id)->name} // Sanity check in Game_Enemy + Utils::MakeArray('S', 'O'), + Utils::MakeSvArray(source->GetName(), lcf::ReaderUtil::GetElement(lcf::Data::enemies, new_monster_id)->name) // Sanity check in Game_Enemy ); } else if (Player::IsRPG2k()) { diff --git a/src/game_interpreter_map.cpp b/src/game_interpreter_map.cpp index 5429dd66f2..678aed0b84 100644 --- a/src/game_interpreter_map.cpp +++ b/src/game_interpreter_map.cpp @@ -368,80 +368,53 @@ bool Game_Interpreter_Map::CommandShowInn(lcf::rpg::EventCommand const& com) { / } auto pm = PendingMessage(); - std::ostringstream out; + + StringView greeting_1, greeting_2, greeting_3, accept, cancel; switch (inn_type) { case 0: - if (Player::IsRPG2kE()) { - out << inn_price; - pm.PushLine( - Utils::ReplacePlaceholders( - lcf::Data::terms.inn_a_greeting_1, - {'V', 'U'}, - {out.str(), lcf::Data::terms.gold} - ) - ); - pm.PushLine( - Utils::ReplacePlaceholders( - lcf::Data::terms.inn_a_greeting_3, - {'V', 'U'}, - {out.str(), lcf::Data::terms.gold} - ) - ); - } - else { - out << lcf::Data::terms.inn_a_greeting_1 - << " " << inn_price << lcf::Data::terms.gold - << " " << lcf::Data::terms.inn_a_greeting_2; - pm.PushLine(out.str()); - pm.PushLine(lcf::Data::terms.inn_a_greeting_3); - } + greeting_1 = lcf::Data::terms.inn_a_greeting_1; + greeting_2 = lcf::Data::terms.inn_a_greeting_2; + greeting_3 = lcf::Data::terms.inn_a_greeting_3; + accept = lcf::Data::terms.inn_a_accept; + cancel = lcf::Data::terms.inn_a_cancel; break; case 1: - if (Player::IsRPG2kE()) { - out << inn_price; - pm.PushLine( - Utils::ReplacePlaceholders( - lcf::Data::terms.inn_b_greeting_1, - {'V', 'U'}, - {out.str(), lcf::Data::terms.gold} - ) - ); - pm.PushLine( - Utils::ReplacePlaceholders( - lcf::Data::terms.inn_b_greeting_3, - {'V', 'U'}, - {out.str(), lcf::Data::terms.gold} - ) - ); - } - else { - out << lcf::Data::terms.inn_b_greeting_1 - << " " << inn_price << lcf::Data::terms.gold - << " " << lcf::Data::terms.inn_b_greeting_2; - pm.PushLine(out.str()); - pm.PushLine(lcf::Data::terms.inn_b_greeting_3); - } + greeting_1 = lcf::Data::terms.inn_b_greeting_1; + greeting_2 = lcf::Data::terms.inn_b_greeting_2; + greeting_3 = lcf::Data::terms.inn_b_greeting_3; + accept = lcf::Data::terms.inn_b_accept; + cancel = lcf::Data::terms.inn_b_cancel; break; - default: - return false; + } + + if (Player::IsRPG2kE()) { + auto price_s = std::to_string(inn_price); + pm.PushLine( + Utils::ReplacePlaceholders( + greeting_1, + Utils::MakeArray('V', 'U'), + Utils::MakeSvArray(price_s, lcf::Data::terms.gold) + ) + ); + pm.PushLine( + Utils::ReplacePlaceholders( + greeting_3, + Utils::MakeArray('V', 'U'), + Utils::MakeSvArray(price_s, lcf::Data::terms.gold) + ) + ); + } + else { + pm.PushLine(fmt::format("{} {}{} {}", greeting_1, inn_price, lcf::Data::terms.gold, greeting_2)); + pm.PushLine(ToString(greeting_3)); } bool can_afford = (Main_Data::game_party->GetGold() >= inn_price); pm.SetChoiceResetColors(true); - switch (inn_type) { - case 0: - pm.PushChoice(lcf::Data::terms.inn_a_accept, can_afford); - pm.PushChoice(lcf::Data::terms.inn_a_cancel); - break; - case 1: - pm.PushChoice(lcf::Data::terms.inn_b_accept, can_afford); - pm.PushChoice(lcf::Data::terms.inn_b_cancel); - break; - default: - return false; - } + pm.PushChoice(ToString(accept), can_afford); + pm.PushChoice(ToString(cancel)); pm.SetShowGoldWindow(true); diff --git a/src/game_message.cpp b/src/game_message.cpp index 6c04e80457..39d52222cf 100644 --- a/src/game_message.cpp +++ b/src/game_message.cpp @@ -157,7 +157,7 @@ int Game_Message::GetRealPosition() { } } -int Game_Message::WordWrap(const std::string& line, const int limit, const std::function callback) { +int Game_Message::WordWrap(StringView line, const int limit, const WordWrapCallback& callback) { int start = 0; int line_count = 0; FontRef font = Font::Default(); @@ -165,7 +165,7 @@ int Game_Message::WordWrap(const std::string& line, const int limit, const std:: do { int next = start; do { - auto found = line.find(" ", next); + auto found = line.find(' ', next); if (found == std::string::npos) { found = line.size(); } diff --git a/src/game_message.h b/src/game_message.h index 7b4e8cd787..1d45d4a6df 100644 --- a/src/game_message.h +++ b/src/game_message.h @@ -22,6 +22,7 @@ #include #include #include +#include "string_view.h" #include "pending_message.h" class Window_Message; @@ -171,6 +172,9 @@ namespace Game_Message { void SetPendingMessage(PendingMessage&& pm); + /** Callback type for WordWrap function */ + using WordWrapCallback = const std::function; + /** * Breaks the line into lines, each of which is equal * or less than a specified limit in pixels in the @@ -189,7 +193,7 @@ namespace Game_Message { * @param[in] limit maximum size of each line after word-breaking. * @param callback a function to be called for each word-wrapped line */ - int WordWrap(const std::string& line, int limit, const std::function callback); + int WordWrap(StringView line, int limit, const WordWrapCallback& callback); /** * Return if it's legal to show a new message box. diff --git a/src/game_system.cpp b/src/game_system.cpp index 701e10c62f..015f9f1924 100644 --- a/src/game_system.cpp +++ b/src/game_system.cpp @@ -56,7 +56,7 @@ bool Game_System::IsStopFilename(const std::string& name, std::string (*find_fun found_name = find_func(name); - return found_name.empty() && (Utils::StartsWith(name, "(") && Utils::EndsWith(name, ")")); + return found_name.empty() && (ToStringView(name).starts_with('(') && ToStringView(name).ends_with(')')); } @@ -468,7 +468,7 @@ void Game_System::OnBgmReady(FileRequestResult* result) { return; } - if (Utils::EndsWith(result->file, ".link")) { + if (ToStringView(result->file).ends_with(".link")) { // Handle Ineluki's MP3 patch auto stream = FileFinder::OpenInputStream(path, std::ios_base::in); if (!stream) { diff --git a/src/message_overlay.cpp b/src/message_overlay.cpp index 045c702fd7..eea17546e5 100644 --- a/src/message_overlay.cpp +++ b/src/message_overlay.cpp @@ -86,8 +86,8 @@ void MessageOverlay::AddMessage(const std::string& message, Color color) { Game_Message::WordWrap( message, SCREEN_TARGET_WIDTH - 6, // hardcoded to screen width because the bitmap is not initialized early enough - [&](const std::string& line) { - messages.emplace_back(line, color); + [&](StringView line) { + messages.emplace_back(std::string(line), color); } ); diff --git a/src/registry_wine.cpp b/src/registry_wine.cpp index 2ea2f81d6d..b4deb74920 100644 --- a/src/registry_wine.cpp +++ b/src/registry_wine.cpp @@ -100,7 +100,7 @@ std::string Registry::ReadStrValue(HKEY hkey, const std::string& key, const std: if (!in_section) { if (line.empty() || line[0] != '[') { continue; - } else if (Utils::StartsWith(Utils::LowerCaseInPlace(line), formatted_key_search)) { + } else if (ToStringView(Utils::LowerCaseInPlace(line)).starts_with(formatted_key_search)) { // Found the section in_section = true; } @@ -110,7 +110,7 @@ std::string Registry::ReadStrValue(HKEY hkey, const std::string& key, const std: break; } - if (Utils::StartsWith(Utils::LowerCase(line), formatted_val)) { + if (ToStringView(Utils::LowerCase(line)).starts_with(formatted_val)) { // value found string_value = line.substr(formatted_val.length()); break; diff --git a/src/scene_battle_rpg2k.cpp b/src/scene_battle_rpg2k.cpp index e8ec4e977a..d00e4fbbe6 100644 --- a/src/scene_battle_rpg2k.cpp +++ b/src/scene_battle_rpg2k.cpp @@ -1466,8 +1466,8 @@ void Scene_Battle_Rpg2k::PushExperienceGainedMessage(PendingMessage& pm, int exp pm.PushLine( Utils::ReplacePlaceholders( lcf::Data::terms.exp_received, - {'V', 'U'}, - {std::to_string(exp), lcf::Data::terms.exp_short} + Utils::MakeArray('V', 'U'), + Utils::MakeSvArray(std::to_string(exp), lcf::Data::terms.exp_short) ) + Player::escape_symbol + "." ); } @@ -1484,8 +1484,8 @@ void Scene_Battle_Rpg2k::PushGoldReceivedMessage(PendingMessage& pm, int money) pm.PushLine( Utils::ReplacePlaceholders( lcf::Data::terms.gold_recieved_a, - {'V', 'U'}, - {std::to_string(money), lcf::Data::terms.gold} + Utils::MakeArray('V', 'U'), + Utils::MakeSvArray(std::to_string(money), lcf::Data::terms.gold) ) + Player::escape_symbol + "." ); } @@ -1511,8 +1511,8 @@ void Scene_Battle_Rpg2k::PushItemRecievedMessages(PendingMessage& pm, std::vecto pm.PushLine( Utils::ReplacePlaceholders( lcf::Data::terms.item_recieved, - {'S'}, - {item_name} + Utils::MakeArray('S'), + Utils::MakeSvArray(item_name) ) + Player::escape_symbol + "." ); } diff --git a/src/span.h b/src/span.h new file mode 100644 index 0000000000..b314fed3e6 --- /dev/null +++ b/src/span.h @@ -0,0 +1,28 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + +#ifndef EP_SPAN_H +#define EP_SPAN_H + +#include + +using lcf::Span; +using lcf::ExtentT; +using lcf::dynamic_extent; +using lcf::MakeSpan; + +#endif diff --git a/src/string_view.h b/src/string_view.h new file mode 100644 index 0000000000..a790b78c43 --- /dev/null +++ b/src/string_view.h @@ -0,0 +1,38 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + +#ifndef EP_STRING_VIEW_H +#define EP_STRING_VIEW_H + +#include +#include + +using StringView = lcf::StringView; + +using lcf::ToString; +using lcf::ToStringView; + +// FIXME: liblcf doesn't depend on fmt, so we need to add this here to enable fmtlib support for our StringView. +namespace nonstd { namespace sv_lite { +template +inline fmt::basic_string_view to_string_view(basic_string_view s) { + return fmt::basic_string_view(s.data(), s.size()); +} +} } + + +#endif diff --git a/src/text.cpp b/src/text.cpp index b150d3ecc2..cb5e93d58b 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -44,7 +44,7 @@ Rect Text::Draw(Bitmap& dest, int x, int y, Font& font, Color color, char32_t ch } } -Rect Text::Draw(Bitmap& dest, const int x, const int y, Font& font, const Bitmap& system, const int color, const std::string& text, const Text::Alignment align) { +Rect Text::Draw(Bitmap& dest, const int x, const int y, Font& font, const Bitmap& system, const int color, StringView text, const Text::Alignment align) { if (text.length() == 0) return { x, y, 0, 0 }; Rect dst_rect = font.GetSize(text); @@ -87,7 +87,7 @@ Rect Text::Draw(Bitmap& dest, const int x, const int y, Font& font, const Bitmap return { x, y, next_glyph_pos, ih }; } -Rect Text::Draw(Bitmap& dest, const int x, const int y, Font& font, const Color color, const std::string& text) { +Rect Text::Draw(Bitmap& dest, const int x, const int y, Font& font, const Color color, StringView text) { if (text.length() == 0) return { x, y, 0, 0 }; int dx = x; diff --git a/src/text.h b/src/text.h index 0aaa63a196..71f57e50b2 100644 --- a/src/text.h +++ b/src/text.h @@ -22,6 +22,7 @@ #include "memory_management.h" #include "rect.h" #include "color.h" +#include "string_view.h" #include class Font; @@ -52,7 +53,7 @@ namespace Text { * * @return Rect describing the sub-rect of dest that was rendered to. Does *not* include shadow pixels. */ - Rect Draw(Bitmap& dest, int x, int y, Font& font, const Bitmap& system, int color, const std::string& text, Text::Alignment align = Text::AlignLeft); + Rect Draw(Bitmap& dest, int x, int y, Font& font, const Bitmap& system, int color, StringView text, Text::Alignment align = Text::AlignLeft); /** * Draws the text onto dest bitmap with given parameters. Does not draw a shadow. @@ -66,7 +67,7 @@ namespace Text { * * @return Rect describing the sub-rect of dest that was rendered to. Does *not* include shadow pixels. */ - Rect Draw(Bitmap& dest, int x, int y, Font& font, Color color, const std::string& text); + Rect Draw(Bitmap& dest, int x, int y, Font& font, Color color, StringView text); /** * Draws the character onto dest bitmap with given parameters. diff --git a/src/utils.cpp b/src/utils.cpp index 3ba205cf68..4378e03619 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -51,8 +51,8 @@ namespace { } -std::string Utils::LowerCase(const std::string& str) { - std::string result = str; +std::string Utils::LowerCase(StringView str) { + auto result = std::string(str); LowerCaseInPlace(result); return result; } @@ -62,12 +62,17 @@ std::string& Utils::LowerCaseInPlace(std::string& str) { return str; } -std::string Utils::UpperCase(const std::string& str) { - std::string result = str; - std::transform(result.begin(), result.end(), result.begin(), Upper); +std::string Utils::UpperCase(StringView str) { + auto result = std::string(str); + UpperCaseInPlace(result); return result; } +std::string& Utils::UpperCaseInPlace(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), Upper); + return str; +} + int Utils::StrICmp(const char* l, const char* r) { assert(l != nullptr); assert(r != nullptr); @@ -82,17 +87,7 @@ int Utils::StrICmp(const char* l, const char* r) { return *l - *r; } -bool Utils::StartsWith(const std::string& str, const std::string& start) { - return str.length() >= start.length() && - 0 == str.compare(0, start.length(), start); -} - -bool Utils::EndsWith(const std::string& str, const std::string& end) { - return str.length() >= end.length() && - 0 == str.compare(str.length() - end.length(), end.length(), end); -} - -std::u16string Utils::DecodeUTF16(const std::string& str) { +std::u16string Utils::DecodeUTF16(StringView str) { std::u16string result; for (auto it = str.begin(), str_end = str.end(); it < str_end; ++it) { uint8_t c1 = *it; @@ -167,7 +162,7 @@ std::u16string Utils::DecodeUTF16(const std::string& str) { return result; } -std::u32string Utils::DecodeUTF32(const std::string& str) { +std::u32string Utils::DecodeUTF32(StringView str) { std::u32string result; for (auto it = str.begin(), str_end = str.end(); it < str_end; ++it) { uint8_t c1 = *it; @@ -425,22 +420,22 @@ Utils::TextRet Utils::TextNext(const char* iter, const char* end, char32_t escap #if !defined(__amigaos4__) && !defined(__AROS__) template -static std::wstring ToWideStringImpl(const std::string&); +static std::wstring ToWideStringImpl(StringView); #if __SIZEOF_WCHAR_T__ == 4 || __WCHAR_MAX__ > 0x10000 template<> // utf32 -std::wstring ToWideStringImpl<4>(const std::string& str) { - std::u32string const tmp = Utils::DecodeUTF32(str); +std::wstring ToWideStringImpl<4>(StringView str) { + const auto tmp = Utils::DecodeUTF32(str); return std::wstring(tmp.begin(), tmp.end()); } #else template<> // utf16 -std::wstring ToWideStringImpl<2>(const std::string& str) { - std::u16string const tmp = Utils::DecodeUTF16(str); +std::wstring ToWideStringImpl<2>(StringView str) { + const auto tmp = Utils::DecodeUTF16(str); return std::wstring(tmp.begin(), tmp.end()); } #endif -std::wstring Utils::ToWideString(const std::string& str) { +std::wstring Utils::ToWideString(StringView str) { return ToWideStringImpl(str); } @@ -596,7 +591,7 @@ std::string Utils::ReadLine(std::istream &is) { } } -std::vector Utils::Tokenize(const std::string &str_to_tokenize, const std::function predicate) { +std::vector Utils::Tokenize(StringView str_to_tokenize, const std::function predicate) { std::u32string text = DecodeUTF32(str_to_tokenize); std::vector tokens; std::u32string cur_token; @@ -630,8 +625,8 @@ std::vector Utils::ReadStream(std::istream& stream) { return outbuf; } -std::string Utils::ReplacePlaceholders(const std::string& text_template, std::vector types, std::vector values) { - std::string str = text_template; +std::string Utils::ReplacePlaceholders(StringView text_template, Span types, Span values) { + auto str = std::string(text_template); size_t index = str.find("%"); while (index != std::string::npos) { if (index + 1 < str.length()) { @@ -642,7 +637,7 @@ std::string Utils::ReplacePlaceholders(const std::string& text_template, std::ve t_it != types.end() && v_it != values.end(); ++t_it, ++v_it) { if (std::toupper(type) == *t_it) { - str.replace(index, 2, *v_it); + str.replace(index, 2, v_it->data(), v_it->size()); index += (*v_it).length() - 2; break; } diff --git a/src/utils.h b/src/utils.h index e1caa8e49d..17bf4c5ac3 100644 --- a/src/utils.h +++ b/src/utils.h @@ -24,6 +24,8 @@ #include #include #include "system.h" +#include "string_view.h" +#include "span.h" namespace Utils { /** @@ -32,7 +34,7 @@ namespace Utils { * @param str string to convert. * @return the converted string. */ - std::string LowerCase(const std::string& str); + std::string LowerCase(StringView str); /** * Converts a string to lower case in-place (ASCII only, faster) @@ -48,7 +50,15 @@ namespace Utils { * @param str string to convert. * @return the converted string. */ - std::string UpperCase(const std::string& str); + std::string UpperCase(StringView str); + + /** + * Converts a string to upper case in-place. (ASCII only, faster) + * + * @param str string to convert. + * @return the converted string. + */ + std::string& UpperCaseInPlace(std::string& str); /** * Case insensitive (ascii only) lexicographical compare of 2 strings. @@ -60,31 +70,13 @@ namespace Utils { */ int StrICmp(const char* l, const char* r); - /** - * Tests if a string starts with a substring. - * - * @param str String to search in - * @param end Substring to check at the start of str - * @return true when the start matches - */ - bool StartsWith(const std::string& str, const std::string& end); - - /** - * Tests if a string ends with a substring. - * - * @param str String to search in - * @param end Substring to check at the end of str - * @return true when the end matches - */ - bool EndsWith(const std::string& str, const std::string& end); - /** * Converts Utf8 to UTF-16. * * @param str string to convert. * @return the converted string. */ - std::u16string DecodeUTF16(const std::string& str); + std::u16string DecodeUTF16(StringView str); /** * Converts UTF-8 to UTF-32. @@ -92,7 +84,7 @@ namespace Utils { * @param str string to convert. * @return the converted string. */ - std::u32string DecodeUTF32(const std::string& str); + std::u32string DecodeUTF32(StringView str); /** * Converts UTF-16 to UTF-8. @@ -132,7 +124,7 @@ namespace Utils { * @param str string to convert. * @return the converted string. */ - std::wstring ToWideString(const std::string& str); + std::wstring ToWideString(StringView str); /** * Converts std::wstring to UTF-8 string. @@ -295,7 +287,7 @@ namespace Utils { * @param predicate Predicate function, must return true when the character is used for splitting. * @return vector containing the elements between the tokens */ - std::vector Tokenize(const std::string& str_to_tokenize, const std::function predicate); + std::vector Tokenize(StringView str_to_tokenize, const std::function predicate); /* * Searches for newlines and calls f(const std::string&) for each line. @@ -304,7 +296,7 @@ namespace Utils { * @param f function of type void(const std::string&) */ template - void ForEachLine(const std::string& line, F&& f); + void ForEachLine(StringView line, F&& f); /** @@ -326,7 +318,7 @@ namespace Utils { * should match types in number of elements and order. * @return A new string with placeholders replaced. */ - std::string ReplacePlaceholders(const std::string& text_template, std::vector types, std::vector values); + std::string ReplacePlaceholders(StringView text_template, Span types, Span values); /** * @return value clamped between min and max @@ -352,6 +344,45 @@ namespace Utils { template std::enable_if_t::value && std::is_arithmetic::value, Dest> RoundTo(Src v); + namespace detail { + template struct MakeArrayReturnHelper { using type = D; }; + template struct MakeArrayReturnHelper : std::common_type {}; + template using MakeArrayReturn = std::array::type, sizeof...(Types)>; + template using MakeVectorReturn = std::vector::type>; + } // namespace detail + + /** + * Create a std::array from the given parameters, automatically deducing the type and size. + */ + template + constexpr detail::MakeArrayReturn MakeArray(Types&& ... t) { + return { std::forward(t)... }; + } + + /** + * Create a std::array from the given parameters, automatically deducing the size. + */ + template + constexpr auto MakeSvArray(Types&& ... t) { + return MakeArray(std::forward(t)...); + } + + /** + * Create a std::vector from the given parameters, automatically deducing the type. + */ + template + constexpr detail::MakeVectorReturn MakeVector(Types&& ... t) { + return { std::forward(t)... }; + } + + /** + * Create a std::vector from the given parameters. + */ + template + constexpr auto MakeSvVector(Types&& ... t) { + return MakeVector(std::forward(t)...); + } + } // namespace Utils template @@ -364,11 +395,11 @@ inline bool Utils::PercentChance(long rate) { } template -inline void Utils::ForEachLine(const std::string& line, F&& f) { +inline void Utils::ForEachLine(StringView line, F&& f) { size_t next = 0; do { auto idx = line.find('\n', next); - if (idx == std::string::npos) { + if (idx == decltype(line)::npos) { if (next == 0) { // Optimize the common case f(line); diff --git a/src/window_battlemessage.cpp b/src/window_battlemessage.cpp index 7affa37d5c..8c03f0d189 100644 --- a/src/window_battlemessage.cpp +++ b/src/window_battlemessage.cpp @@ -35,41 +35,41 @@ Window_BattleMessage::Window_BattleMessage(int ix, int iy, int iwidth, int iheig SetZ(Priority_Window + 50); } -void Window_BattleMessage::Push(const std::string& message) { +void Window_BattleMessage::Push(StringView message) { #ifdef EP_DEBUG_BATTLE2K_MESSAGE Output::Debug("Battle2k Message Push \"{}\"", message); #endif - Utils::ForEachLine(message, [this](const std::string& line) + Utils::ForEachLine(message, [this](StringView line) { PushLine(line); }); } -void Window_BattleMessage::PushLine(const std::string& line) { +void Window_BattleMessage::PushLine(StringView line) { if (Player::IsRPG2kE()) { Game_Message::WordWrap( line, GetWidth() - 20, - [this](const std::string& wrap_line) { - lines.push_back(wrap_line); + [this](StringView wrap_line) { + lines.push_back(std::string(wrap_line)); } ); } else { - lines.push_back(line); + lines.push_back(std::string(line)); } needs_refresh = true; } -void Window_BattleMessage::PushWithSubject(const std::string& message, const std::string& subject) { +void Window_BattleMessage::PushWithSubject(StringView message, StringView subject) { if (Player::IsRPG2kE()) { Push(Utils::ReplacePlaceholders( message, - {'S'}, - {subject} + Utils::MakeArray('S'), + Utils::MakeSvArray(subject) )); } else { - Push(subject + message); + Push(std::string(subject) + std::string(message)); } needs_refresh = true; } diff --git a/src/window_battlemessage.h b/src/window_battlemessage.h index 92616f633f..4275cf1422 100644 --- a/src/window_battlemessage.h +++ b/src/window_battlemessage.h @@ -40,7 +40,7 @@ class Window_BattleMessage : public Window_Base { * * @param message The text to be displayed. */ - void Push(const std::string& message); + void Push(StringView message); /** * Pushes a message, either prepending the subject to it, @@ -50,7 +50,7 @@ class Window_BattleMessage : public Window_Base { * @param message Message to be displayed. * @param subject Subject that will be displayed in the message. */ - void PushWithSubject(const std::string& message, const std::string& subject); + void PushWithSubject(StringView message, StringView subject); void Pop(); @@ -95,7 +95,7 @@ class Window_BattleMessage : public Window_Base { static const int linesPerPage = 4; protected: - void PushLine(const std::string& line); + void PushLine(StringView line); private: std::vector lines; diff --git a/src/window_message.cpp b/src/window_message.cpp index dd4147e0ed..06255ae2df 100644 --- a/src/window_message.cpp +++ b/src/window_message.cpp @@ -161,8 +161,8 @@ void Window_Message::StartMessageProcessing(PendingMessage pm) { Game_Message::WordWrap( line, width - 24, - [&](const std::string& wrapped_line) { - append(wrapped_line); + [&](StringView wrapped_line) { + append(std::string(wrapped_line)); } ); } diff --git a/src/window_name.cpp b/src/window_name.cpp index 1ea1f71a4b..849ac386af 100644 --- a/src/window_name.cpp +++ b/src/window_name.cpp @@ -38,17 +38,19 @@ void Window_Name::Refresh() { contents->TextDraw(2, 2, Font::ColorDefault, name); } -void Window_Name::Set(const std::string& text) { - name = text; +void Window_Name::Set(std::string text) { + name = std::move(text); Refresh(); } -void Window_Name::Append(const std::string& text) { - if(Font::Default()->GetSize(name + text).width <= (12 * 6)) { - name += text; +void Window_Name::Append(StringView text) { + // Avoid string copies by reusing the buffer in name + name.append(text.begin(), text.end()); + if(Font::Default()->GetSize(name).width <= (12 * 6)) { Refresh(); } else { Game_System::SePlay(Game_System::GetSystemSE(Game_System::SFX_Buzzer)); + name.resize(name.size() - text.size()); } } @@ -58,16 +60,13 @@ void Window_Name::Update() { } void Window_Name::Erase() { - if (name.size() < 1) + if (name.empty()) return; - std::u32string u32name = Utils::DecodeUTF32(name); + auto u32name = Utils::DecodeUTF32(name); u32name.pop_back(); name = Utils::EncodeUTF(u32name); Refresh(); } -const std::string& Window_Name::Get() { - return name; -} diff --git a/src/window_name.h b/src/window_name.h index b9b32427e2..5fefb88589 100644 --- a/src/window_name.h +++ b/src/window_name.h @@ -21,6 +21,7 @@ // Headers #include #include "window_base.h" +#include "string_view.h" /** * Window Name Class. @@ -37,10 +38,10 @@ class Window_Name : public Window_Base { */ void Refresh(); - void Set(const std::string& text); - void Append(const std::string& text); + void Set(std::string text); + void Append(StringView text); void Erase(); - const std::string& Get(); + const std::string& Get() const; void Update() override; @@ -48,4 +49,8 @@ class Window_Name : public Window_Base { std::string name; }; +inline const std::string& Window_Name::Get() const { + return name; +} + #endif diff --git a/tests/wordwrap.cpp b/tests/wordwrap.cpp index c8e5c9c648..a39d104d4d 100644 --- a/tests/wordwrap.cpp +++ b/tests/wordwrap.cpp @@ -7,9 +7,9 @@ TEST_SUITE_BEGIN("Word Wrap"); constexpr int limit_2k = SCREEN_TARGET_WIDTH - 20; -static std::vector WordWrap(const std::string& line, int limit = limit_2k) { +static std::vector WordWrap(StringView line, int limit = limit_2k) { std::vector lines; - Game_Message::WordWrap(line, limit, [&](const std::string& l) { lines.push_back(l); }); + Game_Message::WordWrap(line, limit, [&](StringView l) { lines.push_back(std::string(l)); }); return lines; }