Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
CampaignSaveGame.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: CampaignSaveGame.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  CampaignSaveGame contains the logic needed to save and load
13  campaign games in progress.
14 */
15 
16 #include "MemDebug.h"
17 #include "CampaignSaveGame.h"
18 #include "Campaign.h"
19 #include "Combatant.h"
20 #include "CombatAction.h"
21 #include "CombatEvent.h"
22 #include "CombatGroup.h"
23 #include "CombatUnit.h"
24 #include "CombatZone.h"
25 #include "Galaxy.h"
26 #include "Mission.h"
27 #include "StarSystem.h"
28 #include "Player.h"
29 
30 #include "Game.h"
31 #include "DataLoader.h"
32 #include "ParseUtil.h"
33 #include "FormatUtil.h"
34 
35 static const char* SAVE_DIR = "SaveGame";
36 
37 // +--------------------------------------------------------------------+
38 
40 : campaign(c)
41 { }
42 
43 // +--------------------------------------------------------------------+
44 
46 { }
47 
48 // +--------------------------------------------------------------------+
49 
50 Text
51 CampaignSaveGame::GetSaveDirectory()
52 {
53  return GetSaveDirectory(Player::GetCurrentPlayer());
54 }
55 
56 Text
57 CampaignSaveGame::GetSaveDirectory(Player* player)
58 {
59  if (player) {
60  char save_dir[32];
61  sprintf_s(save_dir, "%s/%02d", SAVE_DIR, player->Identity());
62 
63  return save_dir;
64  }
65 
66  return SAVE_DIR;
67 }
68 
69 void
70 CampaignSaveGame::CreateSaveDirectory()
71 {
72  HANDLE hDir = CreateFile(SAVE_DIR, 0, 0, 0, OPEN_EXISTING, 0, 0);
73 
74  if (hDir == INVALID_HANDLE_VALUE)
75  CreateDirectory(SAVE_DIR, NULL);
76  else
77  CloseHandle(hDir);
78 
79  hDir = CreateFile(GetSaveDirectory(), 0, 0, 0, OPEN_EXISTING, 0, 0);
80 
81  if (hDir == INVALID_HANDLE_VALUE)
82  CreateDirectory(GetSaveDirectory(), NULL);
83  else
84  CloseHandle(hDir);
85 }
86 
87 // +--------------------------------------------------------------------+
88 
89 static char multiline[4096];
90 static char* FormatMultiLine(const char* s)
91 {
92  int i = 4095;
93  char* p = multiline;
94 
95  while (*s && i > 0) {
96  if (*s == '\n') {
97  *p++ = '\\';
98  *p++ = 'n';
99  s++;
100  i -= 2;
101  }
102  else if (*s == '"') {
103  *p++ = '\\';
104  *p++ = '"';
105  s++;
106  i -= 2;
107  }
108  else {
109  *p++ = *s++;
110  i--;
111  }
112  }
113 
114  *p = 0;
115  return multiline;
116 }
117 
118 static char* ParseMultiLine(const char* s)
119 {
120  int i = 4095;
121  char* p = multiline;
122 
123  while (*s && i > 0) {
124  if (*s == '\\') {
125  s++;
126  if (*s == 'n') {
127  *p++ = '\n';
128 #pragma warning(suppress: 6269)
129  *s++;
130  i--;
131  }
132  else if (*s == '"') {
133  *p++ = '"';
134 #pragma warning(suppress: 6269)
135  *s++;
136  i--;
137  }
138  else {
139  *p++ = *s++;
140  i--;
141  }
142  }
143  else {
144  *p++ = *s++;
145  i--;
146  }
147  }
148 
149  *p = 0;
150  return multiline;
151 }
152 
153 void
154 CampaignSaveGame::Load(const char* filename)
155 {
156  Print("-------------------------\nLOADING SAVEGAME (%s).\n", filename);
157  campaign = 0;
158 
159  if (!filename || !filename[0]) return;
160 
161  DataLoader* loader = DataLoader::GetLoader();
162  bool use_file_sys = loader->IsFileSystemEnabled();
163  loader->UseFileSystem(true);
164  loader->SetDataPath(GetSaveDirectory() + "/");
165 
166  BYTE* block;
167  loader->LoadBuffer(filename, block, true);
168  loader->UseFileSystem(use_file_sys);
169 
170  Sleep(10);
171 
172  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
173  Term* term = parser.ParseTerm();
174 
175  if (!term) {
176  Print("ERROR: could not parse save game '%s'\n", filename);
177  loader->SetDataPath(0);
178  return;
179  }
180  else {
181  TermText* file_type = term->isText();
182  if (!file_type || file_type->value() != "SAVEGAME") {
183  Print("ERROR: invalid save game file '%s'\n", filename);
184  term->print(10);
185  loader->SetDataPath(0);
186  delete term;
187  return;
188  }
189  }
190 
191  int grp_iff = 0;
192  int grp_type = 0;
193  int grp_id = 0;
194  int status = 0;
195  double baseTime = 0;
196  double time = 0;
197  Text unit;
198  Text sitrep;
199  Text orders;
200 
201  do {
202  Sleep(5);
203  delete term; term = 0;
204  term = parser.ParseTerm();
205 
206  if (term) {
207  TermDef* def = term->isDef();
208  if (def) {
209  if (def->name()->value() == "campaign") {
210  Text cname;
211  int cid=0;
212 
213  if (def->term()) {
214  if (def->term()->isText())
215  cname = def->term()->isText()->value();
216  else if (def->term()->isNumber())
217  cid = (int) def->term()->isNumber()->value();
218  }
219 
220  if (!campaign) {
222 
223  for (int i = 0; i < list.size() && !campaign; i++) {
224  Campaign* c = list.at(i);
225 
226  if (cname == c->Name() || cid == c->GetCampaignId()) {
227  campaign = c;
228  campaign->Load();
229  campaign->Prep(); // restore campaign to pristine state
230 
231  loader->SetDataPath(GetSaveDirectory() + "/");
232  }
233  }
234  }
235  }
236 
237  else if (def->name()->value() == "grp_iff") {
238  GetDefNumber(grp_iff, def, filename);
239  }
240 
241  else if (def->name()->value() == "grp_type") {
242  GetDefNumber(grp_type, def, filename);
243  }
244 
245  else if (def->name()->value() == "grp_id") {
246  GetDefNumber(grp_id, def, filename);
247  }
248 
249  else if (def->name()->value() == "unit") {
250  GetDefText(unit, def, filename);
251  }
252 
253  else if (def->name()->value() == "status") {
254  GetDefNumber(status, def, filename);
255  }
256 
257  else if (def->name()->value() == "basetime") {
258  GetDefNumber(baseTime, def, filename);
259  }
260 
261  else if (def->name()->value() == "time") {
262  GetDefNumber(time, def, filename);
263  }
264 
265  else if (def->name()->value() == "sitrep") {
266  GetDefText(sitrep, def, filename);
267  }
268 
269  else if (def->name()->value() == "orders") {
270  GetDefText(orders, def, filename);
271  }
272 
273  else if (def->name()->value() == "combatant") {
274  if (!def->term() || !def->term()->isStruct()) {
275  ::Print("WARNING: combatant struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
276  }
277  else {
278  TermStruct* val = def->term()->isStruct();
279 
280  char name[64];
281  int iff = 0;
282  int score = 0;
283 
284  ZeroMemory(name, sizeof(name));
285 
286  for (int i = 0; i < val->elements()->size(); i++) {
287  TermDef* pdef = val->elements()->at(i)->isDef();
288  if (pdef) {
289  if (pdef->name()->value() == "name")
290  GetDefText(name, pdef, filename);
291 
292  else if (pdef->name()->value() == "iff") {
293  GetDefNumber(iff, pdef, filename);
294  }
295 
296  else if (pdef->name()->value() == "score") {
297  GetDefNumber(score, pdef, filename);
298  }
299  }
300  }
301 
302  if (campaign && name[0]) {
303  Combatant* combatant = campaign->GetCombatant(name);
304 
305  if (combatant) {
306  CombatGroup::MergeOrderOfBattle(block, filename, iff, combatant, campaign);
307  combatant->SetScore(score);
308  }
309  else {
310  ::Print("WARNING: could not find combatant '%s' in campaign.\n", name);
311  }
312  }
313  }
314  }
315  else if (def->name()->value() == "event") {
316  if (!def->term() || !def->term()->isStruct()) {
317  ::Print("WARNING: event struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
318  }
319  else {
320  TermStruct* val = def->term()->isStruct();
321 
322  char type[64];
323  char source[64];
324  char region[64];
325  char title[256];
326  char file[256];
327  char image[256];
328  char scene[256];
329  char info[4096];
330  int time = 0;
331  int team = 0;
332  int points = 0;
333 
334  type[0] = 0;
335  info[0] = 0;
336  file[0] = 0;
337  image[0] = 0;
338  scene[0] = 0;
339  title[0] = 0;
340  region[0] = 0;
341  source[0] = 0;
342 
343  for (int i = 0; i < val->elements()->size(); i++) {
344  TermDef* pdef = val->elements()->at(i)->isDef();
345  if (pdef) {
346  if (pdef->name()->value() == "type")
347  GetDefText(type, pdef, filename);
348 
349  else if (pdef->name()->value() == "source")
350  GetDefText(source, pdef, filename);
351 
352  else if (pdef->name()->value() == "region")
353  GetDefText(region, pdef, filename);
354 
355  else if (pdef->name()->value() == "title")
356  GetDefText(title, pdef, filename);
357 
358  else if (pdef->name()->value() == "file")
359  GetDefText(file, pdef, filename);
360 
361  else if (pdef->name()->value() == "image")
362  GetDefText(image, pdef, filename);
363 
364  else if (pdef->name()->value() == "scene")
365  GetDefText(scene, pdef, filename);
366 
367  else if (pdef->name()->value() == "info")
368  GetDefText(info, pdef, filename);
369 
370  else if (pdef->name()->value() == "time")
371  GetDefNumber(time, pdef, filename);
372 
373  else if (pdef->name()->value() == "team")
374  GetDefNumber(team, pdef, filename);
375 
376  else if (pdef->name()->value() == "points")
377  GetDefNumber(points, pdef, filename);
378  }
379  }
380 
381  if (campaign && type[0]) {
382  loader->SetDataPath(campaign->Path());
383 
384  CombatEvent* event = new(__FILE__,__LINE__)
385  CombatEvent(campaign,
387  time,
388  team,
390  region);
391 
392  if (event) {
393  event->SetTitle(title);
394  event->SetFilename(file);
395  event->SetImageFile(image);
396  event->SetSceneFile(scene);
397  event->Load();
398 
399  if (info[0])
400  event->SetInformation(ParseMultiLine(info));
401 
402  event->SetVisited(true);
403  campaign->GetEvents().append(event);
404  }
405  }
406  }
407  }
408  else if (def->name()->value() == "action") {
409  if (!def->term() || !def->term()->isStruct()) {
410  ::Print("WARNING: action struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
411  }
412  else {
413  TermStruct* val = def->term()->isStruct();
414 
415  int id = -1;
416  int stat = 0;
417  int count = 0;
418  int after = 0;
419 
420  for (int i = 0; i < val->elements()->size(); i++) {
421  TermDef* pdef = val->elements()->at(i)->isDef();
422  if (pdef) {
423  if (pdef->name()->value() == "id")
424  GetDefNumber(id, pdef, filename);
425 
426  else if (pdef->name()->value() == "stat")
427  GetDefNumber(stat, pdef, filename);
428 
429  else if (pdef->name()->value() == "count")
430  GetDefNumber(count, pdef, filename);
431 
432  else if (pdef->name()->value().contains("after"))
433  GetDefNumber(after, pdef, filename);
434  }
435  }
436 
437  if (campaign && id >= 0) {
438  ListIter<CombatAction> a_iter = campaign->GetActions();
439  while (++a_iter) {
440  CombatAction* a = a_iter.value();
441  if (a->Identity() == id) {
442  a->SetStatus(stat);
443 
444  if (count)
445  a->SetCount(count);
446 
447  if (after)
448  a->SetStartAfter(after);
449 
450  break;
451  }
452  }
453  }
454  }
455  }
456  }
457  }
458  }
459  while (term);
460 
461  if (term) {
462  delete term;
463  term = 0;
464  }
465 
466  if (campaign) {
467  campaign->SetSaveGame(true);
468 
470 
471  if (status < Campaign::CAMPAIGN_SUCCESS) {
472  campaign->SetStatus(status);
473  if (sitrep.length()) campaign->SetSituation(sitrep);
474  if (orders.length()) campaign->SetOrders(orders);
475  campaign->SetStartTime(baseTime);
476  campaign->SetLoadTime(baseTime + time);
477  campaign->LockoutEvents(3600);
478  campaign->Start();
479 
480  if (grp_type >= CombatGroup::FLEET && grp_type <= CombatGroup::PRIVATE) {
481  CombatGroup* player_group = campaign->FindGroup(grp_iff, grp_type, grp_id);
482  if (player_group) {
483  CombatUnit* player_unit = 0;
484 
485  if (unit.length())
486  player_unit = player_group->FindUnit(unit);
487 
488  if (player_unit)
489  campaign->SetPlayerUnit(player_unit);
490  else
491  campaign->SetPlayerGroup(player_group);
492  }
493  }
494  }
495 
496  // failed - restart current campaign:
497  else if (status == Campaign::CAMPAIGN_FAILED) {
498  Print("CampaignSaveGame: Loading FAILED campaign, restarting '%s'\n",
499  campaign->Name());
500 
501  campaign->Load();
502  campaign->Prep(); // restore campaign to pristine state
503  campaign->SetSaveGame(false);
504 
505  loader->SetDataPath(GetSaveDirectory() + "/");
506  }
507 
508  // start next campaign:
509  else if (status == Campaign::CAMPAIGN_SUCCESS) {
510  Print("CampaignSaveGame: Loading COMPLETED campaign '%s', searching for next campaign...\n",
511  campaign->Name());
512 
513  bool found = false;
514 
515  for (int i = 0; i < list.size() && !found; i++) {
516  Campaign* c = list.at(i);
517 
518  if (c->GetCampaignId() == campaign->GetCampaignId()+1) {
519  campaign = c;
520  campaign->Load();
521  campaign->Prep(); // restore campaign to pristine state
522 
523  Print("Advanced to campaign %d '%s'\n",
524  campaign->GetCampaignId(),
525  campaign->Name());
526 
527  loader->SetDataPath(GetSaveDirectory() + "/");
528  found = true;
529  }
530  }
531 
532  // if no next campaign found, start over from the beginning:
533  for (int i = 0; i < list.size() && !found; i++) {
534  Campaign* c = list.at(i);
535 
536  if (c->IsDynamic()) {
537  campaign = c;
538  campaign->Load();
539  campaign->Prep(); // restore campaign to pristine state
540 
541  Print("Completed full series, restarting at %d '%s'\n",
542  campaign->GetCampaignId(),
543  campaign->Name());
544 
545  loader->SetDataPath(GetSaveDirectory() + "/");
546  found = true;
547  }
548  }
549  }
550  }
551 
552  loader->ReleaseBuffer(block);
553  loader->SetDataPath(0);
554  Print("SAVEGAME LOADED (%s).\n\n", filename);
555 }
556 
557 // +--------------------------------------------------------------------+
558 
559 void
560 CampaignSaveGame::Save(const char* name)
561 {
562  if (!campaign) return;
563 
564  CreateSaveDirectory();
565 
566  Text s = GetSaveDirectory() + Text("/") + Text(name);
567 
568  FILE* f;
569  fopen_s(&f, s, "w");
570 
571  if (f) {
572  char timestr[32];
573  FormatDayTime(timestr, campaign->GetTime());
574 
575  CombatGroup* player_group = campaign->GetPlayerGroup();
576  CombatUnit* player_unit = campaign->GetPlayerUnit();
577 
578  fprintf(f, "SAVEGAME\n\n");
579  fprintf(f, "campaign: \"%s\"\n\n", campaign->Name());
580  fprintf(f, "grp_iff: %d\n", (int) player_group->GetIFF());
581  fprintf(f, "grp_type: %d\n", (int) player_group->Type());
582  fprintf(f, "grp_id: %d\n", (int) player_group->GetID());
583  if (player_unit)
584  fprintf(f, "unit: \"%s\"\n", player_unit->Name().data());
585 
586  fprintf(f, "status: %d\n", (int) campaign->GetStatus());
587  fprintf(f, "basetime: %f\n", campaign->GetStartTime());
588  fprintf(f, "time: %f // %s\n\n",
589  campaign->GetTime(),
590  timestr);
591 
592  fprintf(f, "sitrep: \"%s\"\n", campaign->Situation());
593  fprintf(f, "orders: \"%s\"\n\n", campaign->Orders());
594 
595  ListIter<Combatant> c_iter = campaign->GetCombatants();
596  while (++c_iter) {
597  Combatant* c = c_iter.value();
598 
599  fprintf(f, "combatant: {");
600  fprintf(f, " name:\"%s\",", c->Name());
601  fprintf(f, " iff:%d,", c->GetIFF());
602  fprintf(f, " score:%d,", c->Score());
603  fprintf(f, " }\n");
604  }
605 
606  fprintf(f, "\n");
607 
608  ListIter<CombatAction> a_iter = campaign->GetActions();
609  while (++a_iter) {
610  CombatAction* a = a_iter.value();
611  fprintf(f, "action: { id:%4d, stat:%d", a->Identity(), a->Status());
612 
613  if (a->Status() == CombatAction::PENDING) {
614  if (a->Count())
615  fprintf(f, ", count:%d", a->Count());
616 
617  if (a->StartAfter())
618  fprintf(f, ", after:%d", a->StartAfter());
619  }
620 
621  fprintf(f, " }\n");
622  }
623 
624  fprintf(f, "\n");
625 
626  ListIter<CombatEvent> e_iter = campaign->GetEvents();
627  while (++e_iter) {
628  CombatEvent* e = e_iter.value();
629 
630  fprintf(f, "event: {");
631  fprintf(f, " type:%-18s,", e->TypeName());
632  fprintf(f, " time:0x%08x,", e->Time());
633  fprintf(f, " team:%d,", e->GetIFF());
634  fprintf(f, " points:%d,", e->Points());
635  fprintf(f, " source:\"%s\",", e->SourceName());
636  fprintf(f, " region:\"%s\",", e->Region());
637  fprintf(f, " title:\"%s\",", e->Title());
638  if (e->Filename())
639  fprintf(f, " file:\"%s\",", e->Filename());
640  if (e->ImageFile())
641  fprintf(f, " image:\"%s\",", e->ImageFile());
642  if (e->SceneFile())
643  fprintf(f, " scene:\"%s\",", e->SceneFile());
644  if (!e->Filename() || *e->Filename() == 0)
645  fprintf(f, " info:\"%s\"", FormatMultiLine(e->Information()));
646  fprintf(f, " }\n");
647  }
648 
649  fprintf(f, "\n// ORDER OF BATTLE:\n\n");
650  fclose(f);
651 
652  c_iter.reset();
653  while (++c_iter) {
654  Combatant* c = c_iter.value();
655  CombatGroup* g = c->GetForce();
657  }
658  }
659 }
660 
661 void
662 CampaignSaveGame::Delete(const char* name)
663 {
664  DeleteFile(GetSaveDirectory() + "/" + name);
665 }
666 
667 void
669 {
670  List<Text> save_list;
671  Text save_dir = GetSaveDirectory(p) + "/";
672 
673  DataLoader* loader = DataLoader::GetLoader();
674  bool use_file_sys = loader->IsFileSystemEnabled();
675  loader->UseFileSystem(true);
676  loader->SetDataPath(save_dir);
677  loader->ListFiles("*.*", save_list);
678  loader->SetDataPath(0);
679  loader->UseFileSystem(use_file_sys);
680 
681  for (int i = 0; i < save_list.size(); i++) {
682  Text* filename = save_list[i];
683  DeleteFile(save_dir + filename->data());
684  }
685 
686  save_list.destroy();
687 
688  RemoveDirectory(GetSaveDirectory(p));
689 }
690 
691 // +--------------------------------------------------------------------+
692 
693 void
695 {
696  Save("AutoSave");
697 }
698 
699 void
701 {
702  Load("AutoSave");
703 }
704 
705 // +--------------------------------------------------------------------+
706 
707 Text
709 {
710  // check for auto save game:
711  FILE* f;
712  ::fopen_s(&f, GetSaveDirectory() + "/AutoSave", "r");
713 
714  if (f) {
715  ::fclose(f);
716 
717  return "AutoSave";
718  }
719 
720  return Text();
721 }
722 
723 int
725 {
726  DataLoader* loader = DataLoader::GetLoader();
727  bool use_file_sys = loader->IsFileSystemEnabled();
728  loader->UseFileSystem(true);
729  loader->SetDataPath(GetSaveDirectory() + "/");
730  loader->ListFiles("*.*", save_list);
731  loader->SetDataPath(0);
732  loader->UseFileSystem(use_file_sys);
733 
734  return save_list.size();
735 }