Migrate lcf2xml to liblcf
MODIFY
CMakeLists.txt
CMakeLists.txt
@@ -7,6 +7,7 @@ option(BUILD_SHARED_LIBS "Build shared library, disable for building the static
option(DISABLE_ICU "Disable ICU encoding detection (fallback to iconv, not recommended)" OFF)
option(DISABLE_XML "Disable XML reading support (expat)" OFF)
option(DISABLE_UPDATE_MIMEDB "Do not run update-mime-database after install" OFF)
+option(DISABLE_TOOLS "Do not build and install the tools" OFF)
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Override CMAKE_DEBUG_POSTFIX.")
@@ -374,3 +375,18 @@ foreach(i ${BENCH_FILES})
add_executable(bench_${name} ${i})
target_link_libraries(bench_${name} lcf)
endforeach()
+
+# tools
+if(NOT DISABLE_TOOLS)
+ add_custom_target(tools)
+ add_dependencies(tools lcf)
+ if(NOT DISABLE_XML)
+ list(APPEND TOOLS lcf2xml)
+ endif()
+ foreach(tool ${TOOLS})
+ add_executable(${tool} tools/${tool}.cpp)
+ target_link_libraries(${tool} lcf)
+ add_dependencies(tools ${tool})
+ install(TARGETS ${tool} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+ endforeach()
+endif()

MODIFY
Makefile.am
Makefile.am
@@ -259,6 +259,20 @@ test_runner_LDFLAGS = -no-install
check-local:
$(AM_V_at)./test_runner
+lcf2xml_SOURCES = tools/lcf2xml.cpp
+lcf2xml_CPPFLAGS = $(liblcf_la_CPPFLAGS)
+lcf2xml_CXXFLAGS = $(liblcf_la_CXXFLAGS)
+lcf2xml_LDADD = liblcf.la
+
+tools =
+if ENABLE_TOOLS
+if SUPPORT_XML
+tools += lcf2xml
+endif
+endif
+bin_PROGRAMS = $(tools)
+tools: $(tools)
+
install-data-hook:
if ENABLE_UPDATE_MIMEDB
if CROSS_COMPILING

MODIFY
README.md
README.md
@@ -85,6 +85,14 @@ CMake is the only supported way to build liblcf for Windows. All dependencies
must be installed with [vcpkg].
+## Tools
+
+### LCF2XML
+
+LCF2XML is a small tool to convert RPG Maker 2000 and 2003 data format
+(LMU, LMT, LDB, LSD, ...) to XML and vice-versa.
+
+
## Bug reporting
Available options:

MODIFY
configure.ac
configure.ac
@@ -32,6 +32,11 @@ AC_ARG_ENABLE([xml],[AS_HELP_STRING([--disable-xml],[Disable XML reading support
AS_IF([test "x$enable_xml" != "xno"],[
AX_PKG_CHECK_MODULES([EXPAT],[],[expat >= 2.1],[AC_SUBST([LCF_SUPPORT_XML],[1])])
])
+AM_CONDITIONAL(SUPPORT_XML,[test $LCF_SUPPORT_XML == 1])
+
+# Tools
+AC_ARG_ENABLE([tools],[AS_HELP_STRING([--disable-tools],[Do not build and install the tools [default=no]])])
+AM_CONDITIONAL(ENABLE_TOOLS,[test "x$enable_tools" != "xno"])
# Add mime types
AC_ARG_ENABLE([update-mimedb],[AS_HELP_STRING([--disable-update-mimedb],[Do not run update-mime-database after install [default=no]])])

ADD
tools/lcf2xml.cpp
+/*
+ * Copyright (c) 2020 liblcf authors
+ * This file is released under the MIT License
+ * http://opensource.org/licenses/MIT
+ */
+
+#include <algorithm>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <lcf/ldb/reader.h>
+#include <lcf/lmt/reader.h>
+#include <lcf/lmu/reader.h>
+#include <lcf/lsd/reader.h>
+#include <lcf/reader_util.h>
+
+#ifndef _WIN32
+#include <dirent.h>
+#endif
+
+#ifdef __MINGW64_VERSION_MAJOR
+int _dowildcard = -1; /* enable wildcard expansion for mingw-w64 */
+#endif
+
+enum FileCategories
+{
+ FileCategory_LCF,
+ FileCategory_XML,
+ FileCategory_Invalid
+};
+
+enum FileTypes
+{
+ FileType_LCF_MapUnit,
+ FileType_LCF_SaveData,
+ FileType_LCF_Database,
+ FileType_LCF_MapTree,
+ FileType_XML_MapUnit,
+ FileType_XML_SaveData,
+ FileType_XML_Database,
+ FileType_XML_MapTree,
+ FileType_Invalid
+};
+
+std::string GetFilename(const std::string& str);
+FileCategories GetFilecategory(const std::string& in_file);
+FileTypes GetFiletype(const std::string& in_file, std::string& out_extension);
+void PrintReaderError(const std::string data);
+int ReaderWriteToFile(const std::string& in, const std::string& out, FileTypes in_type);
+
+int main(int argc, char** argv)
+{
+ if (argc <= 1)
+ {
+ std::cerr << "LCF2XML - Converts RPG Maker 2000/2003 Files into XML and vice versa" << std::endl;
+ std::cerr << "Usage: " << argv[0] << " file1 [... fileN]" << std::endl;
+
+ return 1;
+ }
+
+ std::vector<std::string> infiles;
+ std::string outfile;
+
+ FileCategories category = FileCategory_Invalid;
+ FileTypes type;
+ std::string extension;
+ unsigned int errors = 0;
+
+ for (int i = 1; i < argc; ++i) {
+ if (category == FileCategory_Invalid) {
+ category = GetFilecategory(argv[i]);
+ if (category == FileCategory_Invalid) {
+ std::cerr << "Invalid file " << argv[i] << "." << std::endl;
+ errors++;
+ continue;
+ }
+ }
+
+ if (category == GetFilecategory(argv[i])) {
+ infiles.push_back(argv[i]);
+ }
+ }
+
+ for (std::vector<std::string>::iterator it = infiles.begin(); it != infiles.end(); ++it) {
+ outfile = GetFilename(*it);
+ type = GetFiletype(*it, extension);
+ outfile += extension;
+ if (ReaderWriteToFile(*it, outfile, type) != 0) {
+ errors++;
+ }
+ }
+
+ if (errors > 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Returns the filename (without extension). */
+std::string GetFilename(const std::string& str)
+{
+ std::string s = str;
+#ifdef _WIN32
+ std::replace(s.begin(), s.end(), '\\', '/');
+#endif
+ // Extension
+ size_t found = s.find_last_of(".");
+ if (found != std::string::npos)
+ {
+ s = s.substr(0, found);
+ }
+
+ // Filename
+ found = s.find_last_of("/");
+ if (found == std::string::npos)
+ {
+ return s;
+ }
+
+ s = s.substr(found + 1);
+ return s;
+}
+
+/** Returns that path (everything left to the last /) */
+std::string GetPath(const std::string& str) {
+ std::string s = str;
+#ifdef _WIN32
+ std::replace(s.begin(), s.end(), '\\', '/');
+#endif
+
+ // Path
+ size_t found = s.find_last_of("/");
+ if (found == std::string::npos)
+ {
+ return ".";
+ }
+
+ s = s.substr(0, found);
+ return s;
+}
+
+FileCategories GetFilecategory(const std::string& in_file)
+{
+ std::ifstream in(in_file.c_str());
+
+ char buf[4];
+ memset(buf, '\0', 4);
+
+ in.seekg(1, std::ios::beg);
+ in.read(buf, 3);
+ std::string input(buf);
+
+ if (input == "Lcf") {
+ return FileCategory_LCF;
+ } else if (input == "?xm") {
+ return FileCategory_XML;
+ }
+
+ return FileCategory_Invalid;
+}
+
+/** Uses heuristics to detect the file type. */
+FileTypes GetFiletype(const std::string& in_file, std::string& out_extension)
+{
+ std::ifstream in(in_file.c_str());
+
+ char buf[128];
+ memset(buf, '\0', 128);
+
+ in.seekg(1, std::ios::beg);
+ in.read(buf, 10);
+ std::string input(buf);
+
+ out_extension = ".xml";
+ if (input == "LcfDataBas") {
+ out_extension = ".edb";
+ return FileType_LCF_Database;
+ } else if (input == "LcfMapTree") {
+ out_extension = ".emt";
+ return FileType_LCF_MapTree;
+ } else if (input == "LcfSaveDat") {
+ out_extension = ".esd";
+ return FileType_LCF_SaveData;
+ } else if (input == "LcfMapUnit") {
+ out_extension = ".emu";
+ return FileType_LCF_MapUnit;
+ } else if (input == "?xml versi") {
+ in.read(buf, 128);
+ std::string in(buf);
+
+ size_t pos = in.find('<');
+ if (pos != std::string::npos)
+ {
+ in = in.substr(pos + 1, 3);
+ if (in == "LDB") {
+ out_extension = ".ldb";
+ return FileType_XML_Database;
+ } else if (in == "LMT") {
+ out_extension = ".lmt";
+ return FileType_XML_MapTree;
+ } else if (in == "LSD") {
+ out_extension = ".lsd";
+ return FileType_XML_SaveData;
+ } else if (in == "LMU") {
+ out_extension = ".lmu";
+ return FileType_XML_MapUnit;
+ }
+ }
+ }
+
+ out_extension = "";
+ return FileType_Invalid;
+}
+
+/** Utility func for errors. */
+void PrintReaderError(const std::string data)
+{
+ std::cerr << data << " error: " << lcf::LcfReader::GetError() << std::endl;
+}
+
+#define LCFXML_ERROR(cond, text) \
+ if (cond) {\
+ PrintReaderError(text);\
+ return 2;\
+ }
+
+/** Takes data from in and writes converted data into out using liblcf. */
+int ReaderWriteToFile(const std::string& in, const std::string& out, FileTypes in_type)
+{
+ std::string path = GetPath(in) + "/";
+ std::string encoding = "";
+
+#ifdef _WIN32
+ encoding = lcf::ReaderUtil::GetEncoding(path + "RPG_RT.ini");
+#else
+ DIR* dir = opendir(path.c_str());
+ if (dir) {
+ struct dirent* ent;
+ while ((ent = ::readdir(dir)) != NULL) {
+ if (ent->d_name[0] == '.') { continue; }
+ std::string name = ent->d_name;
+
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+
+ if (name == "rpg_rt.ini") {
+ encoding = lcf::ReaderUtil::GetEncoding(path + ent->d_name);
+ closedir(dir);
+ goto dirsuccess;
+ break;
+ }
+ }
+ closedir(dir);
+ }
+ else {
+ std::cerr << "Failed opening directory " << path << std::endl;
+ }
+
+ dirsuccess:
+#endif
+
+ switch (in_type)
+ {
+ case FileType_LCF_MapUnit:
+ {
+ std::unique_ptr<lcf::rpg::Map> file = lcf::LMU_Reader::Load(in, encoding);
+ LCFXML_ERROR(file.get() == NULL, "LMU load");
+ LCFXML_ERROR(!lcf::LMU_Reader::SaveXml(out, *file), "LMU XML save");
+ break;
+ }
+ case FileType_LCF_SaveData:
+ {
+ std::unique_ptr<lcf::rpg::Save> file = lcf::LSD_Reader::Load(in, encoding);
+ LCFXML_ERROR(file.get() == NULL, "LSD load");
+ LCFXML_ERROR(!lcf::LSD_Reader::SaveXml(out, *file), "LSD XML save");
+ break;
+ }
+ case FileType_LCF_Database:
+ {
+ LCFXML_ERROR(!lcf::LDB_Reader::Load(in, encoding), "LDB load");
+ LCFXML_ERROR(!lcf::LDB_Reader::SaveXml(out), "LDB XML save");
+ break;
+ }
+ case FileType_LCF_MapTree:
+ {
+ LCFXML_ERROR(!lcf::LMT_Reader::Load(in, encoding), "LMT load");
+ LCFXML_ERROR(!lcf::LMT_Reader::SaveXml(out), "LMT XML save");
+ break;
+ }
+ case FileType_XML_MapUnit:
+ {
+ std::unique_ptr<lcf::rpg::Map> file = lcf::LMU_Reader::LoadXml(in);
+ LCFXML_ERROR(file.get() == NULL, "LMU XML load");
+ LCFXML_ERROR(!lcf::LMU_Reader::Save(out, *file, encoding), "LMU save");
+ break;
+ }
+ case FileType_XML_SaveData:
+ {
+ std::unique_ptr<lcf::rpg::Save> file = lcf::LSD_Reader::LoadXml(in);
+ LCFXML_ERROR(file.get() == NULL, "LSD XML load");
+ LCFXML_ERROR(!lcf::LSD_Reader::Save(out, *file, encoding), "LSD save");
+ break;
+ }
+ case FileType_XML_Database:
+ {
+ LCFXML_ERROR(!lcf::LDB_Reader::LoadXml(in), "LDB XML load");
+ LCFXML_ERROR(!lcf::LDB_Reader::Save(out, encoding), "LDB save");
+ break;
+ }
+ case FileType_XML_MapTree:
+ {
+ LCFXML_ERROR(!lcf::LMT_Reader::LoadXml(in), "LMT XML load");
+ LCFXML_ERROR(!lcf::LMT_Reader::Save(out, encoding), "LMT save");
+ break;
+ }
+ case FileType_Invalid:
+ {
+ std::cerr << in << " unsupported" << std::endl;
+ return 2;
+ }
+ }
+
+ return 0;
+}

Migrate lcf2xml to liblcf