SAVELOAD.CPP (53991B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: /counterstrike/SAVELOAD.CPP 9 3/17/97 1:04a Steve_tall $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : SAVELOAD.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : August 23, 1994 * 28 * * 29 * Last Update : July 8, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * Code_All_Pointers -- Code all pointers. * 34 * Decode_All_Pointers -- Decodes all pointers. * 35 * Get_Savefile_Info -- gets description, scenario #, house * 36 * Load_Game -- loads a saved game * 37 * Load_MPlayer_Values -- Loads multiplayer-specific values * 38 * Load_Misc_Values -- loads miscellaneous variables * 39 * MPlayer_Save_Message -- pops up a "saving..." message * 40 * Put_All -- Store all save game data to the pipe. * 41 * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * 42 * Save_Game -- saves a game to disk * 43 * Save_MPlayer_Values -- Saves multiplayer-specific values * 44 * Save_Misc_Values -- saves miscellaneous variables * 45 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 46 47 #include "function.h" 48 #include "vortex.h" 49 #ifdef WIN32 50 #include "tcpip.h" 51 #include "ccdde.h" 52 53 //#include "WolDebug.h" 54 55 extern bool DLLSave(Pipe &file); 56 extern bool DLLLoad(Straw &file); 57 58 extern bool SpawnedFromWChat; 59 #endif 60 61 //#define SAVE_BLOCK_SIZE 512 62 #define SAVE_BLOCK_SIZE 4096 63 //#define SAVE_BLOCK_SIZE 1024 64 65 /* 66 ********************************** Defines ********************************** 67 */ 68 #define SAVEGAME_VERSION (DESCRIP_MAX + \ 69 0x01000006 + ( \ 70 sizeof(AircraftClass) + \ 71 sizeof(AircraftTypeClass) + \ 72 sizeof(AnimClass) + \ 73 sizeof(AnimTypeClass) + \ 74 sizeof(BaseClass) + \ 75 sizeof(BuildingClass) + \ 76 sizeof(BuildingTypeClass) + \ 77 sizeof(BulletClass) + \ 78 sizeof(BulletTypeClass) + \ 79 sizeof(CellClass) + \ 80 sizeof(FactoryClass) + \ 81 sizeof(HouseClass) + \ 82 sizeof(HouseTypeClass) + \ 83 sizeof(InfantryClass) + \ 84 sizeof(InfantryTypeClass) + \ 85 sizeof(LayerClass) + \ 86 sizeof(MouseClass) + \ 87 sizeof(OverlayClass) + \ 88 sizeof(OverlayTypeClass) + \ 89 sizeof(SmudgeClass) + \ 90 sizeof(SmudgeTypeClass) + \ 91 sizeof(TeamClass) + \ 92 sizeof(TeamTypeClass) + \ 93 sizeof(TemplateClass) + \ 94 sizeof(TemplateTypeClass) + \ 95 sizeof(TerrainClass) + \ 96 sizeof(TerrainTypeClass) + \ 97 sizeof(TriggerClass) + \ 98 sizeof(TriggerTypeClass) + \ 99 sizeof(UnitClass) + \ 100 sizeof(UnitTypeClass) + \ 101 sizeof(VesselClass) + \ 102 sizeof(ScenarioClass) + \ 103 sizeof(ChronalVortexClass))) 104 // sizeof(Waypoint))) 105 106 107 static int Reconcile_Players(void); 108 extern bool Is_Mission_Counterstrike (char *file_name); 109 #ifdef FIXIT_CSII // checked - ajw 9/28/98 110 extern bool Is_Mission_Aftermath (char *file_name); 111 #endif 112 113 /*********************************************************************************************** 114 * Put_All -- Store all save game data to the pipe. * 115 * * 116 * This is the bulk processor of the game related save game data. All the game object * 117 * and state data is stored to the pipe specified. * 118 * * 119 * INPUT: pipe -- Reference to the pipe that will receive the save game data. * 120 * * 121 * OUTPUT: none * 122 * * 123 * WARNINGS: none * 124 * * 125 * HISTORY: * 126 * 07/08/1996 JLB : Created. * 127 *=============================================================================================*/ 128 static void Put_All(Pipe & pipe, int save_net) 129 { 130 /* 131 ** Save the scenario global information. 132 */ 133 pipe.Put(&Scen, sizeof(Scen)); 134 135 /* 136 ** Save the map. The map must be saved first, since it saves the Theater. 137 */ 138 if (!save_net) Call_Back(); 139 Map.Save(pipe); 140 141 if (!save_net) Call_Back(); 142 143 /* 144 ** Save all game objects. This code saves every object that's stored in a 145 ** TFixedIHeap class. 146 */ 147 Houses.Save(pipe); 148 if (!save_net) Call_Back(); 149 TeamTypes.Save(pipe); 150 if (!save_net) Call_Back(); 151 Teams.Save(pipe); 152 if (!save_net) Call_Back(); 153 TriggerTypes.Save(pipe); 154 if (!save_net) Call_Back(); 155 Triggers.Save(pipe); 156 if (!save_net) Call_Back(); 157 Aircraft.Save(pipe); 158 if (!save_net) Call_Back(); 159 Anims.Save(pipe); 160 161 if (!save_net) Call_Back(); 162 163 Buildings.Save(pipe); 164 if (!save_net) Call_Back(); 165 Bullets.Save(pipe); 166 if (!save_net) Call_Back(); 167 Infantry.Save(pipe); 168 if (!save_net) Call_Back(); 169 Overlays.Save(pipe); 170 if (!save_net) Call_Back(); 171 Smudges.Save(pipe); 172 if (!save_net) Call_Back(); 173 Templates.Save(pipe); 174 if (!save_net) Call_Back(); 175 Terrains.Save(pipe); 176 if (!save_net) Call_Back(); 177 Units.Save(pipe); 178 if (!save_net) Call_Back(); 179 Factories.Save(pipe); 180 if (!save_net) Call_Back(); 181 Vessels.Save(pipe); 182 183 if (!save_net) Call_Back(); 184 185 /* 186 ** Save the Logic & Map layers 187 */ 188 Logic.Save(pipe); 189 190 int count = MapTriggers.Count(); 191 pipe.Put(&count, sizeof(count)); 192 int index; 193 for (int index = 0; index < MapTriggers.Count(); index++) { 194 TARGET target = MapTriggers[index]->As_Target(); 195 pipe.Put(&target, sizeof(target)); 196 } 197 if (!save_net) Call_Back(); 198 count = LogicTriggers.Count(); 199 pipe.Put(&count, sizeof(count)); 200 for (index = 0; index < LogicTriggers.Count(); index++) { 201 TARGET target = LogicTriggers[index]->As_Target(); 202 pipe.Put(&target, sizeof(target)); 203 } 204 if (!save_net) Call_Back(); 205 for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { 206 count = HouseTriggers[h].Count(); 207 pipe.Put(&count, sizeof(count)); 208 for (index = 0; index < HouseTriggers[h].Count(); index++) { 209 TARGET target = HouseTriggers[h][index]->As_Target(); 210 pipe.Put(&target, sizeof(target)); 211 } 212 } 213 if (!save_net) Call_Back(); 214 215 for (int i = 0; i < LAYER_COUNT; i++) { 216 Map.Layer[i].Save(pipe); 217 } 218 219 if (!save_net) Call_Back(); 220 221 /* 222 ** Save the Score 223 */ 224 pipe.Put(&Score, sizeof(Score)); 225 if (!save_net) Call_Back(); 226 227 /* 228 ** Save the AI Base 229 */ 230 Base.Save(pipe); 231 if (!save_net) Call_Back(); 232 233 /* 234 ** Save out the carry over list (if present). First see how 235 ** many carry over objects are in the list. 236 */ 237 int carry_count = 0; 238 CarryoverClass const * cptr = Carryover; 239 while (cptr != NULL) { 240 carry_count++; 241 cptr = (CarryoverClass const *)cptr->Get_Next(); 242 } 243 244 if (!save_net) Call_Back(); 245 246 /* 247 ** Save out the number of objects in the list. 248 */ 249 pipe.Put(&carry_count, sizeof(carry_count)); 250 if (!save_net) Call_Back(); 251 252 /* 253 ** Now write out the objects themselves. 254 */ 255 CarryoverClass const * object_to_write = Carryover; 256 while (object_to_write != NULL) { 257 pipe.Put(object_to_write, sizeof(*object_to_write)); 258 object_to_write = (CarryoverClass const *)object_to_write->Get_Next(); 259 } 260 if (!save_net) Call_Back(); 261 262 /* 263 ** Save miscellaneous variables. 264 */ 265 Save_Misc_Values(pipe); 266 267 if (!save_net) Call_Back(); 268 269 /* 270 ** Save multiplayer values 271 */ 272 pipe.Put(&save_net, sizeof(save_net)); // Write out whether we saved the net values so we know if we have to load them again. ST - 10/22/2019 2:10PM 273 if (save_net) { 274 Save_MPlayer_Values(pipe); 275 } 276 277 pipe.Flush(); 278 } 279 280 281 /*************************************************************************** 282 * Save_Game -- saves a game to disk * 283 * * 284 * Saving the Map: * 285 * DisplayClass::Save() invokes CellClass's Write() for every cell * 286 * that needs to be saved. A cell needs to be saved if it contains * 287 * any special data at all, such as a TIcon, or an Occupier. * 288 * The cell saves its own CellTrigger pointer, converted to a TARGET. * 289 * * 290 * Saving game objects: * 291 * - Any object stored in an ArrayOf class needs to be saved. The ArrayOf* 292 * Save() routine invokes each object's Write() routine, if that * 293 * object's IsActive is set. * 294 * * 295 * Saving the layers: * 296 * The Map's Layers (Ground, Air, etc) of things that are on the map, * 297 * and the Logic's Layer of things to process both need to be saved. * 298 * LayerClass::Save() writes the entire layer array to disk * 299 * * 300 * Saving the houses: * 301 * Each house needs to be saved, to record its Credits, Power, etc. * 302 * * 303 * Saving miscellaneous data: * 304 * There are a lot of miscellaneous variables to save, such as the * 305 * map's dimensions, the player's house, etc. * 306 * * 307 * INPUT: * 308 * id numerical ID, for the file extension * 309 * * 310 * OUTPUT: * 311 * true = OK, false = error * 312 * * 313 * WARNINGS: * 314 * none. * 315 * * 316 * HISTORY: * 317 * 12/28/1994 BR : Created. * 318 * 02/27/1996 JLB : Uses simpler game control value save operation. * 319 *=========================================================================*/ 320 bool Save_Game(int id, char const * descr, bool ) 321 { 322 char name[_MAX_FNAME+_MAX_EXT]; 323 324 /* 325 ** Generate the filename to save. If 'id' is -1, it means save a 326 ** network/modem game; otherwise, use 'id' as the file extension. 327 */ 328 if (id==-1) { 329 strcpy(name, NET_SAVE_FILE_NAME); 330 //save_net = 1; 331 } else { 332 sprintf(name, "SAVEGAME.%03d", id); 333 } 334 335 return Save_Game(name, descr); 336 } 337 338 339 340 /* 341 ** Version that takes file name. ST - 9/9/2019 11:10AM 342 */ 343 bool NowSavingGame = false; // TEMP MBL: Need to discuss better solution with Steve 344 bool Save_Game(const char *file_name, const char *descr) 345 { 346 NowSavingGame = true; // TEMP MBL: Need to discuss better solution with Steve 347 348 int save_net = 0; // 1 = save network/modem game 349 350 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 351 save_net = 1; 352 } 353 354 unsigned scenario; 355 HousesType house; 356 357 scenario = Scen.Scenario; // get current scenario # 358 house = PlayerPtr->Class->House; // get current house 359 360 /* 361 ** Code everybody's pointers 362 */ 363 Code_All_Pointers(); 364 365 /* 366 ** Open the file 367 */ 368 BufferIOFileClass file(file_name); 369 370 FilePipe fpipe(&file); 371 372 /* 373 ** Save the DLLs variables first, so we can do a version check in the DLL when we begin the load 374 */ 375 if (RunningAsDLL) { 376 DLLSave(fpipe); 377 } 378 379 /* 380 ** Save the description, scenario #, and house 381 ** (scenario # & house are saved separately from the actual Scenario & 382 ** PlayerPtr globals for convenience; we can quickly find out which 383 ** house & scenario this save-game file is for by reading these values. 384 ** Also, PlayerPtr is stored in a coded form in Save_Misc_Values(), 385 ** which may or may not be a HousesType number; so, saving 'house' 386 ** here ensures we can always pull out the house for this file.) 387 */ 388 char descr_buf[DESCRIP_MAX]; 389 memset(descr_buf, '\0', sizeof(descr_buf)); 390 sprintf(descr_buf, "%s\r\n", descr); // put CR-LF after text 391 //descr_buf[strlen(descr_buf) + 1] = 26; // put CTRL-Z after NULL 392 fpipe.Put(descr_buf, DESCRIP_MAX); 393 394 fpipe.Put(&scenario, sizeof(scenario)); 395 396 fpipe.Put(&house, sizeof(house)); 397 398 /* 399 ** Save the save-game version, for loading verification 400 */ 401 unsigned long version = SAVEGAME_VERSION; 402 #ifdef FIXIT_CSII // checked - ajw 9/28/98 403 version++; 404 #endif 405 fpipe.Put(&version, sizeof(version)); 406 407 int pos = file.Seek(0, SEEK_CUR); 408 409 /* 410 ** Store a dummy message digest. 411 */ 412 char digest[20]; 413 fpipe.Put(digest, sizeof(digest)); 414 415 416 /* 417 ** Dump the save game data to the file. The data is compressed 418 ** and then encrypted. The message digest is calculated in the 419 ** process by using the data just as it is written to disk. 420 */ 421 SHAPipe sha; 422 BlowPipe bpipe(BlowPipe::ENCRYPT); 423 LZOPipe pipe(LZOPipe::COMPRESS, SAVE_BLOCK_SIZE); 424 // LZWPipe pipe(LZWPipe::COMPRESS, SAVE_BLOCK_SIZE); 425 // LCWPipe pipe(LCWPipe::COMPRESS, SAVE_BLOCK_SIZE); 426 bpipe.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); 427 428 sha.Put_To(fpipe); 429 bpipe.Put_To(sha); 430 pipe.Put_To(bpipe); 431 Put_All(pipe, save_net); 432 433 /* 434 ** Output the real final message digest. This is the one that is of 435 ** the data image as it exists on the disk. 436 */ 437 pipe.Flush(); 438 file.Seek(pos, SEEK_SET); 439 sha.Result(digest); 440 fpipe.Put(digest, sizeof(digest)); 441 442 pipe.End(); 443 444 Decode_All_Pointers(); 445 446 NowSavingGame = false; // TEMP MBL: Need to discuss better solution with Steve 447 448 return(true); 449 } 450 451 452 /*************************************************************************** 453 * Load_Game -- loads a saved game * 454 * * 455 * This routine loads the data in the same way it was saved out. * 456 * * 457 * Loading the Map: * 458 * - DisplayClass::Load() invokes CellClass's Load() for every cell * 459 * that was saved. * 460 * - The cell loads its own CellTrigger pointer. * 461 * * 462 * Loading game objects: * 463 * - IHeap's Load() routine loads the # of objects stored, and loads * 464 * each object. * 465 * - Triggers: Add themselves to the HouseTriggers if they're associated * 466 * with a house * 467 * * 468 * Loading the layers: * 469 * LayerClass::Load() reads the entire layer array to disk * 470 * * 471 * Loading the houses: * 472 * Each house is loaded in its entirety. * 473 * * 474 * Loading miscellaneous data: * 475 * There are a lot of miscellaneous variables to load, such as the * 476 * map's dimensions, the player's house, etc. * 477 * * 478 * INPUT: * 479 * id numerical ID, for the file extension * 480 * * 481 * OUTPUT: * 482 * true = OK, false = error * 483 * * 484 * WARNINGS: * 485 * If this routine returns false, the entire game will be in an * 486 * unknown state, so the scenario will have to be re-initialized. * 487 * * 488 * HISTORY: * 489 * 12/28/1994 BR : Created. * 490 * 1/20/97 V.Grippi Added expansion CD check * 491 *=========================================================================*/ 492 bool Load_Game(int id) 493 { 494 char name[_MAX_FNAME+_MAX_EXT]; 495 496 /* 497 ** Generate the filename to load. If 'id' is -1, it means save a 498 ** network/modem game; otherwise, use 'id' as the file extension. 499 */ 500 if (id == -1) { 501 strcpy(name, NET_SAVE_FILE_NAME); 502 //load_net = 1; 503 } else { 504 sprintf(name, "SAVEGAME.%03d", id); 505 } 506 return Load_Game(name); 507 } 508 509 510 /* 511 ** Version that takes a file name instead. ST - 9/9/2019 11:13AM 512 */ 513 bool Load_Game(const char *file_name) 514 { 515 int i; 516 unsigned scenario; 517 HousesType house; 518 char descr_buf[DESCRIP_MAX]; 519 int load_net = 0; // 1 = save network/modem game 520 521 /* 522 ** Open the file 523 */ 524 RawFileClass file(file_name); 525 if (!file.Is_Available()) { 526 return(false); 527 } 528 529 FileStraw fstraw(file); 530 531 Call_Back(); 532 533 /* 534 ** Load the DLLs variables first, in case we need to do something different based on version 535 */ 536 if (RunningAsDLL) { 537 DLLLoad(fstraw); 538 } 539 540 /* 541 ** Read & discard the save-game's header info 542 */ 543 if (fstraw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { 544 return(false); 545 } 546 547 if (fstraw.Get(&scenario, sizeof(scenario)) != sizeof(scenario)) { 548 return(false); 549 } 550 551 if (fstraw.Get(&house, sizeof(house)) != sizeof(house)) { 552 return(false); 553 } 554 555 /* 556 ** Read in & verify the save-game ID code 557 */ 558 unsigned long version; 559 if (fstraw.Get(&version, sizeof(version)) != sizeof(version)) { 560 return(false); 561 } 562 GameVersion = version; 563 #ifdef FIXIT_CSII // checked - ajw 9/28/98 564 if (version != SAVEGAME_VERSION && ((version-1) != SAVEGAME_VERSION) ) { 565 return(false); 566 } 567 #else 568 if (version != SAVEGAME_VERSION /*&& version != 0x0100616D*/){ 569 return(false); 570 } 571 #endif 572 /* 573 ** Get the message digest that is embedded in the file. 574 */ 575 char digest[20]; 576 fstraw.Get(digest, sizeof(digest)); 577 578 /* 579 ** Remember the file position since we must seek back here to 580 ** perform the real saved game read. 581 */ 582 long pos = file.Seek(0, SEEK_CUR); 583 584 /* 585 ** Pass the rest of the file through the hash straw so that 586 ** the digest can be compaired to the one in the file. 587 */ 588 SHAStraw sha; 589 sha.Get_From(fstraw); 590 for (;;) { 591 if (sha.Get(_staging_buffer, sizeof(_staging_buffer)) != sizeof(_staging_buffer)) break; 592 } 593 char actual[20]; 594 sha.Result(actual); 595 sha.Get_From(NULL); 596 597 Call_Back(); 598 599 /* 600 ** Compare the two digests. If they differ then return a failure condition 601 ** before any damage could be done. 602 */ 603 if (memcmp(actual, digest, sizeof(digest)) != 0) { 604 return(false); 605 } 606 607 /* 608 ** Set up the pipe so that the scenario data can be read. 609 */ 610 file.Seek(pos, SEEK_SET); 611 BlowStraw bstraw(BlowStraw::DECRYPT); 612 LZOStraw straw(LZOStraw::DECOMPRESS, SAVE_BLOCK_SIZE); 613 // LZWStraw straw(LZWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); 614 // LCWStraw straw(LCWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); 615 616 bstraw.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); 617 bstraw.Get_From(fstraw); 618 straw.Get_From(bstraw); 619 620 /* 621 ** Clear the scenario so we start fresh; this calls the Init_Clear() routine 622 ** for the Map, and all object arrays. It has the following important 623 ** effects: 624 ** - Every cell is cleared to 0's, via MapClass::Init_Clear() 625 ** - All heap elements' are cleared 626 ** - The Houses are Initialized, which also clears their HouseTriggers 627 ** array 628 ** - The map's Layers & Logic Layer are cleared to empty 629 ** - The list of currently-selected objects is cleared 630 */ 631 Clear_Scenario(); 632 633 /* 634 ** Load the scenario global information. 635 */ 636 straw.Get(&Scen, sizeof(Scen)); 637 638 /* 639 ** Fixup the Sessionclass scenario info so we can work out which 640 ** CD to request later 641 */ 642 if ( load_net ){ 643 644 CCFileClass scenario_file (Scen.ScenarioName); 645 if ( !scenario_file.Is_Available() ){ 646 647 int cd = -1; 648 if (Is_Mission_Counterstrike (Scen.ScenarioName)) { 649 cd = 2; 650 #ifdef FIXIT_CSII // checked - ajw 9/28/98 651 if (Expansion_AM_Present()) { 652 int current_drive = CCFileClass::Get_CD_Drive(); 653 int index = Get_CD_Index(current_drive, 1*60); 654 if (index == 3) cd = 3; 655 } 656 #endif 657 } 658 #ifdef FIXIT_CSII // checked - ajw 9/28/98 659 if (Is_Mission_Aftermath (Scen.ScenarioName)) { 660 cd = 3; 661 #ifdef BOGUSCD 662 cd = -1; 663 #endif 664 } 665 #endif 666 RequiredCD = cd; 667 if (!Force_CD_Available (RequiredCD)) { 668 Emergency_Exit(EXIT_FAILURE); 669 } 670 671 /* 672 ** Update the internal list of scenarios to include the counterstrike 673 ** list. 674 */ 675 Session.Read_Scenario_Descriptions(); 676 } else { 677 /* 678 ** The scenario is available so set RequiredCD to whatever is currently 679 ** in the drive. 680 */ 681 int current_drive = CCFileClass::Get_CD_Drive(); 682 RequiredCD = Get_CD_Index(current_drive, 1*60); 683 } 684 } 685 686 /* 687 ** Load the map. The map comes first, since it loads the Theater & init's 688 ** mixfiles. The map calls all the type-class's Init routines, telling them 689 ** what the Theater is; this must be done before any objects are created, so 690 ** they'll be properly created. 691 */ 692 Map.Load(straw); 693 694 Call_Back(); 695 696 /* 697 ** Load the object data. 698 */ 699 Houses.Load(straw); 700 TeamTypes.Load(straw); 701 Teams.Load(straw); 702 TriggerTypes.Load(straw); 703 Triggers.Load(straw); 704 Aircraft.Load(straw); 705 Anims.Load(straw); 706 Buildings.Load(straw); 707 Bullets.Load(straw); 708 709 Call_Back(); 710 711 Infantry.Load(straw); 712 Overlays.Load(straw); 713 Smudges.Load(straw); 714 Templates.Load(straw); 715 Terrains.Load(straw); 716 Units.Load(straw); 717 Factories.Load(straw); 718 Vessels.Load(straw); 719 720 /* 721 ** Load the Logic & Map Layers 722 */ 723 Logic.Load(straw); 724 725 int count; 726 straw.Get(&count, sizeof(count)); 727 MapTriggers.Clear(); 728 int index; 729 for (index = 0; index < count; index++) { 730 TARGET target; 731 straw.Get(&target, sizeof(target)); 732 MapTriggers.Add(As_Trigger(target)); 733 } 734 735 straw.Get(&count, sizeof(count)); 736 LogicTriggers.Clear(); 737 for (index = 0; index < count; index++) { 738 TARGET target; 739 straw.Get(&target, sizeof(target)); 740 LogicTriggers.Add(As_Trigger(target)); 741 } 742 743 for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { 744 straw.Get(&count, sizeof(count)); 745 HouseTriggers[h].Clear(); 746 for (index = 0; index < count; index++) { 747 TARGET target; 748 straw.Get(&target, sizeof(target)); 749 HouseTriggers[h].Add(As_Trigger(target)); 750 } 751 } 752 753 for (i = 0; i < LAYER_COUNT; i++) { 754 Map.Layer[i].Load(straw); 755 } 756 757 Call_Back(); 758 759 /* 760 ** Load the Score 761 */ 762 straw.Get(&Score, sizeof(Score)); 763 new(&Score) ScoreClass(NoInitClass()); 764 765 /* 766 ** Load the AI Base 767 */ 768 Base.Load(straw); 769 770 /* 771 ** Delete any carryover pseudo-saved game list. 772 */ 773 while (Carryover != NULL) { 774 CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); 775 Carryover->Remove(); 776 delete Carryover; 777 Carryover = cptr; 778 } 779 780 /* 781 ** Load any carryover pseudo-saved game list. 782 */ 783 int carry_count = 0; 784 straw.Get(&carry_count, sizeof(carry_count)); 785 while (carry_count) { 786 CarryoverClass * cptr = new CarryoverClass; 787 assert(cptr != NULL); 788 789 straw.Get(cptr, sizeof(CarryoverClass)); 790 new (cptr) CarryoverClass(NoInitClass()); 791 cptr->Zap(); 792 793 if (!Carryover) { 794 Carryover = cptr; 795 } else { 796 cptr->Add_Tail(*Carryover); 797 } 798 carry_count--; 799 } 800 801 Call_Back(); 802 803 /* 804 ** Load miscellaneous variables, including the map size & the Theater 805 */ 806 Load_Misc_Values(straw); 807 808 /* 809 ** Load multiplayer values 810 */ 811 straw.Get(&load_net, sizeof(load_net)); 812 if (load_net) { 813 Load_MPlayer_Values(straw); 814 } 815 816 file.Close(); 817 Decode_All_Pointers(); 818 Map.Init_IO(); 819 Map.Flag_To_Redraw(true); 820 821 /* 822 ** Fixup any expediency data that can be inferred from the physical 823 ** data loaded. 824 */ 825 Post_Load_Game(load_net); 826 827 /* 828 ** Re-init unit trackers. They will be garbage pointers after the load 829 */ 830 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 831 HouseClass * hptr = HouseClass::As_Pointer(house); 832 if (hptr && hptr->IsActive) { 833 hptr->Init_Unit_Trackers(); 834 } 835 } 836 837 Call_Back(); 838 839 /* 840 ** Set the required CD to be in the drive according to the scenario 841 ** loaded. 842 */ 843 if (RequiredCD != -2 && !load_net) { 844 845 #ifdef FIXIT_ANTS 846 /* 847 ** Determines if this an ant mission. Since the ant mission looks no different from 848 ** a regular mission, examining of the scenario name is the only way to tell. 849 */ 850 if (toupper(Scen.ScenarioName[0]) == 'S' && 851 toupper(Scen.ScenarioName[1]) == 'C' && 852 toupper(Scen.ScenarioName[2]) == 'A' && 853 toupper(Scen.ScenarioName[3]) == '0' && 854 toupper(Scen.ScenarioName[5]) == 'E' && 855 toupper(Scen.ScenarioName[6]) == 'A') { 856 857 AntsEnabled = true; 858 } else{ 859 AntsEnabled = false; 860 } 861 #endif 862 863 if (Scen.Scenario == 1) { 864 RequiredCD = -1; 865 } else { 866 #ifdef FIXIT_ANTS 867 if (Scen.Scenario > 19 || AntsEnabled) { 868 RequiredCD = 2; 869 #ifdef FIXIT_CSII // checked - ajw 9/28/98 870 if(Scen.Scenario >= 36) { 871 RequiredCD = 3; 872 #ifdef BOGUSCD 873 RequiredCD = -1; 874 #endif 875 } 876 #endif 877 } else { 878 #endif //FIXIT_ANTS 879 if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { 880 RequiredCD = 0; 881 } else { 882 RequiredCD = 1; 883 } 884 #ifdef FIXIT_ANTS 885 } 886 #endif //FIXIT_ANTS 887 } 888 889 }else{ 890 891 if ( load_net ){ 892 893 CCFileClass scenario_file (Scen.ScenarioName); 894 895 /* 896 ** Fix up the session class variables 897 */ 898 for ( int s=0 ; s<Session.Scenarios.Count() ; s++ ) { 899 if (Session.Scenarios[s]->Description() == Scen.Description){ 900 901 memcpy (Session.Options.ScenarioDescription, Scen.Description, 902 sizeof (Session.Options.ScenarioDescription)); 903 memcpy (Session.ScenarioFileName, Scen.ScenarioName, 904 sizeof (Session.ScenarioFileName)); 905 Session.ScenarioFileLength = scenario_file.Size(); 906 memcpy (Session.ScenarioDigest, Session.Scenarios[s]->Get_Digest(), 907 sizeof (Session.ScenarioDigest)); 908 Session.ScenarioIsOfficial = Session.Scenarios[s]->Get_Official(); 909 Scen.Scenario = s; 910 Session.Options.ScenarioIndex = s; 911 break; 912 } 913 } 914 } 915 } 916 917 if (!Force_CD_Available(RequiredCD)) { 918 Prog_End("Load_Game Force_CD_Available failed", true); 919 Emergency_Exit(EXIT_FAILURE); 920 } 921 922 ScenarioInit = 0; 923 924 if (load_net) { 925 926 // Removed as this is ensured by the GlyphX & DLL save/load code. ST - 10/22/2019 5:20PM 927 //if (!Reconcile_Players()) { // (must do after Decode pointers) 928 // return(false); 929 //} 930 931 //!!!!!!!!!! put Fixup_Player_Units() here 932 Session.LoadGame = true; 933 } 934 935 Map.Reload_Sidebar(); // re-load sidebar art. 936 937 /* 938 ** Rescan the scenario file for any rules updates. 939 */ 940 CCINIClass ini; 941 int result = ini.Load(CCFileClass(Scen.ScenarioName), true); 942 943 /* 944 ** Reset the rules values to their initial settings. 945 */ 946 Rule.General(RuleINI); 947 Rule.Recharge(RuleINI); 948 Rule.AI(RuleINI); 949 Rule.Powerups(RuleINI); 950 Rule.Land_Types(RuleINI); 951 Rule.Themes(RuleINI); 952 Rule.IQ(RuleINI); 953 Rule.Objects(RuleINI); 954 Rule.Difficulty(RuleINI); 955 #ifdef FIXIT_CSII // checked - ajw 9/28/98 - But does this incorporate *changes*? - NO. 956 Rule.General(AftermathINI); 957 Rule.Recharge(AftermathINI); 958 Rule.AI(AftermathINI); 959 Rule.Powerups(AftermathINI); 960 Rule.Land_Types(AftermathINI); 961 Rule.Themes(AftermathINI); 962 Rule.IQ(AftermathINI); 963 Rule.Objects(AftermathINI); 964 Rule.Difficulty(AftermathINI); 965 #endif 966 967 /* 968 ** Override any rules values specified in this 969 ** particular scenario file. 970 */ 971 Rule.General(ini); 972 Rule.Recharge(ini); 973 Rule.AI(ini); 974 Rule.Powerups(ini); 975 Rule.Land_Types(ini); \ 976 Rule.Themes(ini); 977 Rule.IQ(ini); 978 Rule.Objects(ini); 979 Rule.Difficulty(ini); 980 #ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode. 981 if (load_net) { 982 bool readini = false; 983 switch(Session.Type) { 984 case GAME_NORMAL: 985 readini = false; 986 break; 987 case GAME_SKIRMISH: 988 readini = Is_Aftermath_Installed(); 989 break; 990 default: 991 #ifdef FIXIT_VERSION_3 992 readini = bAftermathMultiplayer; 993 #else 994 if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { 995 readini = true; 996 } 997 #endif 998 break; 999 } 1000 if(readini) { 1001 /* 1002 ** Find out if the CD in the current drive is the Aftermath disc. 1003 */ 1004 if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { 1005 GamePalette.Set(FADE_PALETTE_FAST, Call_Back); 1006 if (!Force_CD_Available(3)) { // force Aftermath CD in drive. 1007 Prog_End("Load_Game Force_CD_Available(3) failed", true); 1008 #ifndef FIXIT_VERSION_3 // WChat eliminated. 1009 #ifdef WIN32 1010 if(Special.IsFromWChat || SpawnedFromWChat) { 1011 char packet[10] = {"Hello"}; 1012 Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); 1013 } 1014 #endif 1015 #endif 1016 Emergency_Exit(EXIT_FAILURE); 1017 } 1018 } 1019 CCINIClass mpini; 1020 if (mpini.Load(CCFileClass("MPLAYER.INI"), false)) { 1021 Rule.General(mpini); 1022 Rule.Recharge(mpini); 1023 Rule.AI(mpini); 1024 Rule.Powerups(mpini); 1025 Rule.Land_Types(mpini); 1026 Rule.Themes(mpini); 1027 Rule.IQ(mpini); 1028 Rule.Objects(mpini); 1029 Rule.Difficulty(mpini); 1030 } 1031 } 1032 1033 } 1034 #endif 1035 1036 if (Scen.TransitTheme == THEME_NONE) { 1037 Theme.Queue_Song(THEME_FIRST); 1038 } else { 1039 Theme.Queue_Song(Scen.TransitTheme); 1040 } 1041 1042 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1043 Rule.IsSmartDefense = true; 1044 } 1045 1046 return(true); 1047 } 1048 1049 1050 /*************************************************************************** 1051 * Save_Misc_Values -- saves miscellaneous variables * 1052 * * 1053 * INPUT: * 1054 * file file to use for writing * 1055 * * 1056 * OUTPUT: * 1057 * true = success, false = failure * 1058 * * 1059 * WARNINGS: * 1060 * none. * 1061 * * 1062 * HISTORY: * 1063 * 12/29/1994 BR : Created. * 1064 * 03/12/1996 JLB : Simplified. * 1065 *=========================================================================*/ 1066 bool Save_Misc_Values(Pipe & file) 1067 { 1068 int i, j; 1069 int count; // # ptrs in 'CurrentObject' 1070 ObjectClass * ptr; // for saving 'CurrentObject' ptrs 1071 1072 /* 1073 ** Player's House. 1074 */ 1075 int x = PlayerPtr->Class->House; 1076 file.Put(&x, sizeof(x)); 1077 1078 /* 1079 ** Save frame #. 1080 */ 1081 file.Put(&Frame, sizeof(Frame)); 1082 1083 /* 1084 ** Save currently-selected objects list. 1085 ** Save the # of ptrs in the list. 1086 */ 1087 for (i = 0; i < SelectedObjectsType::COUNT; i++) { 1088 DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(i); 1089 count = selection.Count(); 1090 file.Put(&count, sizeof(count)); 1091 1092 /* 1093 ** Save the pointers. 1094 */ 1095 for (j = 0; j < count; j++) { 1096 ptr = selection[j]; 1097 file.Put(&ptr, sizeof(ptr)); 1098 } 1099 } 1100 1101 /* 1102 ** Save the chronal vortex 1103 */ 1104 ChronalVortex.Save(file); 1105 1106 /* 1107 ** Save Tanya flags. 1108 */ 1109 file.Put(&IsTanyaDead, sizeof(IsTanyaDead)); 1110 file.Put(&SaveTanya, sizeof(SaveTanya)); 1111 1112 return(true); 1113 } 1114 1115 1116 /*********************************************************************************************** 1117 * Load_Misc_Values -- Loads miscellaneous variables. * 1118 * * 1119 * INPUT: file -- The file to load the misc values from. * 1120 * * 1121 * OUTPUT: Was the misc load process successful? * 1122 * * 1123 * WARNINGS: none * 1124 * * 1125 * HISTORY: * 1126 * 06/24/1995 BRR : Created. * 1127 * 03/12/1996 JLB : Simplified. * 1128 *=============================================================================================*/ 1129 bool Load_Misc_Values(Straw & file) 1130 { 1131 ObjectClass * ptr; // for loading 'CurrentObject' ptrs 1132 1133 /* 1134 ** Player's House. 1135 */ 1136 int x; 1137 file.Get(&x, sizeof(x)); 1138 // file.Get(&PlayerPtr, sizeof(PlayerPtr)); 1139 PlayerPtr = HouseClass::As_Pointer((HousesType)x); 1140 1141 /* 1142 ** Load frame #. 1143 */ 1144 file.Get(&Frame, sizeof(Frame)); 1145 1146 for (int i = 0; i < SelectedObjectsType::COUNT; i++) { 1147 /* 1148 ** Load currently-selected objects list. 1149 ** Load the # of ptrs in the list. 1150 */ 1151 DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(i); 1152 int count; // # ptrs in 'CurrentObject' 1153 file.Get(&count, sizeof(count)); 1154 1155 /* 1156 ** Load the pointers. 1157 */ 1158 for (int j = 0; j < count; j++) { 1159 file.Get(&ptr, sizeof(ptr)); 1160 selection.Add(ptr); // add to the list 1161 } 1162 } 1163 1164 /* 1165 ** Load the chronal vortex 1166 */ 1167 ChronalVortex.Load(file); 1168 1169 /* 1170 ** Save Tanya flags. 1171 */ 1172 file.Get(&IsTanyaDead, sizeof(IsTanyaDead)); 1173 file.Get(&SaveTanya, sizeof(SaveTanya)); 1174 1175 return(true); 1176 } 1177 1178 1179 /*************************************************************************** 1180 * Save_MPlayer_Values -- Saves multiplayer-specific values * 1181 * * 1182 * This routine saves multiplayer values that need to be restored for a * 1183 * save game. In addition to saving the random # seed for this scenario, * 1184 * it saves the contents of the actual random number generator; this * 1185 * ensures that the random # sequencer will pick up where it left off when * 1186 * the game was saved. * 1187 * This routine also saves the header for a Recording file, so it must * 1188 * save some data not needed specifically by a save-game file (ie Seed). * 1189 * * 1190 * INPUT: * 1191 * file file to save to * 1192 * * 1193 * OUTPUT: * 1194 * true = success, false = failure * 1195 * * 1196 * WARNINGS: * 1197 * none. * 1198 * * 1199 * HISTORY: * 1200 * 09/28/1995 BRR : Created. * 1201 *=========================================================================*/ 1202 bool Save_MPlayer_Values(Pipe & file) 1203 { 1204 Session.Save(file); 1205 file.Put(&BuildLevel, sizeof(BuildLevel)); 1206 file.Put(&Debug_Unshroud, sizeof(Debug_Unshroud)); 1207 file.Put(&Seed, sizeof(Seed)); 1208 file.Put(&Whom, sizeof(Whom)); 1209 file.Put(&Special, sizeof(SpecialClass)); 1210 file.Put(&Options, sizeof(GameOptionsClass)); 1211 1212 return (true); 1213 } 1214 1215 1216 /*************************************************************************** 1217 * Load_MPlayer_Values -- Loads multiplayer-specific values * 1218 * * 1219 * INPUT: * 1220 * file file to load from * 1221 * * 1222 * OUTPUT: * 1223 * true = success, false = failure * 1224 * * 1225 * WARNINGS: * 1226 * none. * 1227 * * 1228 * HISTORY: * 1229 * 09/28/1995 BRR : Created. * 1230 *=========================================================================*/ 1231 bool Load_MPlayer_Values(Straw & file) 1232 { 1233 Session.Load(file); 1234 file.Get(&BuildLevel, sizeof(BuildLevel)); 1235 file.Get(&Debug_Unshroud, sizeof(Debug_Unshroud)); 1236 file.Get(&Seed, sizeof(Seed)); 1237 file.Get(&Whom, sizeof(Whom)); 1238 file.Get(&Special, sizeof(SpecialClass)); 1239 file.Get(&Options, sizeof(GameOptionsClass)); 1240 1241 return (true); 1242 } 1243 1244 /* 1245 ** ST - 9/26/2019 11:43AM 1246 */ 1247 extern void DLL_Code_Pointers(void); 1248 extern void DLL_Decode_Pointers(void); 1249 1250 /*********************************************************************************************** 1251 * Code_All_Pointers -- Code all pointers. * 1252 * * 1253 * INPUT: none * 1254 * * 1255 * OUTPUT: none * 1256 * * 1257 * WARNINGS: none * 1258 * * 1259 * HISTORY: * 1260 * 06/24/1995 BRR : Created. * 1261 *=============================================================================================*/ 1262 void Code_All_Pointers(void) 1263 { 1264 int i, j; 1265 1266 /* 1267 ** The Map. 1268 */ 1269 Map.Code_Pointers(); 1270 1271 /* 1272 ** The ArrayOf's. 1273 */ 1274 TeamTypes.Code_Pointers(); 1275 Teams.Code_Pointers(); 1276 Triggers.Code_Pointers(); 1277 Aircraft.Code_Pointers(); 1278 Anims.Code_Pointers(); 1279 Buildings.Code_Pointers(); 1280 Bullets.Code_Pointers(); 1281 Infantry.Code_Pointers(); 1282 Overlays.Code_Pointers(); 1283 Smudges.Code_Pointers(); 1284 Templates.Code_Pointers(); 1285 Terrains.Code_Pointers(); 1286 Units.Code_Pointers(); 1287 Factories.Code_Pointers(); 1288 Vessels.Code_Pointers(); 1289 1290 /* 1291 ** The Layers. 1292 */ 1293 Logic.Code_Pointers(); 1294 for (i = 0; i < LAYER_COUNT; i++) { 1295 Map.Layer[i].Code_Pointers(); 1296 } 1297 1298 /* 1299 ** The Score. 1300 */ 1301 Score.Code_Pointers(); 1302 1303 /* 1304 ** The Base. 1305 */ 1306 Base.Code_Pointers(); 1307 1308 /* 1309 ** PlayerPtr. 1310 */ 1311 // PlayerPtr = (HouseClass *)(PlayerPtr->Class->House); 1312 1313 /* 1314 ** Currently-selected objects. 1315 */ 1316 for (i = 0; i < SelectedObjectsType::COUNT; i++) { 1317 DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(i); 1318 for (j = 0; j < selection.Count(); j++) { 1319 selection[j] = (ObjectClass *)selection[j]->As_Target(); 1320 } 1321 } 1322 1323 /* 1324 ** DLL data 1325 */ 1326 DLL_Code_Pointers(); 1327 1328 /* 1329 ** Houses must be coded last, because the Class->House member of the HouseClass 1330 ** is used to code HouseClass pointers for all other objects, and if Class is 1331 ** coded, it will point to a meaningless value. 1332 */ 1333 Houses.Code_Pointers(); 1334 } 1335 1336 1337 /*********************************************************************************************** 1338 * Decode_All_Pointers -- Decodes all pointers. * 1339 * * 1340 * INPUT: none * 1341 * * 1342 * OUTPUT: none * 1343 * * 1344 * WARNINGS: none * 1345 * * 1346 * HISTORY: * 1347 * 06/24/1995 BRR : Created. * 1348 *=============================================================================================*/ 1349 void Decode_All_Pointers(void) 1350 { 1351 /* 1352 ** The Map. 1353 */ 1354 Map.Decode_Pointers(); 1355 1356 /* 1357 ** Decode houses first, so we can properly decode all other objects' 1358 ** House pointers 1359 */ 1360 Houses.Decode_Pointers(); 1361 1362 /* 1363 ** DLL data 1364 */ 1365 DLL_Decode_Pointers(); 1366 1367 /* 1368 ** The ArrayOf's. 1369 */ 1370 TeamTypes.Decode_Pointers(); 1371 Teams.Decode_Pointers(); 1372 Triggers.Decode_Pointers(); 1373 Aircraft.Decode_Pointers(); 1374 Anims.Decode_Pointers(); 1375 Buildings.Decode_Pointers(); 1376 Bullets.Decode_Pointers(); 1377 Infantry.Decode_Pointers(); 1378 Overlays.Decode_Pointers(); 1379 Smudges.Decode_Pointers(); 1380 Templates.Decode_Pointers(); 1381 Terrains.Decode_Pointers(); 1382 Units.Decode_Pointers(); 1383 Factories.Decode_Pointers(); 1384 Vessels.Decode_Pointers(); 1385 1386 /* 1387 ** The Layers. 1388 */ 1389 Logic.Decode_Pointers(); 1390 for (int i = 0; i < LAYER_COUNT; i++) { 1391 Map.Layer[i].Decode_Pointers(); 1392 } 1393 1394 /* 1395 ** The Score. 1396 */ 1397 Score.Decode_Pointers(); 1398 1399 /* 1400 ** The Base. 1401 */ 1402 Base.Decode_Pointers(); 1403 1404 /* 1405 ** PlayerPtr. 1406 */ 1407 // PlayerPtr = HouseClass::As_Pointer((HousesType)PlayerPtr); 1408 Whom = PlayerPtr->Class->House; 1409 assert(PlayerPtr != NULL); 1410 1411 /* 1412 ** Currently-selected objects. 1413 */ 1414 for (int index = 0; index < SelectedObjectsType::COUNT; index++) { 1415 DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(index); 1416 for (int j = 0; j < selection.Count(); j++) { 1417 selection[j] = As_Object((TARGET)selection[j]); 1418 assert(selection[j] != NULL); 1419 } 1420 } 1421 1422 /* 1423 ** Last-Minute Fixups; to resolve these pointers properly requires all other 1424 ** pointers to be loaded & decoded. 1425 */ 1426 if (Map.PendingObjectPtr) { 1427 Map.PendingObject = &Map.PendingObjectPtr->Class_Of(); 1428 assert(Map.PendingObject != NULL); 1429 Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List(true)); 1430 #ifdef BG 1431 Map.Set_Placement_List(Map.PendingObject->Placement_List(true)); 1432 #endif 1433 } else { 1434 Map.PendingObject = 0; 1435 Map.Set_Cursor_Shape(0); 1436 } 1437 } 1438 1439 1440 /*************************************************************************** 1441 * Get_Savefile_Info -- gets description, scenario #, house * 1442 * * 1443 * INPUT: * 1444 * id numerical ID, for the file extension * 1445 * buf buffer to store description in * 1446 * scenp ptr to variable to hold scenario * 1447 * housep ptr to variable to hold house * 1448 * * 1449 * OUTPUT: * 1450 * true = OK, false = error (save-game file invalid) * 1451 * * 1452 * WARNINGS: * 1453 * none. * 1454 * * 1455 * HISTORY: * 1456 * 01/12/1995 BR : Created. * 1457 *=========================================================================*/ 1458 bool Get_Savefile_Info(int id, char * buf, unsigned * scenp, HousesType * housep) 1459 { 1460 char name[_MAX_FNAME+_MAX_EXT]; 1461 unsigned long version; 1462 char descr_buf[DESCRIP_MAX]; 1463 1464 /* 1465 ** Generate the filename to load 1466 */ 1467 sprintf(name, "SAVEGAME.%03d", id); 1468 BufferIOFileClass file(name); 1469 1470 FileStraw straw(file); 1471 1472 /* 1473 ** Read in the description, scenario #, and the house 1474 */ 1475 if (straw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { 1476 return(false); 1477 } 1478 1479 descr_buf[strlen(descr_buf) - 2] = '\0'; // trim off CR/LF 1480 strcpy(buf, descr_buf); 1481 1482 if (straw.Get(scenp, sizeof(unsigned)) != sizeof(unsigned)) { 1483 return(false); 1484 } 1485 1486 if (straw.Get(housep, sizeof(HousesType)) != sizeof(HousesType)) { 1487 return(false); 1488 } 1489 1490 /* 1491 ** Read & verify the save-game version # 1492 */ 1493 if (straw.Get(&version, sizeof(version)) != sizeof(version)) { 1494 return(false); 1495 } 1496 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1497 if (version != SAVEGAME_VERSION && ((version-1 != SAVEGAME_VERSION)) ) { 1498 #else 1499 if (version != SAVEGAME_VERSION) { 1500 #endif 1501 return(false); 1502 } 1503 return(true); 1504 } 1505 1506 1507 /*************************************************************************** 1508 * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * 1509 * * 1510 * This function is for supporting loading a saved multiplayer game. * 1511 * When the game is loaded, we have to figure out which house goes with * 1512 * which entry in the Players vector. We also have to figure out if * 1513 * everyone who was originally in the game is still with us, and if not, * 1514 * turn their stuff over to the computer. * 1515 * * 1516 * So, this function does the following: * 1517 * - For every name in 'Players', makes sure that name is in the House * 1518 * array; if not, it's a fatal error. * 1519 * - For every human-controlled house, makes sure there's a player * 1520 * with that name; if not, it turns that house over to the computer. * 1521 * - Fills in the Player's house ID * 1522 * * 1523 * This assumes that each player MUST keep their name the same as it was * 1524 * when the game was saved! It's also assumed that the network * 1525 * connections have not been formed yet, since Player[i]->Player.ID will * 1526 * be invalid until this routine has been called. * 1527 * * 1528 * INPUT: * 1529 * none. * 1530 * * 1531 * OUTPUT: * 1532 * true = OK, false = error * 1533 * * 1534 * WARNINGS: * 1535 * none. * 1536 * * 1537 * HISTORY: * 1538 * 09/29/1995 BRR : Created. * 1539 *=========================================================================*/ 1540 static int Reconcile_Players(void) 1541 { 1542 int i; 1543 int found; 1544 HousesType house; 1545 HouseClass * housep; 1546 1547 /* 1548 ** If there are no players, there's nothing to do. 1549 */ 1550 if (Session.Players.Count()==0) 1551 return (true); 1552 1553 /* 1554 ** Make sure every name we're connected to can be found in a House 1555 */ 1556 for (i = 0; i < Session.Players.Count(); i++) { 1557 found = 0; 1558 for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + 1559 Session.MaxPlayers; house++) { 1560 1561 housep = HouseClass::As_Pointer(house); 1562 if (!housep) { 1563 continue; 1564 } 1565 1566 if (!stricmp(Session.Players[i]->Name, housep->IniName)) { 1567 found = 1; 1568 break; 1569 } 1570 } 1571 if (!found) 1572 return (false); 1573 } 1574 1575 // 1576 // Loop through all Houses; if we find a human-owned house that we're 1577 // not connected to, turn it over to the computer. 1578 // 1579 for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + 1580 Session.MaxPlayers; house++) { 1581 housep = HouseClass::As_Pointer(house); 1582 if (!housep) { 1583 continue; 1584 } 1585 1586 // 1587 // Skip this house if it wasn't human to start with. 1588 // 1589 if (!housep->IsHuman) { 1590 continue; 1591 } 1592 1593 // 1594 // Try to find this name in the Players vector; if it's found, set 1595 // its ID to this house. 1596 // 1597 found = 0; 1598 for (i = 0; i < Session.Players.Count(); i++) { 1599 if (!stricmp(Session.Players[i]->Name, housep->IniName)) { 1600 found = 1; 1601 Session.Players[i]->Player.ID = house; 1602 break; 1603 } 1604 } 1605 1606 /* 1607 ** If this name wasn't found, remove it 1608 */ 1609 if (!found) { 1610 1611 /* 1612 ** Turn the player's house over to the computer's AI 1613 */ 1614 housep->IsHuman = false; 1615 housep->IsStarted = true; 1616 // housep->Smartness = IQ_MENSA; 1617 housep->IQ = Rule.MaxIQ; 1618 strcpy (housep->IniName, Text_String(TXT_COMPUTER)); 1619 1620 Session.NumPlayers--; 1621 } 1622 } 1623 1624 // 1625 // If all went well, our Session.NumPlayers value should now equal the value 1626 // from the saved game, minus any players we removed. 1627 // 1628 if (Session.NumPlayers == Session.Players.Count()) { 1629 return (true); 1630 } else { 1631 return (false); 1632 } 1633 } 1634 1635 1636 /*************************************************************************** 1637 * MPlayer_Save_Message -- pops up a "saving..." message * 1638 * * 1639 * INPUT: * 1640 * none. * 1641 * * 1642 * OUTPUT: * 1643 * none. * 1644 * * 1645 * WARNINGS: * 1646 * none. * 1647 * * 1648 * HISTORY: * 1649 * 10/30/1995 BRR : Created. * 1650 *=========================================================================*/ 1651 void MPlayer_Save_Message(void) 1652 { 1653 //char *txt = Text_String( 1654 }