1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6
7 This file is part of the OpenJK source code.
8
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
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, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22
23 // Filename:- g_savegame.cpp
24
25 #include "g_headers.h"
26
27 #ifndef _WIN32
28 #include <unistd.h>
29 #endif
30 #include "g_local.h"
31 #include "fields.h"
32 #include "objectives.h"
33 #include "../cgame/cg_camera.h"
34 #include "g_icarus.h"
35 #include "../../code/qcommon/sstring.h"
36 #include "../code/qcommon/ojk_saved_game_helper.h"
37
38 extern void OBJ_LoadTacticalInfo(void);
39
40 extern int Q3_VariableSave( void );
41 extern int Q3_VariableLoad( void );
42
43 extern void G_LoadSave_WriteMiscData(void);
44 extern void G_LoadSave_ReadMiscData(void);
45
46
47 static const field_t savefields_gEntity[] =
48 {
49 {strFOFS(client), F_GCLIENT},
50 {strFOFS(owner), F_GENTITY},
51 {strFOFS(classname), F_STRING},
52 {strFOFS(model), F_STRING},
53 {strFOFS(model2), F_STRING},
54 // {strFOFS(model3), F_STRING}, - MCG
55 {strFOFS(nextTrain), F_GENTITY},
56 {strFOFS(prevTrain), F_GENTITY},
57 {strFOFS(message), F_STRING},
58 {strFOFS(target), F_STRING},
59 {strFOFS(target2), F_STRING},
60 {strFOFS(target3), F_STRING},
61 {strFOFS(target4), F_STRING},
62 {strFOFS(targetname), F_STRING},
63 {strFOFS(team), F_STRING},
64 {strFOFS(roff), F_STRING},
65 // {strFOFS(target_ent), F_GENTITY}, - MCG
66 {strFOFS(chain), F_GENTITY},
67 {strFOFS(enemy), F_GENTITY},
68 {strFOFS(activator), F_GENTITY},
69 {strFOFS(teamchain), F_GENTITY},
70 {strFOFS(teammaster), F_GENTITY},
71 {strFOFS(item), F_ITEM},
72 {strFOFS(NPC_type), F_STRING},
73 {strFOFS(closetarget), F_STRING},
74 {strFOFS(opentarget), F_STRING},
75 {strFOFS(paintarget), F_STRING},
76 {strFOFS(NPC_targetname), F_STRING},
77 {strFOFS(NPC_target), F_STRING},
78 {strFOFS(ownername), F_STRING},
79 {strFOFS(lastEnemy), F_GENTITY},
80 {strFOFS(behaviorSet), F_BEHAVIORSET},
81 {strFOFS(script_targetname),F_STRING},
82 {strFOFS(sequencer), F_NULL}, // CSequencer *sequencer;
83 {strFOFS(taskManager), F_NULL}, // CTaskManager *taskManager;
84 {strFOFS(NPC), F_BOOLPTR},
85 {strFOFS(soundSet), F_STRING},
86 {strFOFS(cameraGroup), F_STRING},
87 {strFOFS(parms), F_BOOLPTR},
88 {strFOFS(fullName), F_STRING},
89 // {strFOFS(timers), F_BOOLPTR}, // handled directly
90
91 {NULL, 0, F_IGNORE}
92 };
93
94 static const field_t savefields_gNPC[] =
95 {
96 // {strNPCOFS(pendingEnemy), F_GENTITY},
97 {strNPCOFS(touchedByPlayer), F_GENTITY},
98 {strNPCOFS(aimingBeam), F_GENTITY},
99 {strNPCOFS(eventOwner), F_GENTITY},
100 {strNPCOFS(coverTarg), F_GENTITY},
101 {strNPCOFS(tempGoal), F_GENTITY},
102 {strNPCOFS(goalEntity), F_GENTITY},
103 {strNPCOFS(lastGoalEntity), F_GENTITY},
104 {strNPCOFS(eventualGoal), F_GENTITY},
105 {strNPCOFS(captureGoal), F_GENTITY},
106 {strNPCOFS(defendEnt), F_GENTITY},
107 {strNPCOFS(greetEnt), F_GENTITY},
108 {strNPCOFS(group), F_GROUP},
109
110 {NULL, 0, F_IGNORE}
111 };
112
113 static const field_t savefields_LevelLocals[] =
114 {
115 {strLLOFS(locationHead), F_GENTITY},
116 {strLLOFS(alertEvents), F_ALERTEVENT},
117 {strLLOFS(groups), F_AIGROUPS},
118 {NULL, 0, F_IGNORE}
119 };
120
121 /*
122 struct gclient_s {
123 // ps MUST be the first element, because the server expects it
124 ok playerState_t ps; // communicated by server to clients
125
126 // private to game
127 ok clientPersistant_t pers;
128 ok clientSession_t sess;
129
130 ok usercmd_t usercmd; // most recent usercmd
131
132 //Client info - updated when ClientInfoChanged is called, instead of using configstrings
133 ok clientInfo_t clientInfo;
134 ok renderInfo_t renderInfo;
135 };
136 */
137 // I'll keep a blank one for now in case I need to add anything...
138 //
139 static const field_t savefields_gClient[] =
140 {
141 {strCLOFS(ps.saberModel), F_STRING},
142 {strCLOFS(squadname), F_STRING},
143 {strCLOFS(team_leader), F_GENTITY},
144 {strCLOFS(leader), F_GENTITY},
145 {strCLOFS(follower), F_GENTITY},
146 {strCLOFS(formationGoal), F_GENTITY},
147 {strCLOFS(clientInfo.customBasicSoundDir),F_STRING},
148 {strCLOFS(clientInfo.customCombatSoundDir),F_STRING},
149 {strCLOFS(clientInfo.customExtraSoundDir),F_STRING},
150 {strCLOFS(clientInfo.customJediSoundDir),F_STRING},
151
152 {NULL, 0, F_IGNORE}
153 };
154
155
156 static std::list<sstring_t> strList;
157
158
159 /////////// char * /////////////
160 //
161 //
GetStringNum(const char * psString)162 int GetStringNum(const char *psString)
163 {
164 assert( psString != (char *)0xcdcdcdcd );
165
166 // NULL ptrs I'll write out as a strlen of -1...
167 //
168 if (!psString)
169 {
170 return -1;
171 }
172
173 strList.push_back( psString );
174 return strlen(psString) + 1; // this gives us the chunk length for the reader later
175 }
176
GetStringPtr(int iStrlen,char * psOriginal)177 char *GetStringPtr(int iStrlen, char *psOriginal/*may be NULL*/)
178 {
179 if (iStrlen != -1)
180 {
181 static char sString[768]; // arb, inc if nec.
182
183 memset(sString,0, sizeof(sString));
184
185 assert(iStrlen+1<=(int)sizeof(sString));
186
187 ojk::SavedGameHelper saved_game(
188 ::gi.saved_game);
189
190 saved_game.read_chunk(
191 INT_ID('S', 'T', 'R', 'G'),
192 sString,
193 iStrlen);
194
195 // we can't do string recycling with the new g_alloc pool dumping, so just always alloc here...
196 //
197 return G_NewString(sString);
198 }
199
200 return NULL;
201 }
202 //
203 //
204 ////////////////////////////////
205
206
207
208
209 /////////// gentity_t * ////////
210 //
211 //
GetGEntityNum(gentity_t * ent)212 intptr_t GetGEntityNum(gentity_t* ent)
213 {
214 assert( ent != (gentity_t *) 0xcdcdcdcd);
215
216 if (ent == NULL)
217 {
218 return -1;
219 }
220
221 // note that I now validate the return value (to avoid triggering asserts on re-load) because of the
222 // way that the level_locals_t alertEvents struct contains a count of which ones are valid, so I'm guessing
223 // that some of them aren't (valid)...
224 //
225 intptr_t iReturnIndex = ent - g_entities;
226
227 if (iReturnIndex < 0 || iReturnIndex >= MAX_GENTITIES)
228 {
229 iReturnIndex = -1; // will get a NULL ptr on reload
230 }
231 return iReturnIndex;
232 }
233
GetGEntityPtr(intptr_t iEntNum)234 gentity_t *GetGEntityPtr(intptr_t iEntNum)
235 {
236 if (iEntNum == -1)
237 {
238 return NULL;
239 }
240 assert(iEntNum >= 0);
241 assert(iEntNum < MAX_GENTITIES);
242 return (g_entities + iEntNum);
243 }
244
GetGroupNumber(AIGroupInfo_t * pGroup)245 static intptr_t GetGroupNumber(AIGroupInfo_t *pGroup)
246 {
247 assert( pGroup != (AIGroupInfo_t *) 0xcdcdcdcd);
248
249 if (pGroup == NULL)
250 {
251 return -1;
252 }
253
254 int iReturnIndex = pGroup - level.groups;
255 if (iReturnIndex < 0 || iReturnIndex >= (int)(sizeof(level.groups) / sizeof(level.groups[0])) )
256 {
257 iReturnIndex = -1; // will get a NULL ptr on reload
258 }
259 return iReturnIndex;
260 }
261
GetGroupPtr(intptr_t iGroupNum)262 static AIGroupInfo_t *GetGroupPtr(intptr_t iGroupNum)
263 {
264 if (iGroupNum == -1)
265 {
266 return NULL;
267 }
268 assert(iGroupNum >= 0);
269 assert( iGroupNum < (int)ARRAY_LEN( level.groups ) );
270 return (level.groups + iGroupNum);
271 }
272
273
274
275 /////////// gclient_t * ////////
276 //
277 //
GetGClientNum(gclient_t * c)278 intptr_t GetGClientNum(gclient_t *c)
279 {
280 assert(c != (gclient_t *)0xcdcdcdcd);
281
282 if (c == NULL)
283 {
284 return -1;
285 }
286
287 return (c - level.clients);
288 }
289
GetGClientPtr(intptr_t c)290 gclient_t *GetGClientPtr(intptr_t c)
291 {
292 if (c == -1)
293 {
294 return NULL;
295 }
296 if (c == -2)
297 {
298 return (gclient_t *) -2; // preserve this value so that I know to load in one of Mike's private NPC clients later
299 }
300
301 assert(c >= 0);
302 assert(c < level.maxclients);
303 return (level.clients + c);
304 }
305 //
306 //
307 ////////////////////////////////
308
309
310 /////////// gitem_t * //////////
311 //
312 //
GetGItemNum(gitem_t * pItem)313 int GetGItemNum (gitem_t *pItem)
314 {
315 assert(pItem != (gitem_t*) 0xcdcdcdcd);
316
317 if (pItem == NULL)
318 {
319 return -1;
320 }
321
322 return pItem - bg_itemlist;
323 }
324
GetGItemPtr(int iItem)325 gitem_t *GetGItemPtr(int iItem)
326 {
327 if (iItem == -1)
328 {
329 return NULL;
330 }
331
332 assert(iItem >= 0);
333 assert(iItem < bg_numItems);
334 return &bg_itemlist[iItem];
335 }
336 //
337 //
338 ////////////////////////////////
339
340
EnumerateField(const field_t * pField,byte * pbBase)341 void EnumerateField(const field_t *pField, byte *pbBase)
342 {
343 void *pv = (void *)(pbBase + pField->iOffset);
344
345 switch (pField->eFieldType)
346 {
347 case F_STRING:
348 *(intptr_t *)pv = GetStringNum(*(char **)pv);
349 break;
350
351 case F_GENTITY:
352 *(intptr_t *)pv = GetGEntityNum(*(gentity_t **)pv);
353 break;
354
355 case F_GROUP:
356 *(intptr_t *)pv = GetGroupNumber(*(AIGroupInfo_t **)pv);
357 break;
358
359 case F_GCLIENT:
360 {
361 // unfortunately, I now need to see if this is a 'real' client (and therefore resolve to an enum), or
362 // whether it's one of Mike G's private clients that needs saving here (thanks Mike...)
363 //
364 gentity_t *ent = (gentity_t *) pbBase;
365
366 if (ent->NPC == NULL)
367 {
368 // regular client...
369 //
370 *(intptr_t *)pv = GetGClientNum(*(gclient_t **)pv);
371 break;
372 }
373 else
374 {
375 // this must be one of Mike's, so mark it as special...
376 //
377 *(intptr_t *)pv = -2; // yeuch, but distinguishes it from a valid 0 index, or -1 for client==NULL
378 }
379 }
380 break;
381
382 case F_ITEM:
383 *(intptr_t *)pv = GetGItemNum(*(gitem_t **)pv);
384 break;
385
386 case F_BEHAVIORSET:
387 {
388 const char **p = (const char **) pv;
389 for (int i=0; i<NUM_BSETS; i++)
390 {
391 pv = &p[i]; // since you can't ++ a void ptr
392 *(intptr_t *)pv = GetStringNum(*(char **)pv);
393 }
394 }
395 break;
396
397 /*MCG
398 case F_BODYQUEUE:
399 {
400 gentity_t **p = (gentity_t **) pv;
401 for (int i=0; i<BODY_QUEUE_SIZE; i++)
402 {
403 pv = &p[i]; // since you can't ++ a void ptr
404 *(int *)pv = GetGEntityNum(*(gentity_t **)pv);
405 }
406 }
407 break;
408 */
409
410 case F_ALERTEVENT: // convert all gentity_t ptrs in an alertEvent array into indexes...
411 {
412 alertEvent_t* p = (alertEvent_t *) pv;
413
414 for (int i=0; i<MAX_ALERT_EVENTS; i++)
415 {
416 p[i].owner = (gentity_t *) GetGEntityNum(p[i].owner);
417 }
418 }
419 break;
420
421 case F_AIGROUPS: // convert to ptrs within this into indexes...
422 {
423 AIGroupInfo_t* p = (AIGroupInfo_t *) pv;
424
425 for (int i=0; i<MAX_FRAME_GROUPS; i++)
426 {
427 p[i].enemy = (gentity_t *) GetGEntityNum(p[i].enemy);
428 p[i].commander = (gentity_t *) GetGEntityNum(p[i].commander);
429 }
430 }
431 break;
432
433 case F_BOOLPTR:
434 *(qboolean *)pv = (*(int *)pv) ? qtrue : qfalse;
435 break;
436
437 // These are pointers that are always recreated
438 case F_NULL:
439 *(void **)pv = NULL;
440 break;
441
442 case F_IGNORE:
443 break;
444
445 default:
446 G_Error ("EnumerateField: unknown field type");
447 break;
448 }
449 }
450
451 template<typename T>
EnumerateFields(const field_t * pFields,T * src_instance,unsigned int ulChid)452 static void EnumerateFields(
453 const field_t* pFields,
454 T* src_instance,
455 unsigned int ulChid)
456 {
457 strList.clear();
458
459 byte* pbData = reinterpret_cast<byte*>(
460 src_instance);
461
462 // enumerate all the fields...
463 //
464 if (pFields)
465 {
466 for (auto pField = pFields; pField->psName; ++pField)
467 {
468 assert(pField->iOffset < sizeof(T));
469 ::EnumerateField(pField, pbData);
470 }
471 }
472
473 ojk::SavedGameHelper saved_game(
474 ::gi.saved_game);
475
476 // save out raw data...
477 //
478 saved_game.reset_buffer();
479
480 src_instance->sg_export(
481 saved_game);
482
483 saved_game.write_chunk(
484 ulChid);
485
486 // save out any associated strings..
487 //
488 for (const auto& it : strList)
489 {
490 saved_game.write_chunk(
491 INT_ID('S', 'T', 'R', 'G'),
492 it.c_str(),
493 static_cast<int>(it.length() + 1));
494 }
495 }
496
EvaluateField(const field_t * pField,byte * pbBase,byte * pbOriginalRefData)497 static void EvaluateField(const field_t *pField, byte *pbBase, byte *pbOriginalRefData/* may be NULL*/)
498 {
499 void *pv = (void *)(pbBase + pField->iOffset);
500 void *pvOriginal = (void *)(pbOriginalRefData + pField->iOffset);
501
502 switch (pField->eFieldType)
503 {
504 case F_STRING:
505 *(char **)pv = GetStringPtr(*(intptr_t *)pv, pbOriginalRefData?*(char**)pvOriginal:NULL);
506 break;
507
508 case F_GENTITY:
509 *(gentity_t **)pv = GetGEntityPtr(*(intptr_t *)pv);
510 break;
511
512 case F_GROUP:
513 *(AIGroupInfo_t **)pv = GetGroupPtr(*(intptr_t *)pv);
514 break;
515
516 case F_GCLIENT:
517 *(gclient_t **)pv = GetGClientPtr(*(intptr_t *)pv);
518 break;
519
520 case F_ITEM:
521 *(gitem_t **)pv = GetGItemPtr(*(intptr_t *)pv);
522 break;
523
524 case F_BEHAVIORSET:
525 {
526 char **p = (char **) pv;
527 char **pO= (char **) pvOriginal;
528 for (int i=0; i<NUM_BSETS; i++, p++, pO++)
529 {
530 *p = GetStringPtr(*(intptr_t *)p, pbOriginalRefData?*(char **)pO:NULL);
531 }
532 }
533 break;
534
535 /*MCG
536 case F_BODYQUEUE:
537 {
538 gentity_t **p = (gentity_t **) pv;
539 for (int i=0; i<BODY_QUEUE_SIZE; i++, p++)
540 {
541 *p = GetGEntityPtr(*(int *)p);
542 }
543 }
544 break;
545 */
546
547 case F_ALERTEVENT:
548 {
549 alertEvent_t* p = (alertEvent_t *) pv;
550
551 for (int i=0; i<MAX_ALERT_EVENTS; i++)
552 {
553 p[i].owner = GetGEntityPtr((intptr_t)(p[i].owner));
554 }
555 }
556 break;
557
558 case F_AIGROUPS: // convert to ptrs within this into indexes...
559 {
560 AIGroupInfo_t* p = (AIGroupInfo_t *) pv;
561
562 for (int i=0; i<MAX_FRAME_GROUPS; i++)
563 {
564 p[i].enemy = GetGEntityPtr((intptr_t)(p[i].enemy));
565 p[i].commander = GetGEntityPtr((intptr_t)(p[i].commander));
566 }
567 }
568 break;
569
570 // // These fields are patched in when their relevant owners are loaded
571 case F_BOOLPTR:
572 case F_NULL:
573 break;
574
575 case F_IGNORE:
576 break;
577
578 default:
579 G_Error ("EvaluateField: unknown field type");
580 break;
581 }
582 }
583
584
585 // copy of function in sv_savegame
SG_GetChidText(unsigned int chid)586 static const char *SG_GetChidText(unsigned int chid)
587 {
588 static char chidtext[5];
589
590 byteAlias_t *ba = (byteAlias_t *)&chidtext;
591 ba->ui = BigLong( chid );
592 chidtext[4] = '\0';
593
594 return chidtext;
595 }
596
597 template<typename T>
EvaluateFields(const field_t * pFields,T * pbData,T * pbOriginalRefData,unsigned int ulChid)598 static void EvaluateFields(
599 const field_t* pFields,
600 T* pbData,
601 T* pbOriginalRefData,
602 unsigned int ulChid)
603 {
604 ojk::SavedGameHelper saved_game(
605 ::gi.saved_game);
606
607 if (!saved_game.try_read_chunk(
608 ulChid,
609 *pbData))
610 {
611 ::G_Error(
612 ::va("EvaluateFields(): variable-sized chunk '%s' without handler!",
613 ::SG_GetChidText(ulChid)));
614 }
615
616 if (pFields)
617 {
618 for (auto pField = pFields; pField->psName; ++pField)
619 {
620 ::EvaluateField(
621 pField,
622 reinterpret_cast<byte*>(pbData),
623 reinterpret_cast<byte*>(pbOriginalRefData));
624 }
625 }
626 }
627
628 /*
629 ==============
630 WriteLevelLocals
631
632 All pointer variables (except function pointers) must be handled specially.
633 ==============
634 */
WriteLevelLocals()635 static void WriteLevelLocals ()
636 {
637 level_locals_t *temp = (level_locals_t *)gi.Malloc(sizeof(level_locals_t), TAG_TEMP_WORKSPACE, qfalse);
638 *temp = level; // copy out all data into a temp space
639
640 EnumerateFields(savefields_LevelLocals, temp, INT_ID('L','V','L','C'));
641 gi.Free(temp);
642 }
643
644 /*
645 ==============
646 ReadLevelLocals
647
648 All pointer variables (except function pointers) must be handled specially.
649 ==============
650 */
ReadLevelLocals()651 static void ReadLevelLocals ()
652 {
653 // preserve client ptr either side of the load, because clients are already saved/loaded through Read/Writegame...
654 //
655 gclient_t *pClients = level.clients; // save clients
656
657 level_locals_t *temp = (level_locals_t *)gi.Malloc(sizeof(level_locals_t), TAG_TEMP_WORKSPACE, qfalse);
658 *temp = level;
659 EvaluateFields(savefields_LevelLocals, temp, &level, INT_ID('L','V','L','C'));
660 level = *temp; // struct copy
661
662 level.clients = pClients; // restore clients
663 gi.Free(temp);
664 }
665
WriteGEntities(qboolean qbAutosave)666 static void WriteGEntities(qboolean qbAutosave)
667 {
668 int iCount = 0;
669 int i;
670
671 for (i=0; i<(qbAutosave?1:globals.num_entities); i++)
672 {
673 gentity_t* ent = &g_entities[i];
674
675 if ( ent->inuse )
676 {
677 iCount++;
678 }
679 }
680
681 ojk::SavedGameHelper saved_game(
682 ::gi.saved_game);
683
684 saved_game.write_chunk<int32_t>(
685 INT_ID('N', 'M', 'E', 'D'),
686 iCount);
687
688 for (i=0; i<(qbAutosave?1:globals.num_entities); i++)
689 {
690 gentity_t* ent = &g_entities[i];
691
692 if ( ent->inuse)
693 {
694 saved_game.write_chunk<int32_t>(
695 INT_ID('E', 'D', 'N', 'M'),
696 i);
697
698 qboolean qbLinked = ent->linked;
699 gi.unlinkentity( ent );
700 gentity_t tempEnt = *ent; // make local copy
701 tempEnt.linked = qbLinked;
702
703 if (qbLinked)
704 {
705 gi.linkentity( ent );
706 }
707
708 EnumerateFields(savefields_gEntity, &tempEnt, INT_ID('G','E','N','T'));
709
710 // now for any fiddly bits that would be rather awkward to build into the enumerator...
711 //
712 if (tempEnt.NPC)
713 {
714 gNPC_t npc = *ent->NPC; // NOT *tempEnt.NPC; !! :-)
715
716 EnumerateFields(savefields_gNPC, &npc, INT_ID('G','N','P','C'));
717 }
718
719 if (tempEnt.client == (gclient_t *)-2) // I know, I know...
720 {
721 gclient_t client = *ent->client; // NOT *tempEnt.client!!
722 EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I'));
723 }
724
725 if (tempEnt.parms)
726 {
727 saved_game.write_chunk(
728 INT_ID('P', 'A', 'R', 'M'),
729 *ent->parms);
730 }
731
732 // the scary ghoul2 saver stuff... (fingers crossed)
733 //
734 gi.G2API_SaveGhoul2Models(tempEnt.ghoul2);
735 tempEnt.ghoul2.kill(); // this handle was shallow copied from an ent. We don't want it destroyed
736 }
737 }
738
739 //Write out all entity timers
740 TIMER_Save();//WriteEntityTimers();
741
742 if (!qbAutosave)
743 {
744 //Save out ICARUS information
745 iICARUS->Save();
746
747 // this marker needs to be here, it lets me know if Icarus doesn't load everything back later,
748 // which has happened, and doesn't always show up onscreen until certain game situations.
749 // This saves time debugging, and makes things easier to track.
750 //
751 static int iBlah = 1234;
752
753 saved_game.write_chunk<int32_t>(
754 INT_ID('I', 'C', 'O', 'K'),
755 iBlah);
756 }
757 if (!qbAutosave )//really shouldn't need to write these bits at all, just restore them from the ents...
758 {
759 WriteInUseBits();
760 }
761 }
762
ReadGEntities(qboolean qbAutosave)763 static void ReadGEntities(qboolean qbAutosave)
764 {
765 int iCount = 0;
766 int i;
767
768 ojk::SavedGameHelper saved_game(
769 ::gi.saved_game);
770
771 saved_game.read_chunk<int32_t>(
772 INT_ID('N', 'M', 'E', 'D'),
773 iCount);
774
775 int iPreviousEntRead = -1;
776 for (i=0; i<iCount; i++)
777 {
778 int iEntIndex = 0;
779
780 saved_game.read_chunk<int32_t>(
781 INT_ID('E', 'D', 'N', 'M'),
782 iEntIndex);
783
784 if (iEntIndex >= globals.num_entities)
785 {
786 globals.num_entities = iEntIndex + 1;
787 }
788
789 if (iPreviousEntRead != iEntIndex-1)
790 {
791 for (int j=iPreviousEntRead+1; j!=iEntIndex; j++)
792 {
793 if ( g_entities[j].inuse ) // not actually necessary
794 {
795 G_FreeEntity(&g_entities[j]);
796 }
797 }
798 }
799 iPreviousEntRead = iEntIndex;
800
801 // slightly naff syntax here, but makes a few ops clearer later...
802 //
803 gentity_t entity;
804 // gentity_t* pEntOriginal = &g_entities[iEntIndex];
805 // gentity_t* pEnt = &entity;
806 gentity_t* pEntOriginal = &entity;
807 gentity_t* pEnt = &g_entities[iEntIndex];
808 *pEntOriginal = *pEnt; // struct copy, so we can refer to original
809 pEntOriginal->ghoul2.kill();
810 gi.unlinkentity(pEnt);
811 ICARUS_FreeEnt (pEnt);
812 //
813 // sneaky: destroy the ghoul2 object within this struct before binary-loading over the top of it...
814 //
815 gi.G2API_LoadSaveCodeDestructGhoul2Info(pEnt->ghoul2);
816 pEnt->ghoul2.kill();
817 EvaluateFields(savefields_gEntity, pEnt, pEntOriginal, INT_ID('G','E','N','T'));
818 pEnt->ghoul2.kill();
819
820 // now for any fiddly bits...
821 //
822 if (pEnt->NPC) // will be qtrue/qfalse
823 {
824 gNPC_t tempNPC;
825
826 EvaluateFields(savefields_gNPC, &tempNPC,pEntOriginal->NPC, INT_ID('G','N','P','C'));
827
828 // so can we pinch the original's one or do we have to alloc a new one?...
829 //
830 if (pEntOriginal->NPC)
831 {
832 // pinch this G_Alloc handle...
833 //
834 pEnt->NPC = pEntOriginal->NPC;
835 }
836 else
837 {
838 // original didn't have one (hmmm...), so make a new one...
839 //
840 //assert(0); // I want to know about this, though not in release
841 pEnt->NPC = (gNPC_t *) G_Alloc(sizeof(*pEnt->NPC));
842 }
843
844 // copy over the one we've just loaded...
845 //
846 *pEnt->NPC = tempNPC; // struct copy
847
848 }
849
850 if (pEnt->client == (gclient_t*) -2) // one of Mike G's NPC clients?
851 {
852 gclient_t tempGClient;
853
854 EvaluateFields(savefields_gClient, &tempGClient, pEntOriginal->client, INT_ID('G','C','L','I'));
855
856 // can we pinch the original's client handle or do we have to alloc a new one?...
857 //
858 if (pEntOriginal->client)
859 {
860 // pinch this G_Alloc handle...
861 //
862 pEnt->client = pEntOriginal->client;
863 }
864 else
865 {
866 // original didn't have one (hmmm...) so make a new one...
867 //
868 pEnt->client = (gclient_t *) G_Alloc(sizeof(*pEnt->client));
869 }
870
871 // copy over the one we've just loaded....
872 //
873 *pEnt->client = tempGClient; // struct copy
874 }
875
876 // Some Icarus thing... (probably)
877 //
878 if (pEnt->parms) // will be qtrue/qfalse
879 {
880 parms_t tempParms;
881
882 saved_game.read_chunk(
883 INT_ID('P', 'A', 'R', 'M'),
884 tempParms);
885
886 // so can we pinch the original's one or do we have to alloc a new one?...
887 //
888 if (pEntOriginal->parms)
889 {
890 // pinch this G_Alloc handle...
891 //
892 pEnt->parms = pEntOriginal->parms;
893 }
894 else
895 {
896 // original didn't have one, so make a new one...
897 //
898 pEnt->parms = (parms_t *) G_Alloc(sizeof(*pEnt->parms));
899 }
900
901 // copy over the one we've just loaded...
902 //
903 *pEnt->parms = tempParms; // struct copy
904 }
905
906 // the scary ghoul2 stuff... (fingers crossed)
907 //
908 {
909 #ifdef JK2_MODE
910 // Skip GL2 data size
911 saved_game.read_chunk(
912 INT_ID('G', 'L', '2', 'S'));
913 #endif // JK2_MODE
914
915 saved_game.read_chunk(
916 INT_ID('G', 'H', 'L', '2'));
917
918 gi.G2API_LoadGhoul2Models(
919 pEnt->ghoul2,
920 nullptr);
921 }
922
923 // gi.unlinkentity (pEntOriginal);
924 // ICARUS_FreeEnt( pEntOriginal );
925 // *pEntOriginal = *pEnt; // struct copy
926 // qboolean qbLinked = pEntOriginal->linked;
927 // pEntOriginal->linked = qfalse;
928 // if (qbLinked)
929 // {
930 // gi.linkentity (pEntOriginal);
931 // }
932
933 // because the sytem stores sfx_t handles directly instead of the set, we have to reget the set's sfx_t...
934 //
935 if (pEnt->s.eType == ET_MOVER && pEnt->s.loopSound>0)
936 {
937 if ( VALIDSTRING( pEnt->soundSet ))
938 {
939 extern int BMS_MID; // from g_mover
940 pEnt->s.loopSound = CAS_GetBModelSound( pEnt->soundSet, BMS_MID );
941 if (pEnt->s.loopSound == -1)
942 {
943 pEnt->s.loopSound = 0;
944 }
945 }
946 }
947
948 qboolean qbLinked = pEnt->linked;
949 pEnt->linked = qfalse;
950 if (qbLinked)
951 {
952 gi.linkentity (pEnt);
953 }
954 }
955
956 //Read in all the entity timers
957 TIMER_Load();//ReadEntityTimers();
958
959 if (!qbAutosave)
960 {
961 // now zap any g_ents that were inuse when the level was loaded, but are no longer in use in the saved version
962 // that we've just loaded...
963 //
964 for (i=iPreviousEntRead+1; i<globals.num_entities; i++)
965 {
966 if ( g_entities[i].inuse ) // not actually necessary
967 {
968 G_FreeEntity(&g_entities[i]);
969 }
970 }
971
972 //Load ICARUS information
973 ICARUS_EntList.clear();
974 iICARUS->Load();
975
976 // check that Icarus has loaded everything it saved out by having a marker chunk after it...
977 //
978 static int iBlah = 1234;
979
980 saved_game.read_chunk<int32_t>(
981 INT_ID('I', 'C', 'O', 'K'),
982 iBlah);
983 }
984 if (!qbAutosave)
985 {
986 ReadInUseBits();//really shouldn't need to read these bits in at all, just restore them from the ents...
987 }
988 }
989
990
WriteLevel(qboolean qbAutosave)991 void WriteLevel(qboolean qbAutosave)
992 {
993 if (!qbAutosave) //-always save the client
994 {
995 // write out one client - us!
996 //
997 assert(level.maxclients == 1); // I'll need to know if this changes, otherwise I'll need to change the way ReadGame works
998 gclient_t client = level.clients[0];
999 EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I'));
1000 WriteLevelLocals(); // level_locals_t level
1001 }
1002
1003 OBJ_SaveObjectiveData();
1004
1005 /////////////
1006 WriteGEntities(qbAutosave);
1007 Q3_VariableSave();
1008 G_LoadSave_WriteMiscData();
1009
1010 extern void CG_WriteTheEvilCGHackStuff(void);
1011 CG_WriteTheEvilCGHackStuff();
1012
1013 // (Do NOT put any write-code below this line)
1014 //
1015 // put out an end-marker so that the load code can check everything was read in...
1016 //
1017 static int iDONE = 1234;
1018
1019 ojk::SavedGameHelper saved_game(
1020 ::gi.saved_game);
1021
1022 saved_game.write_chunk<int32_t>(
1023 INT_ID('D', 'O', 'N', 'E'),
1024 iDONE);
1025 }
1026
ReadLevel(qboolean qbAutosave,qboolean qbLoadTransition)1027 void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition)
1028 {
1029 if ( qbLoadTransition )
1030 {
1031 //loadtransitions do not need to read the objectives and client data from the level they're going to
1032 //In a loadtransition, client data is carried over on the server and will be stomped later anyway.
1033 //The objective info (in client->sess data), however, is read in from G_ReadSessionData which is called before this func,
1034 //we do NOT want to stomp that session data when doing a load transition
1035
1036 //However, we should still save this info out because these savegames may need to be
1037 //loaded normally later- perhaps if you die and need to respawn, perhaps as some kind
1038 //of emergency savegame for resuming, etc.
1039
1040 //SO: We read it in, but throw it away.
1041
1042 //Read & throw away gclient info
1043 gclient_t junkClient;
1044 EvaluateFields(savefields_gClient, &junkClient, &level.clients[0], INT_ID('G','C','L','I'));
1045
1046 //Read & throw away objective info
1047 ojk::SavedGameHelper saved_game(
1048 ::gi.saved_game);
1049
1050 saved_game.read_chunk(
1051 INT_ID('O', 'B', 'J', 'T'));
1052
1053 ReadLevelLocals(); // level_locals_t level
1054 }
1055 else
1056 {
1057 if (!qbAutosave )//always load the client unless it's an autosave
1058 {
1059 assert(level.maxclients == 1); // I'll need to know if this changes, otherwise I'll need to change the way things work
1060
1061 gclient_t GClient;
1062 EvaluateFields(savefields_gClient, &GClient, &level.clients[0], INT_ID('G','C','L','I'));
1063 level.clients[0] = GClient; // struct copy
1064 ReadLevelLocals(); // level_locals_t level
1065 }
1066
1067 OBJ_LoadObjectiveData();//loads mission objectives AND tactical info
1068 }
1069
1070 /////////////
1071
1072 ReadGEntities(qbAutosave);
1073 Q3_VariableLoad();
1074 G_LoadSave_ReadMiscData();
1075
1076 extern void CG_ReadTheEvilCGHackStuff(void);
1077 CG_ReadTheEvilCGHackStuff();
1078
1079 // (Do NOT put any read-code below this line)
1080 //
1081 // check that the whole file content was loaded by specifically requesting an end-marker...
1082 //
1083 static int iDONE = 1234;
1084
1085 ojk::SavedGameHelper saved_game(
1086 ::gi.saved_game);
1087
1088 saved_game.read_chunk<int32_t>(
1089 INT_ID('D', 'O', 'N', 'E'),
1090 iDONE);
1091 }
1092
1093 extern int killPlayerTimer;
GameAllowedToSaveHere(void)1094 qboolean GameAllowedToSaveHere(void)
1095 {
1096 return (qboolean)(!in_camera && !killPlayerTimer);
1097 }
1098
1099 //////////////////// eof /////////////////////
1100