liblcf
Loading...
Searching...
No Matches
reader_struct_impl.h
Go to the documentation of this file.
1/*
2 * This file is part of liblcf. Copyright (c) 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 <cstring>
11#include <iostream>
12#include <iomanip>
13#include <type_traits>
14#include "lcf/ldb/reader.h"
15#include "lcf/lmt/reader.h"
16#include "lcf/lmu/reader.h"
17#include "lcf/lsd/reader.h"
18#include "reader_struct.h"
19#include "lcf/rpg/save.h"
20#include "log.h"
21
22namespace lcf {
23
24// Read/Write Struct
25
26template <class S>
28 if (!field_map.empty())
29 return;
30 for (int i = 0; fields[i] != NULL; i++)
31 field_map[fields[i]->id] = fields[i];
32}
33
34template <class S>
36 if (!tag_map.empty())
37 return;
38 for (int i = 0; fields[i] != NULL; i++)
39 tag_map[fields[i]->name] = fields[i];
40}
41
42template <typename T>
44 static T make(bool) {
45 return T();
46 }
47};
48
49template <>
50struct StructDefault<rpg::Actor> {
51 static rpg::Actor make(bool is2k3) {
52 auto actor = rpg::Actor();
53 actor.Setup(is2k3);
54 return actor;
55 }
56};
57
58
59
60template <class S>
61void Struct<S>::ReadLcf(S& obj, LcfReader& stream) {
63
64 LcfReader::Chunk chunk_info;
65
66 while (!stream.Eof()) {
67 chunk_info.ID = stream.ReadInt();
68 if (chunk_info.ID == 0)
69 break;
70
71 chunk_info.length = stream.ReadInt();
72
73 auto it = field_map.find(chunk_info.ID);
74 if (it != field_map.end()) {
75#ifdef LCF_DEBUG_TRACE
76 fprintf(stderr, "0x%02x (size: %" PRIu32 ", pos: 0x%" PRIx32 "): %s\n", chunk_info.ID, chunk_info.length, stream.Tell(), it->second->name);
77#endif
78 const uint32_t off = stream.Tell();
79 it->second->ReadLcf(obj, stream, chunk_info.length);
80 const uint32_t bytes_read = stream.Tell() - off;
81 if (bytes_read != chunk_info.length) {
82 Log::Warning("%s: Corrupted Chunk 0x%02" PRIx32 " (size: %" PRIu32 ", pos: 0x%" PRIx32 "): %s : Read %" PRIu32 " bytes!",
83 Struct<S>::name, chunk_info.ID, chunk_info.length, off, it->second->name, bytes_read);
84 stream.Seek(off + chunk_info.length);
85 }
86 }
87 else {
88 stream.Skip(chunk_info, Struct<S>::name);
89 }
90 }
91}
92
93template<typename T>
94typename std::enable_if<std::is_same<T, rpg::Save>::value ||
95 std::is_same<T, rpg::Database>::value>::type
97 // no-op
98}
99
100template<typename T>
101typename std::enable_if<!std::is_same<T, rpg::Save>::value &&
102 !std::is_same<T, rpg::Database>::value>::type
103conditional_zero_writer(LcfWriter& stream) {
104 stream.WriteInt(0);
105}
106
107template <class S>
108void Struct<S>::WriteLcf(const S& obj, LcfWriter& stream) {
109 const bool db_is2k3 = stream.Is2k3();
110
111 auto ref = StructDefault<S>::make(db_is2k3);
112 int last = -1;
113 for (int i = 0; fields[i] != NULL; i++) {
114 const Field<S>* field = fields[i];
115 if (!db_is2k3 && field->is2k3) {
116 continue;
117 }
118 if (field->id < last)
119 std::cerr << "field order mismatch: " << field->id
120 << " after " << last
121 << " in struct " << name
122 << std::endl;
123 if (!field->isPresentIfDefault(db_is2k3) && field->IsDefault(obj, ref, db_is2k3)) {
124 continue;
125 }
126 stream.WriteInt(field->id);
127 auto len = field->LcfSize(obj, stream);
128 stream.WriteInt(len);
129 if (len > 0) {
130 field->WriteLcf(obj, stream);
131 }
132 }
133 // Writing a 0-byte after rpg::Database or rpg::Save breaks the parser in RPG_RT
135}
136
137template <class S>
138int Struct<S>::LcfSize(const S& obj, LcfWriter& stream) {
139 const bool db_is2k3 = stream.Is2k3();
140 int result = 0;
141 auto ref = StructDefault<S>::make(db_is2k3);
142 for (int i = 0; fields[i] != NULL; i++) {
143 const Field<S>* field = fields[i];
144 if (!db_is2k3 && field->is2k3) {
145 continue;
146 }
147 //printf("%s\n", field->name);
148 if (!field->isPresentIfDefault(db_is2k3) && field->IsDefault(obj, ref, db_is2k3)) {
149 continue;
150 }
151 result += LcfReader::IntSize(field->id);
152 int size = field->LcfSize(obj, stream);
153 result += LcfReader::IntSize(size);
154 result += size;
155 }
156 result += LcfReader::IntSize(0);
157 return result;
158}
159
160template <class S>
161void Struct<S>::WriteXml(const S& obj, XmlWriter& stream) {
162 IDReader::WriteXmlTag(obj, name, stream);
163 for (int i = 0; fields[i] != NULL; i++) {
164 const Field<S>* field = fields[i];
165 field->WriteXml(obj, stream);
166 }
167 stream.EndElement(name);
168}
169
170template <class S>
171class StructXmlHandler : public XmlHandler {
172public:
176
177 void StartElement(XmlReader& stream, const char* name, const char** /* atts */) {
179 field->BeginXml(ref, stream);
180 }
181
182 void EndElement(XmlReader& /* stream */, const char* /* name */) {
183 field = NULL;
184 }
185
186 void CharacterData(XmlReader& /* stream */, const std::string& data) {
187 if (field != NULL)
188 field->ParseXml(ref, data);
189 }
190private:
191 S& ref;
193};
194
195template <class S>
196class StructFieldXmlHandler : public XmlHandler {
197public:
199
200 void StartElement(XmlReader& stream, const char* name, const char** atts) {
201 if (strcmp(name, Struct<S>::name) != 0)
202 Log::Error("XML: Expecting %s but got %s", Struct<S>::name, name);
204 stream.SetHandler(new StructXmlHandler<S>(ref));
205 }
206private:
207 S& ref;
208};
209
210template <class S>
211void Struct<S>::BeginXml(S& obj, XmlReader& stream) {
212 stream.SetHandler(new StructFieldXmlHandler<S>(obj));
213}
214
215// Read/Write std::vector<Struct>
216
217template <class S>
218void Struct<S>::ReadLcf(std::vector<S>& vec, LcfReader& stream) {
219 int count = stream.ReadInt();
220 vec.resize(count);
221 for (int i = 0; i < count; i++) {
222 IDReader::ReadID(vec[i], stream);
223 TypeReader<S>::ReadLcf(vec[i], stream, 0);
224 }
225}
226
227template <class S>
228void Struct<S>::WriteLcf(const std::vector<S>& vec, LcfWriter& stream) {
229 int count = vec.size();
230 stream.WriteInt(count);
231 for (int i = 0; i < count; i++) {
232 IDReader::WriteID(vec[i], stream);
233 TypeReader<S>::WriteLcf(vec[i], stream);
234 }
235}
236
237template <class S>
238int Struct<S>::LcfSize(const std::vector<S>& vec, LcfWriter& stream) {
239 int result = 0;
240 int count = vec.size();
241 result += LcfReader::IntSize(count);
242 for (int i = 0; i < count; i++) {
243 result += IDReader::IDSize(vec[i]);
244 result += TypeReader<S>::LcfSize(vec[i], stream);
245 }
246 return result;
247}
248
249template <class S>
250void Struct<S>::WriteXml(const std::vector<S>& vec, XmlWriter& stream) {
251 int count = vec.size();
252 for (int i = 0; i < count; i++)
253 TypeReader<S>::WriteXml(vec[i], stream);
254}
255
256template <class S>
257class StructVectorXmlHandler : public XmlHandler {
258public:
259 StructVectorXmlHandler(std::vector<S>& ref) : ref(ref) {}
260
261 void StartElement(XmlReader& stream, const char* name, const char** atts) {
262 if (strcmp(name, Struct<S>::name) != 0)
263 Log::Error("XML: Expecting %s but got %s", Struct<S>::name, name);
264 ref.resize(ref.size() + 1);
265 S& obj = ref.back();
267 stream.SetHandler(new StructXmlHandler<S>(obj));
268 }
269private:
270 std::vector<S>& ref;
271};
272
273template <class S>
274void Struct<S>::BeginXml(std::vector<S>& obj, XmlReader& stream) {
275 stream.SetHandler(new StructVectorXmlHandler<S>(obj));
276}
277
278} //namespace lcf
279
280#include "fwd_struct_impl.h"
void StartElement(XmlReader &stream, const char *name, const char **atts)
static void BeginXml(S &obj, XmlReader &stream)
static int LcfSize(const S &obj, LcfWriter &stream)
static void MakeFieldMap()
static const Field< S > * fields[]
static tag_map_type tag_map
static void WriteXml(const S &obj, XmlWriter &stream)
static field_map_type field_map
static void MakeTagMap()
static void WriteLcf(const S &obj, LcfWriter &stream)
friend class StructFieldXmlHandler
static void ReadLcf(S &obj, LcfReader &stream)
static const char *const name
friend class StructVectorXmlHandler
void StartElement(XmlReader &stream, const char *name, const char **atts)
StructVectorXmlHandler(std::vector< S > &ref)
void EndElement(XmlReader &, const char *)
const Field< S > * field
void StartElement(XmlReader &stream, const char *name, const char **)
void CharacterData(XmlReader &, const std::string &data)
void Warning(const char *fmt,...) LIKE_PRINTF
void Error(const char *fmt,...) LIKE_PRINTF
const char *const Struct< rpg::Actor >::name
std::enable_if< std::is_same< T, rpg::Save >::value||std::is_same< T, rpg::Database >::value >::type conditional_zero_writer(LcfWriter &)
bool isPresentIfDefault(bool db_is2k3) const
virtual void WriteLcf(const S &obj, LcfWriter &stream) const =0
virtual bool IsDefault(const S &obj, const S &ref, bool is2k3) const =0
virtual void WriteXml(const S &obj, XmlWriter &stream) const =0
virtual int LcfSize(const S &obj, LcfWriter &stream) const =0
static rpg::Actor make(bool is2k3)