OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
serialize.cpp
Go to the documentation of this file.
1#include "serialize.h"
2
3#include <cstring>
4
5#include "savegameheader.h"
6#include "world/world.h"
7#include "world/fplock.h"
8#include "world/waypoint.h"
9
10#include <Tempest/MemReader>
11#include <Tempest/MemWriter>
12#include <Tempest/Log>
13#include <Tempest/Application>
14
15size_t Serialize::writeFunc(void* pOpaque, uint64_t file_ofs, const void* pBuf, size_t n) {
16 auto& self = *reinterpret_cast<Serialize*>(pOpaque);
17 file_ofs += mz_zip_get_archive_file_start_offset(&self.impl);
18 if(file_ofs!=self.curOffset) {
19 self.impl.m_last_error = MZ_ZIP_FILE_SEEK_FAILED;
20 return 0;
21 }
22 size_t ret = self.fout->write(pBuf,n);
23 self.curOffset+=ret;
24 return ret;
25 }
26
27size_t Serialize::readFunc(void* pOpaque, uint64_t file_ofs, void* pBuf, size_t n) {
28 auto& self = *reinterpret_cast<Serialize*>(pOpaque);
29 file_ofs += mz_zip_get_archive_file_start_offset(&self.impl);
30 if(self.curOffset<file_ofs) {
31 size_t diff = size_t(file_ofs-self.curOffset);
32 size_t mv = self.fin->seek(diff);
33 self.curOffset += uint64_t(mv);
34 if(diff!=mv) {
35 self.impl.m_last_error = MZ_ZIP_FILE_SEEK_FAILED;
36 return 0;
37 }
38 }
39 if(self.curOffset>file_ofs) {
40 size_t diff = size_t(self.curOffset-file_ofs);
41 size_t mv = self.fin->unget(diff);
42 self.curOffset -= uint64_t(mv);
43 if(diff!=mv) {
44 self.impl.m_last_error = MZ_ZIP_FILE_SEEK_FAILED;
45 return 0;
46 }
47 }
48 size_t ret = self.fin->read(pBuf,n);
49 self.curOffset+=ret;
50 return ret;
51 }
52
54
55//static uint64_t time0 = 0;
56
57Serialize::Serialize(Tempest::ODevice& fout) : fout(&fout) {
58 //time0 = Tempest::Application::tickCount();
59 entryBuf .reserve(1*1024*1024);
60 entryName.reserve(256);
61
62 impl.m_pWrite = Serialize::writeFunc;
63 impl.m_pIO_opaque = this;
64 impl.m_zip_type = MZ_ZIP_TYPE_USER;
65 mz_zip_writer_init_v2(&impl, 0, 0);
66 }
67
68Serialize::Serialize(Tempest::IDevice& fin) : fin(&fin) {
69 entryBuf .resize(1*1024*1024);
70 entryName.reserve(256);
71
72 impl.m_pRead = Serialize::readFunc;
73 impl.m_pIO_opaque = this;
74 impl.m_zip_type = MZ_ZIP_TYPE_USER;
75 mz_zip_reader_init(&impl, fin.size(), 0);
76 }
77
79 closeEntry();
80 if(fout!=nullptr) {
81 mz_zip_writer_finalize_archive(&impl);
82 mz_zip_writer_end(&impl);
83 //Tempest::Log::d("save time = ", Tempest::Application::tickCount()-time0);
84 }
85 }
86
87std::string_view Serialize::worldName() const {
88 if(ctx!=nullptr)
89 return ctx->name();
90 return "_";
91 }
92
93void Serialize::closeEntry() {
94 if(fout==nullptr)
95 return;
96 if(entryBuf.empty())
97 return;
98
99 mz_uint level = entryBuf.size()>256 ? MZ_BEST_SPEED : MZ_NO_COMPRESSION;
100 mz_bool status = mz_zip_writer_add_mem(&impl, entryName.c_str(), entryBuf.data(), entryBuf.size(), level);
101 entryBuf .clear();
102 entryName.clear();
103 if(!status)
104 throw std::runtime_error("unable to write entry in game archive");
105 }
106
107bool Serialize::implSetEntry(std::string_view fname) {
108 size_t prefix = 0;
109 if(fout!=nullptr) {
110 while(prefix<fname.size() && prefix<entryName.size()) {
111 if(entryName[prefix]!=fname[prefix])
112 break;
113 ++prefix;
114 }
115 }
116 closeEntry();
117 entryName = fname;
118 if(fout!=nullptr) {
119 for(size_t i=prefix; i<entryName.size(); ++i) {
120 if(entryName[i]=='/' && i+1<entryName.size()) {
121 const char prev = entryName[i+1];
122 entryName[i+1] = '\0';
123 const auto it = outFileList.insert(entryName.c_str());
124 if(it.second) {
125 mz_bool status = mz_zip_writer_add_mem(&impl, entryName.c_str(), NULL, 0, MZ_NO_COMPRESSION);
126 if(!status)
127 throw std::runtime_error("unable to allocate entry in game archive");
128 }
129 entryName[i+1] = prev;
130 }
131 }
132 return true;
133 }
134 if(fin!=nullptr) {
135 mz_uint32 id = mz_uint32(-1);
136 if(mz_zip_reader_locate_file_v2(&impl, entryName.c_str(), nullptr, 0, &id)) {
137 mz_zip_archive_file_stat stat = {};
138 mz_zip_reader_file_stat(&impl,id,&stat);
139 entryBuf.resize(size_t(stat.m_uncomp_size));
140 mz_zip_reader_extract_file_to_mem(&impl,entryName.c_str(),entryBuf.data(),entryBuf.size(),0);
141 } else {
142 entryBuf.clear();
143 }
144 readOffset = 0;
145 return !entryBuf.empty();
146 }
147 return false;
148 }
149
150uint32_t Serialize::implDirectorySize(std::string_view e) {
151 // Get and print information about each file in the archive.
152 uint32_t cnt = 0;
153 for(mz_uint i = 0; i<mz_zip_reader_get_num_files(&impl); i++) {
154 mz_zip_archive_file_stat stat = {};
155 if(!mz_zip_reader_file_stat(&impl, i, &stat))
156 throw std::runtime_error("unable to locate entry in game archive");
157 auto len = std::strlen(stat.m_filename);
158 if(len>e.size() && std::memcmp(e.data(),stat.m_filename,e.size())==0) {
159 auto sep = std::strchr(stat.m_filename+e.size(),'/');
160 if(sep==nullptr || (sep+1)==(stat.m_filename+len))
161 ++cnt;
162 }
163 }
164 return cnt;
165 }
166
167void Serialize::writeBytes(const void* buf, size_t sz) {
168 if(sz==0)
169 return;
170 size_t at = entryBuf.size();
171 entryBuf.resize(entryBuf.size()+sz);
172 std::memcpy(&entryBuf[at],buf,sz);
173 }
174
175void Serialize::readBytes(void* buf, size_t sz) {
176 if(fin==nullptr || readOffset+sz>entryBuf.size())
177 throw std::runtime_error("unable to read save-game file");
178 std::memcpy(buf,&entryBuf[size_t(readOffset)],sz);
179 readOffset+=sz;
180 }
181
182void Serialize::implWrite(const std::string& s) {
183 uint32_t sz=uint32_t(s.size());
184 implWrite(sz);
185 writeBytes(s.data(),sz);
186 }
187
188void Serialize::implRead(std::string& s) {
189 uint32_t sz=0;
190 implRead(sz);
191 s.resize(sz);
192 if(sz>0)
193 readBytes(&s[0],sz);
194 }
195
196void Serialize::implWrite(std::string_view s) {
197 uint32_t sz=uint32_t(s.size());
198 implWrite(sz);
199 writeBytes(s.data(),sz);
200 }
201
202void Serialize::implWrite(WeaponState w) {
203 implWrite(uint8_t(w));
204 }
205
206void Serialize::implRead(WeaponState &w) {
207 implRead(reinterpret_cast<uint8_t&>(w));
208 }
209
210void Serialize::implWrite(const WayPoint* wptr) {
211 implWrite(wptr ? wptr->name : "");
212 }
213
214void Serialize::implRead(const WayPoint*& wptr) {
215 implRead(tmpStr);
216 wptr = ctx==nullptr ? nullptr : ctx->findPoint(tmpStr,false);
217 }
218
219void Serialize::implWrite(const ScriptFn& fn) {
220 uint32_t v = uint32_t(-1);
221 if(fn.ptr<uint64_t(std::numeric_limits<uint32_t>::max()))
222 v = uint32_t(fn.ptr);
223 implWrite(v);
224 }
225
226void Serialize::implRead(ScriptFn& fn) {
227 uint32_t v=0;
228 implRead(v);
229 if(v==std::numeric_limits<uint32_t>::max())
230 fn.ptr = size_t(-1); else
231 fn.ptr = v;
232 }
233
234void Serialize::implWrite(const Npc* npc) {
235 uint32_t id = ctx==nullptr ? uint32_t(-1) : ctx->npcId(npc);
236 implWrite(id);
237 }
238
239void Serialize::implRead(const Npc *&npc) {
240 uint32_t id=uint32_t(-1);
241 implRead(id);
242 npc = ctx==nullptr ? nullptr : ctx->npcById(id);
243 }
244
245void Serialize::implWrite(Npc *npc) {
246 uint32_t id = ctx==nullptr ? uint32_t(-1) : ctx->npcId(npc);
247 implWrite(id);
248 }
249
250void Serialize::implRead(Npc *&npc) {
251 uint32_t id=uint32_t(-1);
252 read(id);
253 npc = ctx==nullptr ? nullptr : ctx->npcById(id);
254 }
255
256void Serialize::implWrite(Interactive* mobsi) {
257 uint32_t id = ctx==nullptr ? uint32_t(-1) : ctx->mobsiId(mobsi);
258 implWrite(id);
259 }
260
261void Serialize::implRead(Interactive*& mobsi) {
262 uint32_t id=uint32_t(-1);
263 implRead(id);
264 mobsi = ctx==nullptr ? nullptr : ctx->mobsiById(id);
265 }
266
267void Serialize::implWrite(const SaveGameHeader& p) {
268 p.save(*this);
269 }
270
271void Serialize::implRead(SaveGameHeader& p) {
272 p = SaveGameHeader(*this);
273 }
274
275void Serialize::implWrite(const Tempest::Pixmap& p) {
276 std::vector<uint8_t> tmp;
277 tmp.reserve(4*1024*1024);
278 Tempest::MemWriter w{tmp};
279 p.save(w);
280 writeBytes(tmp.data(),tmp.size());
281 }
282
283void Serialize::implRead(Tempest::Pixmap& p) {
284 Tempest::MemReader r{&entryBuf[size_t(readOffset)],size_t(entryBuf.size()-readOffset)};
285 p = Tempest::Pixmap(r);
286 readOffset += r.cursorPosition();
287 }
288
289void Serialize::implWrite(const zenkit::INpc& h) {
290 write(uint32_t(h.symbol_index()));
291 write(h.id,h.name,h.slot,h.effect,int32_t(h.type));
292 write(int32_t(h.flags));
293 write(h.attribute,h.hitchance,h.protection,h.damage);
294 write(h.damage_type,h.guild,h.level);
295 write(h.mission);
296 write(h.fight_tactic,h.weapon,h.voice,h.voice_pitch,h.body_mass);
297 write(h.daily_routine,h.start_aistate);
298 write(h.spawnpoint,h.spawn_delay,h.senses,h.senses_range);
299 write(h.aivar);
300 write(h.wp,h.exp,h.exp_next,h.lp,h.bodystate_interruptable_override,h.no_focus);
301 }
302
303void Serialize::readNpc(zenkit::DaedalusVm& vm, std::shared_ptr<zenkit::INpc>& h) {
304 uint32_t instanceSymbol=0;
305 read(instanceSymbol);
306
307 auto sym = vm.find_symbol_by_index(instanceSymbol);
308
309 if (sym != nullptr) {
310 vm.allocate_instance(h, sym);
311 } else {
312 Tempest::Log::e("Cannot load serialized NPC ", instanceSymbol, ": Symbol not found.");
313 }
314
315 read(h->id,h->name,h->slot,h->effect, reinterpret_cast<int32_t&>(h->type));
316 read(reinterpret_cast<int32_t&>(h->flags));
317 read(h->attribute,h->hitchance,h->protection,h->damage);
318 read(h->damage_type,h->guild,h->level);
319 read(h->mission);
320 read(h->fight_tactic,h->weapon,h->voice,h->voice_pitch,h->body_mass);
321 read(h->daily_routine,h->start_aistate);
322 read(h->spawnpoint,h->spawn_delay,h->senses,h->senses_range);
323 read(h->aivar);
324 read(h->wp,h->exp,h->exp_next,h->lp,h->bodystate_interruptable_override,h->no_focus);
325 }
326
327void Serialize::implWrite(const FpLock &fp) {
328 fp.save(*this);
329 }
330
331void Serialize::implRead(FpLock &fp) {
332 fp.load(*this);
333 }
334
335void Serialize::implWrite(const zenkit::AnimationSample& i) {
336 writeBytes(&i,sizeof(i));
337 }
338
339void Serialize::implRead(zenkit::AnimationSample& i) {
340 if (version() < 40) {
341 read(i.position.x,i.position.y,i.position.z);
342 read(i.rotation.x,i.rotation.y,i.rotation.z,i.rotation.w);
343 } else {
344 readBytes (&i,sizeof(i));
345 }
346 }
Definition fplock.h:6
void save(Serialize &fout) const
Definition fplock.cpp:44
void load(Serialize &fin)
Definition fplock.cpp:38
Definition npc.h:25
void save(Serialize &fout) const
size_t ptr
Definition gamescript.h:36
void readNpc(zenkit::DaedalusVm &vm, std::shared_ptr< zenkit::INpc > &npc)
void write(const Arg &... a)
Definition serialize.h:76
void writeBytes(const void *v, size_t sz)
void readBytes(void *v, size_t sz)
uint16_t version() const
Definition serialize.h:51
void read(Arg &... a)
Definition serialize.h:81
std::string_view worldName() const
Definition serialize.cpp:87
Serialize(Tempest::ODevice &fout)
Definition serialize.cpp:57
std::string name
Definition waypoint.h:35
const WayPoint * findPoint(std::string_view name, bool inexact=true) const
Definition world.cpp:871
Npc * npcById(uint32_t id)
Definition world.cpp:204
std::string_view name() const
Definition world.h:42
Interactive * mobsiById(uint32_t id)
Definition world.cpp:218
WeaponState
Definition constants.h:191