TerraForge3D  2.3.1
3D Terrain And Landscape Generator
Serializer.cpp
1#include "Data/Serializer.h"
2#include "Utils/Utils.h"
3#include "Base/ModelImporter.h"
4#include "Data/ProjectData.h"
5#include "Data/VersionInfo.h"
6#include "Data/ApplicationState.h"
7#include "Misc/AppStyles.h"
8#include "Shading/ShadingManager.h"
9#include "Platform.h"
10
11#ifdef TERR3D_WIN32
12#include "dirent/dirent.h"
13#else
14#include "sys/stat.h"
15#include "dirent.h"
16#endif
17#include "zip.h"
18
19#include <fstream>
20#include <iostream>
21
22
23#define TRY_CATCH_ERR_SERIALIZE(code, message) try{ code } catch(...){onError(message, true);}
24#define TRY_CATCH_ERR_DESERIALIZE(code, message) try{ code } catch(...){onError(message, false);}
25#define TRY_CATCH_ERR_DESERIALIZE_WITH_CODE(code, message, errorCode) try{ code } catch(...){onError(message, false); errorCode}
26
27static void zip_walk(struct zip_t *zip, const char *path, bool isFristLayer = true, std::string prevPath = "")
28{
29 DIR *dir;
30 struct dirent *entry;
31 char fullpath[MAX_PATH];
32 struct stat s;
33 memset(fullpath, 0, MAX_PATH);
34 dir = opendir(path);
35 assert(dir);
36
37 while ((entry = readdir(dir)))
38 {
39 // skip "." and ".."
40 if (!strcmp(entry->d_name, ".\0") || !strcmp(entry->d_name, "..\0"))
41 {
42 continue;
43 }
44
45 snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
46 stat(fullpath, &s);
47
48 if (S_ISDIR(s.st_mode))
49 {
50 zip_walk(zip, fullpath, false, prevPath + entry->d_name + PATH_SEPARATOR);
51 }
52
53 else
54 {
55 zip_entry_open(zip, (prevPath + entry->d_name).c_str());
56 zip_entry_fwrite(zip, fullpath);
57 zip_entry_close(zip);
58 }
59 }
60
61 closedir(dir);
62}
63
64
65Serializer::Serializer(ApplicationState *state)
66 :appState(state)
67{
68 onError = [](std::string message, bool whileSerialize)
69 {
70 std::cout << (whileSerialize ? "Serailizer Error : " : "Deserailizer Error : ") << message << std::endl;;
71 };
72}
73
74Serializer::Serializer(ApplicationState *state, std::function<void(std::string, bool)> errorFunc)
75 :appState(state)
76{
77}
78
79Serializer::~Serializer()
80{
81}
82
83nlohmann::json Serializer::Serialize()
84{
86 data = nlohmann::json();
87 data["type"] = "SAVEFILE";
88 data["serailizerVersion"] = TERR3D_SERIALIZER_VERSION;
89 data["maxSerailizerVersion"] = TERR3D_MAX_SERIALIZER_VERSION;
90 data["minSerailizerVersion"] = TERR3D_MIN_SERIALIZER_VERSION;
91 data["versionHash"] = MD5File(GetExecutablePath()).ToString();
92 data["name"] = "TerraForge3D v2.2.0";
93 data["mode"] = appState->mode;
94 // SAVE NODE GENERATORS DATA
95 TRY_CATCH_ERR_SERIALIZE(data["appData"] = appState->globals.appData;, "Failed to save App Data");
96 TRY_CATCH_ERR_SERIALIZE(data["appStyles"] = GetStyleData();, "Failed to save app styles");
97 TRY_CATCH_ERR_SERIALIZE(data["imguiData"] = std::string(ImGui::SaveIniSettingsToMemory());, "Failed to save ImGui Data.");
98 TRY_CATCH_ERR_SERIALIZE(data["camera"] = appState->cameras.Save();, "Failed to save cameras.");
99 TRY_CATCH_ERR_SERIALIZE(data["windows"] = appState->windows.Save();, "Failed to save window states");
100 TRY_CATCH_ERR_SERIALIZE(data["states"] = appState->states.Save();, "Failed to save states.");
101 TRY_CATCH_ERR_SERIALIZE(data["globals"] = appState->globals.Save();, "Failed to save globals.");
102 TRY_CATCH_ERR_SERIALIZE(data["sea"] = appState->seaManager->Save();, "Failed to save Sea.");
103 TRY_CATCH_ERR_SERIALIZE(data["foliage"] = appState->foliageManager->Save();, "Failed to save foliage manager.");
104 TRY_CATCH_ERR_SERIALIZE(data["projectID"] = appState->projectManager->GetId();, "Failed to save project id.");
105 TRY_CATCH_ERR_SERIALIZE(data["textureBaker"] = appState->textureBaker->Save();, "Failed to save texture baker settings.");
106 TRY_CATCH_ERR_SERIALIZE(appState->projectManager->SaveDatabase(); data["projectDatabase"] = appState->projectManager->GetDatabase();, "Failed to save project database.");
107 TRY_CATCH_ERR_SERIALIZE(data["lighting"] = appState->lightManager->Save();, "Failed to save lighting data.");
108 TRY_CATCH_ERR_SERIALIZE(data["shading"] = appState->shadingManager->Save();, "Failed to save shading data.");
109 TRY_CATCH_ERR_SERIALIZE(data["generators"] = appState->meshGenerator->Save();, "Failed to save generators.");
110 TRY_CATCH_ERR_SERIALIZE(
111
112 if (!appState->mode == ApplicationMode::CUSTOM_BASE)
113{
114 std::string baseHash = MD5File(appState->globals.currentBaseModelPath)
115 .ToString();
116 std::string projectAsset = appState->projectManager->GetAsset(baseHash);
117
118 if (projectAsset == "")
119 {
120 MkDir(appState->projectManager->GetResourcePath() + PATH_SEPARATOR "models");
121 CopyFileData(appState->globals.currentBaseModelPath, appState->projectManager->GetResourcePath() + PATH_SEPARATOR "models" PATH_SEPARATOR + baseHash + appState->globals.currentBaseModelPath.substr(appState->globals.currentBaseModelPath.find_last_of(".")));
122 appState->projectManager->RegisterAsset(baseHash, "models" PATH_SEPARATOR + baseHash + appState->globals.currentBaseModelPath.substr(appState->globals.currentBaseModelPath.find_last_of(".")));
123 }
124
125 data["baseid"] = baseHash;
126 }
127 , "Failed to save custom base model."
128 );
129 return data;
130}
131
132void Serializer::PackProject(std::string path)
133{
134 if (path.find(".terr3dpack") == std::string::npos)
135 {
136 path = path + ".terr3dpack";
137 }
138
139 Serialize();
140 std::ofstream outfile;
141 outfile.open(appState->projectManager->GetResourcePath() + PATH_SEPARATOR "project.terr3d");
142 outfile << data.dump(4, ' ', false);
143 outfile.close();
144 MkDir(GetExecutableDir() + PATH_SEPARATOR "Data" PATH_SEPARATOR "temp");
145 std::string uid = GenerateId(64);
146 SaveFile(GetExecutableDir() + "Data" + PATH_SEPARATOR "temp" PATH_SEPARATOR + uid);
147 zip_t *packed = zip_open(path.c_str(), 9, 'w');
148 zip_walk(packed, appState->projectManager->GetResourcePath().c_str());
149 zip_close(packed);
150}
151
152void Serializer::LoadPackedProject(std::string path)
153{
154 if (path.find(".terr3dpack") == std::string::npos)
155 {
156 path = path + ".terr3dpack";
157 }
158
159 std::string uid = GenerateId(64);
160 MkDir(GetExecutableDir() + PATH_SEPARATOR "Data" PATH_SEPARATOR "temp"
161 PATH_SEPARATOR "pcache_" + uid);
162 zip_extract(path.c_str(), (GetExecutableDir() + PATH_SEPARATOR "Data"
163 PATH_SEPARATOR "temp" PATH_SEPARATOR "pcache_" + uid).c_str(),
164 [](const char *filename, void *arg)
165 {
166 return 1;
167 }, (void *)0);
168
169 if (FileExists(GetExecutableDir() + PATH_SEPARATOR "Data" PATH_SEPARATOR "temp" PATH_SEPARATOR "pcache_" + uid + PATH_SEPARATOR "project.terr3d", true))
170 {
171 bool tmp = false;
172 std::string sdata = ReadShaderSourceFile(GetExecutableDir() +
173 PATH_SEPARATOR "Data" PATH_SEPARATOR "temp" PATH_SEPARATOR
174 "pcache_" + uid + PATH_SEPARATOR "project.terr3d", &tmp);
175
176 if (sdata.size() <= 0)
177 {
178 Log("Emppty Project File!");
179 return;
180 }
181
182 nlohmann::json data;
183
184 try
185 {
186 data = nlohmann::json::parse(sdata);
187 }
188
189 catch (...)
190 {
191 Log("Failed to Parse Project file!");
192 return;
193 }
194
195 std::string projId = data["projectID"];
196 zip_extract(path.c_str(), (GetExecutableDir() + PATH_SEPARATOR "Data"
197 PATH_SEPARATOR "cache" PATH_SEPARATOR "project_data"
198 PATH_SEPARATOR "project_" + projId).c_str(), [](const char
199 *filename, void *arg)
200 {
201 Log(std::string("Extracted ") + filename);
202 return 1;
203 }, (void *)0);
204 std::string oriDir = path.substr(0, path.rfind(PATH_SEPARATOR));
205 std::string oriName = path.substr(path.rfind(PATH_SEPARATOR) + 1);
206 CopyFileData((GetExecutableDir() + PATH_SEPARATOR "Data" PATH_SEPARATOR
207 "cache" PATH_SEPARATOR "project_data" PATH_SEPARATOR
208 "project_" + projId + PATH_SEPARATOR
209 "project.terr3d").c_str(), oriDir + PATH_SEPARATOR +
210 oriName + ".terr3d");
211 LoadFile(oriDir + PATH_SEPARATOR + oriName + ".terr3d");
212 }
213
214 else
215 {
216 Log("Not a valid terr3dpack file!");
217 }
218}
219
220void Serializer::SaveFile(std::string path)
221{
222 if (path.size() <= 3)
223 {
224 return;
225 }
226
227 if (path.find(".terr3d") == std::string::npos)
228 {
229 path = path + ".terr3d";
230 }
231
232 if (path != appState->globals.currentOpenFilePath)
233 {
234 appState->globals.currentOpenFilePath = path;
235 }
236
237 Serialize();
238 std::ofstream outfile;
239 outfile.open(appState->projectManager->GetResourcePath() + PATH_SEPARATOR "project.terr3d");
240 outfile << data.dump(4, ' ', false);
241 outfile.close();
242 outfile.open(path);
243 outfile << data.dump(4, ' ', false);
244 outfile.close();
245}
246
247void Serializer::LoadFile(std::string path)
248{
249 if (path.size() <= 3)
250 {
251 return;
252 }
253
254 if (path.find(".terr3d") == std::string::npos)
255 {
256 path = path + ".terr3d";
257 }
258
259 bool flagRd = true;
260 std::string sdata = ReadShaderSourceFile(path, &flagRd);
261
262 if (!flagRd)
263 {
264 Log("Could not read file : " + path);
265 return;
266 }
267
268 if (sdata.size() == 0)
269 {
270 Log("Empty file.");
271 return;
272 }
273
274 nlohmann::json tmp;
275
276 try
277 {
278 tmp = nlohmann::json::parse(sdata);
279 }
280
281 catch (...)
282 {
283 Log("Failed to Parse file : " + path);
284 }
285
286 if (path != appState->globals.currentOpenFilePath)
287 {
288 appState->globals.currentOpenFilePath = path;
289 }
290
291 Deserialize(tmp);
292}
293
294ApplicationState *Serializer::Deserialize(nlohmann::json d)
295{
296 data = d;
297 std::cout << "Wating for remesh cycle to finish ...\n";
298
299 while (appState->states.remeshing);
300
301 std::cout << "Finished remesing." << std::endl;
302 TRY_CATCH_ERR_DESERIALIZE(
303
304 if (data["versionHash"] != MD5File(GetExecutablePath()).ToString())
305{
306 onError("The file you are tryng to open was made with a different version of TerraForge3D!\nTrying to check Serializer compatibility", false);
307 }
308 , "Failed to verify file version."
309 );
310 TRY_CATCH_ERR_DESERIALIZE_WITH_CODE(
311 int serializerVersion = data["serailizerVersion"];
312
313 if (serializerVersion < TERR3D_MIN_SERIALIZER_VERSION)
314{
315 onError("This file cannot be opened as it was serialized using serializer V" + std::to_string(serializerVersion) + " but the minimum serializer version required is V" + std::to_string(TERR3D_MIN_SERIALIZER_VERSION), false);
316 return nullptr;
317 }
318
319 if (serializerVersion > TERR3D_MAX_SERIALIZER_VERSION)
320{
321 onError("This file cannot be opened as it was serialized using serializer V" + std::to_string(serializerVersion) + " but the maximum serializer version supported is V" + std::to_string(TERR3D_MAX_SERIALIZER_VERSION), false);
322 return nullptr;
323 }
324 , "Failed to verify Serializer",
325 return nullptr;
326 );
327
328 if (data["type"] != "SAVEFILE")
329 {
330 onError("This file is not a savefile.", false);
331 return nullptr;
332 }
333
334 TRY_CATCH_ERR_DESERIALIZE(LoadThemeFromStr(data["styleData"]);, "Failed to load theme.");
335 TRY_CATCH_ERR_DESERIALIZE(appState->globals.appData = data["appData"];, "Failed to app data.");
336 TRY_CATCH_ERR_DESERIALIZE(appState->globals.appData = data["appData"];, "Failed to app data.");
337 TRY_CATCH_ERR_DESERIALIZE(appState->mode = data["mode"];, "Failed to app mode.");
338 TRY_CATCH_ERR_DESERIALIZE(appState->projectManager->SetId(data["projectID"]);, "Failed to load Project ID.");
339 TRY_CATCH_ERR_DESERIALIZE(appState->projectManager->SetDatabase(data["projectDatabase"]);, "Failed to load project database.");
340 std::cout << "Loaded Project ID : " << appState->projectManager->GetId() << std::endl;
341 TRY_CATCH_ERR_DESERIALIZE(appState->cameras.Load(data["camera"]);, "Failed to load camera.");
342 TRY_CATCH_ERR_DESERIALIZE(appState->windows.Load(data["windows"]);, "Failed to load windows.");
343 TRY_CATCH_ERR_DESERIALIZE(appState->states.Load(data["states"]);, "Failed to load states.");
344 TRY_CATCH_ERR_DESERIALIZE(appState->globals.Load(data["globals"]);, "Failed to load globals.");
345 TRY_CATCH_ERR_DESERIALIZE(appState->textureBaker->Load(data["textureBaker"]);, "Failed to load texture baker settings.");
346 TRY_CATCH_ERR_DESERIALIZE(appState->seaManager->Load(data["sea"]);, "Failed to load sea.");
347 TRY_CATCH_ERR_DESERIALIZE(appState->foliageManager->Load(data["foliage"]);, "Failed to load foliage.");
348 TRY_CATCH_ERR_DESERIALIZE(appState->lightManager->Load(data["lighting"]);, "Failed to load lighting.");
349 TRY_CATCH_ERR_DESERIALIZE(appState->meshGenerator->Load(data["generators"]);, "Failed to load generators.");
350 TRY_CATCH_ERR_DESERIALIZE(appState->shadingManager->Load(data["shading"]);, "Failed to load shading data.");
351 TRY_CATCH_ERR_DESERIALIZE_WITH_CODE(
352
353 if (appState->mode == ApplicationMode::CUSTOM_BASE)
354{
355 std::string baseHash = data["baseid"];
356 std::string projectAsset = appState->projectManager->GetAsset(baseHash);
357
358 if (projectAsset == "")
359 {
360 std::cout << "Failed to load base model from save file!\n";
361 appState->states.usingBase = true;
362 }
363
364 else
365 {
366 if (!appState->models.customBase)
367 {
368 delete appState->models.customBase;
369 delete appState->models.customBaseCopy;
370 }
371
372 appState->globals.currentBaseModelPath = appState->projectManager->GetResourcePath() + PATH_SEPARATOR + projectAsset;
373 appState->models.customBase = LoadModel(appState->globals.currentBaseModelPath);
374 appState->models.customBaseCopy = LoadModel(appState->globals.currentBaseModelPath);
375 appState->states.usingBase = false;
376 }
377 }
378 , "Failed to load custom base model. Falling back to plane.",
379
380 if (!appState->models.customBase)
381{
382 delete appState->models.customBase;
383 delete appState->models.customBaseCopy;
384}
385appState->states.usingBase = true;
386);
387 return appState;
388}
389
static JSON_HEDLEY_WARN_UNUSED_RESULT basic_json parse(InputType &&i, const parser_callback_t cb=nullptr, const bool allow_exceptions=true, const bool ignore_comments=false)
deserialize from a compatible input
Definition: json.hpp:24605
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
Definition: json.hpp:20117
a class to store JSON values
Definition: json.hpp:17860
basic_json<> json
default JSON class
Definition: json.hpp:3411