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