1 /*
2  * This file is part of liblcf. Copyright (c) 2021 liblcf authors.
3  * https://github.com/EasyRPG/liblcf - https://easyrpg.org
4  *
5  * liblcf is Free/Libre Open Source Software, released under the MIT License.
6  * For the full copyright and license information, please view the COPYING
7  * file that was distributed with this source code.
8  */
9 
10 #include <fstream>
11 #include <cerrno>
12 #include <cstring>
13 
14 #include "lcf/ldb/reader.h"
15 #include "lcf/ldb/chunks.h"
16 #include "lcf/reader_util.h"
17 #include "reader_struct.h"
18 
19 namespace lcf {
20 
PrepareSave(rpg::Database & db)21 void LDB_Reader::PrepareSave(rpg::Database& db) {
22 	++db.system.save_count;
23 }
24 
Load(StringView filename,StringView encoding)25 std::unique_ptr<lcf::rpg::Database> LDB_Reader::Load(StringView filename, StringView encoding) {
26 	std::ifstream stream(ToString(filename), std::ios::binary);
27 	if (!stream.is_open()) {
28 		fprintf(stderr, "Failed to open LDB file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno));
29 		return nullptr;
30 	}
31 	return LDB_Reader::Load(stream, encoding);
32 }
33 
Save(StringView filename,const lcf::rpg::Database & db,StringView encoding,SaveOpt opt)34 bool LDB_Reader::Save(StringView filename, const lcf::rpg::Database& db, StringView encoding, SaveOpt opt) {
35 	std::ofstream stream(ToString(filename), std::ios::binary);
36 	if (!stream.is_open()) {
37 		fprintf(stderr, "Failed to open LDB file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno));
38 		return false;
39 	}
40 	return LDB_Reader::Save(stream, db, encoding, opt);
41 }
42 
SaveXml(StringView filename,const lcf::rpg::Database & db)43 bool LDB_Reader::SaveXml(StringView filename, const lcf::rpg::Database& db) {
44 	std::ofstream stream(ToString(filename), std::ios::binary);
45 	if (!stream.is_open()) {
46 		fprintf(stderr, "Failed to open LDB XML file `%s' for writing : %s\n", ToString(filename).c_str(), strerror(errno));
47 		return false;
48 	}
49 	return LDB_Reader::SaveXml(stream, db);
50 }
51 
LoadXml(StringView filename)52 std::unique_ptr<lcf::rpg::Database> LDB_Reader::LoadXml(StringView filename) {
53 	std::ifstream stream(ToString(filename), std::ios::binary);
54 	if (!stream.is_open()) {
55 		fprintf(stderr, "Failed to open LDB XML file `%s' for reading : %s\n", ToString(filename).c_str(), strerror(errno));
56 		return nullptr;
57 	}
58 	return LDB_Reader::LoadXml(stream);
59 }
60 
Load(std::istream & filestream,StringView encoding)61 std::unique_ptr<lcf::rpg::Database> LDB_Reader::Load(std::istream& filestream, StringView encoding) {
62 	LcfReader reader(filestream, ToString(encoding));
63 	if (!reader.IsOk()) {
64 		LcfReader::SetError("Couldn't parse database file.\n");
65 		return nullptr;
66 	}
67 	std::string header;
68 	reader.ReadString(header, reader.ReadInt());
69 	if (header.length() != 11) {
70 		LcfReader::SetError("This is not a valid RPG2000 database.\n");
71 		return nullptr;
72 	}
73 	if (header != "LcfDataBase") {
74 		fprintf(stderr, "Warning: This header is not LcfDataBase and might not be a valid RPG2000 database.\n");
75 	}
76 	auto db = std::make_unique<lcf::rpg::Database>();
77 	db->ldb_header = header;
78 	TypeReader<rpg::Database>::ReadLcf(*db, reader, 0);
79 
80 	const auto engine = GetEngineVersion(*db);
81 	// Delayed initialization of some actor fields because they are engine
82 	// dependent
83 	for (auto& actor: db->actors) {
84 		actor.Setup(engine == EngineVersion::e2k3);
85 	}
86 
87 	return db;
88 }
89 
Save(std::ostream & filestream,const lcf::rpg::Database & db,StringView encoding,SaveOpt opt)90 bool LDB_Reader::Save(std::ostream& filestream, const lcf::rpg::Database& db, StringView encoding, SaveOpt opt) {
91 	const auto engine = GetEngineVersion(db);
92 	LcfWriter writer(filestream, engine, ToString(encoding));
93 	if (!writer.IsOk()) {
94 		LcfReader::SetError("Couldn't parse database file.\n");
95 		return false;
96 	}
97 	std::string header;
98 	if ( db.ldb_header.empty() || !bool(opt & SaveOpt::ePreserveHeader)) {
99 		header = "LcfDataBase";
100 	} else {
101 		header= db.ldb_header;
102 	}
103 	writer.WriteInt(header.size());
104 	writer.Write(header);
105 	TypeReader<rpg::Database>::WriteLcf(db, writer);
106 	return true;
107 }
108 
SaveXml(std::ostream & filestream,const lcf::rpg::Database & db)109 bool LDB_Reader::SaveXml(std::ostream& filestream, const lcf::rpg::Database& db) {
110 	const auto engine = GetEngineVersion(db);
111 	XmlWriter writer(filestream, engine);
112 	if (!writer.IsOk()) {
113 		LcfReader::SetError("Couldn't parse database file.\n");
114 		return false;
115 	}
116 	writer.BeginElement("LDB");
117 	TypeReader<rpg::Database>::WriteXml(db, writer);
118 	writer.EndElement("LDB");
119 	return true;
120 }
121 
LoadXml(std::istream & filestream)122 std::unique_ptr<lcf::rpg::Database> LDB_Reader::LoadXml(std::istream& filestream) {
123 	XmlReader reader(filestream);
124 	if (!reader.IsOk()) {
125 		LcfReader::SetError("Couldn't parse database file.\n");
126 		return nullptr;;
127 	}
128 	auto db = std::make_unique<lcf::rpg::Database>();
129 	reader.SetHandler(new RootXmlHandler<rpg::Database>(*db, "LDB"));
130 	reader.Parse();
131 
132 	const auto engine = GetEngineVersion(*db);
133 	// Delayed initialization of some actor fields because they are engine
134 	// dependent
135 	for (auto& actor: db->actors) {
136 		actor.Setup(engine == EngineVersion::e2k3);
137 	}
138 
139 	return db;
140 }
141 
142 } // namespace lcf
143