https://github.com/tome2/tome2/pull/78
Compatibility with the packaged version of dev-libs/libfmt

--- a/src/cmd2.cc
+++ b/src/cmd2.cc
@@ -18,6 +18,7 @@
 #include "feature_flag.hpp"
 #include "feature_type.hpp"
 #include "files.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "gods.hpp"
 #include "hook_chat_in.hpp"
--- a/src/cmd3.cc
+++ b/src/cmd3.cc
@@ -12,6 +12,7 @@
 #include "cave_type.hpp"
 #include "cli_comm.hpp"
 #include "files.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "gods.hpp"
 #include "hook_drop_in.hpp"
@@ -1744,12 +1745,12 @@ void do_cmd_cli()
  */
 void do_cmd_cli_help()
 {
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 	for (int i = 0, j = -1; i < cli_total; i++)
 	{
 		if (j < i - 1)
 		{
-			w << "/";
+			w.write("/");
 		}
 
 		w.write("[[[[[G{}]", cli_info[i].comm);
--- a/src/cmd4.cc
+++ b/src/cmd4.cc
@@ -14,6 +14,7 @@
 #include "dungeon_info_type.hpp"
 #include "feature_type.hpp"
 #include "files.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "hooks.hpp"
 #include "init1.hpp"
@@ -3068,7 +3069,7 @@ void do_cmd_knowledge_artifacts()
 	}
 
 	/* Output buffer */
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	/* Scan the artifacts */
 	for (std::size_t k = 0; k < a_info.size(); k++)
@@ -3201,7 +3202,7 @@ static void do_cmd_knowledge_uniques()
 		  });
 
 	// Scan the monster races
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 	for (std::size_t r_idx : unique_r_idxs)
 	{
 		auto r_ptr = &r_info[r_idx];
@@ -3332,7 +3333,7 @@ static void do_cmd_knowledge_pets()
 	int t_levels = 0;
 
 	// Buffer
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	/* Process the monsters (backwards) */
 	for (int i = m_max - 1; i >= 1; i--)
@@ -3394,7 +3395,7 @@ static void do_cmd_knowledge_kill_count()
 	s32b Total = 0;
 
 	// Buffer
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	// Summary of monsters slain
 	{
@@ -3493,7 +3494,7 @@ static void do_cmd_knowledge_dungeons()
 {
 	auto const &d_info = game->edit_data.d_info;
 
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	/* Scan all dungeons */
 	for (std::size_t y = 1; y < d_info.size(); y++)
@@ -3521,7 +3522,7 @@ void do_cmd_knowledge_towns()
 {
 	auto const &d_info = game->edit_data.d_info;
 
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	/* Scan all dungeons */
 	for (auto const &d_ref: d_info)
@@ -3573,7 +3574,7 @@ static void do_cmd_knowledge_quests()
 	});
 
 	/* Write */
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 	for (int z = 0; z < MAX_Q_IDX; z++)
 	{
 		int const i = order[z];
--- a/src/cmd5.cc
+++ b/src/cmd5.cc
@@ -13,6 +13,7 @@
 #include "cave_type.hpp"
 #include "corrupt.hpp"
 #include "dungeon_flag.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "lua_bind.hpp"
 #include "monster2.hpp"
--- a/src/cmd6.cc
+++ b/src/cmd6.cc
@@ -18,6 +18,7 @@
 #include "dungeon_info_type.hpp"
 #include "ego_item_type.hpp"
 #include "files.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "hook_eat_in.hpp"
 #include "hooks.hpp"
--- a/src/cmd7.cc
+++ b/src/cmd7.cc
@@ -16,6 +16,7 @@
 #include "dungeon_flag.hpp"
 #include "ego_item_type.hpp"
 #include "files.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "hooks.hpp"
 #include "mimic.hpp"
--- a/src/corrupt.cc
+++ b/src/corrupt.cc
@@ -1,5 +1,6 @@
 #include "corrupt.hpp"
 
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "init1.hpp"
 #include "object_flag.hpp"
@@ -925,7 +926,7 @@ void lose_corruption()
  */
 std::string dump_corruptions(bool color, bool header)
 {
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	for (int i = 0; i < CORRUPTIONS_MAX; i++)
 	{
--- a/src/files.cc
+++ b/src/files.cc
@@ -3453,7 +3453,7 @@ static bool show_file_aux(const char *name, const char *what, int line)
 	return true;
 }
 
-void show_string(const char *lines, const char *title, int line)
+void show_string(const std::string &lines, const char *title, int line)
 {
 	// Temporary file
 	auto const file_name = fs::unique_path().string();
--- a/src/files.hpp
+++ b/src/files.hpp
@@ -26,7 +26,7 @@ std::string describe_player_location();
 errr file_character(const char *name);
 errr process_pref_file_aux(char *buf);
 errr process_pref_file(std::string const &name);
-void show_string(const char *lines, const char *title, int line = 0);
+void show_string(const std::string &lines, const char *title, int line = 0);
 void show_file(const char *name, const char *what, int line = 0);
 void do_cmd_help();
 void get_name();
--- a/src/format_ext.cc
+++ b/src/format_ext.cc
@@ -1,24 +1,18 @@
+#include <cassert>
 #include "format_ext.hpp"
 
 #include "util.hpp"
 
-void singular_prefix::write(fmt::Writer &w) const
+std::string format_as(const singular_prefix &sp)
 {
-	assert(!m_s.empty());
+	assert(!sp.m_s.empty());
 
-	if (is_a_vowel(m_s[0]))
+	if (is_a_vowel(sp.m_s[0]))
 	{
-		w.write("an ");
+		return "an " + sp.m_s;
 	}
 	else
 	{
-		w.write("a ");
+		return "a " + sp.m_s;
 	}
-
-	w.write(m_s);
-}
-
-void format_arg(fmt::BasicFormatter<char> &formatter, const char *&format_str, const singular_prefix &sp)
-{
-	sp.write(formatter.writer());
 }
--- a/src/format_ext.hpp
+++ b/src/format_ext.hpp
@@ -10,11 +10,11 @@ struct singular_prefix {
 private:
 	std::string m_s;
 
-	friend void format_arg(fmt::BasicFormatter<char> &formatter, const char *&format_str, const singular_prefix &sp);
+	friend std::string format_as(const singular_prefix &sp);
 
 public:
-	explicit singular_prefix(std::string s)
-		: m_s(std::move(s))
+	explicit singular_prefix(const std::string& s)
+		: m_s(s)
 	{
 	}
 
@@ -23,11 +23,30 @@ public:
 	{
 	}
 
-	void write(fmt::Writer &w) const;
-
 };
 
 //
 // Formatting support for fmtlib
 //
-void format_arg(fmt::BasicFormatter<char> &formatter, const char *&format_str, const singular_prefix &sp);
+std::string format_as(const singular_prefix &sp);
+
+// Class to simplify migration off deprecated fmt::MemoryWriter.
+// My goal here was to minimize the diff necessary.
+// This class should probably be removed and fmt:: (or std::format) used directly.
+class fmtMemoryWriter {
+private:
+	fmt::memory_buffer m_buf;
+
+public:
+	template <typename... Ts> void write(Ts &&...ts) {
+		fmt::format_to(std::back_inserter(m_buf), std::forward<Ts>(ts)...);
+	}
+
+	std::string str() {
+		return fmt::to_string(m_buf);
+	}
+
+	std::string c_str() {
+		return str();
+	}
+};
--- a/src/q_bounty.cc
+++ b/src/q_bounty.cc
@@ -1,5 +1,6 @@
 #include "q_bounty.hpp"
 
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "monster2.hpp"
 #include "monster_race.hpp"
@@ -159,7 +160,7 @@ void quest_bounty_get_item()
 std::string quest_bounty_describe()
 {
 	char mdesc[512];
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	if (cquest.status == QUEST_STATUS_TAKEN)
 	{
--- a/src/q_fireprof.cc
+++ b/src/q_fireprof.cc
@@ -4,6 +4,7 @@
 #include "dungeon_flag.hpp"
 #include "feature_flag.hpp"
 #include "feature_type.hpp"
+#include "format_ext.hpp"
 #include "hook_get_in.hpp"
 #include "hook_quest_gen_in.hpp"
 #include "hooks.hpp"
@@ -430,7 +431,7 @@ std::string quest_fireproof_describe()
 	num_staff = get_item_points_remaining() / FIREPROOF_STAFF_POINTS;
 	num_scroll = get_item_points_remaining() / FIREPROOF_SCROLL_POINTS;
 
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	if (status == QUEST_STATUS_TAKEN)
 	{
--- a/src/q_god.cc
+++ b/src/q_god.cc
@@ -5,6 +5,7 @@
 #include "dungeon_info_type.hpp"
 #include "feature_flag.hpp"
 #include "feature_type.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "hook_chardump_in.hpp"
 #include "hook_get_in.hpp"
@@ -282,7 +283,7 @@ static std::string make_directions(bool feel_it)
 
 std::string quest_god_describe()
 {
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	if (cquest.status == QUEST_STATUS_TAKEN)
 	{
--- a/src/q_library.cc
+++ b/src/q_library.cc
@@ -2,6 +2,7 @@
 
 #include "cave_type.hpp"
 #include "dungeon_flag.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "hooks.hpp"
 #include "hook_quest_gen_in.hpp"
@@ -476,7 +477,7 @@ void quest_library_building(bool *paid, bool *recreate)
 
 std::string quest_library_describe()
 {
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	if (cquest.status == QUEST_STATUS_TAKEN)
 	{
--- a/src/q_rand.cc
+++ b/src/q_rand.cc
@@ -5,6 +5,7 @@
 #include "cave_type.hpp"
 #include "dungeon_flag.hpp"
 #include "dungeon_info_type.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "generate.hpp"
 #include "hook_build_room1_in.hpp"
@@ -655,7 +656,7 @@ std::string quest_random_describe()
 		return "";
 	}
 
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	if (!is_randhero(dun_level))
 	{
--- a/src/xtra1.cc
+++ b/src/xtra1.cc
@@ -16,6 +16,7 @@
 #include "dungeon_flag.hpp"
 #include "dungeon_info_type.hpp"
 #include "files.hpp"
+#include "format_ext.hpp"
 #include "game.hpp"
 #include "gods.hpp"
 #include "hook_calculate_hp_in.hpp"
@@ -4410,7 +4411,7 @@ std::string fate_desc(int fate)
 {
 	auto const &a_info = game->edit_data.a_info;
 
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	if (fates[fate].serious)
 	{
@@ -4517,7 +4518,7 @@ std::string dump_fates()
 {
 	bool pending = false;
 
-	fmt::MemoryWriter w;
+	fmtMemoryWriter w;
 
 	for (int i = 0; i < MAX_FATES; i++)
 	{
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,8 +56,8 @@ ENDIF()
 # fmt
 #
-ADD_DEFINITIONS(-DFMT_HEADER_ONLY)
 find_package(jsoncons REQUIRED)
-SET(LIBS ${LIBS} jsoncons::jsoncons)
+find_package(fmt REQUIRED)
+SET(LIBS ${LIBS} jsoncons::jsoncons fmt::fmt)
 
 # Add standard math library
 SET(LIBS ${LIBS} m)
