/* * Copyright (c) 2020 liblcf authors * This file is released under the MIT License * http://opensource.org/licenses/MIT */ #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #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 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::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 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 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 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 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; }