CnC_Remastered_Collection/TIBERIANDAWN/SAVELOAD.CPP
PG-SteveT fc5cd5a775 Command & Conquer Remastered post-launch patch
Improvements to harvester resource finding logic.

Don't allow the Advanced Comm Center to be capturable in skirmish or multiplayer.

Increased failed pathfinding fudge factor.

Buildings accept the Guard command if they can attack.

Don't allow force capturing of ally structures.

Fixes for laser Orcas in S3cr3t M1ss10n. Properly restore them after save. Reset Orcas after loads.

Fixed flag animation rendering in CTF.

Potentially fix a crash if aircraft are destroyed outside the map bounds.

Fixed legacy Obelisk line rendering.

Fix out-of-bounds crash in TD; issue was already fixed in RA.

Disable capture flag on Commandos.

Drop the flag when entering the limbo state.

Fixed end game race condition, winning team ID is always sent before individual player win/lose messages.

Fixed Chan spawn on SCB10EA.

Don't show enter cursor for enemy units on refineries and repair pads.

Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold.

Don't debug reveal legacy rendering when a player is defeated.

Fixed crash when loading saves of custom campaign maps.

Reset harvester archived target when given a direct harvest order.

Prevent NOD cargo planes from being force attacked.

Fixed unit selection on load.

Migrated queued repair pad functionality from RA to TD.

Randomly animate infantry in area guard mode.

Fixed crash accessing inactive objects.

Added some walls in SCG08EB to prevent civilians from killing themselves.

TD + RA: Audio: Overiding "Our base is under attack" cooldown timing from legacy from 2 minutes to 30 seconds, so it will be heard 4x as often.

Fixed adjacent cell out-of-bounds crash issues.

Kill player on disconnect

Fixed and improved build time calculations to be consistent between TD and RA.

Don't show health bars for cloaked Stealth Tanks in the legacy renderer.

Fix selection of individual control groups after mixed selection.

More adjustments to SCG08EB; switch C7 to C5, and add civilian AI to avoid Tiberium.

Extra safety checks for units that have no weapons and aircraft that can't hunt.

Fix loading of multiple infantry onto an APC.

Additional safety checks for invalid coordinates.

Prevent units from being instantly repaired.

Fix map passability.

Fail Allied mission 5B if the spy re-boards the starting transport (matches 5A and 5C behavior).

Fixed multiplayer formation move causing units to move at light speed.

Ignore movement destination checks if a unit is part of a mission-driven team.

Fix buffer overrun crash.

Ignore mines when determining win conditions.

Fixed river passability in Blue Lakes.
2020-06-22 09:43:21 -07:00

1616 lines
58 KiB
C++

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/* $Header: F:\projects\c&c\vcs\code\saveload.cpv 2.18 16 Oct 1995 16:48:44 JOE_BOSTIC $ */
/***********************************************************************************************
*** 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 ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* File Name : SAVELOAD.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : August 23, 1994 *
* *
* Last Update : June 24, 1995 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Code_All_Pointers -- Code all pointers. *
* Decode_All_Pointers -- Decodes all pointers. *
* Get_Savefile_Info -- gets description, scenario #, house *
* Load_Game -- loads a saved game *
* Load_Misc_Values -- Loads miscellaneous variables. *
* Load_Misc_Values -- loads miscellaneous variables *
* Read_Object -- reads an object from disk, in a safe way *
* Save_Game -- saves a game to disk *
* Save_Misc_Values -- saves miscellaneous variables *
* Target_To_TechnoType -- converts TARGET to TechnoTypeClass *
* TechnoType_To_Target -- converts TechnoTypeClass to TARGET *
* Write_Object -- reads an object from disk, in a safe way *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
extern bool DLLSave(FileClass &file);
extern bool DLLLoad(FileClass &file);
/*
********************************** Defines **********************************
*/
#define SAVEGAME_VERSION (DESCRIP_MAX + \
0x01000003 + ( \
sizeof(AircraftClass) + \
sizeof(AircraftTypeClass) + \
sizeof(AnimClass) + \
sizeof(AnimTypeClass) + \
sizeof(BuildingClass) + \
sizeof(BuildingTypeClass) + \
sizeof(BulletClass) + \
sizeof(BulletTypeClass) + \
sizeof(HouseClass) + \
sizeof(HouseTypeClass) + \
sizeof(InfantryClass) + \
sizeof(InfantryTypeClass) + \
sizeof(OverlayClass) + \
sizeof(OverlayTypeClass) + \
sizeof(SmudgeClass) + \
sizeof(SmudgeTypeClass) + \
sizeof(TeamClass) + \
sizeof(TeamTypeClass) + \
sizeof(TemplateClass) + \
sizeof(TemplateTypeClass) + \
sizeof(TerrainClass) + \
sizeof(TerrainTypeClass) + \
sizeof(UnitClass) + \
sizeof(UnitTypeClass) + \
sizeof(MouseClass) + \
sizeof(CellClass) + \
sizeof(FactoryClass) + \
sizeof(BaseClass) + \
sizeof(LayerClass) + \
sizeof(BriefingText) + \
sizeof(Waypoint)))
/***************************************************************************
* Save_Game -- saves a game to disk *
* *
* Saving the Map: *
* DisplayClass::Save() invokes CellClass's Write() for every cell *
* that needs to be saved. A cell needs to be saved if it contains *
* any special data at all, such as a TIcon, or an Occupier. *
* The cell saves its own CellTrigger pointer, converted to a TARGET. *
* *
* Saving game objects: *
* - Any object stored in an ArrayOf class needs to be saved. The ArrayOf*
* Save() routine invokes each object's Write() routine, if that *
* object's IsActive is set. *
* *
* Saving the layers: *
* The Map's Layers (Ground, Air, etc) of things that are on the map, *
* and the Logic's Layer of things to process both need to be saved. *
* LayerClass::Save() writes the entire layer array to disk *
* *
* Saving the houses: *
* Each house needs to be saved, to record its Credits, Power, etc. *
* *
* Saving miscellaneous data: *
* There are a lot of miscellaneous variables to save, such as the *
* map's dimensions, the player's house, etc. *
* *
* INPUT: *
* id numerical ID, for the file extension *
* *
* OUTPUT: *
* true = OK, false = error *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 12/28/1994 BR : Created. *
*=========================================================================*/
bool Save_Game(int id,char *descr)
{
char name[_MAX_FNAME+_MAX_EXT];
/*
** Generate the filename to save
*/
sprintf(name, "SAVEGAME.%03d", id);
return Save_Game(name, descr);
}
/*
** Version that takes file name. ST - 9/9/2019 11:10AM
*/
bool Save_Game(const char *file_name, const char *descr)
{
RawFileClass file;
int i;
unsigned long version;
unsigned scenario;
HousesType house;
char descr_buf[DESCRIP_MAX];
scenario = Scenario; // get current scenario #
house = PlayerPtr->Class->House; // get current house
/*
** Code everybody's pointers
*/
Code_All_Pointers();
/*
** Open the file
*/
if (!file.Open(file_name, WRITE)) {
Decode_All_Pointers();
return(false);
}
/*
** Save the DLLs variables first, so we can do a version check in the DLL when we begin the load
*/
if (RunningAsDLL) {
if (!DLLSave(file)) {
file.Close();
Decode_All_Pointers();
return false;
}
}
/*
** Save the description, scenario #, and house
** (scenario # & house are saved separately from the actual Scenario &
** PlayerPtr globals for convenience; we can quickly find out which
** house & scenario this save-game file is for by reading these values.
** Also, PlayerPtr is stored in a coded form in Save_Misc_Values(),
** which may or may not be a HousesType number; so, saving 'house'
** here ensures we can always pull out the house for this file.)
*/
sprintf(descr_buf, "%s\r\n",descr); // put CR-LF after text
descr_buf[strlen(descr_buf) + 1] = 26; // put CTRL-Z after NULL
if (file.Write(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) {
file.Close();
return(false);
}
if (file.Write(&scenario, sizeof(scenario)) != sizeof(scenario)) {
file.Close();
return(false);
}
if (file.Write(&house, sizeof(house)) != sizeof(house)) {
file.Close();
return(false);
}
/*
** Save the save-game version, for loading verification
*/
version = SAVEGAME_VERSION;
if (file.Write(&version, sizeof(version)) != sizeof(version)) {
file.Close();
return(false);
}
Call_Back();
/*
** Save the map. The map must be saved first, since it saves the Theater.
*/
Map.Save(file);
Call_Back();
/*
** Save all game objects. This code saves every object that's stored in a
** TFixedIHeap class.
*/
if (!Houses.Save(file) ||
!TeamTypes.Save(file) ||
!Teams.Save(file) ||
!Triggers.Save(file) ||
!Aircraft.Save(file) ||
!Anims.Save(file) ||
!Buildings.Save(file) ||
!Bullets.Save(file) ||
!Infantry.Save(file) ||
!Overlays.Save(file) ||
!Smudges.Save(file) ||
!Templates.Save(file) ||
!Terrains.Save(file) ||
!Units.Save(file) ||
!Factories.Save(file)) {
file.Close();
Decode_All_Pointers();
return(false);
}
Call_Back();
/*
** Save the Logic & Map layers
*/
if (!Logic.Save(file)) {
file.Close();
Decode_All_Pointers();
return(false);
}
for (i = 0; i < LAYER_COUNT; i++) {
if (!Map.Layer[i].Save(file)) {
file.Close();
Decode_All_Pointers();
return(false);
}
}
/*
** Save the Score
*/
if (!Score.Save(file)) {
file.Close();
Decode_All_Pointers();
return(false);
}
/*
** Save the AI Base
*/
if (!Base.Save(file)) {
file.Close();
Decode_All_Pointers();
return(false);
}
/*
** Save miscellaneous variables.
*/
if (!Save_Misc_Values(file)) {
file.Close();
Decode_All_Pointers();
return(false);
}
Call_Back();
/*
** Close the file; we're done
*/
file.Close();
Decode_All_Pointers();
return(true);
}
/***************************************************************************
* Load_Game -- loads a saved game *
* *
* This routine loads the data in the same way it was saved out. *
* *
* Loading the Map: *
* - DisplayClass::Load() invokes CellClass's Load() for every cell *
* that was saved. *
* - The cell loads its own CellTrigger pointer. *
* *
* Loading game objects: *
* - IHeap's Load() routine loads the # of objects stored, and loads *
* each object. *
* - Triggers: Add themselves to the HouseTriggers if they're associated *
* with a house *
* *
* Loading the layers: *
* LayerClass::Load() reads the entire layer array to disk *
* *
* Loading the houses: *
* Each house is loaded in its entirety. *
* *
* Loading miscellaneous data: *
* There are a lot of miscellaneous variables to load, such as the *
* map's dimensions, the player's house, etc. *
* *
* INPUT: *
* id numerical ID, for the file extension *
* *
* OUTPUT: *
* true = OK, false = error *
* *
* WARNINGS: *
* If this routine returns false, the entire game will be in an *
* unknown state, so the scenario will have to be re-initialized. *
* *
* HISTORY: *
* 12/28/1994 BR : Created. *
*=========================================================================*/
bool Load_Game(int id)
{
char name[_MAX_FNAME+_MAX_EXT];
/*
** Generate the filename to load
*/
sprintf(name, "SAVEGAME.%03d", id);
return Load_Game(name);
}
/*
** Version that takes a file name instead. ST - 9/9/2019 11:13AM
*/
bool Load_Game(const char *file_name)
{
RawFileClass file;
int i;
unsigned long version;
unsigned scenario;
HousesType house;
char descr_buf[DESCRIP_MAX];
/*
** Open the file
*/
if (!file.Open(file_name, READ)) {
return(false);
}
/*
** Load the DLLs variables first, in case we need to do something different based on version
*/
if (RunningAsDLL) {
if (!DLLLoad(file)) {
file.Close();
return false;
}
}
/*
** Read & discard the save-game's header info
*/
if (file.Read(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) {
file.Close();
return(false);
}
if (file.Read(&scenario, sizeof(scenario)) != sizeof(scenario)) {
file.Close();
return(false);
}
if (file.Read(&house, sizeof(house)) != sizeof(house)) {
file.Close();
return(false);
}
Call_Back();
/*
** Clear the scenario so we start fresh; this calls the Init_Clear() routine
** for the Map, and all object arrays. It has the following important
** effects:
** - Every cell is cleared to 0's, via MapClass::Init_Clear()
** - All heap elements' are cleared
** - The Houses are Initialized, which also clears their HouseTriggers
** array
** - The map's Layers & Logic Layer are cleared to empty
** - The list of currently-selected objects is cleared
*/
Clear_Scenario();
/*
** Read in & verify the save-game ID code
*/
if (file.Read(&version,sizeof(version)) != sizeof(version)) {
file.Close();
return(false);
}
if (version != SAVEGAME_VERSION) {
file.Close();
return(false);
}
Call_Back();
/*
** Set the required CD to be in the drive according to the scenario
** loaded.
*/
if (RequiredCD != -2) {
if (scenario >= 20 && scenario <60 && GameToPlay == GAME_NORMAL) {
RequiredCD = 2;
} else {
if (scenario >= 60){
/*
** This is a gateway bonus scenario
*/
RequiredCD = -1;
}else{
if (house == HOUSE_GOOD) {
RequiredCD = 0;
} else {
RequiredCD = 1;
}
}
}
}
if(!Force_CD_Available(RequiredCD)) {
Prog_End("Load_Game - CD not found", true);
if (!RunningAsDLL) {
exit(EXIT_FAILURE);
}
return false;
}
Call_Back();
/*
** Load the map. The map comes first, since it loads the Theater & init's
** mixfiles. The map calls all the type-class's Init routines, telling them
** what the Theater is; this must be done before any objects are created, so
** they'll be properly created.
*/
Map.Load(file);
Call_Back();
/*
** Load the object data.
*/
if (!Houses.Load(file) ||
!TeamTypes.Load(file) ||
!Teams.Load(file) ||
!Triggers.Load(file) ||
!Aircraft.Load(file) ||
!Anims.Load(file) ||
!Buildings.Load(file) ||
!Bullets.Load(file) ||
!Infantry.Load(file) ||
!Overlays.Load(file) ||
!Smudges.Load(file) ||
!Templates.Load(file) ||
!Terrains.Load(file) ||
!Units.Load(file) ||
!Factories.Load(file)) {
file.Close();
return(false);
}
Call_Back();
/*
** Load the Logic & Map Layers
*/
if (!Logic.Load(file)) {
file.Close();
return(false);
}
for (i = 0; i < LAYER_COUNT; i++) {
if (!Map.Layer[i].Load(file)) {
file.Close();
return(false);
}
}
Call_Back();
/*
** Load the Score
*/
if (!Score.Load(file)) {
file.Close();
return(false);
}
/*
** Load the AI Base
*/
if (!Base.Load(file)) {
file.Close();
return(false);
}
/*
** Load miscellaneous variables, including the map size & the Theater
*/
if (!Load_Misc_Values(file)) {
file.Close();
return(false);
}
file.Close();
Decode_All_Pointers();
Map.Init_IO();
Map.Flag_To_Redraw(true);
Fixup_Scenario();
ScenarioInit = 0;
/*
** Fixup remap tables. ST - 2/28/2020 1:50PM
*/
for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
HouseClass * hptr = HouseClass::As_Pointer(house);
if (hptr && hptr->IsActive) {
hptr->Init_Data(hptr->RemapColor, hptr->ActLike, hptr->Credits);
}
}
/*
** Re-init unit trackers. They will be garbage pointers after the load
*/
for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
HouseClass * hptr = HouseClass::As_Pointer(house);
if (hptr && hptr->IsActive) {
hptr->Init_Unit_Trackers();
}
}
#ifdef DEMO
if (Scenario != 10 && Scenario != 1 && Scenario != 6) {
Clear_Scenario();
return(false);
}
#endif
Call_Back();
return(true);
}
/***************************************************************************
* Save_Misc_Values -- saves miscellaneous variables *
* *
* INPUT: *
* file file to use for writing *
* *
* OUTPUT: *
* true = success, false = failure *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 12/29/1994 BR : Created. *
*=========================================================================*/
bool Save_Misc_Values(FileClass &file)
{
int i, j;
int count; // # ptrs in 'CurrentObject'
ObjectClass * ptr; // for saving 'CurrentObject' ptrs
/*
** Player's House.
*/
if (file.Write(&PlayerPtr, sizeof(PlayerPtr)) != sizeof(PlayerPtr)) {
return(false);
}
/*
** Save this scenario number.
*/
if (file.Write(&Scenario, sizeof(Scenario)) != sizeof(Scenario)) {
return(false);
}
/*
** Save frame #.
*/
if (file.Write(&Frame, sizeof(Frame)) != sizeof(Frame)) {
return(false);
}
/*
** Save VQ Movie names.
*/
if (file.Write(WinMovie, sizeof(WinMovie)) != sizeof(WinMovie)) {
return(false);
}
if (file.Write(LoseMovie, sizeof(LoseMovie)) != sizeof(LoseMovie)) {
return(false);
}
/*
** Save currently-selected objects list.
** Save the # of ptrs in the list.
*/
for (i = 0; i < SelectedObjectsType::COUNT; i++) {
DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(i);
count = selection.Count();
if (file.Write(&count, sizeof(count)) != sizeof(count)) {
return(false);
}
/*
** Save the pointers.
*/
for (j = 0; j < count; j++) {
ptr = selection[j];
if (file.Write(&ptr, sizeof(ptr)) != sizeof(ptr)) {
return(false);
}
}
}
/*
** Save the list of waypoints.
*/
if (file.Write(Waypoint, sizeof(Waypoint)) != sizeof(Waypoint)) {
return(false);
}
file.Write(&ScenDir, sizeof(ScenDir));
file.Write(&ScenVar, sizeof(ScenVar));
file.Write(&CarryOverMoney, sizeof(CarryOverMoney));
file.Write(&CarryOverPercent, sizeof(CarryOverPercent));
file.Write(&BuildLevel, sizeof(BuildLevel));
file.Write(BriefMovie, sizeof(BriefMovie));
file.Write(Views, sizeof(Views));
file.Write(&EndCountDown, sizeof(EndCountDown));
file.Write(BriefingText, sizeof(BriefingText));
// This is new...
file.Write(ActionMovie, sizeof(ActionMovie));
return(true);
}
/***********************************************************************************************
* Load_Misc_Values -- Loads miscellaneous variables. *
* *
* INPUT: file -- The file to load the misc values from. *
* *
* OUTPUT: Was the misc load process successful? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 06/24/1995 BRR : Created. *
*=============================================================================================*/
bool Load_Misc_Values(FileClass &file)
{
int i, j;
int count; // # ptrs in 'CurrentObject'
ObjectClass * ptr; // for loading 'CurrentObject' ptrs
/*
** Player's House.
*/
if (file.Read(&PlayerPtr, sizeof(PlayerPtr)) != sizeof(PlayerPtr)) {
return(false);
}
/*
** Read this scenario number.
*/
if (file.Read(&Scenario,sizeof(Scenario)) != sizeof(Scenario)) {
return(false);
}
/*
** Load frame #.
*/
if (file.Read(&Frame, sizeof(Frame)) != sizeof(Frame)) {
return(false);
}
/*
** Load VQ Movie names.
*/
if (file.Read(WinMovie, sizeof(WinMovie)) != sizeof(WinMovie)) {
return(false);
}
if (file.Read(LoseMovie, sizeof(LoseMovie)) != sizeof(LoseMovie)) {
return(false);
}
for (i = 0; i < SelectedObjectsType::COUNT; i++) {
/*
** Load currently-selected objects list.
** Load the # of ptrs in the list.
*/
DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(i);
if (file.Read(&count, sizeof(count)) != sizeof(count)) {
return(false);
}
/*
** Load the pointers.
*/
for (j = 0; j < count; j++) {
if (file.Read(&ptr, sizeof(ptr)) != sizeof(ptr)) {
return(false);
}
selection.Add(ptr); // add to the list
}
}
/*
** Save the list of waypoints.
*/
if (file.Read(Waypoint, sizeof(Waypoint)) != sizeof(Waypoint)) {
return(false);
}
file.Read(&ScenDir, sizeof(ScenDir));
file.Read(&ScenVar, sizeof(ScenVar));
file.Read(&CarryOverMoney, sizeof(CarryOverMoney));
file.Read(&CarryOverPercent, sizeof(CarryOverPercent));
file.Read(&BuildLevel, sizeof(BuildLevel));
file.Read(BriefMovie, sizeof(BriefMovie));
file.Read(Views, sizeof(Views));
file.Read(&EndCountDown, sizeof(EndCountDown));
file.Read(BriefingText, sizeof(BriefingText));
if (file.Seek(0, SEEK_CUR) < file.Size()) {
file.Read(ActionMovie, sizeof(ActionMovie));
}
return(true);
}
/*
** ST - 9/26/2019 11:43AM
*/
extern void DLL_Code_Pointers(void);
extern void DLL_Decode_Pointers(void);
/***********************************************************************************************
* Code_All_Pointers -- Code all pointers. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 06/24/1995 BRR : Created. *
*=============================================================================================*/
void Code_All_Pointers(void)
{
int i, j;
/*
** The Map.
*/
Map.Code_Pointers();
/*
** The ArrayOf's.
*/
TeamTypes.Code_Pointers();
Teams.Code_Pointers();
Triggers.Code_Pointers();
Aircraft.Code_Pointers();
Anims.Code_Pointers();
Buildings.Code_Pointers();
Bullets.Code_Pointers();
Infantry.Code_Pointers();
Overlays.Code_Pointers();
Smudges.Code_Pointers();
Templates.Code_Pointers();
Terrains.Code_Pointers();
Units.Code_Pointers();
Factories.Code_Pointers();
/*
** The Layers.
*/
Logic.Code_Pointers();
for (i = 0; i < LAYER_COUNT; i++) {
Map.Layer[i].Code_Pointers();
}
/*
** The Score.
*/
Score.Code_Pointers();
/*
** The Base.
*/
Base.Code_Pointers();
/*
** PlayerPtr.
*/
PlayerPtr = (HouseClass *)(PlayerPtr->Class->House);
/*
** Currently-selected objects.
*/
for (i = 0; i < SelectedObjectsType::COUNT; i++) {
DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(i);
for (j = 0; j < selection.Count(); j++) {
selection[j] = (ObjectClass *)selection[j]->As_Target();
}
}
/*
** DLL data
*/
DLL_Code_Pointers();
/*
** Houses must be coded last, because the Class->House member of the HouseClass
** is used to code HouseClass pointers for all other objects, and if Class is
** coded, it will point to a meaningless value.
*/
Houses.Code_Pointers();
}
/***********************************************************************************************
* Decode_All_Pointers -- Decodes all pointers. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 06/24/1995 BRR : Created. *
*=============================================================================================*/
void Decode_All_Pointers(void)
{
int i, j;
/*
** The Map.
*/
Map.Decode_Pointers();
/*
** Decode houses first, so we can properly decode all other objects'
** House pointers
*/
Houses.Decode_Pointers();
/*
** DLL data
*/
DLL_Decode_Pointers();
/*
** The ArrayOf's.
*/
TeamTypes.Decode_Pointers();
Teams.Decode_Pointers();
Triggers.Decode_Pointers();
Aircraft.Decode_Pointers();
Anims.Decode_Pointers();
Buildings.Decode_Pointers();
Bullets.Decode_Pointers();
Infantry.Decode_Pointers();
Overlays.Decode_Pointers();
Smudges.Decode_Pointers();
Templates.Decode_Pointers();
Terrains.Decode_Pointers();
Units.Decode_Pointers();
Factories.Decode_Pointers();
/*
** The Layers.
*/
Logic.Decode_Pointers();
for (i = 0; i < LAYER_COUNT; i++) {
Map.Layer[i].Decode_Pointers();
}
/*
** The Score.
*/
Score.Decode_Pointers();
/*
** The Base.
*/
Base.Decode_Pointers();
/*
** PlayerPtr.
*/
PlayerPtr = HouseClass::As_Pointer(*((HousesType*)&PlayerPtr));
Whom = PlayerPtr->Class->House;
switch (PlayerPtr->Class->House) {
case HOUSE_GOOD:
ScenPlayer = SCEN_PLAYER_GDI;
break;
case HOUSE_BAD:
ScenPlayer = SCEN_PLAYER_NOD;
break;
case HOUSE_JP:
ScenPlayer = SCEN_PLAYER_JP;
break;
}
Check_Ptr(PlayerPtr,__FILE__,__LINE__);
if (PlayerPtr->ActLike == HOUSE_JP) {
ScenPlayer = SCEN_PLAYER_JP;
}
Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, ScenVar);
/*
** Currently-selected objects.
*/
for (i = 0; i < SelectedObjectsType::COUNT; i++) {
DynamicVectorClass<ObjectClass *>& selection = CurrentObject.Raw(i);
for (j = 0; j < selection.Count(); j++) {
unsigned long target_as_object_ptr = reinterpret_cast<unsigned long>(selection[j]);
TARGET target = (TARGET)target_as_object_ptr;
selection[j] = As_Object(target);
Check_Ptr(selection[j],__FILE__,__LINE__);
}
}
/*
** Last-Minute Fixups; to resolve these pointers properly requires all other
** pointers to be loaded & decoded.
*/
if (Map.PendingObjectPtr) {
Map.PendingObject = &Map.PendingObjectPtr->Class_Of();
Check_Ptr((void *)Map.PendingObject, __FILE__, __LINE__);
Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List(true));
} else {
Map.PendingObject = 0;
Map.Set_Cursor_Shape(0);
}
}
/***********************************************************************************************
* Read_Object -- reads an object from a file *
* *
* Replacement for the original code below, which doesn't work with MSVC *
* We now assume that the vtable is 4 bytes, and is at the beginning of the class *
* It's the caller's responsibility to make sure the VTable is correct *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 01/10/1995 BR : Created. *
* 9/10/2019 12:34PM ST : Updated for MS compiler *
*=============================================================================================*/
bool Read_Object(void *ptr, int class_size, FileClass & file, bool has_vtable)
{
int size;
/*
** Read size of this chunk.
*/
if (file.Read(&size,sizeof(size)) != sizeof(size)) {
return(false);
}
/*
** Error if incorrect size.
*/
if (size != class_size) {
return false;
}
int vtable_adjust = has_vtable ? 4 : 0;
unsigned char *object_ptr = static_cast<unsigned char *>(ptr);
if (has_vtable) {
/*
** Need to skip the vtable read.
*/
int dummy;
file.Read(&dummy, vtable_adjust);
}
/*
** Read object data.
*/
if (file.Read(object_ptr + vtable_adjust, class_size - vtable_adjust) != (class_size - vtable_adjust)) {
return(false);
}
return true;
}
#if (0)
/***********************************************************************************************
* Write_Object -- writes an object to a file *
* *
* This routine writes an object, skipping the embedded virtual function table pointer. *
* *
* INPUT: *
* ptr pointer to object to write *
* class_size size of the class itself *
* file file to use for I/O *
* *
* OUTPUT: *
* true = OK, false = error *
* *
* WARNINGS: *
* This routine ASSUMES the program modules are compiled with: *
* -Vb- Always make the virtual function table ptr 2 bytes long *
* -Vt Put the virtual function table after the 1st class's data *
* *
* Also see warnings for Read_Object(). *
* *
* HISTORY: *
* 01/10/1995 BR : Created. *
* 9/10/2019 12:34PM ST : Updated for MS compiler *
*=============================================================================================*/
bool Write_Object(void *ptr, int class_size, FileClass & file)
{
/*
** Test assumptions about class size.
*/
class TestClass {
virtual void Test(void) = 0;
};
if (sizeof(TestClass) != 4) {
/*
** Crash.
*/
*((int*)0) = 0;
}
/*
** Save size of this chunk.
*/
if (file.Write(&class_size,sizeof(class_size)) != sizeof(class_size)) {
return(false);
}
/*
** Save object data.
*/
if (file.Write(ptr, class_size) != (class_size)) {
return(false);
}
return(true);
}
#endif
#if (0) //ST - 9/10/2019 12:43PM
/***********************************************************************************************
* Read_Object -- reads an object from disk *
* *
* This routine reads in an object and fills in the virtual function table pointer. *
* *
* INPUT: *
* ptr pointer to object to read *
* base_size size of object's absolute base class *
* class_size size of the class itself *
* file file to use for I/O *
* vtable virtual function table pointer value, NULL if none *
* *
* OUTPUT: *
* true = OK, false = error *
* *
* WARNINGS: *
* This routine ASSUMES the program modules are compiled with: *
* -Vb- Always make the virtual function table ptr 2 bytes long *
* -Vt Put the virtual function table after the 1st class's data *
* *
* ALSO, the class used to compute 'base_size' must come first in a multiple-inheritence *
* hierarchy. AND, if your class multiply-inherits from other classes, only ONE of those *
* classes can contain virtual functions! If you include virtual functions in the other *
* classes, the compiler will generate multiple virtual function tables, and this load/save *
* technique will fail. *
* *
* Each class hierarchy is stored in memory as a chain: first the data for the base-est *
* class, then the virtual function table pointer for this hierarchy, then the data for *
* all derived classes. If any of these derived classes multiply-inherit, the base class *
* for the multiple inheritance is stored as a separate chain following this chain. The *
* new chain will contain its own virtual function table pointer, if the multiply- *
* inherited hierarchy contains any virtual functions. Thus, the declaration *
* class A *
* class B: public A *
* class C: public B, X *
* is stored as: *
* A data *
* A's Virtual Table Pointer *
* B data *
* X data *
* [X's Virtual Table Pointer] *
* C data *
* *
* and *
* class A *
* class B: public A *
* class C: public X, B *
* is stored in memory as: *
* X data *
* [X's Virtual Table Pointer] *
* A data *
* A's Virtual Table Pointer *
* B data *
* C data *
* *
* *
* HISTORY: *
* 01/10/1995 BR : Created. *
*=============================================================================================*/
bool Read_Object(void *ptr, int base_size, int class_size, FileClass & file, void * vtable)
{
int size; // object size in bytes
/*
** Read size of this chunk.
*/
if (file.Read(&size,sizeof(size)) != sizeof(size)) {
return(false);
}
/*
** Error if incorrect size.
*/
if (size != class_size) {
return(false);
}
/*
** Read object data.
*/
if (file.Read(ptr, class_size) != (class_size)) {
return(false);
}
/*
** Fill in VTable.
*/
if (vtable) {
((void **)(((char *)ptr) + base_size - 4))[0] = vtable;
}
return(true);
}
#endif
/***********************************************************************************************
* Write_Object -- reads an object from disk, in a safe way *
* *
* This routine writes an object in 2 pieces, skipping the embedded *
* virtual function table pointer. *
* *
* INPUT: *
* ptr pointer to object to write *
* class_size size of the class itself *
* file file to use for I/O *
* *
* OUTPUT: *
* true = OK, false = error *
* *
* WARNINGS: *
* This routine ASSUMES the program modules are compiled with: *
* -Vb- Always make the virtual function table ptr 2 bytes long *
* -Vt Put the virtual function table after the 1st class's data *
* *
* Also see warnings for Read_Object(). *
* *
* HISTORY: *
* 01/10/1995 BR : Created. *
*=============================================================================================*/
bool Write_Object(void *ptr, int class_size, FileClass & file)
{
/*
** Save size of this chunk.
*/
if (file.Write(&class_size,sizeof(class_size)) != sizeof(class_size)) {
return(false);
}
/*
** Save object data.
*/
if (file.Write(ptr, class_size) != (class_size)) {
return(false);
}
return(true);
}
/***************************************************************************
* Get_Savefile_Info -- gets description, scenario #, house *
* *
* INPUT: *
* id numerical ID, for the file extension *
* buf buffer to store description in *
* scenp ptr to variable to hold scenario *
* housep ptr to variable to hold house *
* *
* OUTPUT: *
* true = OK, false = error (save-game file invalid) *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 01/12/1995 BR : Created. *
*=========================================================================*/
bool Get_Savefile_Info(int id, char *buf, unsigned *scenp, HousesType *housep)
{
RawFileClass file;
char name[_MAX_FNAME+_MAX_EXT];
unsigned long version;
char descr_buf[DESCRIP_MAX];
/*
** Generate the filename to load
*/
sprintf(name, "SAVEGAME.%03d", id);
/*
** If the file opens OK, read the file
*/
if (file.Open(name, READ)) {
/*
** Read in the description, scenario #, and the house
*/
if (file.Read(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) {
file.Close();
return(false);
}
descr_buf[strlen(descr_buf) - 2] = '\0'; // trim off CR/LF
strcpy(buf, descr_buf);
if (file.Read(scenp, sizeof(unsigned)) != sizeof(unsigned)) {
file.Close();
return(false);
}
if (file.Read(housep, sizeof(HousesType)) != sizeof(HousesType)) {
file.Close();
return(false);
}
/*
** Read & verify the save-game version #
*/
if (file.Read(&version,sizeof(version)) != sizeof(version)) {
file.Close();
return(false);
}
if (version!=SAVEGAME_VERSION) {
file.Close();
return(false);
}
file.Close();
return(true);
}
return(false);
}
/***************************************************************************
* TechnoType_To_Target -- converts TechnoTypeClass to TARGET *
* *
* INPUT: *
* ptr pointer to convert *
* *
* OUTPUT: *
* target value *
* *
* WARNINGS: *
* Be certain that you only use the returned target value by passing *
* it to Target_To_TechnoType; do NOT call As_Techno, or you'll get *
* a totally invalid pointer. *
* *
* HISTORY: *
* 01/12/1995 BR : Created. *
*=========================================================================*/
TARGET TechnoType_To_Target(TechnoTypeClass const * ptr)
{
TARGET target;
switch (ptr->What_Am_I()) {
case RTTI_INFANTRYTYPE:
target = Build_Target(KIND_INFANTRY, ((InfantryTypeClass const *)ptr)->Type);
break;
case RTTI_UNITTYPE:
target = Build_Target(KIND_UNIT, ((UnitTypeClass const *)ptr)->Type);
break;
case RTTI_AIRCRAFTTYPE:
target = Build_Target(KIND_AIRCRAFT, ((AircraftTypeClass const *)ptr)->Type);
break;
case RTTI_BUILDINGTYPE:
target = Build_Target(KIND_BUILDING, ((BuildingTypeClass const *)ptr)->Type);
break;
default:
target = 0;
break;
}
return(target);
}
/***************************************************************************
* Target_To_TechnoType -- converts TARGET to TechnoTypeClass *
* *
* INPUT: *
* target TARGET value to convert *
* *
* OUTPUT: *
* pointer to the TechnoTypeClass for this target value *
* *
* WARNINGS: *
* The TARGET value MUST have been generated with TechnoType_To_Target;*
* If you give this routine a target generated by an As_Target() *
* routine, it will return a bogus pointer. *
* *
* HISTORY: *
* 01/12/1995 BR : Created. *
*=========================================================================*/
TechnoTypeClass const * Target_To_TechnoType(TARGET target)
{
switch (Target_Kind(target)) {
case KIND_INFANTRY:
return(&InfantryTypeClass::As_Reference((InfantryType)Target_Value(target)));
case KIND_UNIT:
return(&UnitTypeClass::As_Reference((UnitType)Target_Value(target)));
case KIND_AIRCRAFT:
return(&AircraftTypeClass::As_Reference((AircraftType)Target_Value(target)));
case KIND_BUILDING:
return(&BuildingTypeClass::As_Reference((StructType)Target_Value(target)));
}
return(NULL);
}
/***************************************************************************
* Get_VTable -- gets the VTable pointer for the given object *
* *
* INPUT: *
* ptr pointer to check *
* *
* OUTPUT: *
* none *
* *
* WARNINGS: *
* none *
* *
* HISTORY: *
* 01/12/1995 BR : Created. *
*=========================================================================*/
void * Get_VTable(void *ptr, int base_size)
{
return(((void **)(((char *)ptr) + base_size - 4))[0]);
}
/***************************************************************************
* Set_VTable -- sets the VTable pointer for the given object *
* *
* INPUT: *
* ptr pointer to check *
* base_size size of base class *
* vtable value of VTable to plug in *
* *
* OUTPUT: *
* none *
* *
* WARNINGS: *
* none *
* *
* HISTORY: *
* 01/12/1995 BR : Created. *
*=========================================================================*/
void Set_VTable(void *ptr, int base_size, void *vtable)
{
((void **)(((char *)ptr) + base_size - 4))[0] = vtable;
}
#if 0
/****************************************************************************
Dump routine: prints everything about everything related to the Save/Load
process (OK, not exactly everything, but lots of stuff)
****************************************************************************/
void Dump(void)
{
int i,j;
FILE *fp;
char *layername[] = {
"Ground",
"Air",
"Top"
};
/*
------------------------------- Open file --------------------------------
*/
fp = fopen("dump.txt","wt");
/*
------------------------------ Logic Layer -------------------------------
*/
fprintf(fp,"--------------------- Logic Layer ---------------------\n");
fprintf(fp,"Count: %d\n",Logic.Count());
for (j = 0; j < Logic.Count(); j++) {
fprintf(fp, "Entry %d: %x \n",j,Logic[j]);
}
fprintf(fp,"\n");
/*
------------------------------- Map Layers -------------------------------
*/
for (i = 0; i < LAYER_COUNT; i++) {
fprintf(fp,"----------------- Map Layer %s ---------------------\n",
layername[i]);
fprintf(fp,"Count: %d\n",Map.Layer[i].Count());
for (j = 0; j < Map.Layer[i].Count(); j++) {
fprintf(fp, "Entry %d: %x \n",j,Map.Layer[i][j]);
}
}
fprintf(fp,"\n");
fprintf(fp,"------------------ TeamTypes --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",TeamTypes.ActiveCount);
for (i = 0; i < TEAMTYPE_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,TeamTypes[i].IsActive,
TeamTypes[i].Get_Name());
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Teams --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Teams.ActiveCount);
for (i = 0; i < TEAM_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,Teams[i].IsActive,
Teams[i].Class->Get_Name());
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Triggers --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Triggers.ActiveCount);
for (i = 0; i < TRIGGER_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,Triggers[i].IsActive,
Triggers[i].Get_Name());
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Aircraft --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Aircraft.ActiveCount);
for (i = 0; i < AIRCRAFT_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Aircraft[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Anims --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Anims.ActiveCount);
for (i = 0; i < ANIM_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Anims[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Buildings --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Buildings.ActiveCount);
for (i = 0; i < BUILDING_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Buildings[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Bullets --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Bullets.ActiveCount);
for (i = 0; i < BULLET_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Bullets[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Infantry --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Infantry.ActiveCount);
for (i = 0; i < INFANTRY_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Infantry[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Overlays --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Overlays.ActiveCount);
for (i = 0; i < OVERLAY_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Overlays[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Reinforcements --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Reinforcements.ActiveCount);
for (i = 0; i < REINFORCEMENT_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Reinforcements[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Smudges --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Smudges.ActiveCount);
for (i = 0; i < SMUDGE_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Smudges[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Templates --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Templates.ActiveCount);
for (i = 0; i < TEMPLATE_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Templates[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Terrains --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Terrains.ActiveCount);
for (i = 0; i < TERRAIN_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Terrains[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Units --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Units.ActiveCount);
for (i = 0; i < UNIT_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Units[i].IsActive);
}
fprintf(fp,"\n");
fprintf(fp,"------------------ Factories --------------------------\n");
fprintf(fp,"ActiveCount: %d\n",Factories.ActiveCount);
for (i = 0; i < FACTORY_MAX; i++) {
fprintf(fp,"Entry %d: Active:%d \n",i,Factories[i].IsActive);
}
fprintf(fp,"\n");
fclose(fp);
/*
---------------------------- Flush the cache -----------------------------
*/
fp = fopen("dummy.bin","wt");
for (i = 0; i < 100; i++) {
fprintf(fp,"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
}
fclose(fp);
}
#endif