From f67d6e740f10e971807c6a7e97ab0b5e3c91fa7b Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 15 Mar 2021 17:25:14 +0100 Subject: [PATCH 1/5] Add LCF logger --- CMakeLists.txt | 3 ++ Makefile.am | 3 ++ src/lcf/log_handler.h | 47 +++++++++++++++++++++++ src/log.h | 24 ++++++++++++ src/log_handler.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++ src/lsd_reader.cpp | 3 +- src/reader_lcf.cpp | 3 +- 7 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/lcf/log_handler.h create mode 100644 src/log.h create mode 100644 src/log_handler.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 14ab6bf7..f48e8e26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,9 @@ set(LCF_SOURCES src/lmt_treemap.cpp src/lmu_movecommand.cpp src/lmu_reader.cpp + src/log.h src/lsd_reader.cpp + src/log_handler.cpp src/reader_flags.cpp src/reader_lcf.cpp src/reader_struct.h @@ -208,6 +210,7 @@ set(LCF_HEADERS src/lcf/lmt/reader.h src/lcf/lmu/reader.h src/lcf/lsd/reader.h + src/lcf/log_handler.h src/lcf/reader_lcf.h src/lcf/reader_util.h src/lcf/reader_xml.h diff --git a/Makefile.am b/Makefile.am index d378e2be..fe10c1e7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,7 +57,9 @@ liblcf_la_SOURCES = \ src/lmt_treemap.cpp \ src/lmu_movecommand.cpp \ src/lmu_reader.cpp \ + src/log.h \ src/lsd_reader.cpp \ + src/log_handler.cpp \ src/reader_flags.cpp \ src/reader_lcf.cpp \ src/reader_struct.h \ @@ -219,6 +221,7 @@ lcfinclude_HEADERS = \ src/lcf/flag_set.h \ src/lcf/ini.h \ src/lcf/inireader.h \ + src/lcf/log_handler.h \ src/lcf/reader_lcf.h \ src/lcf/reader_util.h \ src/lcf/reader_xml.h \ diff --git a/src/lcf/log_handler.h b/src/lcf/log_handler.h new file mode 100644 index 00000000..c6eb69ac --- /dev/null +++ b/src/lcf/log_handler.h @@ -0,0 +1,47 @@ +/* + * This file is part of liblcf. Copyright (c) 2020 liblcf authors. + * https://github.com/EasyRPG/liblcf - https://easyrpg.org + * + * liblcf is Free/Libre Open Source Software, released under the MIT License. + * For the full copyright and license information, please view the COPYING + * file that was distributed with this source code. + */ + +#ifndef LCF_OUTPUT_H +#define LCF_OUTPUT_H + +#include "string_view.h" + +namespace lcf { +namespace LogHandler { + +enum class Level { + Debug, + Warning, + Error, + Highest +}; + +using LogHandlerFn = void (*)(Level level, StringView message); + +/** + * Sets the output handler for all lcf logging. + * The default handler prints to standard error. + * + * @param fn New output handler. nullptr for default handler. + */ +void SetHandler(LogHandlerFn fn); + +/** + * Only report issues that have at least this log level. + * Use Highest to disable logging. + * Default: Debug + * + * @param new_level New log level + */ +void SetLevel(Level new_level); + +} // namespace LogHandler +} // namespace lcf + +#endif diff --git a/src/log.h b/src/log.h new file mode 100644 index 00000000..d2915102 --- /dev/null +++ b/src/log.h @@ -0,0 +1,24 @@ +/* + * This file is part of liblcf. Copyright (c) 2020 liblcf authors. + * https://github.com/EasyRPG/liblcf - https://easyrpg.org + * + * liblcf is Free/Libre Open Source Software, released under the MIT License. + * For the full copyright and license information, please view the COPYING + * file that was distributed with this source code. + */ + +#ifndef LCF_LOG_H +#define LCF_LOG_H + +#include "lcf/log_handler.h" + +namespace lcf { +namespace Log { + +void Debug(const char* fmt, ...); +void Warning(const char* fmt, ...); + +} // namespace Log +} // namespace lcf + +#endif diff --git a/src/log_handler.cpp b/src/log_handler.cpp new file mode 100644 index 00000000..36855cee --- /dev/null +++ b/src/log_handler.cpp @@ -0,0 +1,88 @@ +/* + * This file is part of liblcf. Copyright (c) 2020 liblcf authors. + * https://github.com/EasyRPG/liblcf - https://easyrpg.org + * + * liblcf is Free/Libre Open Source Software, released under the MIT License. + * For the full copyright and license information, please view the COPYING + * file that was distributed with this source code. + */ + +#include "lcf/log_handler.h" +#include +#include +#include +#include + +namespace lcf { +namespace LogHandler { +namespace { + void DefaultHandler(lcf::LogHandler::Level level, StringView message) { + switch (level) { + case Level::Debug: + std::cerr << "Debug: "; + break; + case Level::Warning: + std::cerr << "Warning: "; + break; + case Level::Error: + std::cerr << "Error: "; + break; + default: + assert(false && "Invalid Log Level"); + } + std::cerr << message << "\n"; + } + + Level level = Level::Debug; + LogHandlerFn output_fn = DefaultHandler; +} + +void SetHandler(LogHandlerFn fn) { + if (!fn) { + output_fn = DefaultHandler; + } else { + output_fn = fn; + } +} + +void SetLevel(Level new_level) { + level = new_level; +} + +} // namespace Output + +namespace Log { +namespace { + std::string format_string(char const* fmt, va_list args) { + char buf[4096]; + int const result = vsnprintf(buf, sizeof(buf), fmt, args); + if (result < 0) { + return std::string(); + } + + return std::string(buf, static_cast(result) < sizeof(buf) ? result : sizeof(buf)); + } +} + +void Debug(const char* fmt, ...) { + if (static_cast(LogHandler::Level::Debug) >= static_cast(LogHandler::level)) { + va_list args; + va_start(args, fmt); + auto msg = format_string(fmt, args); + LogHandler::output_fn(LogHandler::Level::Debug, msg); + va_end(args); + } +} + +void Warning(const char* fmt, ...) { + if (static_cast(LogHandler::Level::Warning) >= static_cast(LogHandler::level)) { + va_list args; + va_start(args, fmt); + auto msg = format_string(fmt, args); + LogHandler::output_fn(LogHandler::Level::Warning, msg); + va_end(args); + } +} + +} // namespace Log +} // namespace lcf diff --git a/src/lsd_reader.cpp b/src/lsd_reader.cpp index cc64a44e..60af4cad 100644 --- a/src/lsd_reader.cpp +++ b/src/lsd_reader.cpp @@ -16,6 +16,7 @@ #include "lcf/lsd/chunks.h" #include "lcf/rpg/save.h" #include "lcf/reader_util.h" +#include "log.h" #include "reader_struct.h" namespace lcf { @@ -43,7 +44,7 @@ void LSD_Reader::PrepareSave(rpg::Save& save, int32_t version, int32_t codepage) std::unique_ptr LSD_Reader::Load(StringView filename, StringView encoding) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LSD file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + lcf::Log::Warning("Failed to open LSD file '%s' for reading : %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LSD_Reader::Load(stream, encoding); diff --git a/src/reader_lcf.cpp b/src/reader_lcf.cpp index 4c6ff0b3..6ab9ea52 100644 --- a/src/reader_lcf.cpp +++ b/src/reader_lcf.cpp @@ -13,6 +13,7 @@ #include #include "lcf/reader_lcf.h" +#include "log.h" namespace lcf { // Statics @@ -276,7 +277,7 @@ int LcfReader::Peek() { } void LcfReader::Skip(const struct LcfReader::Chunk& chunk_info, const char* where) { - fprintf(stderr, "Skipped Chunk %02X (%" PRIu32 " byte) in lcf at %" PRIX32 " (%s)\n", + Log::Debug("Skipped Chunk %02X (%" PRIu32 " byte) in lcf at %" PRIX32 " (%s)\n", chunk_info.ID, chunk_info.length, Tell(), where); for (uint32_t i = 0; i < chunk_info.length; ++i) { From 9ecdb0d3c7d41583829275552d4ff68c57ac74f2 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Fri, 26 May 2023 09:16:13 +0200 Subject: [PATCH 2/5] All the reporting goes now through the logger. For XML the SetError function was removed and is printed directly. Tracing stuff (LCF_DEBUG_TRACE) goes now through fprintf. Not worth to send this through the logger as is off by default. --- src/dbstring_struct.cpp | 11 +++++------ src/encoder.cpp | 7 ++++--- src/lcf/reader_xml.h | 5 ----- src/ldb_equipment.cpp | 5 +++-- src/ldb_eventcommand.cpp | 7 ++++--- src/ldb_parameters.cpp | 2 +- src/ldb_reader.cpp | 17 +++++++++-------- src/lmt_reader.cpp | 21 +++++++++++---------- src/lmt_rect.cpp | 2 +- src/lmt_treemap.cpp | 3 ++- src/lmu_movecommand.cpp | 4 ++-- src/lmu_reader.cpp | 27 ++++++++++++++------------- src/log.h | 1 + src/log_handler.cpp | 12 +++++++++++- src/lsd_reader.cpp | 28 ++++++++++++++-------------- src/reader_flags.cpp | 4 ++-- src/reader_lcf.cpp | 24 ++++++++++++++++-------- src/reader_struct.h | 31 ++++++++++++++----------------- src/reader_struct_impl.h | 9 +++++---- src/reader_util.cpp | 3 ++- src/reader_xml.cpp | 11 ++--------- 21 files changed, 123 insertions(+), 111 deletions(-) diff --git a/src/dbstring_struct.cpp b/src/dbstring_struct.cpp index 601d8d37..401df477 100644 --- a/src/dbstring_struct.cpp +++ b/src/dbstring_struct.cpp @@ -8,6 +8,7 @@ */ #include "lcf/dbstring.h" +#include "log.h" #include "reader_struct.h" #include @@ -43,7 +44,7 @@ struct RawStruct > { void RawStruct::ReadLcf(DBString& ref, LcfReader& stream, uint32_t length) { stream.ReadString(ref, length); #ifdef LCF_DEBUG_TRACE - printf(" %s\n", ref.c_str()); + fprintf(stderr, " %s\n", ref.c_str()); #endif } @@ -106,9 +107,7 @@ void RawStruct>::ReadLcf(std::vector& ref, LcfRe } if (stream.Tell() != endpos) { -#ifdef LCF_DEBUG_TRACE - fprintf(stderr, "Misaligned!\n"); -#endif + Log::Warning("vector Misaligned at 0x" PRIx32 "", stream.Tell()); stream.Seek(endpos); } } @@ -177,7 +176,7 @@ class DbStringVectorXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** atts) { if (strcmp(name, "item") != 0) { - stream.Error("Expecting %s but got %s", "item", name); + Log::Error("XML: Expecting %s but got %s", "item", name); return; } @@ -191,7 +190,7 @@ class DbStringVectorXmlHandler : public XmlHandler { } if (id <= last_id || id < -1) { - stream.Error("Bad Id %d / %d", id, last_id); + Log::Error("XML: Bad Id %d / %d", id, last_id); return; } diff --git a/src/encoder.cpp b/src/encoder.cpp index 31d89a4a..d968b5bd 100644 --- a/src/encoder.cpp +++ b/src/encoder.cpp @@ -10,6 +10,7 @@ #include "lcf/encoder.h" #include "lcf/reader_util.h" #include "lcf/scope_guard.h" +#include "log.h" #include #include @@ -86,7 +87,7 @@ void Encoder::Init() { auto conv_runtime = ucnv_open(runtime_encoding, &status); if (conv_runtime == nullptr) { - fprintf(stderr, "liblcf: ucnv_open() error for encoding \"%s\": %s\n", runtime_encoding, u_errorName(status)); + Log::Error("ucnv_open() error for encoding \"%s\": %s", runtime_encoding, u_errorName(status)); return; } status = U_ZERO_ERROR; @@ -95,7 +96,7 @@ void Encoder::Init() { auto conv_storage = ucnv_open(storage_encoding.c_str(), &status); if (conv_storage == nullptr) { - fprintf(stderr, "liblcf: ucnv_open() error for dest encoding \"%s\": %s\n", storage_encoding.c_str(), u_errorName(status)); + Log::Error("ucnv_open() error for dest encoding \"%s\": %s", storage_encoding.c_str(), u_errorName(status)); return; } @@ -143,7 +144,7 @@ void Encoder::Convert(std::string& str, UConverter* conv_dst, UConverter* conv_s &status); if (U_FAILURE(status)) { - fprintf(stderr, "liblcf: ucnv_convertEx() error when encoding \"%s\": %s\n", src.c_str(), u_errorName(status)); + Log::Error("ucnv_convertEx() error when encoding \"%s\": %s", src.c_str(), u_errorName(status)); _buffer.clear(); } diff --git a/src/lcf/reader_xml.h b/src/lcf/reader_xml.h index 379c5143..ba68cb70 100644 --- a/src/lcf/reader_xml.h +++ b/src/lcf/reader_xml.h @@ -54,11 +54,6 @@ class XmlReader { */ bool IsOk() const; - /** - * Reports a parsing error. - */ - void Error(const char* fmt, ...); - /** * Parses the XML file. */ diff --git a/src/ldb_equipment.cpp b/src/ldb_equipment.cpp index 2da2354f..b5e39687 100644 --- a/src/ldb_equipment.cpp +++ b/src/ldb_equipment.cpp @@ -9,6 +9,7 @@ #include "lcf/ldb/reader.h" #include "lcf/ldb/chunks.h" +#include "log.h" #include "reader_struct.h" namespace lcf { @@ -27,7 +28,7 @@ struct RawStruct { */ void RawStruct::ReadLcf(rpg::Equipment& ref, LcfReader& stream, uint32_t length) { if (length != 10) { - fprintf(stderr, "Equipment has incorrect size %" PRIu32 " (expected 10)\n", length); + Log::Warning("Equipment has incorrect size %" PRIu32 " (expected 10)", length); LcfReader::Chunk chunk_info; chunk_info.ID = 0x33; @@ -85,7 +86,7 @@ class EquipmentXmlHandler : public XmlHandler { else if (strcmp(name, "accessory_id") == 0) field = &ref.accessory_id; else { - stream.Error("Unrecognized field '%s'", name); + Log::Error("XML: Unrecognized field '%s'", name); field = NULL; } } diff --git a/src/ldb_eventcommand.cpp b/src/ldb_eventcommand.cpp index 3d02a457..2f292f22 100644 --- a/src/ldb_eventcommand.cpp +++ b/src/ldb_eventcommand.cpp @@ -9,6 +9,7 @@ #include #include +#include "log.h" #include "reader_struct.h" #include "lcf/rpg/eventcommand.h" @@ -108,7 +109,7 @@ class EventCommandXmlHandler : public XmlHandler { else if (strcmp(name, "parameters") == 0) field = Parameters; else { - stream.Error("Unrecognized field '%s'", name); + Log::Error("XML: Unrecognized field '%s'", name); field = None; } } @@ -161,7 +162,7 @@ void RawStruct >::ReadLcf( if (stream.Tell() >= endpos) { stream.Seek(endpos, LcfReader::FromStart); - fprintf(stderr, "Event command corrupted at %" PRIu32 "\n", stream.Tell()); + Log::Warning("Event command corrupted at %" PRIu32 "", stream.Tell()); for (;;) { // Try finding the real end of the event command (4 0-bytes) int i = 0; @@ -216,7 +217,7 @@ class EventCommandVectorXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** /* atts */) { if (strcmp(name, "EventCommand") != 0) - stream.Error("Expecting %s but got %s", "EventCommand", name); + Log::Error("XML: Expecting %s but got %s", "EventCommand", name); ref.resize(ref.size() + 1); rpg::EventCommand& obj = ref.back(); stream.SetHandler(new EventCommandXmlHandler(obj)); diff --git a/src/ldb_parameters.cpp b/src/ldb_parameters.cpp index 3da08154..28b8c83f 100644 --- a/src/ldb_parameters.cpp +++ b/src/ldb_parameters.cpp @@ -79,7 +79,7 @@ class ParametersXmlHandler : public XmlHandler { else if (strcmp(name, "agility") == 0) field = &ref.agility; else { - stream.Error("Unrecognized field '%s'", name); + Log::Error("XML: Unrecognized field '%s'", name); field = NULL; } } diff --git a/src/ldb_reader.cpp b/src/ldb_reader.cpp index 406e4243..cfc20c38 100644 --- a/src/ldb_reader.cpp +++ b/src/ldb_reader.cpp @@ -14,6 +14,7 @@ #include "lcf/ldb/reader.h" #include "lcf/ldb/chunks.h" #include "lcf/reader_util.h" +#include "log.h" #include "reader_struct.h" namespace lcf { @@ -25,7 +26,7 @@ void LDB_Reader::PrepareSave(rpg::Database& db) { std::unique_ptr LDB_Reader::Load(StringView filename, StringView encoding) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LDB file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LDB file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LDB_Reader::Load(stream, encoding); @@ -34,7 +35,7 @@ std::unique_ptr LDB_Reader::Load(StringView filename, String bool LDB_Reader::Save(StringView filename, const lcf::rpg::Database& db, StringView encoding, SaveOpt opt) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LDB file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LDB file '%s' for writing: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LDB_Reader::Save(stream, db, encoding, opt); @@ -43,7 +44,7 @@ bool LDB_Reader::Save(StringView filename, const lcf::rpg::Database& db, StringV bool LDB_Reader::SaveXml(StringView filename, const lcf::rpg::Database& db) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LDB XML file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LDB XML file '%s' for writing: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LDB_Reader::SaveXml(stream, db); @@ -52,7 +53,7 @@ bool LDB_Reader::SaveXml(StringView filename, const lcf::rpg::Database& db) { std::unique_ptr LDB_Reader::LoadXml(StringView filename) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LDB XML file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LDB XML file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LDB_Reader::LoadXml(stream); @@ -61,17 +62,17 @@ std::unique_ptr LDB_Reader::LoadXml(StringView filename) { std::unique_ptr LDB_Reader::Load(std::istream& filestream, StringView encoding) { LcfReader reader(filestream, ToString(encoding)); if (!reader.IsOk()) { - LcfReader::SetError("Couldn't parse database file.\n"); + LcfReader::SetError("Couldn't parse database file."); return nullptr; } std::string header; reader.ReadString(header, reader.ReadInt()); if (header.length() != 11) { - LcfReader::SetError("This is not a valid RPG2000 database.\n"); + LcfReader::SetError("This is not a valid RPG2000 database."); return nullptr; } if (header != "LcfDataBase") { - fprintf(stderr, "Warning: This header is not LcfDataBase and might not be a valid RPG2000 database.\n"); + Log::Warning("Header %s != LcfDataBase and might not be a valid RPG2000 database.", header.c_str()); } auto db = std::make_unique(); db->ldb_header = header; @@ -91,7 +92,7 @@ bool LDB_Reader::Save(std::ostream& filestream, const lcf::rpg::Database& db, St const auto engine = GetEngineVersion(db); LcfWriter writer(filestream, engine, ToString(encoding)); if (!writer.IsOk()) { - LcfReader::SetError("Couldn't parse database file.\n"); + LcfReader::SetError("Couldn't parse database file."); return false; } std::string header; diff --git a/src/lmt_reader.cpp b/src/lmt_reader.cpp index 30620109..c603b31c 100644 --- a/src/lmt_reader.cpp +++ b/src/lmt_reader.cpp @@ -14,6 +14,7 @@ #include "lcf/lmt/reader.h" #include "lcf/lmt/chunks.h" #include "lcf/reader_util.h" +#include "log.h" #include "reader_struct.h" namespace lcf { @@ -21,7 +22,7 @@ namespace lcf { std::unique_ptr LMT_Reader::Load(StringView filename, StringView encoding) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMT file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMT file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LMT_Reader::Load(stream, encoding); @@ -30,7 +31,7 @@ std::unique_ptr LMT_Reader::Load(StringView filename, StringV bool LMT_Reader::Save(StringView filename, const lcf::rpg::TreeMap& tmap, EngineVersion engine, StringView encoding, SaveOpt opt) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMT file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMT file '%s' for writing: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LMT_Reader::Save(stream, tmap, engine, encoding, opt); @@ -39,7 +40,7 @@ bool LMT_Reader::Save(StringView filename, const lcf::rpg::TreeMap& tmap, Engine bool LMT_Reader::SaveXml(StringView filename, const lcf::rpg::TreeMap& tmap, EngineVersion engine) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMT XML file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMT XML file '%s' for writing: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LMT_Reader::SaveXml(stream, tmap, engine); @@ -48,7 +49,7 @@ bool LMT_Reader::SaveXml(StringView filename, const lcf::rpg::TreeMap& tmap, Eng std::unique_ptr LMT_Reader::LoadXml(StringView filename) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMT XML file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMT XML file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LMT_Reader::LoadXml(stream); @@ -57,17 +58,17 @@ std::unique_ptr LMT_Reader::LoadXml(StringView filename) { std::unique_ptr LMT_Reader::Load(std::istream& filestream, StringView encoding) { LcfReader reader(filestream, ToString(encoding)); if (!reader.IsOk()) { - LcfReader::SetError("Couldn't parse map tree file.\n"); + LcfReader::SetError("Couldn't parse map tree file."); return nullptr; } std::string header; reader.ReadString(header, reader.ReadInt()); if (header.length() != 10) { - LcfReader::SetError("This is not a valid RPG2000 map tree.\n"); + LcfReader::SetError("This is not a valid RPG2000 map tree."); return nullptr; } if (header != "LcfMapTree") { - fprintf(stderr, "Warning: This header is not LcfMapTree and might not be a valid RPG2000 map tree.\n"); + Log::Warning("Header %s != LcfMapTree and might not be a valid RPG2000 map tree.", header.c_str()); } auto tmap = std::make_unique(); tmap->lmt_header = std::move(header); @@ -78,7 +79,7 @@ std::unique_ptr LMT_Reader::Load(std::istream& filestream, St bool LMT_Reader::Save(std::ostream& filestream, const lcf::rpg::TreeMap& tmap, EngineVersion engine, StringView encoding, SaveOpt opt) { LcfWriter writer(filestream, engine, ToString(encoding)); if (!writer.IsOk()) { - LcfReader::SetError("Couldn't parse map tree file.\n"); + LcfReader::SetError("Couldn't parse map tree file."); return false; } std::string header; @@ -96,7 +97,7 @@ bool LMT_Reader::Save(std::ostream& filestream, const lcf::rpg::TreeMap& tmap, E bool LMT_Reader::SaveXml(std::ostream& filestream, const lcf::rpg::TreeMap& tmap, EngineVersion engine) { XmlWriter writer(filestream, engine); if (!writer.IsOk()) { - LcfReader::SetError("Couldn't parse map tree file.\n"); + LcfReader::SetError("Couldn't parse map tree file."); return false; } writer.BeginElement("LMT"); @@ -108,7 +109,7 @@ bool LMT_Reader::SaveXml(std::ostream& filestream, const lcf::rpg::TreeMap& tmap std::unique_ptr LMT_Reader::LoadXml(std::istream& filestream) { XmlReader reader(filestream); if (!reader.IsOk()) { - LcfReader::SetError("Couldn't parse map tree file.\n"); + LcfReader::SetError("Couldn't parse map tree file."); return nullptr; } auto tmap = std::make_unique(); diff --git a/src/lmt_rect.cpp b/src/lmt_rect.cpp index 53608697..12ec5c5f 100644 --- a/src/lmt_rect.cpp +++ b/src/lmt_rect.cpp @@ -70,7 +70,7 @@ class RectXmlHandler : public XmlHandler { else if (strcmp(name, "b") == 0) field = &ref.b; else { - stream.Error("Unrecognized field '%s'", name); + Log::Error("XML: Unrecognized field '%s'", name); field = NULL; } } diff --git a/src/lmt_treemap.cpp b/src/lmt_treemap.cpp index bba15e8b..9f26360b 100644 --- a/src/lmt_treemap.cpp +++ b/src/lmt_treemap.cpp @@ -9,6 +9,7 @@ #include "lcf/lmt/reader.h" #include "lcf/lmt/chunks.h" +#include "log.h" #include "reader_struct.h" namespace lcf { @@ -89,7 +90,7 @@ class TreeMapXmlHandler : public XmlHandler { else if (strcmp(name, "start") == 0) Struct::BeginXml(ref.start, stream); else { - stream.Error("Unrecognized field '%s'", name); + Log::Error("XML: Unrecognized field '%s'", name); } } void EndElement(XmlReader& /* stream */, const char* /* name */) { diff --git a/src/lmu_movecommand.cpp b/src/lmu_movecommand.cpp index 0d8a1b5b..3d532d84 100644 --- a/src/lmu_movecommand.cpp +++ b/src/lmu_movecommand.cpp @@ -158,7 +158,7 @@ class MoveCommandXmlHandler : public XmlHandler { else if (strcmp(name, "parameter_string") == 0) parameter_string = true; else { - stream.Error("Unrecognized field '%s'", name); + Log::Error("XML: Unrecognized field '%s'", name); field = NULL; parameter_string = false; } @@ -218,7 +218,7 @@ class MoveCommandVectorXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** /* atts */) { if (strcmp(name, "MoveCommand") != 0) - stream.Error("Expecting %s but got %s", "MoveCommand", name); + Log::Error("XML: Expecting %s but got %s", "MoveCommand", name); ref.resize(ref.size() + 1); rpg::MoveCommand& obj = ref.back(); stream.SetHandler(new MoveCommandXmlHandler(obj)); diff --git a/src/lmu_reader.cpp b/src/lmu_reader.cpp index d3a2e9f0..c1b635a5 100644 --- a/src/lmu_reader.cpp +++ b/src/lmu_reader.cpp @@ -16,6 +16,7 @@ #include "lcf/lmu/chunks.h" #include "lcf/reader_lcf.h" #include "lcf/reader_util.h" +#include "log.h" #include "reader_struct.h" namespace lcf { @@ -27,7 +28,7 @@ void LMU_Reader::PrepareSave(rpg::Map& map) { std::unique_ptr LMU_Reader::Load(StringView filename, StringView encoding) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMU file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMU file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LMU_Reader::Load(stream, encoding); @@ -36,7 +37,7 @@ std::unique_ptr LMU_Reader::Load(StringView filename, StringView encod bool LMU_Reader::Save(StringView filename, const rpg::Map& save, EngineVersion engine, StringView encoding, SaveOpt opt) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMU file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMU file '%s' for writing: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LMU_Reader::Save(stream, save, engine, encoding, opt); @@ -45,7 +46,7 @@ bool LMU_Reader::Save(StringView filename, const rpg::Map& save, EngineVersion e bool LMU_Reader::SaveXml(StringView filename, const rpg::Map& save, EngineVersion engine) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMU XML file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMU XML file '%s' for writing: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LMU_Reader::SaveXml(stream, save, engine); @@ -54,7 +55,7 @@ bool LMU_Reader::SaveXml(StringView filename, const rpg::Map& save, EngineVersio std::unique_ptr LMU_Reader::LoadXml(StringView filename) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LMU XML file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LMU XML file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LMU_Reader::LoadXml(stream); @@ -63,17 +64,17 @@ std::unique_ptr LMU_Reader::LoadXml(StringView filename) { std::unique_ptr LMU_Reader::Load(std::istream& filestream, StringView encoding) { LcfReader reader(filestream, ToString(encoding)); if (!reader.IsOk()) { - LcfReader::SetError("Couldn't parse map file.\n"); - return std::unique_ptr(); + LcfReader::SetError("Couldn't parse map file."); + return {}; } std::string header; reader.ReadString(header, reader.ReadInt()); if (header.length() != 10) { - LcfReader::SetError("This is not a valid RPG2000 map.\n"); - return std::unique_ptr(); + LcfReader::SetError("This is not a valid RPG2000 map."); + return {}; } if (header != "LcfMapUnit") { - fprintf(stderr, "Warning: This header is not LcfMapUnit and might not be a valid RPG2000 map.\n"); + Log::Warning("Header %s != LcfMapUnit and might not be a valid RPG2000 map.", header.c_str()); } auto map = std::make_unique(); @@ -85,7 +86,7 @@ std::unique_ptr LMU_Reader::Load(std::istream& filestream, StringView bool LMU_Reader::Save(std::ostream& filestream, const rpg::Map& map, EngineVersion engine, StringView encoding, SaveOpt opt) { LcfWriter writer(filestream, engine, ToString(encoding)); if (!writer.IsOk()) { - LcfReader::SetError("Couldn't parse map file.\n"); + LcfReader::SetError("Couldn't parse map file."); return false; } std::string header; @@ -104,7 +105,7 @@ bool LMU_Reader::Save(std::ostream& filestream, const rpg::Map& map, EngineVersi bool LMU_Reader::SaveXml(std::ostream& filestream, const rpg::Map& map, EngineVersion engine) { XmlWriter writer(filestream, engine); if (!writer.IsOk()) { - LcfReader::SetError("Couldn't parse map file.\n"); + LcfReader::SetError("Couldn't parse map file."); return false; } writer.BeginElement("LMU"); @@ -116,8 +117,8 @@ bool LMU_Reader::SaveXml(std::ostream& filestream, const rpg::Map& map, EngineVe std::unique_ptr LMU_Reader::LoadXml(std::istream& filestream) { XmlReader reader(filestream); if (!reader.IsOk()) { - LcfReader::SetError("Couldn't parse map file.\n"); - return std::unique_ptr(); + LcfReader::SetError("Couldn't parse map file."); + return {}; } auto map = std::make_unique(); diff --git a/src/log.h b/src/log.h index d2915102..874fb7ee 100644 --- a/src/log.h +++ b/src/log.h @@ -17,6 +17,7 @@ namespace Log { void Debug(const char* fmt, ...); void Warning(const char* fmt, ...); +void Error(const char* fmt, ...); } // namespace Log } // namespace lcf diff --git a/src/log_handler.cpp b/src/log_handler.cpp index 36855cee..33272c8c 100644 --- a/src/log_handler.cpp +++ b/src/log_handler.cpp @@ -57,7 +57,7 @@ namespace { char buf[4096]; int const result = vsnprintf(buf, sizeof(buf), fmt, args); if (result < 0) { - return std::string(); + return {}; } return std::string(buf, static_cast(result) < sizeof(buf) ? result : sizeof(buf)); @@ -84,5 +84,15 @@ void Warning(const char* fmt, ...) { } } +void Error(const char* fmt, ...) { + if (static_cast(LogHandler::Level::Error) >= static_cast(LogHandler::level)) { + va_list args; + va_start(args, fmt); + auto msg = format_string(fmt, args); + LogHandler::output_fn(LogHandler::Level::Error, msg); + va_end(args); + } +} + } // namespace Log } // namespace lcf diff --git a/src/lsd_reader.cpp b/src/lsd_reader.cpp index 60af4cad..3b1896cc 100644 --- a/src/lsd_reader.cpp +++ b/src/lsd_reader.cpp @@ -44,7 +44,7 @@ void LSD_Reader::PrepareSave(rpg::Save& save, int32_t version, int32_t codepage) std::unique_ptr LSD_Reader::Load(StringView filename, StringView encoding) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - lcf::Log::Warning("Failed to open LSD file '%s' for reading : %s", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LSD file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LSD_Reader::Load(stream, encoding); @@ -53,7 +53,7 @@ std::unique_ptr LSD_Reader::Load(StringView filename, StringView enco bool LSD_Reader::Save(StringView filename, const rpg::Save& save, EngineVersion engine, StringView encoding) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LSD file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LSD file '%s' for reading: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LSD_Reader::Save(stream, save, engine, encoding); @@ -62,7 +62,7 @@ bool LSD_Reader::Save(StringView filename, const rpg::Save& save, EngineVersion bool LSD_Reader::SaveXml(StringView filename, const rpg::Save& save, EngineVersion engine) { std::ofstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LSD XML file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LSD XML file '%s' for writing: %s", ToString(filename).c_str(), strerror(errno)); return false; } return LSD_Reader::SaveXml(stream, save, engine); @@ -71,7 +71,7 @@ bool LSD_Reader::SaveXml(StringView filename, const rpg::Save& save, EngineVersi std::unique_ptr LSD_Reader::LoadXml(StringView filename) { std::ifstream stream(ToString(filename), std::ios::binary); if (!stream.is_open()) { - fprintf(stderr, "Failed to open LSD XML file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno)); + Log::Error("Failed to open LSD XML file `%s' for reading : %s", ToString(filename).c_str(), strerror(errno)); return nullptr; } return LSD_Reader::LoadXml(stream); @@ -81,17 +81,17 @@ std::unique_ptr LSD_Reader::Load(std::istream& filestream, StringView LcfReader reader(filestream, ToString(encoding)); if (!reader.IsOk()) { - LcfReader::SetError("Couldn't parse save file.\n"); - return std::unique_ptr(); + LcfReader::SetError("Couldn't parse save file."); + return {}; } std::string header; reader.ReadString(header, reader.ReadInt()); if (header.length() != 11) { - LcfReader::SetError("This is not a valid RPG2000 save.\n"); - return std::unique_ptr(); + LcfReader::SetError("This is not a valid RPG2000 save."); + return {}; } if (header != "LcfSaveData") { - fprintf(stderr, "Warning: This header is not LcfSaveData and might not be a valid RPG2000 save.\n"); + Log::Warning("Header %s != LcfSaveData and might not be a valid RPG2000 save.", header.c_str()); } auto pos = reader.Tell(); @@ -104,8 +104,8 @@ std::unique_ptr LSD_Reader::Load(std::istream& filestream, StringView filestream.seekg(pos, std::ios_base::beg); LcfReader reader2(filestream, std::to_string(save->easyrpg_data.codepage)); if (!reader2.IsOk()) { - LcfReader::SetError("Couldn't parse save file.\n"); - return std::unique_ptr(); + LcfReader::SetError("Couldn't parse save file."); + return {}; } save.reset(new rpg::Save()); Struct::ReadLcf(*save, reader2); @@ -139,7 +139,7 @@ bool LSD_Reader::Save(std::ostream& filestream, const rpg::Save& save, EngineVer bool LSD_Reader::SaveXml(std::ostream& filestream, const rpg::Save& save, EngineVersion engine) { XmlWriter writer(filestream, engine); if (!writer.IsOk()) { - LcfReader::SetError("Couldn't parse save file.\n"); + LcfReader::SetError("Couldn't parse save file."); return false; } @@ -152,8 +152,8 @@ bool LSD_Reader::SaveXml(std::ostream& filestream, const rpg::Save& save, Engine std::unique_ptr LSD_Reader::LoadXml(std::istream& filestream) { XmlReader reader(filestream); if (!reader.IsOk()) { - LcfReader::SetError("Couldn't parse save file.\n"); - return std::unique_ptr(); + LcfReader::SetError("Couldn't parse save file."); + return {}; } rpg::Save* save = new rpg::Save(); diff --git a/src/reader_flags.cpp b/src/reader_flags.cpp index e6439254..ace0190b 100644 --- a/src/reader_flags.cpp +++ b/src/reader_flags.cpp @@ -46,7 +46,7 @@ void Flags::ReadLcf(S& obj, LcfReader& stream, uint32_t length) { for (size_t i = 0; i < obj.flags.size(); ++i) { x |= (obj.flags[i] << i); } - printf("0x%lx\n", x); + fprintf(stderr, "0x%lx\n", x); #endif } @@ -118,7 +118,7 @@ class FlagsXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** /* atts */) { const auto idx = Flags::idx(name); if (idx < 0) { - stream.Error("Unrecognized field '%s'", name); + Log::Error("XML: Unrecognized field '%s'", name); field = NULL; return; } diff --git a/src/reader_lcf.cpp b/src/reader_lcf.cpp index 6ab9ea52..55587f02 100644 --- a/src/reader_lcf.cpp +++ b/src/reader_lcf.cpp @@ -9,8 +9,10 @@ #include #include +#include #include #include +#include #include "lcf/reader_lcf.h" #include "log.h" @@ -49,7 +51,7 @@ void LcfReader::Read(void *ptr, size_t size, size_t nmemb) { Read0(ptr, size, nmemb); #else if (Read0(ptr, size, nmemb) != nmemb) { - fprintf(stderr, "Read error at %" PRIu32 ". The file is probably corrupted\n", Tell()); + Log::Warning("Read error at %" PRIu32 ". The file is probably corrupted", Tell()); } #endif } @@ -94,7 +96,7 @@ int LcfReader::ReadInt() { value |= temp & 0x7F; if (loops > 5) { - fprintf(stderr, "Invalid compressed integer at %" PRIu32 "\n", Tell()); + Log::Warning("Invalid compressed integer at %" PRIu32 "", Tell()); } ++loops; } while (temp & 0x80); @@ -115,7 +117,7 @@ uint64_t LcfReader::ReadUInt64() { value |= static_cast(temp & 0x7F); if (loops > 9) { - fprintf(stderr, "Invalid compressed integer at %" PRIu32 "\n", Tell()); + Log::Warning("Invalid compressed integer at %" PRIu32 "", Tell()); } ++loops; } while (temp & 0x80); @@ -277,21 +279,27 @@ int LcfReader::Peek() { } void LcfReader::Skip(const struct LcfReader::Chunk& chunk_info, const char* where) { - Log::Debug("Skipped Chunk %02X (%" PRIu32 " byte) in lcf at %" PRIX32 " (%s)\n", + Log::Debug("Skipped Chunk %02X (%" PRIu32 " byte) in lcf at %" PRIX32 " (%s)", chunk_info.ID, chunk_info.length, Tell(), where); + std::stringstream ss; + ss << std::hex; + for (uint32_t i = 0; i < chunk_info.length; ++i) { uint8_t byte; LcfReader::Read(byte); - fprintf(stderr, "%02X ", byte); + ss << std::setfill('0') << std::setw(2) << (int)byte << " "; if ((i+1) % 16 == 0) { - fprintf(stderr, "\n"); + Log::Debug(ss.str().c_str()); + ss.str(""); } if (Eof()) { break; } } - fprintf(stderr, "\n"); + if (!ss.str().empty()) { + Log::Debug(ss.str().c_str()); + } } void LcfReader::SetError(const char* fmt, ...) { @@ -302,7 +310,7 @@ void LcfReader::SetError(const char* fmt, ...) { vsprintf(str, fmt, args); error_str = str; - //Output::ErrorStr((std::string)str); + Log::Error("%s", error_str.c_str()); va_end(args); } diff --git a/src/reader_struct.h b/src/reader_struct.h index de928321..960282fe 100644 --- a/src/reader_struct.h +++ b/src/reader_struct.h @@ -38,6 +38,7 @@ #include "lcf/rpg/terms.h" #include "lcf/rpg/saveeasyrpgtext.h" #include "lcf/rpg/saveeasyrpgwindow.h" +#include "log.h" namespace lcf { @@ -184,7 +185,7 @@ struct Primitive { // FIXME: Bug #174 if (length != LcfSizeT::value) { dif = length - LcfSizeT::value; - fprintf(stderr, "Reading Primitive of incorrect size %" PRIu32 " (expected %" PRIu32 ") at %" PRIX32 "\n", + Log::Warning("Reading Primitive of incorrect size %" PRIu32 " (expected %" PRIu32 ") at %" PRIX32 "", length, LcfSizeT::value, stream.Tell()); } @@ -195,9 +196,7 @@ struct Primitive { if (dif != 0) { // Fix incorrect read pointer position -#ifdef LCF_DEBUG_TRACE - printf("Invalid %s at %X\n", typeid(T).name(), stream.Tell()); -#endif + Log::Warning("Invalid %s at %X", typeid(T).name(), stream.Tell()); stream.Seek(dif, LcfReader::FromCurrent); } } @@ -237,11 +236,11 @@ struct Primitive> { stream.Read(ref, length); #ifdef LCF_DEBUG_TRACE typename std::vector::iterator it; - printf(" "); + fprintf(stderr, " "); for (it = ref.begin(); it != ref.end(); ++it) { - printf("%d, ", static_cast(*it)); + fprintf(stderr, "%d, ", static_cast(*it)); } - printf("\n"); + fprintf(stderr, "\n"); #endif } static void WriteLcf(const std::vector& ref, LcfWriter& stream) { @@ -267,13 +266,11 @@ struct Primitive { if (length >= 1 && length <= 5) { ref = stream.ReadInt(); #ifdef LCF_DEBUG_TRACE - printf(" %d\n", ref); + fprintf(stderr, " %d\n", ref); #endif } else { ref = 0; -#ifdef LCF_DEBUG_TRACE - printf("Invalid integer at %X\n", stream.Tell()); -#endif + Log::Warning("Invalid integer at %X", stream.Tell()); stream.Seek(length, LcfReader::FromCurrent); } @@ -300,7 +297,7 @@ struct Primitive { static void ReadLcf(std::string& ref, LcfReader& stream, uint32_t length) { stream.ReadString(ref, length); #ifdef LCF_DEBUG_TRACE - printf(" %s\n", ref.c_str()); + fprintf(stderr, " %s\n", ref.c_str()); #endif } static void WriteLcf(const std::string& ref, LcfWriter& stream) { @@ -325,11 +322,11 @@ struct Primitive { static void ReadLcf(DBBitArray& ref, LcfReader& stream, uint32_t length) { stream.ReadBits(ref, length); #ifdef LCF_DEBUG_TRACE - printf(" "); + fprintf(stderr, " "); for (auto& b: ref) { - print("%d", static_cast(b)); + fprintf(stderr, "%d", static_cast(b)); } - printf("\n"); + fprintf(stderr, "\n"); #endif } static void WriteLcf(const DBBitArray& ref, LcfWriter& stream) { @@ -773,7 +770,7 @@ class WrapperXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** /* atts */) { if (strcmp(name, this->name) != 0) - stream.Error("Expecting %s but got %s", this->name, name); + Log::Error("XML: Expecting %s but got %s", this->name, name); stream.SetHandler(handler); } @@ -793,7 +790,7 @@ class RootXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** /* atts */) { if (strcmp(name, this->name) != 0) - stream.Error("Expecting %s but got %s", this->name, name); + Log::Error("XML: Expecting %s but got %s", this->name, name); TypeReader::BeginXml(ref, stream); } diff --git a/src/reader_struct_impl.h b/src/reader_struct_impl.h index 0ee118e4..87cd1317 100644 --- a/src/reader_struct_impl.h +++ b/src/reader_struct_impl.h @@ -17,6 +17,7 @@ #include "lcf/lsd/reader.h" #include "reader_struct.h" #include "lcf/rpg/save.h" +#include "log.h" namespace lcf { @@ -72,13 +73,13 @@ void Struct::ReadLcf(S& obj, LcfReader& stream) { auto it = field_map.find(chunk_info.ID); if (it != field_map.end()) { #ifdef LCF_DEBUG_TRACE - printf("0x%02x (size: %" PRIu32 ", pos: 0x%" PRIx32 "): %s\n", chunk_info.ID, chunk_info.length, stream.Tell(), it->second->name); + fprintf(stderr, "0x%02x (size: %" PRIu32 ", pos: 0x%" PRIx32 "): %s\n", chunk_info.ID, chunk_info.length, stream.Tell(), it->second->name); #endif const uint32_t off = stream.Tell(); it->second->ReadLcf(obj, stream, chunk_info.length); const uint32_t bytes_read = stream.Tell() - off; if (bytes_read != chunk_info.length) { - fprintf(stderr, "%s: Corrupted Chunk 0x%02" PRIx32 " (size: %" PRIu32 ", pos: 0x%" PRIx32 "): %s : Read %" PRIu32 " bytes! Reseting...\n", + Log::Warning("%s: Corrupted Chunk 0x%02" PRIx32 " (size: %" PRIu32 ", pos: 0x%" PRIx32 "): %s : Read %" PRIu32 " bytes!", Struct::name, chunk_info.ID, chunk_info.length, off, it->second->name, bytes_read); stream.Seek(off + chunk_info.length); } @@ -198,7 +199,7 @@ class StructFieldXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** atts) { if (strcmp(name, Struct::name) != 0) - stream.Error("Expecting %s but got %s", Struct::name, name); + Log::Error("XML: Expecting %s but got %s", Struct::name, name); Struct::IDReader::ReadIDXml(ref, atts); stream.SetHandler(new StructXmlHandler(ref)); } @@ -259,7 +260,7 @@ class StructVectorXmlHandler : public XmlHandler { void StartElement(XmlReader& stream, const char* name, const char** atts) { if (strcmp(name, Struct::name) != 0) - stream.Error("Expecting %s but got %s", Struct::name, name); + Log::Error("XML: Expecting %s but got %s", Struct::name, name); ref.resize(ref.size() + 1); S& obj = ref.back(); Struct::IDReader::ReadIDXml(obj, atts); diff --git a/src/reader_util.cpp b/src/reader_util.cpp index 7b9887c6..1bea1ebf 100644 --- a/src/reader_util.cpp +++ b/src/reader_util.cpp @@ -34,6 +34,7 @@ #include "lcf/inireader.h" #include "lcf/ldb/reader.h" #include "lcf/reader_util.h" +#include "log.h" namespace lcf { @@ -275,7 +276,7 @@ std::string ReaderUtil::Normalize(StringView str) { if (U_FAILURE(err)) { static bool err_reported = false; if (!err_reported) { - fprintf(stderr, "Normalizer2::getNFKCInstance failed (%s). \"nrm\" is probably missing in the ICU data file. Unicode normalization will not work!\n", u_errorName(err)); + Log::Warning("Normalizer2::getNFKCInstance failed (%s). \"nrm\" is probably missing in the ICU data file. Unicode normalization will not work!", u_errorName(err)); err_reported = true; } uni.toUTF8String(res); diff --git a/src/reader_xml.cpp b/src/reader_xml.cpp index f44983ca..6eb2a2ad 100644 --- a/src/reader_xml.cpp +++ b/src/reader_xml.cpp @@ -12,6 +12,7 @@ #include "lcf/reader_lcf.h" #include "lcf/reader_xml.h" #include "lcf/dbstring.h" +#include "log.h" // Expat callbacks #if LCF_SUPPORT_XML @@ -59,14 +60,6 @@ bool XmlReader::IsOk() const { return (stream.good() && parser != NULL); } -void XmlReader::Error(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - fputc('\n', stderr); - va_end(ap); -} - void XmlReader::Parse() { #if LCF_SUPPORT_XML static const int bufsize = 4096; @@ -75,7 +68,7 @@ void XmlReader::Parse() { int len = stream.read(reinterpret_cast(buffer),bufsize).gcount(); int result = XML_ParseBuffer(parser, len, len <= 0); if (result == 0) - Error("%s", XML_ErrorString(XML_GetErrorCode(parser))); + Log::Error("XML: %s", XML_ErrorString(XML_GetErrorCode(parser))); } #endif } From 5f071388b91673f202fa2146bc266347b560acb1 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Tue, 5 Dec 2023 22:42:05 +0100 Subject: [PATCH 3/5] Add a UserData param to the LogHander function Like most other APIs do it --- src/lcf/log_handler.h | 14 ++++++++++++-- src/log_handler.cpp | 15 +++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/lcf/log_handler.h b/src/lcf/log_handler.h index c6eb69ac..26a2165d 100644 --- a/src/lcf/log_handler.h +++ b/src/lcf/log_handler.h @@ -11,6 +11,7 @@ #define LCF_OUTPUT_H #include "string_view.h" +#include "enum_tags.h" namespace lcf { namespace LogHandler { @@ -22,15 +23,24 @@ enum class Level { Highest }; -using LogHandlerFn = void (*)(Level level, StringView message); +static constexpr auto kLevelTags = lcf::makeEnumTags( + "Debug", + "Warning", + "Error", + "Highest" +); + +using UserData = void*; +using LogHandlerFn = void (*)(Level level, StringView message, UserData userdata); /** * Sets the output handler for all lcf logging. * The default handler prints to standard error. * * @param fn New output handler. nullptr for default handler. + * @param userdata Passed to the log handler function. Is not touched by liblcf. */ -void SetHandler(LogHandlerFn fn); +void SetHandler(LogHandlerFn fn, UserData userdata = nullptr); /** * Only report issues that have at least this log level. diff --git a/src/log_handler.cpp b/src/log_handler.cpp index 33272c8c..623aad8d 100644 --- a/src/log_handler.cpp +++ b/src/log_handler.cpp @@ -16,7 +16,7 @@ namespace lcf { namespace LogHandler { namespace { - void DefaultHandler(lcf::LogHandler::Level level, StringView message) { + void DefaultHandler(LogHandler::Level level, StringView message, UserData) { switch (level) { case Level::Debug: std::cerr << "Debug: "; @@ -35,13 +35,16 @@ namespace { Level level = Level::Debug; LogHandlerFn output_fn = DefaultHandler; + UserData output_userdata = nullptr; } -void SetHandler(LogHandlerFn fn) { +void SetHandler(LogHandlerFn fn, UserData userdata) { if (!fn) { output_fn = DefaultHandler; + output_userdata = nullptr; } else { output_fn = fn; + output_userdata = userdata; } } @@ -60,7 +63,7 @@ namespace { return {}; } - return std::string(buf, static_cast(result) < sizeof(buf) ? result : sizeof(buf)); + return {buf, static_cast(result) < sizeof(buf) ? result : sizeof(buf)}; } } @@ -69,7 +72,7 @@ void Debug(const char* fmt, ...) { va_list args; va_start(args, fmt); auto msg = format_string(fmt, args); - LogHandler::output_fn(LogHandler::Level::Debug, msg); + LogHandler::output_fn(LogHandler::Level::Debug, msg, LogHandler::output_userdata); va_end(args); } } @@ -79,7 +82,7 @@ void Warning(const char* fmt, ...) { va_list args; va_start(args, fmt); auto msg = format_string(fmt, args); - LogHandler::output_fn(LogHandler::Level::Warning, msg); + LogHandler::output_fn(LogHandler::Level::Warning, msg, LogHandler::output_userdata); va_end(args); } } @@ -89,7 +92,7 @@ void Error(const char* fmt, ...) { va_list args; va_start(args, fmt); auto msg = format_string(fmt, args); - LogHandler::output_fn(LogHandler::Level::Error, msg); + LogHandler::output_fn(LogHandler::Level::Error, msg, LogHandler::output_userdata); va_end(args); } } From 032ccdc3b793ecd28158c2128e0ee119ecc867dc Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 25 Mar 2024 23:23:23 +0100 Subject: [PATCH 4/5] Log: Fix copyright header --- src/lcf/log_handler.h | 2 +- src/log.h | 2 +- src/log_handler.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lcf/log_handler.h b/src/lcf/log_handler.h index 26a2165d..e39c28b6 100644 --- a/src/lcf/log_handler.h +++ b/src/lcf/log_handler.h @@ -1,5 +1,5 @@ /* - * This file is part of liblcf. Copyright (c) 2020 liblcf authors. + * This file is part of liblcf. Copyright (c) liblcf authors. * https://github.com/EasyRPG/liblcf - https://easyrpg.org * * liblcf is Free/Libre Open Source Software, released under the MIT License. diff --git a/src/log.h b/src/log.h index 874fb7ee..f489ca34 100644 --- a/src/log.h +++ b/src/log.h @@ -1,5 +1,5 @@ /* - * This file is part of liblcf. Copyright (c) 2020 liblcf authors. + * This file is part of liblcf. Copyright (c) liblcf authors. * https://github.com/EasyRPG/liblcf - https://easyrpg.org * * liblcf is Free/Libre Open Source Software, released under the MIT License. diff --git a/src/log_handler.cpp b/src/log_handler.cpp index 623aad8d..c7468028 100644 --- a/src/log_handler.cpp +++ b/src/log_handler.cpp @@ -1,5 +1,5 @@ /* - * This file is part of liblcf. Copyright (c) 2020 liblcf authors. + * This file is part of liblcf. Copyright (c) liblcf authors. * https://github.com/EasyRPG/liblcf - https://easyrpg.org * * liblcf is Free/Libre Open Source Software, released under the MIT License. From 701dcc8b045ebaa58a8b358278c16e3ab54197ed Mon Sep 17 00:00:00 2001 From: Ghabry Date: Tue, 26 Mar 2024 02:07:22 +0100 Subject: [PATCH 5/5] Log: Add __attribute__ to indicate that functions are like printf --- src/dbstring_struct.cpp | 2 +- src/log.h | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/dbstring_struct.cpp b/src/dbstring_struct.cpp index 401df477..fbe5b8d3 100644 --- a/src/dbstring_struct.cpp +++ b/src/dbstring_struct.cpp @@ -107,7 +107,7 @@ void RawStruct>::ReadLcf(std::vector& ref, LcfRe } if (stream.Tell() != endpos) { - Log::Warning("vector Misaligned at 0x" PRIx32 "", stream.Tell()); + Log::Warning("vector Misaligned at 0x%" PRIx32 "", stream.Tell()); stream.Seek(endpos); } } diff --git a/src/log.h b/src/log.h index f489ca34..098e384f 100644 --- a/src/log.h +++ b/src/log.h @@ -12,14 +12,22 @@ #include "lcf/log_handler.h" +#ifdef __GNUG__ + #define LIKE_PRINTF __attribute__((format(printf, 1, 2))) +#else + #define LIKE_PRINTF +#endif + namespace lcf { namespace Log { -void Debug(const char* fmt, ...); -void Warning(const char* fmt, ...); -void Error(const char* fmt, ...); +void Debug(const char* fmt, ...) LIKE_PRINTF; +void Warning(const char* fmt, ...) LIKE_PRINTF; +void Error(const char* fmt, ...) LIKE_PRINTF; } // namespace Log } // namespace lcf +#undef LIKE_PRINTF + #endif