1 #include "xr_ai_version.h"
2 #include "xr_level_game.h"
3 #include "xr_level_graph.h"
4 #include "xr_level_spawn.h"
5 #include "xr_level_gct.h"
6 #include "xr_level_ai.h"
7 #include "xr_level_env_mod.h"
8 #include "xr_level_ps_static.h"
9 #include "xr_game_spawn.h"
10 #include "xr_game_graph.h"
11 #include "xr_utils.h"
12 #include "xr_entity_factory.h"
13 #include "xr_file_system.h"
14 #include "xr_string_utils.h"
15 #include "syncer.h"
16 
17 using namespace xray_re;
18 
scan_levels()19 void syncer::scan_levels()
20 {
21 	if (m_scan_done)
22 		return;
23 
24 	msg("loading %s", "game_levels.ltx");
25 	if (!m_levels_ini.load(PA_GAME_CONFIG, "game_levels.ltx")) {
26 		msg("can't load game_levels.ltx");
27 		throw sync_error();
28 	}
29 	m_num_levels = m_levels_ini.line_count("levels");
30 	for (size_t i = 0; i != m_num_levels; ++i) {
31 		const char* section;
32 		m_levels_ini.r_line("levels", i, &section, 0);
33 		if (!m_levels_ini.line_exist(section, "name")) {
34 			msg("can't read level name in section %s", section);
35 			throw sync_error();
36 		}
37 	}
38 	m_scan_done = true;
39 }
40 
level_name(size_t level_idx) const41 const char* syncer::level_name(size_t level_idx) const
42 {
43 	const char* section;
44 	m_levels_ini.r_line("levels", level_idx, &section, 0);
45 	return m_levels_ini.r_string(section, "name");
46 }
47 
check_or_create_fs_path(const char * path,bool & status,bool may_create=false)48 static void check_or_create_fs_path(const char* path, bool& status, bool may_create = false)
49 {
50 	xr_file_system& fs = xr_file_system::instance();
51 	if (!fs.folder_exist(path, "")) {
52 		if (may_create && status) {
53 			if (fs.create_folder(path, ""))
54 				return;
55 			msg("can't create path %s", path);
56 		} else {
57 			msg("path %s does not exist", path);
58 		}
59 		status = false;
60 	}
61 }
62 
check_fs_paths() const63 void syncer::check_fs_paths() const
64 {
65 	bool status = true;
66 	check_or_create_fs_path(PA_GAME_DATA, status);
67 	check_or_create_fs_path(PA_GAME_CONFIG, status);
68 	check_or_create_fs_path(PA_GAME_SPAWN, status);
69 	check_or_create_fs_path(PA_GAME_LEVELS, status);
70 	check_or_create_fs_path(PA2215_FS_ROOT, status);
71 	check_or_create_fs_path(PA2215_GAME_DATA, status, true);
72 	check_or_create_fs_path(PA2215_GAME_CONFIG, status, true);
73 	check_or_create_fs_path(PA2215_GAME_SPAWN, status, true);
74 	check_or_create_fs_path(PA2215_GAME_LEVELS, status, true);
75 	if (!status)
76 		throw sync_error();
77 }
78 
load_ini(const char * name)79 void syncer::load_ini(const char* name)
80 {
81 	msg("loading %s", name);
82 	if (!m_ini.load(name) && !m_ini.load(PA_SDK_ROOT, name))
83 		throw sync_error();
84 }
85 
load_links_ini(const char * name)86 void syncer::load_links_ini(const char* name)
87 {
88 	msg("loading %s", name);
89 	if (!m_links_ini.load(name))
90 		throw sync_error();
91 }
92 
split_spawns(bool use_orig_gp) const93 void syncer::split_spawns(bool use_orig_gp) const
94 {
95 	xr_game_spawn gspawn;
96 	msg("loading %s\\%s", PA_GAME_SPAWN, "all.spawn");
97 	if (!gspawn.load(PA_GAME_SPAWN, "all.spawn"))
98 		throw sync_error();
99 
100 	msg("loading %s\\%s", PA_GAME_DATA, "game.graph");
101 	if (!gspawn.graph().load(PA_GAME_DATA, "game.graph"))
102 		throw sync_error();
103 
104 	xr_level_spawn* by_level_id[256];
105 	std::uninitialized_fill_n(by_level_id, xr_dim(by_level_id), static_cast<xr_level_spawn*>(0));
106 
107 	xr_file_system& fs = xr_file_system::instance();
108 
109 	msg("collecting %s", "graph points");
110 	for (const gg_level *it = gspawn.graph().levels(),
111 			*end = it + gspawn.graph().num_levels(); it != end; ++it) {
112 		const char* name = it->name.c_str();
113 		if (!fs.folder_exist(PA_GAME_LEVELS, name))
114 			continue;
115 
116 		xr_level_spawn* spawn = by_level_id[it->level_id];
117 		if (spawn == 0)
118 			by_level_id[it->level_id] = spawn = new xr_level_spawn;
119 
120 		fs.update_path(PA_LEVEL, PA_GAME_LEVELS, name);
121 #if 0
122 		msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, "level.spawn");
123 		if (!load_graph_points(*spawn, PA_LEVEL, "level.spawn"))
124 			throw sync_error();
125 #else
126 		bool make_aiw_bak = true;
127 		const char* level_spawn_name = "level.spawn";
128 		if (fs.file_exist(PA_LEVEL, "level.spawn.aiw_bak")) {
129 			level_spawn_name = "level.spawn.aiw_bak";
130 			make_aiw_bak = false;
131 		}
132 		if (make_aiw_bak) {
133 			msg("making %s\\%s\\%s", PA_GAME_LEVELS, name, "level.spawn.aiw_bak");
134 			if (!fs.copy_file(PA_LEVEL, "level.spawn", PA_LEVEL, "level.spawn.aiw_bak"))
135 				throw sync_error();
136 		}
137 		msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, level_spawn_name);
138 		if (!load_graph_points(*spawn, PA_LEVEL, level_spawn_name))
139 			throw sync_error();
140 #endif
141 	}
142 
143 	msg("sorting %s", "spawns");
144 	if (!split_spawns(gspawn, by_level_id))
145 		throw sync_error();
146 
147 	for (const gg_level *it = gspawn.graph().levels(),
148 			*end = it + gspawn.graph().num_levels(); it != end; ++it) {
149 		const char* name = it->name.c_str();
150 		if (!fs.folder_exist(PA_GAME_LEVELS, name))
151 			continue;
152 		for (const gg_level *it1 = gspawn.graph().levels(),
153 				*end1 = it1 + gspawn.graph().num_levels();
154 				it1 != end1; ++it1) {
155 			if (_stricmp(it1->name.c_str(), name) == 0) {
156 				xr_assert(it1->level_id < xr_dim(by_level_id));
157 				xr_level_spawn* spawn = by_level_id[it1->level_id];
158 				xr_assert(spawn);
159 				fs.update_path(PA_LEVEL, PA_GAME_LEVELS, name);
160 				msg("saving %s\\%s\\%s", PA_GAME_LEVELS, name, "level.spawn");
161 				if (!spawn->save(PA_LEVEL, "level.spawn"))
162 					throw sync_error();
163 				break;
164 			}
165 		}
166 	}
167 	delete_elements(by_level_id, xr_dim(by_level_id));
168 }
169 
split_paths() const170 void syncer::split_paths() const
171 {
172 	xr_game_spawn gspawn;
173 	msg("loading %s\\%s", PA_GAME_SPAWN, "all.spawn");
174 	if (!gspawn.load(PA_GAME_SPAWN, "all.spawn"))
175 		throw sync_error();
176 
177 	msg("loading %s\\%s", PA_GAME_DATA, "game.graph");
178 	if (!gspawn.load_graph(PA_GAME_DATA, "game.graph"))
179 		throw sync_error();
180 
181 	xr_level_game* by_level_id[256];
182 	std::uninitialized_fill_n(by_level_id, xr_dim(by_level_id), static_cast<xr_level_game*>(0));
183 
184 	xr_file_system& fs = xr_file_system::instance();
185 
186 	msg("collecting %s", "$rpoints");
187 	for (const gg_level *it = gspawn.graph().levels(),
188 			*end = it + gspawn.graph().num_levels(); it != end; ++it) {
189 		const char* name = it->name.c_str();
190 		if (!fs.folder_exist(PA_GAME_LEVELS, name))
191 			continue;
192 
193 		xr_level_game* game = by_level_id[it->level_id];
194 		if (game == 0)
195 			by_level_id[it->level_id] = game = new xr_level_game;
196 
197 		fs.update_path(PA_LEVEL, PA_GAME_LEVELS, name);
198 #if 0
199 		msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, "level.game");
200 		if (!load_mp_rpoints(*game, PA_LEVEL, "level.game"))
201 			throw sync_error();
202 #else
203 		bool make_aiw_bak = true;
204 		const char* level_game_name = "level.game";
205 		if (fs.file_exist(PA_LEVEL, "level.game.aiw_bak")) {
206 			level_game_name = "level.game.aiw_bak";
207 			make_aiw_bak = false;
208 		}
209 		if (make_aiw_bak) {
210 			msg("making %s\\%s\\%s", PA_GAME_LEVELS, name, "level.game.aiw_bak");
211 			if (!fs.copy_file(PA_LEVEL, "level.game", PA_LEVEL, "level.game.aiw_bak"))
212 				throw sync_error();
213 		}
214 		msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, level_game_name);
215 		if (!load_mp_rpoints(*game, PA_LEVEL, level_game_name))
216 			throw sync_error();
217 #endif
218 	}
219 
220 	msg("sorting %s", "paths");
221 	if (!split_paths(gspawn, by_level_id, m_ini))
222 		throw sync_error();
223 
224 	for (const gg_level *it = gspawn.graph().levels(),
225 			*end = it + gspawn.graph().num_levels(); it != end; ++it) {
226 		const char* name = it->name.c_str();
227 		if (!fs.folder_exist(PA_GAME_LEVELS, name))
228 			continue;
229 		for (const gg_level *it1 = gspawn.graph().levels(),
230 				*end1 = it1 + gspawn.graph().num_levels();
231 				it1 != end1; ++it1) {
232 			if (_stricmp(it1->name.c_str(), name) == 0) {
233 				xr_assert(it1->level_id < xr_dim(by_level_id));
234 				xr_level_game* game = by_level_id[it1->level_id];
235 				xr_assert(game);
236 				fs.update_path(PA_LEVEL, PA_GAME_LEVELS, name);
237 				msg("saving %s\\%s\\%s", PA_GAME_LEVELS, name, "level.game");
238 				if (!game->save(PA_LEVEL, "level.game"))
239 					throw sync_error();
240 				break;
241 			}
242 		}
243 	}
244 	delete_elements(by_level_id, xr_dim(by_level_id));
245 }
246 
check_paths() const247 void syncer::check_paths() const
248 {
249 	xr_game_spawn gspawn;
250 	msg("loading %s\\%s", PA_GAME_SPAWN, "all.spawn");
251 	if (!gspawn.load(PA_GAME_SPAWN, "all.spawn"))
252 		throw sync_error();
253 
254 	check_paths(gspawn);
255 }
256 
split_graphs()257 void syncer::split_graphs()
258 {
259 	msg("not yet implemented");
260 }
261 
dump_links(const char * path)262 void syncer::dump_links(const char* path)
263 {
264 	scan_levels();
265 
266 	xr_file_system& fs = xr_file_system::instance();
267 	xr_writer* w = fs.w_open(path);
268 	if (w == 0)
269 		throw sync_error();
270 	for (size_t i = 0; i != m_num_levels; ++i) {
271 		const char* name = level_name(i);
272 		if (!fs.folder_exist(PA_GAME_LEVELS, name))
273 			continue;
274 		fs.update_path(PA_LEVEL, PA_GAME_LEVELS, name);
275 		if (!fs.file_exist(PA_LEVEL, "level.spawn"))
276 			continue;
277 		xr_level_spawn spawn;
278 		msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, "level.spawn");
279 		if (!spawn.load(PA_LEVEL, "level.spawn"))
280 			throw sync_error();
281 		dump_links(spawn, *w, name);
282 	}
283 	fs.w_close(w);
284 }
285 
upgrade(int bld_ver)286 void syncer::upgrade(int bld_ver)
287 {
288 #if 0
289 	if (0) {
290 		xr_game_spawn gspawn;
291 		msg("loading %s\\%s", PA_GAME_SPAWN, "all.spawn.orig");
292 		if (!gspawn.load(PA_GAME_SPAWN, "all.spawn.orig"))
293 			throw sync_error();
294 		msg("saving %s\\%s", PA_GAME_SPAWN, "all.spawn.out");
295 		if (!gspawn.save(PA_GAME_SPAWN, "all.spawn.out"))
296 			throw sync_error();
297 	} else {
298 		bool status = true;
299 		check_or_create_fs_path(PA9_FS_ROOT, status);
300 		check_or_create_fs_path(PA9_GAME_DATA, status);
301 		check_or_create_fs_path(PA9_GAME_CONFIG, status);
302 		check_or_create_fs_path(PA9_GAME_SPAWN, status);
303 		if (!status)
304 			throw sync_error();
305 		load_system_ini(PA9_GAME_CONFIG);
306 		xr_game_spawn gspawn;
307 		msg("loading %s\\%s", PA9_GAME_SPAWN, "marsh.spawn");
308 		if (!gspawn.load(PA9_GAME_SPAWN, "marsh.spawn"))
309 			throw sync_error();
310 		msg("saving %s\\%s", PA9_GAME_SPAWN, "marsh.spawn.out");
311 		if (!gspawn.save(PA9_GAME_SPAWN, "marsh.spawn.out"))
312 			throw sync_error();
313 	}
314 	return;
315 #endif
316 
317 	uint32_t new_ai_version;
318 	if (bld_ver == 3120) {
319 		new_ai_version = AI_VERSION_9;
320 	} else if (bld_ver >= 3502) {
321 		new_ai_version = AI_VERSION_10;
322 	} else {
323 		msg("unsupported version");
324 		throw sync_error();
325 	}
326 
327 	bool status = true;
328 	check_or_create_fs_path(PA9_FS_ROOT, status);
329 	check_or_create_fs_path(PA9_GAME_DATA, status, true);
330 	check_or_create_fs_path(PA9_GAME_SPAWN, status, true);
331 	check_or_create_fs_path(PA9_GAME_LEVELS, status, true);
332 
333 	check_or_create_fs_path(PA10_FS_ROOT, status);
334 	check_or_create_fs_path(PA10_GAME_DATA, status, true);
335 	check_or_create_fs_path(PA10_GAME_SPAWN, status, true);
336 	check_or_create_fs_path(PA10_GAME_LEVELS, status, true);
337 
338 	if (!status)
339 		throw sync_error();
340 
341 	xr_game_spawn gspawn;
342 	msg("loading %s\\%s", PA_GAME_SPAWN, "all.spawn");
343 	if (!gspawn.load(PA_GAME_SPAWN, "all.spawn"))
344 		throw sync_error();
345 	msg("loading %s\\%s", PA_GAME_DATA, "game.graph");
346 	if (!gspawn.load_graph(PA_GAME_DATA, "game.graph"))
347 		throw sync_error();
348 	gspawn.graph().version() = gspawn.version() = new_ai_version;
349 
350 	if (bld_ver >= 3502) {
351 		msg("upgrading spawn version");
352 	char *PA_UPG_GAME_SPAWN = NULL;
353 	char *PA_UPG_GAME_LEVELS = NULL;
354 	char *PA_UPG_LEVEL = NULL;
355 
356 	if (bld_ver >= 3870) {
357 		load_system_ini(PA10_GAME_CONFIG);
358 		PA_UPG_GAME_LEVELS = (char *)PA10_GAME_LEVELS;
359 		PA_UPG_LEVEL = (char *)PA10_LEVEL;
360 		PA_UPG_GAME_SPAWN = (char *)PA10_GAME_SPAWN;
361 		msg("upgrading spawn version to CoP format");
362 		xr_entity_vec* gs = new xr_entity_vec;
363 		int i = 0;
364 		for (xr_entity_vec_it it = gspawn.spawns().begin(),
365 				end = gspawn.spawns().end(); it != end; ++it) {
366 			cse_abstract* entity = *it;
367 
368 			if (get_entity_clsid(entity->name()) != &entity->clsid())
369 			{
370 				cse_abstract* new_entity = create_entity(entity->name());
371 				xr_packet packet;
372 
373 				entity->spawn_write(packet, true);
374 				new_entity->spawn_read(packet);
375 
376 				packet.clear();
377 				entity->update_write(packet);
378 				new_entity->update_read(packet);
379 
380 				new_entity->version() = CSE_VERSION_COP;
381 				new_entity->script_version() = 12;
382 				new_entity->spawn_id() = i++;
383 
384 				gs->push_back(new_entity);
385 			}
386 			else
387 			{
388 				entity->version() = CSE_VERSION_COP;
389 				entity->script_version() = 12;
390 				entity->spawn_id() = i++;
391 
392 				gs->push_back(entity);
393 			}
394 		}
395 		gspawn.spawns() = *gs;
396 	}
397 	else if (bld_ver >= 3502) {
398 		load_system_ini(PA9_GAME_CONFIG);
399 		PA_UPG_GAME_LEVELS = (char *)PA9_GAME_LEVELS;
400 		PA_UPG_LEVEL = (char *)PA9_LEVEL;
401 		PA_UPG_GAME_SPAWN = (char *)PA9_GAME_SPAWN;
402 		msg("upgrading spawn version to CS format");
403 		xr_entity_vec* gs = new xr_entity_vec;
404 		int i = 0;
405 		for (xr_entity_vec_it it = gspawn.spawns().begin(),
406 				end = gspawn.spawns().end(); it != end; ++it) {
407 			cse_abstract* entity = *it;
408 			entity->version() = CSE_VERSION_CS;
409 			entity->script_version() = 8;
410 		}
411 	}
412 
413 	xr_file_system& fs = xr_file_system::instance();
414 	for (const gg_level *it = gspawn.graph().levels(),
415 			*end = it + gspawn.graph().num_levels(); it != end; ++it) {
416 		const char* name = it->name.c_str();
417 
418 		if (!fs.folder_exist(PA_GAME_LEVELS, name))
419 			continue;
420 
421 		if (!fs.folder_exist(PA_UPG_GAME_LEVELS, name)) {
422 			msg("creating %s\\%s\\", PA_UPG_GAME_LEVELS, name);
423 			if (!fs.create_folder(PA_UPG_GAME_LEVELS, name))
424 				throw sync_error();
425 		}
426 
427 		fs.update_path(PA_LEVEL, PA_GAME_LEVELS, name);
428 		fs.update_path(PA_UPG_LEVEL, PA_UPG_GAME_LEVELS, name);
429 
430 		xr_level_ai ai;
431 		msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, "level.ai");
432 		if (!ai.load(PA_LEVEL, "level.ai"))
433 			throw sync_error();
434 		ai.version() = new_ai_version;
435 		msg("saving %s\\%s\\%s", PA_UPG_GAME_LEVELS, name, "level.ai");
436 		if (!ai.save(PA_UPG_LEVEL, "level.ai"))
437 			throw sync_error();
438 
439 		xr_level_gct gct;
440 		msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, "level.gct");
441 		if (!gct.load(PA_LEVEL, "level.gct"))
442 			throw sync_error();
443 		msg("embedding cross table");
444 		if (!import_cross_table(gspawn, gct))
445 			throw sync_error();
446 
447 		if (bld_ver >= 3456) {
448 			xr_level_env_mod env_mod;
449 			msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, "level.env_mod");
450 			if (env_mod.load(PA_LEVEL, "level.env_mod")) {
451 				env_mod.version() = ENV_MOD_VERSION_23;
452 				msg("saving %s\\%s\\%s", PA_UPG_GAME_LEVELS, name, "level.env_mod");
453 				if (!env_mod.save(PA_UPG_LEVEL, "level.env_mod"))
454 					throw sync_error();
455 			}
456 
457 			xr_level_ps_static ps_static;
458 			msg("loading %s\\%s\\%s", PA_GAME_LEVELS, name, "level.ps_static");
459 			if (ps_static.load(PA_LEVEL, "level.ps_static")) {
460 				ps_static.version() = PS_VERSION_1;
461 				msg("saving %s\\%s\\%s", PA_UPG_GAME_LEVELS, name, "level.ps_static");
462 				if (!ps_static.save(PA_UPG_LEVEL, "level.ps_static"))
463 					throw sync_error();
464 			}
465 		}
466 	}
467 //	msg("moving actor to military");
468 //	move_actor(gspawn.spawns(), -334.578491210938f, -25.5103607177734f, 45.0102348327637f, 16231, 1546);
469 	msg("saving %s\\%s", PA_UPG_GAME_SPAWN, "all.spawn");
470 	if (!gspawn.save(PA_UPG_GAME_SPAWN, "all.spawn"))
471 		throw sync_error();
472 	}
473 }
474 
sync_build_aimap()475 void syncer::sync_build_aimap()
476 {
477 	if (up_to_date(m_src_level, "build.aimap", m_tgt_level)) {
478 		msg("%s\\%s is up to date", m_tgt_level_path, "build.aimap");
479 	} else {
480 		msg("updating %s\\%s", m_tgt_level_path, "build.aimap");
481 		xr_file_system& fs = xr_file_system::instance();
482 		if (!fs.copy_file(m_src_level, "build.aimap", m_tgt_level))
483 			throw sync_error();
484 	}
485 }
486 
sync_build_cform()487 void syncer::sync_build_cform()
488 {
489 	if (up_to_date(m_src_level, "build.cform", m_tgt_level)) {
490 		msg("%s\\%s is up to date", m_tgt_level_path, "build.cform");
491 	} else {
492 		msg("updating %s\\%s", m_tgt_level_path, "build.cform");
493 		xr_file_system& fs = xr_file_system::instance();
494 		if (!fs.copy_file(m_src_level, "build.cform", m_tgt_level))
495 			throw sync_error();
496 	}
497 }
498 
sync_build_prj()499 void syncer::sync_build_prj()
500 {
501 	if (up_to_date(m_src_level, "build.prj", m_tgt_level)) {
502 		msg("%s\\%s is up to date", m_tgt_level_path, "build.prj");
503 	} else {
504 		msg("updating %s\\%s", m_tgt_level_path, "build.prj");
505 		xr_file_system& fs = xr_file_system::instance();
506 		if (!fs.copy_file(m_src_level, "build.prj", m_tgt_level))
507 			throw sync_error();
508 	}
509 }
510 
sync_level_ai()511 void syncer::sync_level_ai()
512 {
513 	if (up_to_date(m_src_level, "level.ai", m_tgt_level)) {
514 		msg("%s\\%s is up to date", m_tgt_level_path, "level.ai");
515 	} else {
516 		msg("updating %s\\%s", m_tgt_level_path, "level.ai");
517 		xr_file_system& fs = xr_file_system::instance();
518 		if (!fs.copy_file(m_src_level, "level.ai", m_tgt_level))
519 			throw sync_error();
520 	}
521 }
522 
sync_level_gct()523 void syncer::sync_level_gct()
524 {
525 	if (up_to_date(m_src_level, "level.gct", m_tgt_level)) {
526 		msg("%s\\%s is up to date", m_tgt_level_path, "level.gct");
527 	} else {
528 		msg("updating %s\\%s", m_tgt_level_path, "level.gct");
529 		xr_file_system& fs = xr_file_system::instance();
530 		fs.copy_file(m_src_level, "level.gct", m_tgt_level);
531 	}
532 }
533 
sync_level_gct_raw()534 void syncer::sync_level_gct_raw()
535 {
536 	xr_file_system& fs = xr_file_system::instance();
537 	bool rebuild = !m_xrai_compat && !fs.file_exist(m_src_level, "level.gct.raw");
538 	if (up_to_date(m_src_level, rebuild ? "level.gct" : "level.gct.raw", m_tgt_level, "level.gct.raw")) {
539 		msg("%s\\%s is up to date", m_tgt_level_path, "level.gct.raw");
540 		return;
541 	}
542 	if (rebuild) {
543 		msg("rebuilding %s\\%s", m_tgt_level_path, "level.gct.raw");
544 		xr_level_gct gct;
545 		if (!gct.load(m_src_level, "level.gct"))
546 			throw sync_error();
547 		to_raw(gct);
548 		if (!gct.save(m_tgt_level, "level.gct.raw"))
549 			throw sync_error();
550 	} else {
551 		msg("updating %s\\%s", m_tgt_level_path, "level.gct.raw");
552 		fs.copy_file(m_src_level, "level.gct.raw", m_tgt_level);
553 	}
554 }
555 
sync_level_graph()556 void syncer::sync_level_graph()
557 {
558 	if (up_to_date(m_src_level, "level.graph", m_tgt_level)) {
559 		msg("%s\\%s is up to date", m_tgt_level_path, "level.graph");
560 	} else {
561 		msg("updating %s\\%s", m_tgt_level_path, "level.graph");
562 		xr_level_graph graph;
563 		if (!graph.load(m_src_level, "level.graph"))
564 			throw sync_error();
565 		graph.version() = m_xrai_compat ? AI_VERSION_8 : AI_VERSION_2215;
566 		if (!graph.save(m_tgt_level, "level.graph"))
567 			throw sync_error();
568 	}
569 }
570 
sync_level_spawn(const char * name)571 void syncer::sync_level_spawn(const char* name)
572 {
573 	xr_assert(!m_xrai_compat);
574 	if (m_links_ini.empty() && up_to_date(PA_LEVEL, "level.spawn", PA2215_LEVEL)) {
575 		msg("%s\\%s is up to date", m_tgt_level_path, "level.spawn");
576 	} else {
577 		msg("updating %s\\%s", m_tgt_level_path, "level.spawn");
578 		xr_level_spawn spawn;
579 		if (!spawn.load(PA_LEVEL, "level.spawn"))
580 			throw sync_error();
581 		edit_links(spawn, m_links_ini, name);
582 		to_xrai_compat(spawn, m_ini, "entity_compat");
583 		if (!spawn.save(PA2215_LEVEL, "level.spawn"))
584 			throw sync_error();
585 	}
586 }
587 
sync_level_sectors_ai()588 void syncer::sync_level_sectors_ai()
589 {
590 	if (up_to_date(m_src_level, "level_sectors.ai", m_tgt_level)) {
591 		msg("%s\\%s is up to date", m_tgt_level_path, "level_sectors.ai");
592 	} else {
593 		msg("updating %s\\%s", m_tgt_level_path, "level_sectors.ai");
594 		xr_file_system& fs = xr_file_system::instance();
595 		fs.copy_file(m_src_level, "level_sectors.ai", m_tgt_level);
596 	}
597 }
598 
sync_game_ltx(const char * name)599 void syncer::sync_game_ltx(const char* name)
600 {
601 	xr_assert(!m_xrai_compat);
602 	if (up_to_date(PA_GAME_CONFIG, name, PA2215_GAME_CONFIG)) {
603 		msg("%s\\%s is up to date", PA2215_GAME_CONFIG, name);
604 	} else {
605 		msg("updating %s\\%s", PA2215_GAME_CONFIG, name);
606 		xr_file_system& fs = xr_file_system::instance();
607 		fs.copy_file(PA_GAME_CONFIG, name, PA2215_GAME_CONFIG);
608 	}
609 }
610 
sync_game_graph()611 void syncer::sync_game_graph()
612 {
613 	if (up_to_date(m_src_game_data, "game.graph", m_tgt_game_data)) {
614 		msg("%s\\%s is up to date", m_tgt_game_data, "game.graph");
615 	} else {
616 		msg("updating %s\\%s", m_tgt_game_data, "game.graph");
617 		xr_game_graph graph;
618 		if (!graph.load(m_src_game_data, "game.graph"))
619 			throw sync_error();
620 		graph.version() = m_xrai_compat ? AI_VERSION_8 : AI_VERSION_2215;
621 		if (!graph.save(m_tgt_game_data, "game.graph"))
622 			throw sync_error();
623 	}
624 }
625 
sync_game_spawn()626 void syncer::sync_game_spawn()
627 {
628 	xr_assert(m_xrai_compat);
629 	m_actor_level = 0;
630 	xr_game_spawn gspawn;
631 	xr_file_system& fs = xr_file_system::instance();
632 	for (size_t i = 0; i != m_num_levels; ++i) {
633 		const char* name = level_name(i);
634 		if (!fs.folder_exist(PA_GAME_LEVELS, name))
635 			continue;
636 
637 		fs.update_path(PA_LEVEL, PA_GAME_LEVELS, name);
638 		if (!fs.file_exist(PA_LEVEL, "level.spawn") ||
639 				!fs.file_exist(PA_LEVEL, "level.game")) {
640 			continue;
641 		}
642 
643 		update_tgt_level_path(name);
644 
645 		xr_level_spawn spawn;
646 		msg("loading %s\\%s", m_tgt_level_path, "level.spawn");
647 		if (!spawn.load(PA_LEVEL, "level.spawn"))
648 			throw sync_error();
649 
650 		xr_level_game game;
651 		msg("loading %s\\%s", m_tgt_level_path, "level.game");
652 		if (!game.load(PA_LEVEL, "level.game"))
653 			throw sync_error();
654 
655 		xr_level_ai ai;
656 		msg("loading %s\\%s", m_tgt_level_path, "level.ai");
657 		if (!ai.load(PA_LEVEL, "level.ai"))
658 			throw sync_error();
659 
660 		xr_level_gct gct;
661 		msg("loading %s\\%s", m_tgt_level_path, "level.gct");
662 		if (!gct.load(PA_LEVEL, "level.gct"))
663 			throw sync_error();
664 
665 		msg("adding %s to game spawn", name);
666 		if (!add_level(gspawn, name, spawn, game, ai, gct))
667 			throw sync_error();
668 	}
669 	if (m_actor_level == 0) {
670 		msg("can't find %s", "actor");
671 		throw sync_error();
672 	}
673 	char gs2215[256];
674 	xr_snprintf(gs2215, sizeof(gs2215), "%s.spawn", m_actor_level);
675 	if (up_to_date(PA2215_GAME_SPAWN, gs2215, PA_GAME_SPAWN, "all.spawn")) {
676 		msg("%s\\%s is up to date", PA_GAME_SPAWN, "all.spawn");
677 		return;
678 	}
679 	msg("merging xrAI-generated %s\\%s", PA2215_GAME_SPAWN, gs2215);
680 	if (!merge_xrai_compat(gspawn, PA2215_GAME_SPAWN, gs2215))
681 		throw sync_error();
682 
683 	msg("saving %s\\%s", PA_GAME_DATA, "all.spawn");
684 	gspawn.graph().version() = gspawn.version() = AI_VERSION_8;
685 	if (!gspawn.save(PA_GAME_SPAWN, "all.spawn"))
686 		throw sync_error();
687 }
688 
up_to_date(const char * src_path,const char * src_name,const char * tgt_path,const char * tgt_name)689 bool syncer::up_to_date(const char* src_path, const char* src_name,
690 		const char* tgt_path, const char* tgt_name)
691 {
692 	if (m_flags & SYNC_FORCE)
693 		return false;
694 	if (tgt_name == 0)
695 		tgt_name = src_name;
696 	xr_file_system& fs = xr_file_system::instance();
697 	uint32_t src_age = fs.file_age(src_path, src_name);
698 	uint32_t tgt_age = fs.file_age(tgt_path, tgt_name);
699 	return src_age <= tgt_age;
700 }
701 
set_target(sync_target target,unsigned flags)702 void syncer::set_target(sync_target target, unsigned flags)
703 {
704 	m_flags = flags;
705 	if (target == SYNC_TARGET_FINAL) {
706 		m_xrai_compat = true;
707 		m_src_game_data = PA2215_GAME_DATA;
708 		m_src_game_spawn = PA2215_GAME_SPAWN;
709 		m_src_game_levels = PA2215_GAME_LEVELS;
710 		m_src_level = PA2215_LEVEL;
711 		m_tgt_game_data = PA_GAME_DATA;
712 		m_tgt_game_spawn = PA_GAME_SPAWN;
713 		m_tgt_game_levels = PA_GAME_LEVELS;
714 		m_tgt_level = PA_LEVEL;
715 	} else {
716 		m_xrai_compat = false;
717 		m_src_game_data = PA_GAME_DATA;
718 		m_src_game_spawn = PA_GAME_SPAWN;
719 		m_src_game_levels = PA_GAME_LEVELS;
720 		m_src_level = PA_LEVEL;
721 		m_tgt_game_data = PA2215_GAME_DATA;
722 		m_tgt_game_spawn = PA2215_GAME_SPAWN;
723 		m_tgt_game_levels = PA2215_GAME_LEVELS;
724 		m_tgt_level = PA2215_LEVEL;
725 	}
726 }
727 
update_tgt_level_path(const char * name)728 void syncer::update_tgt_level_path(const char* name)
729 {
730 	xr_snprintf(m_tgt_level_path, sizeof(m_tgt_level_path),
731 			"%s\\%s", m_tgt_game_levels, name);
732 }
733 
do_sync(const char * name)734 void syncer::do_sync(const char* name)
735 {
736 	xr_file_system& fs = xr_file_system::instance();
737 	if (!fs.folder_exist(m_src_game_levels, name))
738 		return;
739 	if (!fs.folder_exist(m_tgt_game_levels, name)) {
740 		msg("creating %s\\%s\\", m_tgt_game_levels, name);
741 		if (!fs.create_folder(m_tgt_game_levels, name))
742 			throw sync_error();
743 	}
744 	update_tgt_level_path(name);
745 	fs.update_path(m_src_level, m_src_game_levels, name);
746 	fs.update_path(m_tgt_level, m_tgt_game_levels, name);
747 	if (m_flags & SYNC_BUILD_AIMAP)
748 		sync_build_aimap();
749 	if (m_flags & SYNC_BUILD_CFORM)
750 		sync_build_cform();
751 	if (m_flags & SYNC_BUILD_PRJ)
752 		sync_build_prj();
753 	if (m_flags & SYNC_LEVEL_AI)
754 		sync_level_ai();
755 	if (m_flags & SYNC_LEVEL_GCT)
756 		sync_level_gct();
757 	if (m_flags & SYNC_LEVEL_GCT_RAW)
758 		sync_level_gct_raw();
759 	if (m_flags & SYNC_LEVEL_GRAPH)
760 		sync_level_graph();
761 	if (m_flags & SYNC_LEVEL_SPAWN)
762 		sync_level_spawn(name);
763 	if (m_flags & SYNC_LEVEL_SECTORS_AI)
764 		sync_level_sectors_ai();
765 }
766 
do_sync()767 void syncer::do_sync()
768 {
769 	if (m_flags & SYNC_GAME_GRAPH)
770 		sync_game_graph();
771 	if (m_flags & SYNC_GAME_SPAWN)
772 		sync_game_spawn();
773 	if (m_flags & SYNC_LEVEL) {
774 		scan_levels();
775 		for (size_t i = 0; i != m_num_levels; ++i)
776 			do_sync(level_name(i));
777 		if (m_flags & SYNC_GAME_LTX) {
778 			sync_game_ltx("game_graphs.ltx");
779 			sync_game_ltx("game_levels.ltx");
780 			sync_game_ltx("game_story_ids.ltx");
781 		}
782 	}
783 }
784 
to_xrai(unsigned flags)785 void syncer::to_xrai(unsigned flags)
786 {
787 	set_target(SYNC_TARGET_XRAI, flags|SYNC_GAME_LTX);
788 	do_sync();
789 }
790 
from_xrai(unsigned flags)791 void syncer::from_xrai(unsigned flags)
792 {
793 	set_target(SYNC_TARGET_FINAL, flags);
794 	do_sync();
795 }
796 
to_xrai(const char * name,unsigned flags)797 void syncer::to_xrai(const char* name, unsigned flags)
798 {
799 	set_target(SYNC_TARGET_XRAI, flags|SYNC_GAME_LTX);
800 	do_sync(name);
801 }
802 
from_xrai(const char * name,unsigned flags)803 void syncer::from_xrai(const char* name, unsigned flags)
804 {
805 	set_target(SYNC_TARGET_FINAL, flags);
806 	do_sync(name);
807 }
808