1 /**
2 ** data_utils.h: Generic data template functions.
3 **/
4
5 /*
6 Copyright (C) 2009 Exult Team
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23 #ifndef INCL_DATA_UTILS
24 #define INCL_DATA_UTILS 1
25
26 #include <algorithm>
27 #include <map>
28 #include <sstream>
29 #include <string>
30 #include <vector>
31 #include "exult_constants.h"
32 #include "utils.h"
33 #include "U7obj.h"
34 #include "databuf.h"
35 #include "msgfile.h"
36 #include "fnames.h"
37 #include "ignore_unused_variable_warning.h"
38
39 /*
40 * Generic vector data handler routines.
41 * They all assume that the template class has the following
42 * operators defined:
43 * (1) operator< (which must be a strict weak order)
44 * (2) operator==
45 * (3) operator!=
46 * (4) operator= that sets modified flag if needed.
47 * They also assume that the vector is totally ordered
48 * with the operator< -- using these functions will ensure
49 * that this is the case.
50 */
51 template <typename T>
add_vector_info(const T & inf,std::vector<T> & vec)52 void add_vector_info(const T &inf, std::vector<T> &vec) {
53 // Find using operator<.
54 auto it = std::lower_bound(vec.begin(), vec.end(), inf);
55 if (it == vec.end() || *it != inf) // Not found.
56 vec.insert(it, inf); // Add new.
57 else { // Already exists.
58 bool st = it->have_static();
59 bool inv = it->is_invalid();
60 it->set(inf); // Replace information.
61 if (st && inv && !inf.is_invalid() &&
62 !inf.from_patch() && !inf.was_modified())
63 it->set_modified(false);
64 }
65 }
66 template <typename T>
copy_vector_info(const std::vector<T> & from,std::vector<T> & to)67 void copy_vector_info(const std::vector<T> &from, std::vector<T> &to) {
68 if (from.size()) {
69 to.resize(from.size());
70 std::copy(from.begin(), from.end(), to.begin());
71 } else
72 to.clear();
73 }
74 template <typename T>
set_vector_info(bool tf,std::vector<T> & vec)75 std::vector<T> &set_vector_info(bool tf, std::vector<T> &vec) {
76 invalidate_vector(vec);
77 if (!tf)
78 clean_vector(vec);
79 return vec;
80 }
81 template <typename T>
invalidate_vector(std::vector<T> & vec)82 void invalidate_vector(std::vector<T> &vec) {
83 for (auto& elem : vec)
84 elem.set_invalid(true);
85 }
86
87 template <typename T>
clean_vector(std::vector<T> & vec)88 void clean_vector(std::vector<T> &vec) {
89 vec.erase(std::remove_if(vec.begin(), vec.end(),
90 [](T& elem) {
91 return elem.is_invalid() && !elem.have_static();
92 }
93 ), vec.end());
94 }
95
96 template <class T, typename U>
Search_vector_data_single_wildcard(const std::vector<T> & vec,int src,U T::* dat)97 static const T *Search_vector_data_single_wildcard(
98 const std::vector<T> &vec,
99 int src,
100 U T::*dat
101 ) {
102 if (vec.empty()) // Not found.
103 return nullptr;
104 T inf;
105 inf.*dat = src;
106 // Try finding exact match first.
107 auto it = std::lower_bound(vec.begin(), vec.end(), inf);
108 if (it == vec.end()) // Nowhere to be found.
109 return nullptr;
110 else if (*it == inf && !it->is_invalid()) // Have it already.
111 return &*it;
112 // Try wildcard shape.
113 inf.*dat = -1;
114 it = std::lower_bound(it, vec.end(), inf);
115 if (it == vec.end() || *it != inf // It just isn't there...
116 || it->is_invalid()) // ... or it is invalid.
117 return nullptr;
118 else // At last!
119 return &*it;
120 }
121
122 template <class T>
Search_vector_data_double_wildcards(const std::vector<T> & vec,int frame,int quality,short T::* fr,short T::* qual)123 static const T *Search_vector_data_double_wildcards(
124 const std::vector<T> &vec,
125 int frame, int quality,
126 short T::*fr, short T::*qual
127 ) {
128 if (vec.empty())
129 return nullptr; // No name.
130 T inf;
131 inf.*fr = frame;
132 inf.*qual = quality;
133 // Try finding exact match first.
134 auto it = std::lower_bound(vec.begin(), vec.end(), inf);
135 if (it == vec.end()) // Nowhere to be found.
136 return nullptr;
137 else if (*it == inf && !it->is_invalid()) // Have it already.
138 return &*it;
139 // We only have to search forward for a match.
140 if (quality != -1) {
141 if ((*it).*fr == frame) {
142 // Maybe quality is to blame. Try wildcard quality.
143 inf.*qual = -1;
144 it = std::lower_bound(it, vec.end(), inf);
145 if (it == vec.end()) // Nowhere to be found.
146 return nullptr;
147 else if (*it == inf && !it->is_invalid()) // We got it!
148 return &*it;
149 }
150 // Maybe frame is to blame? Try search for specific
151 // quality with wildcard frame.
152 inf.*qual = quality;
153 inf.*fr = -1;
154 it = std::lower_bound(it, vec.end(), inf);
155 if (it == vec.end()) // Nowhere to be found.
156 return nullptr;
157 else if (*it == inf && !it->is_invalid()) // We got it!
158 return &*it;
159 inf.*qual = -1;
160 }
161 // *Still* haven't found it. Last try: wildcard frame *and* quality.
162 inf.*fr = -1;
163 it = std::lower_bound(it, vec.end(), inf);
164 if (it == vec.end() || *it != inf || *it != inf // It just isn't there...
165 || it->is_invalid()) // ... or it is invalid.
166 return nullptr;
167 else // At last!
168 return &*it;
169 }
170
171 /*
172 * Generic data handler routine.
173 */
174 template <typename T>
set_info(bool tf,T * & pt)175 T *set_info(bool tf, T *&pt) {
176 if (!tf) {
177 delete pt;
178 pt = nullptr;
179 } else if (!pt)
180 pt = new T();
181 return pt;
182 }
183
184 /*
185 * Get # entries of binary data file (with Exult extension).
186 */
Read_count(std::istream & in)187 inline int Read_count(std::istream &in) {
188 int cnt = Read1(in); // How the originals did it.
189 if (cnt == 255)
190 cnt = Read2(in); // Exult extension.
191 return cnt;
192 }
193
194 /*
195 * Write # entries of binary data file (with Exult extension).
196 */
Write_count(std::ofstream & out,int cnt)197 inline void Write_count(
198 std::ofstream &out,
199 int cnt
200 ) {
201 if (cnt >= 255) {
202 // Exult extension.
203 out.put(static_cast<char>(255));
204 Write2(out, cnt);
205 } else
206 out.put(static_cast<char>(cnt));
207 }
208
209 /*
210 * Generic base data-agnostic reader class.
211 */
212 class Base_reader {
213 protected:
214 bool haveversion;
read_data(std::istream & in,size_t index,int version,bool patch,Exult_Game game,bool binary)215 virtual void read_data(std::istream &in, size_t index, int version,
216 bool patch, Exult_Game game, bool binary) {
217 ignore_unused_variable_warning(in, index, version, patch, game, binary);
218 }
219 // Binary data file.
read_binary_internal(std::istream & in,bool patch,Exult_Game game)220 void read_binary_internal(std::istream &in, bool patch, Exult_Game game) {
221 int vers = 0;
222 if (haveversion)
223 vers = Read1(in);
224 size_t cnt = Read_count(in);
225 for (size_t j = 0; j < cnt; j++)
226 read_data(in, j, vers, patch, game, true);
227 }
228 public:
Base_reader(bool h)229 Base_reader(bool h)
230 : haveversion(h)
231 { }
232 virtual ~Base_reader() = default;
233 // Text data file.
parse(std::vector<std::string> & strings,int version,bool patch,Exult_Game game)234 void parse(std::vector<std::string> &strings, int version, bool patch, Exult_Game game) {
235 for (size_t j = 0; j < strings.size(); j++) {
236 if (!strings[j].empty()) {
237 std::istringstream strin(strings[j], std::ios::in);
238 read_data(strin, j, version, patch, game, false);
239 }
240 }
241 strings.clear();
242 }
243 // Binary data file.
read(const char * fname,bool patch,Exult_Game game)244 void read(const char *fname, bool patch, Exult_Game game) {
245 if (!U7exists(fname))
246 return;
247 std::ifstream fin;
248 U7open(fin, fname);
249 read_binary_internal(fin, patch, game);
250 fin.close();
251 }
252 // Binary resource file.
read(Exult_Game game,int resource)253 void read(Exult_Game game, int resource) {
254 // Only for BG and SI.
255 if (game != BLACK_GATE && game != SERPENT_ISLE)
256 return;
257 /* ++++ Not because of ES.
258 snprintf(buf, 50, "config/%s", fname);
259 str_int_pair resource = game->get_resource(buf);
260 U7object txtobj(resource.str, resource.num);
261 */
262 bool bg = game == BLACK_GATE;
263 const char *flexfile =
264 bg ? BUNDLE_CHECK(BUNDLE_EXULT_BG_FLX, EXULT_BG_FLX)
265 : BUNDLE_CHECK(BUNDLE_EXULT_SI_FLX, EXULT_SI_FLX);
266 U7object txtobj(flexfile, resource);
267 std::size_t len;
268 auto txt = txtobj.retrieve(len);
269 std::string databuf(reinterpret_cast<char*>(txt.get()), len);
270 std::istringstream strin(databuf, std::ios::in | std::ios::binary);
271 read_binary_internal(strin, false, game);
272 }
273 };
274
275 class ID_reader_functor {
276 public:
operator()277 int operator()(std::istream &in, int index, int version, bool binary) {
278 ignore_unused_variable_warning(index, version);
279 return binary ? Read2(in) : ReadInt(in);
280 }
281 };
282
283 /*
284 * Post-read data transformation functors.
285 */
286 template <class Info>
287 class Null_functor {
288 public:
operator()289 void operator()(std::istream &in, int version, bool patch,
290 Exult_Game game, Info &info) {
291 ignore_unused_variable_warning(in, info, version, patch, game);
292 }
293 };
294
295 template <int flag, class Info>
296 class Patch_flags_functor {
297 public:
operator()298 void operator()(std::istream &in, int version, bool patch,
299 Exult_Game game, Info &info) {
300 ignore_unused_variable_warning(in, version, game);
301 if (patch)
302 info.frompatch_flags |= flag;
303 }
304 };
305
306 /*
307 * Generic functor-based reader class for maps.
308 */
309 template < class Info, class Functor, class Transform = Null_functor<Info>,
310 class ReadID = ID_reader_functor >
311 class Functor_multidata_reader : public Base_reader {
312 protected:
313 std::map<int, Info> &info;
314 Functor reader;
315 Transform postread;
316 ReadID idread;
read_data(std::istream & in,size_t index,int version,bool patch,Exult_Game game,bool binary)317 void read_data(std::istream &in, size_t index, int version,
318 bool patch, Exult_Game game, bool binary) override {
319 int id = idread(in, index, version, binary);
320 if (id >= 0) {
321 Info &inf = info[id];
322 reader(in, version, patch, game, inf);
323 postread(in, version, patch, game, inf);
324 }
325 }
326 public:
327 Functor_multidata_reader(std::map<int, Info> &nfo, bool h = false)
Base_reader(h)328 : Base_reader(h), info(nfo)
329 { }
330 };
331
332 /*
333 * Generic functor-based reader class.
334 */
335 template <class Info, class Functor, class Transform = Null_functor<Info> >
336 class Functor_data_reader : public Base_reader {
337 protected:
338 Info &info;
339 Functor reader;
340 Transform postread;
read_data(std::istream & in,size_t index,int version,bool patch,Exult_Game game,bool binary)341 void read_data(std::istream &in, size_t index, int version,
342 bool patch, Exult_Game game, bool binary) override {
343 ignore_unused_variable_warning(index, binary);
344 reader(in, version, patch, game, info);
345 postread(in, version, patch, game, info);
346 }
347 public:
348 Functor_data_reader(Info &nfo, bool h = false)
Base_reader(h)349 : Base_reader(h), info(nfo)
350 { }
351 };
352
353 /*
354 * Data reader functors.
355 */
356 template <typename T, class Info, T Info::*data>
357 class Text_reader_functor {
358 public:
operator()359 bool operator()(std::istream &in, int version, bool patch,
360 Exult_Game game, Info &info) {
361 ignore_unused_variable_warning(version, patch, game);
362 info.*data = ReadInt(in);
363 return true;
364 }
365 };
366
367 template <typename T, class Info, T Info::*data1, T Info::*data2>
368 class Text_pair_reader_functor {
369 public:
operator()370 bool operator()(std::istream &in, int version, bool patch,
371 Exult_Game game, Info &info) {
372 ignore_unused_variable_warning(version, patch, game);
373 info.*data1 = ReadInt(in, -1);
374 info.*data2 = ReadInt(in, -1);
375 return true;
376 }
377 };
378
379 template <typename T, class Info, T Info::*data, int bit>
380 class Bit_text_reader_functor {
381 public:
operator()382 bool operator()(std::istream &in, int version, bool patch,
383 Exult_Game game, Info &info) {
384 ignore_unused_variable_warning(version, patch, game);
385 // For backwards compatibility.
386 bool biton = ReadInt(in, 1) != 0;
387 if (biton)
388 info.*data |= (static_cast<T>(1) << bit);
389 else
390 info.*data &= ~(static_cast<T>(1) << bit);
391 return true;
392 }
393 };
394
395 template <typename T, class Info, T Info::*data>
396 class Bit_field_text_reader_functor {
397 public:
operator()398 bool operator()(std::istream &in, int version, bool patch,
399 Exult_Game game, Info &info) {
400 ignore_unused_variable_warning(version, patch, game);
401 int size = 8 * sizeof(T); // Bit count.
402 int bit = 0;
403 T flags = 0;
404 while (in.good() && bit < size) {
405 if (ReadInt(in) != 0)
406 flags |= (static_cast<T>(1) << bit);
407 else
408 flags &= ~(static_cast<T>(1) << bit);
409 bit++;
410 }
411 info.*data = flags;
412 return true;
413 }
414 };
415
416 template <typename T, class Info, T Info::*data, unsigned pad>
417 class Binary_reader_functor {
418 public:
operator()419 bool operator()(std::istream &in, int version, bool patch,
420 Exult_Game game, Info &info) {
421 ignore_unused_variable_warning(version, patch, game);
422 in.read(reinterpret_cast<char *>(&(info.*data)), sizeof(T));
423 if (pad) // Skip some bytes.
424 in.ignore(pad);
425 return true;
426 }
427 };
428
429 template < typename T1, typename T2, class Info,
430 T1 Info::*data1, T2 Info::*data2, unsigned pad >
431 class Binary_pair_reader_functor {
432 public:
operator()433 bool operator()(std::istream &in, int version, bool patch,
434 Exult_Game game, Info &info) {
435 ignore_unused_variable_warning(version, patch, game);
436 in.read(reinterpret_cast<char *>(&(info.*data1)), sizeof(T1));
437 in.read(reinterpret_cast<char *>(&(info.*data2)), sizeof(T2));
438 if (pad) // Skip some bytes.
439 in.ignore(pad);
440 return true;
441 }
442 };
443
444 template <typename T, class Info, T *Info::*data>
445 class Class_reader_functor {
446 public:
operator()447 bool operator()(std::istream &in, int version, bool patch,
448 Exult_Game game, Info &info) {
449 T *cls = new T();
450 cls->set_patch(patch); // Set patch flag.
451 if (!cls->read(in, version, game)) {
452 delete cls;
453 return false;
454 }
455 T *&pt = info.*data;
456 if (cls->is_invalid() && pt) {
457 // 'Delete old' flag.
458 delete pt;
459 pt = nullptr;
460 delete cls;
461 return false;
462 }
463 if (!patch) // This is a static data file.
464 info.have_static_flags |= T::get_info_flag();
465 // Delete old.
466 delete pt;
467 pt = cls;
468 return true;
469 }
470 };
471
472 template <typename T, class Info, std::vector<T> Info::*data>
473 class Vector_reader_functor {
474 public:
operator()475 bool operator()(std::istream &in, int version, bool patch,
476 Exult_Game game, Info &info) {
477 T cls;
478 if (!patch)
479 cls.set_static(true);
480 cls.set_patch(patch);
481 cls.read(in, version, game);
482 std::vector<T> &vec = info.*data;
483 add_vector_info(cls, vec);
484 return true;
485 }
486 };
487
488 /*
489 * Reads text data file and parses it according to passed
490 * parser functions.
491 */
492 template <class Parser>
Read_text_data_file(const char * fname,Parser * parsers[],const char * sections[],int numsections,bool editing,Exult_Game game,int resource)493 static void Read_text_data_file(
494 const char *fname, // Name of file to read, sans extension
495 Parser *parsers[], // What to use to parse data.
496 const char *sections[], // The names of the sections
497 int numsections, // Number of sections
498 bool editing,
499 Exult_Game game,
500 int resource
501 ) {
502 int static_version = 1;
503 int patch_version = 1;
504 std::vector<std::vector<std::string> > static_strings;
505 std::vector<std::vector<std::string> > patch_strings;
506 char buf[50];
507 if (game == BLACK_GATE || game == SERPENT_ISLE) {
508 /* ++++ Not because of ES.
509 snprintf(buf, 50, "config/%s", fname);
510 str_int_pair resource = game->get_resource(buf);
511 U7object txtobj(resource.str, resource.num);
512 */
513 bool bg = game == BLACK_GATE;
514 const char *flexfile =
515 bg ? BUNDLE_CHECK(BUNDLE_EXULT_BG_FLX, EXULT_BG_FLX)
516 : BUNDLE_CHECK(BUNDLE_EXULT_SI_FLX, EXULT_SI_FLX);
517 IExultDataSource ds(flexfile, resource);
518 static_version = Read_text_msg_file_sections(&ds,
519 static_strings, sections, numsections);
520 } else {
521 try {
522 snprintf(buf, 50, "<STATIC>/%s.txt", fname);
523 std::ifstream in;
524 U7open(in, buf, false);
525 static_version = Read_text_msg_file_sections(in,
526 static_strings, sections, numsections);
527 in.close();
528 } catch (std::exception &e) {
529 if (!editing) {
530 for (int i = 0; i < numsections; i++)
531 delete parsers[i];
532 throw;
533 }
534 static_strings.resize(numsections);
535 }
536 }
537 patch_strings.resize(numsections);
538 snprintf(buf, 50, "<PATCH>/%s.txt", fname);
539 if (U7exists(buf)) {
540 std::ifstream in;
541 U7open(in, buf, false);
542 patch_version = Read_text_msg_file_sections(in, patch_strings,
543 sections, numsections);
544 in.close();
545 }
546 for (int i = 0; i < numsections; i++) {
547 parsers[i]->parse(static_strings[i], static_version, false, game);
548 parsers[i]->parse(patch_strings[i], patch_version, true, game);
549 }
550 static_strings.clear();
551 patch_strings.clear();
552 }
553
554 /*
555 * Generic base data-agnostic writer class.
556 */
557 class Base_writer {
558 protected:
559 const char *name;
560 const int version;
561 int cnt;
check_write()562 virtual int check_write() {
563 return 0;
564 }
write_data(std::ostream & out,Exult_Game game)565 virtual void write_data(std::ostream &out, Exult_Game game) {
566 ignore_unused_variable_warning(out, game);
567 }
568 public:
569 Base_writer(const char *s, int v = -1)
name(s)570 : name(s), version(v), cnt(-1)
571 { }
572 virtual ~Base_writer() = default;
check()573 int check() {
574 // Return cached value, if any.
575 return cnt > -1 ? cnt : cnt = check_write();
576 }
write_text(std::ostream & out,Exult_Game game)577 void write_text
578 (
579 std::ostream &out,
580 Exult_Game game
581 ) {
582 if (cnt <= 0) // Nothing to do.
583 return;
584 // Section is not empty.
585 out << "%%section " << name << std::endl;
586 write_data(out, game);
587 out << "%%endsection" << std::endl;
588 }
write_binary(Exult_Game game)589 void write_binary
590 (
591 Exult_Game game
592 ) {
593 if (cnt <= 0) // Nothing to do.
594 return;
595 std::ofstream fout; // Open file.
596 U7open(fout, name);
597 if (version >= 0) // container.dat has version #.
598 fout.put(version);
599 Write_count(fout, cnt); // Object count, with Exult extension.
600 write_data(fout, game);
601 fout.close();
602 }
603 };
604
605 /*
606 * Generic functor-based writer class for maps.
607 */
608 template <class Info, class Functor>
609 class Functor_multidata_writer : public Base_writer {
610 protected:
611 std::map<int, Info> &info;
612 const int numshapes;
613 Functor writer;
check_write()614 int check_write() override {
615 int num = 0;
616 for (auto& kvpair : info)
617 if (writer(kvpair.second))
618 num++;
619 return num;
620 }
write_data(std::ostream & out,Exult_Game game)621 void write_data(std::ostream &out, Exult_Game game) override {
622 for (auto& kvpair : info)
623 if (writer(kvpair.second))
624 writer(out, kvpair.first, game, kvpair.second);
625 }
626 public:
627 Functor_multidata_writer(const char *s, std::map<int, Info> &nfo,
628 int n, int v = -1)
Base_writer(s,v)629 : Base_writer(s, v), info(nfo), numshapes(n) {
630 check();
631 }
632 };
633
634 /*
635 * Generic functor-based writer class.
636 */
637 template <class Info, class Functor>
638 class Functor_data_writer : public Base_writer {
639 protected:
640 Info &info;
641 Functor writer;
check_write()642 int check_write() override {
643 return writer(info) ? 1 : 0;
644 }
write_data(std::ostream & out,Exult_Game game)645 void write_data(std::ostream &out, Exult_Game game) override {
646 if (writer(info))
647 writer(out, -1, game, info);
648 }
649 public:
650 Functor_data_writer(const char *s, Info &nfo, int v = -1)
Base_writer(s,v)651 : Base_writer(s, v), info(nfo) {
652 check();
653 }
654 };
655
656
657 /*
658 * Base flag checker functor.
659 */
660 template <int flag, class Info>
661 class Flag_check_functor {
662 public:
operator()663 bool operator()(Info &info) {
664 return (info.modified_flags | info.frompatch_flags) & flag;
665 }
666 };
667
WriteIndex(std::ostream & out,int index)668 inline void WriteIndex(std::ostream &out, int index) {
669 if (index >= 0)
670 WriteInt(out, index);
671 }
672
673 /*
674 * Data checker and writer functors.
675 */
676 template <int flag, typename T, class Info, T Info::*data>
677 class Text_writer_functor {
678 Flag_check_functor<flag, Info> check;
679 public:
operator()680 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
681 ignore_unused_variable_warning(game);
682 out << ":";
683 WriteIndex(out, index);
684 WriteInt(out, info.*data, true);
685 }
operator()686 bool operator()(Info &info) {
687 return check(info);
688 }
689 };
690
691 template <int flag, typename T, class Info, T Info::*data1, T Info::*data2>
692 class Text_pair_writer_functor {
693 Flag_check_functor<flag, Info> check;
694 public:
operator()695 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
696 ignore_unused_variable_warning(game);
697 out << ":";
698 WriteIndex(out, index);
699 WriteInt(out, info.*data1);
700 WriteInt(out, info.*data2, true);
701 }
operator()702 bool operator()(Info &info) {
703 return check(info);
704 }
705 };
706
707 template <int flag, typename T, class Info, T Info::*data, int bit>
708 class Bit_text_writer_functor {
709 Flag_check_functor<flag, Info> check;
710 public:
operator()711 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
712 ignore_unused_variable_warning(game);
713 bool val = ((info.*data) & (static_cast<T>(1) << bit));
714 out << ":";
715 WriteIndex(out, index);
716 WriteInt(out, val, true);
717 }
operator()718 bool operator()(Info &info) {
719 return check(info);
720 }
721 };
722
723 template <int flag, typename T, class Info, T Info::*data>
724 class Bit_field_text_writer_functor {
725 Flag_check_functor<flag, Info> check;
726 public:
operator()727 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
728 ignore_unused_variable_warning(game);
729 out << ":";
730 WriteIndex(out, index);
731 int size = 8 * sizeof(T) - 1; // Bit count.
732 int bit = 0;
733 T flags = info.*data;
734 while (bit < size) {
735 out << static_cast<bool>((flags & (static_cast<T>(1) << bit)) != 0) << '/';
736 bit++;
737 }
738 out << static_cast<bool>((flags & (static_cast<T>(1) << size)) != 0) << std::endl;
739 }
operator()740 bool operator()(Info &info) {
741 return check(info);
742 }
743 };
744
745 template <int flag, typename T, class Info, T Info::*data, int pad>
746 class Binary_writer_functor {
747 Flag_check_functor<flag, Info> check;
748 public:
operator()749 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
750 ignore_unused_variable_warning(game);
751 Write2(out, index);
752 out.write(reinterpret_cast<char *>(&(info.*data)), sizeof(T));
753 for (int i = 0; i < pad; i++)
754 out.put(0);
755 }
operator()756 bool operator()(Info &info) {
757 return check(info);
758 }
759 };
760
761 template < int flag, typename T1, typename T2, class Info,
762 T1 Info::*data1, T2 Info::*data2, int pad >
763 class Binary_pair_writer_functor {
764 Flag_check_functor<flag, Info> check;
765 public:
operator()766 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
767 ignore_unused_variable_warning(game);
768 Write2(out, index);
769 out.write(reinterpret_cast<char *>(&(info.*data1)), sizeof(T1));
770 out.write(reinterpret_cast<char *>(&(info.*data2)), sizeof(T2));
771 for (int i = 0; i < pad; i++)
772 out.put(0);
773 }
operator()774 bool operator()(Info &info) {
775 return check(info);
776 }
777 };
778
779 template <typename T, class Info, T *Info::*data>
780 class Class_writer_functor {
781 public:
operator()782 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
783 T *cls = info.*data;
784 if (!cls) { // Write 'remove' block.
785 if (!T::is_binary) // Text entry.
786 out << ':' << index << "/-255" << std::endl;
787 else if (T::entry_size >= 3) {
788 // T::entry_size should be >= 3!
789 // For stupid compilers...
790 const size_t nelems = T::entry_size >= 3 ? T::entry_size : 1;
791 auto *buf = new unsigned char[nelems];
792 unsigned char *ptr = buf;
793 Write2(ptr, index);
794 if (T::entry_size >= 4)
795 memset(ptr, 0, T::entry_size - 3);
796 buf[T::entry_size - 1] = 0xff;
797 out.write(reinterpret_cast<const char *>(buf), T::entry_size);
798 delete [] buf;
799 }
800 } else
801 cls->write(out, index, game);
802 }
operator()803 bool operator()(Info &info) {
804 T *cls = info.*data;
805 if (!cls)
806 return (info.have_static_flags & T::get_info_flag()) != 0;
807 return (cls->need_write());
808 }
809 };
810
811 template <typename T, class Info, std::vector<T> Info::*data>
812 class Vector_writer_functor {
need_write(T & inf)813 bool need_write(T &inf) const {
814 return inf.need_write() || (inf.is_invalid() && inf.have_static());
815 }
816 public:
operator()817 void operator()(std::ostream &out, int index, Exult_Game game, Info &info) {
818 std::vector<T> &vec = info.*data;
819 for (auto& elem : vec)
820 if (need_write(elem))
821 elem.write(out, index, game);
822 }
operator()823 bool operator()(Info &info) {
824 std::vector<T> &vec = info.*data;
825 if (vec.empty()) // Nothing to do.
826 return false;
827 for (auto& elem : vec)
828 if (need_write(elem))
829 return true;
830 return false;
831 }
832 };
833
834 /*
835 * Writes text data file according to passed writer functions.
836 */
837 template <class Writer>
Write_text_data_file(const char * fname,Writer * writers[],int numsections,int version,Exult_Game game)838 static void Write_text_data_file(
839 const char *fname, // Name of file to read, sans extension
840 Writer *writers[], // What to use to write data.
841 int numsections, // Number of sections
842 int version,
843 Exult_Game game
844 ) {
845 int cnt = 0;
846 for (int i = 0; i < numsections; i++)
847 cnt += writers[i]->check();
848 if (!cnt) {
849 // Nothing to do but delete the writers.
850 for (int i = 0; i < numsections; i++)
851 delete writers[i];
852 return;
853 }
854 std::ofstream out;
855 char buf[50];
856 snprintf(buf, 50, "<PATCH>/%s.txt", fname);
857 U7open(out, buf, true); // (It's a text file.)
858 out << "#\tExult " << VERSION << " text message file."
859 << "\tWritten by ExultStudio." << std::endl;
860 out << "%%section version" << std::endl
861 << ":" << version << std::endl
862 << "%%endsection" << std::endl;
863 for (int i = 0; i < numsections; i++) {
864 writers[i]->write_text(out, game);
865 delete writers[i];
866 }
867 out.close();
868 }
869
870 #endif
871