diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt
index 137d06855..8966dc5b6 100644
--- a/code/CMakeLists.txt
+++ b/code/CMakeLists.txt
@@ -177,11 +177,6 @@ target_compile_definitions(OpenTS PRIVATE
WIN32
_WINDOWS
NOMINMAX
-
- # Compiles Blowfish into the binary instead of reaching it through the COM object in
- # blowfish.dll. It decides the layout of BlowfishEngine, so every translation unit has
- # to agree on it and it belongs on the compile line rather than in a header.
- NO_BLOWFISH_DLL
)
#
@@ -217,7 +212,7 @@ target_link_libraries(OpenTS PRIVATE
winmm
ws2_32
kernel32 user32 gdi32 winspool comdlg32 advapi32 shell32
- ole32 oleaut32 uuid odbc32 odbccp32
+ odbc32 odbccp32
)
if(MSVC)
@@ -272,37 +267,13 @@ foreach(f ${OPENTS_SRC})
endif()
endforeach()
-# List of all interface filenames (headers + C stubs)
-set(INTERFACE_FILES
- iblockci.h iblockci_i.c
- iblowfish.h iblowfish_i.c
- iflyctrl.h iflyctrl_i.c
- ilinkstm.h
- iloco.h iloco_i.c
- ilocos.h ilocos_i.c
- ipiggy.h ipiggy_i.c
- isun.h isun_i.c
+# The interface headers the locomotors are written against.
+source_group("Interface Files" FILES
+ "${CMAKE_CURRENT_SOURCE_DIR}/iflyctrl.h"
+ "${CMAKE_CURRENT_SOURCE_DIR}/iloco.h"
+ "${CMAKE_CURRENT_SOURCE_DIR}/ipiggy.h"
)
-# Convert to full paths relative to source dir
-set(FULL_INTERFACE_FILES "")
-foreach(f IN LISTS INTERFACE_FILES)
- list(APPEND FULL_INTERFACE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${f}")
-endforeach()
-
-# Add them to the "Interface Files" group and exclude *_i.c from build
-foreach(f IN LISTS FULL_INTERFACE_FILES)
-
- # Put into VS filter
- source_group("Interface Files" FILES "${f}")
-
- # Exclude *_i.c from build, but DO NOT mark headers as header-only
- if(f MATCHES "_i\\.c$")
- set_source_files_properties("${f}" PROPERTIES HEADER_FILE_ONLY TRUE)
- endif()
-
-endforeach()
-
# General source files
source_group("Source Files" REGULAR_EXPRESSION ".*\\.(c|cpp)$")
source_group("Header Files" REGULAR_EXPRESSION ".*\\.(h|hpp)$")
diff --git a/code/abstract.cpp b/code/abstract.cpp
index 355435def..72bdafea2 100644
--- a/code/abstract.cpp
+++ b/code/abstract.cpp
@@ -58,7 +58,6 @@
///
AbstractClass::AbstractClass(void) :
ID(-1),
- RefCount(0),
Dirty(false)
{
}
@@ -107,75 +106,13 @@ void AbstractClass::Create_ID(void)
}
-///
-/// Fetches a COM interface pointer from this object.
-/// This is the IUnknown implementation shared by every game object. Abstract
-/// objects expose IUnknown, IPersistStream and IPersist; the save game system
-/// reaches the whole object hierarchy through them.
-///
-/// The identifier of the interface being asked for.
-/// Receives the interface pointer, or NULL when the
-/// interface is not supported.
-///
-/// Returns with S_OK when the interface was supplied. Otherwise E_NOINTERFACE is
-/// returned for an unsupported interface, or E_POINTER when no output pointer was given.
-///
-HRESULT STDMETHODCALLTYPE AbstractClass::QueryInterface(REFIID riid, LPVOID * ppvObject)
-{
- if (ppvObject == NULL) {
- return(E_POINTER);
- }
-
- *ppvObject = NULL;
-
- if (riid == IID_IUnknown) {
- *ppvObject = (IUnknown *)(IPersistStream *)this;
- }
- if (riid == IID_IPersistStream) {
- *ppvObject = (IPersistStream *)this;
- }
- if (riid == IID_IPersist) {
- *ppvObject = (IPersist *)this;
- }
- if (*ppvObject == NULL) {
- return(E_NOINTERFACE);
- }
-
- AddRef();
- return(S_OK);
-}
-
-
-///
-/// Satisfies the IUnknown reference count contract.
-/// The game owns its objects outright and they outlive any interface pointer
-/// handed out, so nothing is actually counted.
-///
-/// Returns with the reference count, which is always one.
-ULONG STDMETHODCALLTYPE AbstractClass::AddRef(void)
-{
- return(1);
-}
-
-
-///
-/// Satisfies the IUnknown release contract.
-/// Releasing an interface never destroys a game object -- see AddRef.
-///
-/// Returns with the reference count, which is always one.
-ULONG STDMETHODCALLTYPE AbstractClass::Release(void)
-{
- return(1);
-}
-
-
///
/// Writes this object to the save stream.
///
/// The stream to write to.
/// Should the object be marked clean once it has been written?
-/// Returns with S_OK when the object was written, otherwise a failure code.
-HRESULT STDMETHODCALLTYPE AbstractClass::Save(IStream * stream, BOOL cleardirty)
+/// bool; Was the record written whole?
+bool AbstractClass::Save(SaveStreamClass & stream, bool cleardirty)
{
return(Save_Members(stream, cleardirty));
}
@@ -185,8 +122,8 @@ HRESULT STDMETHODCALLTYPE AbstractClass::Save(IStream * stream, BOOL cleardirty)
/// Reads this object back from the save stream.
///
/// The stream to read from.
-/// Returns with S_OK when the object was read, otherwise a failure code.
-HRESULT STDMETHODCALLTYPE AbstractClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool AbstractClass::Load(SaveStreamClass & stream)
{
return(Load_Members(stream));
}
@@ -199,28 +136,16 @@ HRESULT STDMETHODCALLTYPE AbstractClass::Load(IStream * stream)
///
/// The stream to write to.
/// Should the object be marked clean once it has been written?
-/// Returns with S_OK when the record was written, otherwise a failure code.
-HRESULT AbstractClass::Save_Members(IStream * stream, BOOL cleardirty)
+/// bool; Was the record written whole?
+bool AbstractClass::Save_Members(SaveStreamClass & stream, bool cleardirty)
{
- if (stream == NULL) {
- return(E_POINTER);
- }
-
SwizzleIDType id = Swizzler.ID_Of(this);
-
- HRESULT result = stream->Write(&id, sizeof(id), NULL);
- if (FAILED(result)) {
- return(result);
+ stream.Serialize(id);
+ Serialize(stream);
+ if (!stream.Was_Error() && cleardirty) {
+ Dirty = false;
}
-
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
-
- if (SUCCEEDED(savestream.Result()) && cleardirty) {
- Dirty = false;
- }
-
- return(savestream.Result());
+ return(!stream.Was_Error());
}
@@ -230,31 +155,24 @@ HRESULT AbstractClass::Save_Members(IStream * stream, BOOL cleardirty)
/// save game can be remapped onto this object, and the members follow.
///
/// The stream to read from.
-/// Returns with S_OK when the record was read, otherwise a failure code.
-HRESULT AbstractClass::Load_Members(IStream * stream)
+/// bool; Was the record read whole?
+bool AbstractClass::Load_Members(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(E_POINTER);
+ SwizzleIDType id = 0;
+ stream.Serialize(id);
+ if (stream.Was_Error()) {
+ return(false);
}
-
- SwizzleIDType id;
-
- HRESULT result = stream->Read(&id, sizeof(id), NULL);
- if (FAILED(result)) {
- return(result);
- }
-
Swizzle_Here_I_Am(id, this);
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context(typeid(*this).name(), id);
- Serialize(savestream);
+ // A nested record borrows the stream, so the owner's context is put back afterwards.
+ char const * const outertype = stream.Context_Type();
+ SwizzleIDType const outerid = stream.Context_ID();
+ stream.Set_Context(typeid(*this).name(), id);
+ Serialize(stream);
+ stream.Set_Context(outertype, outerid);
- if (SUCCEEDED(savestream.Result())) {
- Post_Load();
- }
-
- return(savestream.Result());
+ return(!stream.Was_Error());
}
@@ -274,25 +192,10 @@ void AbstractClass::Post_Load(void)
void AbstractClass::Serialize(SaveStreamClass & stream)
{
stream.Serialize(ID);
- // RefCount -- belongs to the running session rather than the record.
stream.Serialize(Dirty);
}
-///
-/// Fetches the number of bytes that Save will write.
-/// A record is as long as the members a class names, so the count is not known before
-/// the members have been written. Nothing in the game asks for it, so rather than
-/// walk the object twice this reports that the size cannot be supplied.
-///
-/// Receives the maximum size, in bytes.
-/// Returns with E_NOTIMPL.
-HRESULT STDMETHODCALLTYPE AbstractClass::GetSizeMax(ULARGE_INTEGER *pcbSize)
-{
- return(E_NOTIMPL);
-}
-
-
///
/// Folds this object's state into a running CRC.
/// The multiplayer sync check walks every object each frame and accumulates its
@@ -335,23 +238,6 @@ bool AbstractClass::Is_Techno(void) const
}
-///
-/// Determines if this object has changed since it was last saved.
-///
-/// Returns with S_OK when the object is dirty, or S_FALSE when it is not.
-HRESULT AbstractClass::IsDirty(void)
-{
- /*
- * Per IPersistStream::IsDirty specifications this method returns S_OK to indicate that the object has changed.
- * Otherwise, it returns S_FALSE.
- */
- if (Dirty) {
- return(S_OK);
- }
- return(S_FALSE);
-}
-
-
///
/// Resets this object to its start of scenario state.
/// The bare abstract object carries no scenario state, so there is nothing to do.
diff --git a/code/abstract.h b/code/abstract.h
index 8e9278fe0..46d3ebf59 100644
--- a/code/abstract.h
+++ b/code/abstract.h
@@ -39,7 +39,7 @@
#include "house.hh"
#include "rtti.hh"
-#include
+#include "persist.h"
class AbstractTypeClass;
class CRCEngine;
@@ -62,7 +62,7 @@ class MonoClass;
** This class is the base class for all game objects that have an existence on the
** battlefield.
*/
-class AbstractClass : public IPersistStream
+class AbstractClass : public IPersistent
{
public:
@@ -74,8 +74,8 @@ class AbstractClass : public IPersistStream
* the members are read -- dropping a registration keyed by the identity the read
* is about to replace, say.
*/
- HRESULT Save_Members(IStream * stream, BOOL cleardirty);
- HRESULT Load_Members(IStream * stream);
+ bool Save_Members(SaveStreamClass & stream, bool cleardirty);
+ bool Load_Members(SaveStreamClass & stream);
public:
@@ -87,16 +87,9 @@ class AbstractClass : public IPersistStream
__declspec(property(get = Fetch_RTTI)) RTTIType RTTI;
int ID;
- /*
- * This is the count of outstanding COM references to this object. Only projectiles
- * are genuinely reference counted -- everything else answers 1 to AddRef and to
- * Release -- so elsewhere it merely rides along, preserved by hand across a load.
- */
- LONG RefCount;
-
/*
* If this object has changed since it was last written out, then this flag will be
- * true. Save clears it on request and IsDirty reports it, as IPersistStream asks.
+ * true. Save clears it on request.
*/
bool Dirty;
@@ -106,14 +99,9 @@ class AbstractClass : public IPersistStream
AbstractClass(void);
virtual ~AbstractClass(void);
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID * ppvObject) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
- virtual ULONG STDMETHODCALLTYPE Release(void) override;
- virtual HRESULT STDMETHODCALLTYPE IsDirty(void) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
- virtual HRESULT STDMETHODCALLTYPE Save(IStream * stream, BOOL cleardirty) override;
- virtual HRESULT STDMETHODCALLTYPE GetSizeMax(ULARGE_INTEGER *pcbSize) override;
+ virtual bool Load(SaveStreamClass & stream) override;
+ virtual bool Save(SaveStreamClass & stream, bool cleardirty) override;
virtual int What_Am_I(void) const;
virtual int Fetch_ID(void) const;
@@ -122,7 +110,6 @@ class AbstractClass : public IPersistStream
AbstractClass & operator = (const AbstractClass & that)
{
ID = that.ID;
- RefCount = that.RefCount;
Dirty = that.Dirty;
return(*this);
}
@@ -137,9 +124,9 @@ class AbstractClass : public IPersistStream
/*
* Restores whatever the record could not carry -- artwork fetched by name, tables
* shared with other objects, registrations that depend on the loaded identity.
- * Load_Members calls this once the members are in place, so a base class fixup
- * runs even when the load was entered through a derived class. An implementation
- * chains to its base first and never touches the stream.
+ * Load_Object calls this once the record has been checked, so an object never takes
+ * its place in the map or a side table while its record is still in doubt. An
+ * implementation chains to its base first and never touches the stream.
*/
virtual void Post_Load(void);
diff --git a/code/aircraft.cpp b/code/aircraft.cpp
index d4314b432..b234f90b1 100644
--- a/code/aircraft.cpp
+++ b/code/aircraft.cpp
@@ -89,7 +89,6 @@
* _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "aircraft.h"
@@ -221,7 +220,7 @@ AircraftClass::AircraftClass(AircraftTypeClass const * type, HouseClass * house)
Create_ID();
if (Class != NULL) {
- Locomotion.CreateInstance(Class->Locomotor);
+ Locomotion = Create_Locomotor(Class->Locomotor);
Locomotion->Link_To_Object(this);
}
@@ -266,48 +265,6 @@ void AircraftClass::Init(void)
}
-///
-/// Fetches the requested interface from this aircraft.
-/// Aircraft add the fly control interface to the set that every game object supports, so
-/// that the flying locomotor can interrogate them about how they wish to be flown.
-///
-/// The identifier of the interface being asked for.
-/// Pointer to the pointer to fill in with the interface.
-/// Returns with S_OK if the interface was supplied.
-HRESULT STDMETHODCALLTYPE AircraftClass::QueryInterface(struct _GUID const &guid, void **ppv)
-{
- HRESULT res = BASECLASS::QueryInterface(guid, ppv);
- if (FAILED(res)) {
- if (guid == IID_IFlyControl) {
- *ppv = (IFlyControl *)(this);
- }
- res = S_OK;
- AddRef();
- }
- return(res);
-}
-
-
-///
-/// Adds a reference to this aircraft.
-///
-/// Returns with the new number of references outstanding.
-ULONG STDMETHODCALLTYPE AircraftClass::AddRef(void)
-{
- return(BASECLASS::AddRef());
-}
-
-
-///
-/// Releases a reference to this aircraft.
-///
-/// Returns with the number of references still outstanding.
-ULONG STDMETHODCALLTYPE AircraftClass::Release(void)
-{
- return(BASECLASS::Release());
-}
-
-
/***********************************************************************************************
* AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. *
* *
@@ -1397,8 +1354,7 @@ void AircraftClass::Drop_Off_Cargo(void)
unit->IsOnBridge = false;
}
- unit->Locomotion.Release();
- unit->Locomotion = ILocomotionPtr(unit->TClass->Locomotor);
+ unit->Locomotion = Create_Locomotor(unit->TClass->Locomotor);
unit->Locomotion->Link_To_Object(unit);
if (!unit->Unlimbo(coord)) {
@@ -3932,8 +3888,8 @@ void AircraftClass::Read_INI(CCINIClass const & ini)
/// again once that identity has arrived.
///
/// The stream to read this object from.
-/// Returns with S_OK if the aircraft was loaded successfully.
-HRESULT STDMETHODCALLTYPE AircraftClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool AircraftClass::Load(SaveStreamClass & stream)
{
TargetTracker.Remove_Index(Fetch_ID());
return(BASECLASS::Load(stream));
@@ -4029,7 +3985,7 @@ void AircraftClass::Detach(AbstractClass const * target, bool all)
/// can pick up or set down its cargo.
///
/// Returns with the height above ground level to settle at.
-LONG STDMETHODCALLTYPE AircraftClass::Landing_Altitude(void)
+LONG AircraftClass::Landing_Altitude(void)
{
if (Class->IsCarryall && !Cargo.Is_Something_Attached() && In_Radio_Contact()) {
BuildingClass * bptr = (BuildingClass *)Contact_With_Whom();
@@ -4057,7 +4013,7 @@ LONG STDMETHODCALLTYPE AircraftClass::Landing_Altitude(void)
/// while loaded, or settles into the default parked pose.
///
/// Returns with the facing to land at.
-LONG STDMETHODCALLTYPE AircraftClass::Landing_Direction(void)
+LONG AircraftClass::Landing_Direction(void)
{
TechnoClass * tptr = Contact_With_Whom();
if (tptr != NULL) {
@@ -4076,7 +4032,7 @@ LONG STDMETHODCALLTYPE AircraftClass::Landing_Direction(void)
/// empty one.
///
/// Returns with true if there is cargo aboard this aircraft.
-BOOL STDMETHODCALLTYPE AircraftClass::Is_Loaded(void)
+BOOL AircraftClass::Is_Loaded(void)
{
return(Cargo.Is_Something_Attached());
}
@@ -4088,7 +4044,7 @@ BOOL STDMETHODCALLTYPE AircraftClass::Is_Loaded(void)
/// from a hover. Only a visible and unguided projectile is suited to strafing.
///
/// Returns with true if the aircraft should make strafing attack runs.
-LONG STDMETHODCALLTYPE AircraftClass::Is_Strafe(void)
+LONG AircraftClass::Is_Strafe(void)
{
const WeaponDataStruct * data = Get_Class_Weapon_Data(0);
if (data == NULL) {
@@ -4114,7 +4070,7 @@ LONG STDMETHODCALLTYPE AircraftClass::Is_Strafe(void)
/// to an attack run.
///
/// Returns with true if the aircraft must hold its present heading.
-LONG STDMETHODCALLTYPE AircraftClass::Is_Locked(void)
+LONG AircraftClass::Is_Locked(void)
{
return(IsLockedStraight);
}
@@ -4233,18 +4189,9 @@ RTTIType AircraftClass::Fetch_RTTI(void) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence support. The save/load machinery uses the
-/// class identifier to recreate an object of the correct type when a game is restored.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE AircraftClass::GetClassID(CLSID * retval)
+ClassID AircraftClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_AircraftClass;
- return(S_OK);
+ return(ClassID_AircraftClass);
}
diff --git a/code/aircraft.h b/code/aircraft.h
index 73a4baa03..591ddd970 100644
--- a/code/aircraft.h
+++ b/code/aircraft.h
@@ -59,23 +59,20 @@ class AircraftClass : public FootClass, public IFlyControl
AircraftClass(AircraftTypeClass const * type = NULL, HouseClass * house = NULL);
virtual ~AircraftClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Load(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID * ppvObject) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
- virtual ULONG STDMETHODCALLTYPE Release(void) override;
/*
* IFlyControl methods.
*/
- virtual LONG STDMETHODCALLTYPE Landing_Altitude(void) override;
- virtual LONG STDMETHODCALLTYPE Landing_Direction(void) override;
- virtual BOOL STDMETHODCALLTYPE Is_Loaded(void) override;
- virtual LONG STDMETHODCALLTYPE Is_Strafe(void) override;
- virtual LONG STDMETHODCALLTYPE Is_Locked(void) override;
+ virtual LONG Landing_Altitude(void) override;
+ virtual LONG Landing_Direction(void) override;
+ virtual BOOL Is_Loaded(void) override;
+ virtual LONG Is_Strafe(void) override;
+ virtual LONG Is_Locked(void) override;
virtual void Init(void) override;
virtual void Detach(AbstractClass const * target, bool all = true) override;
diff --git a/code/airctype.cpp b/code/airctype.cpp
index c86250758..6d3453a59 100644
--- a/code/airctype.cpp
+++ b/code/airctype.cpp
@@ -45,7 +45,6 @@
* AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "airctype.h"
@@ -327,18 +326,9 @@ void AircraftTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of the aircraft type.
-/// The save game machinery asks each object for this identifier so that it can create an
-/// object of the right class again when the game is loaded.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if there was nowhere to put the answer.
-HRESULT STDMETHODCALLTYPE AircraftTypeClass::GetClassID(CLSID * retval)
+ClassID AircraftTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_AircraftTypeClass;
- return(S_OK);
+ return(ClassID_AircraftTypeClass);
}
diff --git a/code/airctype.h b/code/airctype.h
index 8f462fbf2..913c8e1a8 100644
--- a/code/airctype.h
+++ b/code/airctype.h
@@ -57,7 +57,7 @@ class AircraftTypeClass : public TechnoTypeClass
AircraftTypeClass(char const * ininame = NULL);
virtual ~AircraftTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/aitrig.cpp b/code/aitrig.cpp
index 8b0f19074..cfd2391dc 100644
--- a/code/aitrig.cpp
+++ b/code/aitrig.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "aitrig.h"
@@ -92,17 +91,9 @@ AITriggerTypeClass::~AITriggerTypeClass(void)
}
-///
-/// Fetches the class identifier of this object.
-/// The save game system uses this identifier to work out which class to build when the
-/// object is read back in.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE AITriggerTypeClass::GetClassID(CLSID * retval)
+ClassID AITriggerTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_AITriggerTypeClass;
- return(S_OK);
+ return(ClassID_AITriggerTypeClass);
}
diff --git a/code/aitrig.h b/code/aitrig.h
index 6d141b801..62dd872b1 100644
--- a/code/aitrig.h
+++ b/code/aitrig.h
@@ -60,7 +60,7 @@ class AITriggerTypeClass : public AbstractTypeClass
AITriggerTypeClass(const char *name = NULL);
~AITriggerTypeClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
static AITriggerTypeClass * Find_Or_Make(char const * ininame);
diff --git a/code/alphashp.cpp b/code/alphashp.cpp
index b7cc98727..dcd3b66ec 100644
--- a/code/alphashp.cpp
+++ b/code/alphashp.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "alphashp.h"
@@ -93,18 +92,9 @@ AlphaShapeClass::~AlphaShapeClass(void)
}
-///
-/// Fetches the class identifier used to persist this object.
-/// The save system writes this identifier ahead of the object data so that the loader
-/// knows what kind of object to reconstruct.
-///
-/// Pointer to the buffer that will receive the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE AlphaShapeClass::GetClassID(CLSID * retval)
+ClassID AlphaShapeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_AlphaShapeClass;
- return(S_OK);
+ return(ClassID_AlphaShapeClass);
}
diff --git a/code/alphashp.h b/code/alphashp.h
index fc891a87d..04a67befa 100644
--- a/code/alphashp.h
+++ b/code/alphashp.h
@@ -34,7 +34,7 @@ class AlphaShapeClass : public AbstractClass
AlphaShapeClass(void);
~AlphaShapeClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/anim.cpp b/code/anim.cpp
index 62116e0ce..a8e78ca84 100644
--- a/code/anim.cpp
+++ b/code/anim.cpp
@@ -50,7 +50,6 @@
* Shorten_Attached_Anims -- Reduces attached animation durations. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "anim.h"
@@ -245,7 +244,7 @@ AnimClass::AnimClass(AnimTypeClass const * type, Coord const & coord, int timede
///
/// Constructs a blank animation object.
/// This constructor serves the load system, which creates an empty animation through the
-/// class factory and then fills it in from the save game. The animation joins the master
+/// class table and then fills it in from the save game. The animation joins the master
/// animation list but has no type and is nowhere on the map.
///
AnimClass::AnimClass(void) :
@@ -1729,18 +1728,9 @@ void AnimClass::Post_Load_Game(void)
}
-///
-/// Fetches the persistent class identifier for animation objects.
-/// The save and load machinery uses this identifier to recreate the right kind of object
-/// when a game is restored.
-///
-/// Pointer to the location to store the class identifier at.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE AnimClass::GetClassID(CLSID * retval)
+ClassID AnimClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_AnimClass;
- return(S_OK);
+ return(ClassID_AnimClass);
}
diff --git a/code/anim.h b/code/anim.h
index 8be08019f..8cf2c763a 100644
--- a/code/anim.h
+++ b/code/anim.h
@@ -64,7 +64,7 @@ class AnimClass : public ObjectClass, public StageClass
AnimClass(void);
virtual ~AnimClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/animtype.cpp b/code/animtype.cpp
index 95ed94f4d..8e4d29d6d 100644
--- a/code/animtype.cpp
+++ b/code/animtype.cpp
@@ -38,7 +38,6 @@
* AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "animtype.h"
@@ -577,18 +576,9 @@ void AnimTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the persistent class identifier of this object.
-/// This routine is used by the save game machinery to recognize an animation type when it
-/// comes back off the stream.
-///
-/// Pointer to the place to store the class identifier.
-/// Returns with S_OK, or E_POINTER if there was nowhere to store the answer.
-HRESULT STDMETHODCALLTYPE AnimTypeClass::GetClassID(CLSID * retval)
+ClassID AnimTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_AnimTypeClass;
- return(S_OK);
+ return(ClassID_AnimTypeClass);
}
diff --git a/code/animtype.h b/code/animtype.h
index 11d63eed9..1b2432bf8 100644
--- a/code/animtype.h
+++ b/code/animtype.h
@@ -430,7 +430,7 @@ class AnimTypeClass : public ObjectTypeClass
AnimTypeClass(char const * ininame = NULL);
virtual ~AnimTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/base.cpp b/code/base.cpp
index b4344eb5a..c3d566cbb 100644
--- a/code/base.cpp
+++ b/code/base.cpp
@@ -547,31 +547,6 @@ void BaseClass::Write_INI(CCINIClass & ini, char const * hname)
/// Reads the base back in from a save game.
///
/// Returns with the result reported by the stream read.
-HRESULT STDMETHODCALLTYPE BaseClass::Load(IStream *stream)
-{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("BaseClass");
- Serialize(savestream);
- return(savestream.Result());
-}
-
-
-///
-/// Writes the base out to a save game.
-///
-/// Returns with the result reported by the stream write.
-HRESULT STDMETHODCALLTYPE BaseClass::Save(IStream * stream)
-{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
- return(savestream.Result());
-}
-
-
-///
-/// Lists the members the base plan holds.
-///
-/// The stream carrying the members.
void BaseClass::Serialize(SaveStreamClass & stream)
{
stream.Serialize(Nodes);
diff --git a/code/base.h b/code/base.h
index 352064766..7c8a0ed0b 100644
--- a/code/base.h
+++ b/code/base.h
@@ -37,7 +37,6 @@
#include "house.hh"
#include "struct.hh"
-#include
class CCINIClass;
@@ -103,8 +102,6 @@ class BaseClass
*/
void Read_INI(CCINIClass const & ini, char const * hname);
void Write_INI(CCINIClass & ini, char const * hname);
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream);
- virtual HRESULT STDMETHODCALLTYPE Save(IStream * stream);
void Serialize(SaveStreamClass & stream);
virtual void Compute_CRC(CRCEngine &) const;
diff --git a/code/blight.cpp b/code/blight.cpp
index 7850a58b5..a45f459db 100644
--- a/code/blight.cpp
+++ b/code/blight.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "blight.h"
@@ -280,17 +279,9 @@ void BuildingLightClass::AI(void)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the save game code so that an object of the right kind can
-/// be created when the game is loaded back in.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE BuildingLightClass::GetClassID(CLSID * retval)
+ClassID BuildingLightClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_BuildingLightClass;
- return(S_OK);
+ return(ClassID_BuildingLightClass);
}
diff --git a/code/blight.h b/code/blight.h
index c12e01e09..5a5ba3731 100644
--- a/code/blight.h
+++ b/code/blight.h
@@ -25,7 +25,7 @@ class BuildingLightClass : public ObjectClass
BuildingLightClass(TechnoClass * owner = NULL);
virtual ~BuildingLightClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/blowfish.cpp b/code/blowfish.cpp
index cc9f746f1..91ec2c67e 100644
--- a/code/blowfish.cpp
+++ b/code/blowfish.cpp
@@ -39,15 +39,11 @@
#include "blowfish.h"
-#ifndef NO_BLOWFISH_DLL
-#include "iblowfish.h"
-#endif
#include
-#ifdef NO_BLOWFISH_DLL
/*
** Byte order controlled long integer. This integer is constructed
** so that character 0 (C0) is the most significant byte of the
@@ -63,7 +59,6 @@ typedef union {
unsigned char C0;
} Char;
} Int;
-#endif
///
@@ -73,11 +68,7 @@ typedef union {
///
/// You must submit the key before calling the encrypt or decrypt routines.
BlowfishEngine::BlowfishEngine(void) :
-#ifndef NO_BLOWFISH_DLL
- BlockCypher(CLSID_BlowfishObject)
-#else
IsKeyed(false)
-#endif
{
}
@@ -99,11 +90,9 @@ BlowfishEngine::BlowfishEngine(void) :
*=============================================================================================*/
BlowfishEngine::~BlowfishEngine(void)
{
-#ifdef NO_BLOWFISH_DLL
if (IsKeyed) {
Submit_Key(NULL, 0);
}
-#endif
}
@@ -133,10 +122,6 @@ BlowfishEngine::~BlowfishEngine(void)
*=============================================================================================*/
void BlowfishEngine::Submit_Key(void const * key, int length)
{
-#ifndef NO_BLOWFISH_DLL
- BlockCypher->Set_Key(length, key);
- return;
-#else
assert(length <= MAX_KEY_LENGTH);
/*
@@ -210,7 +195,6 @@ void BlowfishEngine::Submit_Key(void const * key, int length)
}
IsKeyed = true;
-#endif
}
@@ -238,10 +222,6 @@ void BlowfishEngine::Submit_Key(void const * key, int length)
*=============================================================================================*/
int BlowfishEngine::Encrypt(void const * plaintext, int length, void * cyphertext)
{
-#ifndef NO_BLOWFISH_DLL
- BlockCypher->Encrypt(length, plaintext, cyphertext);
- return(length);
-#else
if (plaintext == 0 || length == 0) {
return(0);
}
@@ -281,7 +261,6 @@ int BlowfishEngine::Encrypt(void const * plaintext, int length, void * cyphertex
memmove(cyphertext, plaintext, length);
}
return(length);
-#endif
}
@@ -309,10 +288,6 @@ int BlowfishEngine::Encrypt(void const * plaintext, int length, void * cyphertex
*=============================================================================================*/
int BlowfishEngine::Decrypt(void const * cyphertext, int length, void * plaintext)
{
-#ifndef NO_BLOWFISH_DLL
- BlockCypher->Decrypt(length, cyphertext, plaintext);
- return(length);
-#else
if (cyphertext == 0 || length == 0) {
return(0);
}
@@ -352,11 +327,9 @@ int BlowfishEngine::Decrypt(void const * cyphertext, int length, void * plaintex
memmove(plaintext, cyphertext, length);
}
return(length);
-#endif
}
-#ifdef NO_BLOWFISH_DLL
/***********************************************************************************************
* BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. *
* *
@@ -631,4 +604,3 @@ unsigned int const BlowfishEngine::S_Init[4][UCHAR_MAX+1] = {
0x90D4F869U,0xA65CDEA0U,0x3F09252DU,0xC208E69FU,0xB74E6132U,0xCE77E25BU,0x578FDFE3U,0x3AC372E6U
}
};
-#endif
diff --git a/code/blowfish.h b/code/blowfish.h
index 3cbc2d7c6..41d2e7e2b 100644
--- a/code/blowfish.h
+++ b/code/blowfish.h
@@ -33,14 +33,7 @@
#include "win.h"
-/// Names and comments from TLBs
-
#include
-#ifndef NO_BLOWFISH_DLL
-#include "iblockci.h"
-#include
-_COM_SMARTPTR_TYPEDEF(IBlockCipher, __uuidof(IBlockCipher));
-#endif
/*
** This engine will process data blocks by encryption and decryption.
@@ -70,14 +63,6 @@ class BlowfishEngine {
enum {MAX_KEY_LENGTH=56};
private:
-#ifndef NO_BLOWFISH_DLL
- /*
- * This points to the block cipher object that performs the actual key setup and
- * block processing. Where the cipher is available as a component, this engine is
- * only a convenience wrapper around it and keeps no tables of its own.
- */
- IBlockCipherPtr BlockCypher;
-#else
bool IsKeyed;
void Sub_Key_Encrypt(unsigned int & left, unsigned int & right);
@@ -107,5 +92,4 @@ class BlowfishEngine {
** S-Box tables (four).
*/
unsigned int bf_S[4][UCHAR_MAX+1];
-#endif
};
diff --git a/code/brain.cpp b/code/brain.cpp
index b09549d86..ef7aae44c 100644
--- a/code/brain.cpp
+++ b/code/brain.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "brain.h"
@@ -48,18 +47,9 @@ NeuronClass::~NeuronClass(void)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the save game system so that it knows what kind of object to
-/// construct when the stream is read back in.
-///
-/// Pointer to the place to store the class identifier.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE NeuronClass::GetClassID(CLSID * retval)
+ClassID NeuronClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_NeuronClass;
- return(S_OK);
+ return(ClassID_NeuronClass);
}
@@ -155,19 +145,12 @@ bool BrainClass::Add_Neuron(NeuronClass *neuron)
/// Saves this brain to the save game stream.
///
/// Should the neurons be marked clean once they are written?
-///
-/// Returns with S_OK when the brain was written, E_POINTER when no stream was supplied,
-/// or the stream's own failure code.
-///
-HRESULT BrainClass::Save(IStream * stream, BOOL cleardirty)
+/// bool; Was the record written whole?
+bool BrainClass::Save(SaveStreamClass & stream, bool cleardirty)
{
- if (stream == NULL) {
- return(E_POINTER);
- }
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream, cleardirty);
- return(savestream.Result());
+ Serialize(stream, cleardirty);
+ return(!stream.Was_Error());
}
@@ -176,20 +159,13 @@ HRESULT BrainClass::Save(IStream * stream, BOOL cleardirty)
/// Whatever neurons the brain was holding are destroyed first, so the stream's neurons
/// entirely replace them.
///
-///
-/// Returns with S_OK when the brain was read, E_POINTER when no stream was supplied, or
-/// the stream's own failure code.
-///
-HRESULT BrainClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool BrainClass::Load(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(E_POINTER);
- }
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("BrainClass");
- Serialize(savestream);
- return(savestream.Result());
+ stream.Set_Context("BrainClass");
+ Serialize(stream);
+ return(!stream.Was_Error());
}
@@ -200,7 +176,7 @@ HRESULT BrainClass::Load(IStream * stream)
///
/// The stream carrying the members.
/// Should the neurons be marked clean once they are written?
-void BrainClass::Serialize(SaveStreamClass & stream, BOOL cleardirty)
+void BrainClass::Serialize(SaveStreamClass & stream, bool cleardirty)
{
int count = Neurons.Count();
stream.Serialize(count);
@@ -212,10 +188,10 @@ void BrainClass::Serialize(SaveStreamClass & stream, BOOL cleardirty)
for (int i = 0; i < count && !stream.Was_Error(); i++) {
if (stream.Is_Loading()) {
NeuronClass * neuron = new NeuronClass;
- neuron->Load(stream.Get_Stream());
+ neuron->Load(stream);
Add_Neuron(neuron);
} else {
- Neurons[i]->Save(stream.Get_Stream(), cleardirty);
+ Neurons[i]->Save(stream, cleardirty);
}
}
// MinCount -- the limits a brain was prepared with rather than anything it accumulated.
diff --git a/code/brain.h b/code/brain.h
index 9287f630c..198a18a5f 100644
--- a/code/brain.h
+++ b/code/brain.h
@@ -24,7 +24,7 @@ class NeuronClass : public AbstractClass
NeuronClass(void);
virtual ~NeuronClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual RTTIType Fetch_RTTI(void) const override { return(RTTI_NEURON); }
@@ -62,10 +62,10 @@ class BrainClass
void Init(int min, int max);
bool Add_Neuron(NeuronClass *neuron);
- HRESULT Load(IStream * stream);
- HRESULT Save(IStream * stream, BOOL cleardirty);
+ bool Load(SaveStreamClass & stream);
+ bool Save(SaveStreamClass & stream, bool cleardirty);
- void Serialize(SaveStreamClass & stream, BOOL cleardirty = FALSE);
+ void Serialize(SaveStreamClass & stream, bool cleardirty = false);
private:
/*
diff --git a/code/building.cpp b/code/building.cpp
index 4e9c1ee30..55e5d25a1 100644
--- a/code/building.cpp
+++ b/code/building.cpp
@@ -102,7 +102,6 @@
* BuildingClass::~BuildingClass -- Destructor for building type objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "building.h"
@@ -124,6 +123,7 @@
#include "bullettype.h"
#include "ccrand.h"
#include "cell.h"
+#include "classids.h"
#include "combat.h"
#include "conquer.h"
#include "dbgprint.h"
@@ -138,7 +138,6 @@
#include "house.h"
#include "houstype.h"
#include "iloco.h"
-#include "ilocos.h"
#include "incdec.h"
#include "infantry.h"
#include "infatype.h"
@@ -5533,10 +5532,8 @@ int BuildingClass::Do_MISSION_REPAIR(void)
** distance check. Fixed-wing aircraft are very inaccurate with
** their landings.
*/
- IPersistPtr persist(tech->Locomotion);
- CLSID clsid;
- persist->GetClassID(&clsid);
- bool hover = (clsid == CLSID_HoverLocomotion) != 0;
+ ClassID const clsid = Locomotion_Class_ID(tech->Locomotion.get());
+ bool hover = (clsid == ClassID_HoverLocomotion) != 0;
if (hover) {
distance = 0x96;
}
@@ -5993,7 +5990,7 @@ int BuildingClass::Do_MISSION_MISSILE(void)
Status = DONE;
return(1);
} else {
- bullet->Release();
+ delete bullet;
Begin_Mode(BSTATE_IDLE); // keep the door closed.
Assign_Mission(MISSION_GUARD);
return(4 * TICKS_PER_SECOND);
@@ -6253,27 +6250,25 @@ int BuildingClass::Do_MISSION_UNLOAD(void)
if (unit) {
unit->Assign_Mission(MISSION_MOVE);
- IPersistPtr persist(unit->Locomotion);
- CLSID clsid;
- persist->GetClassID(&clsid);
+ ClassID const clsid = Locomotion_Class_ID(unit->Locomotion.get());
- if (clsid == CLSID_TunnelLocomotion) {
- IPiggybackPtr piggy(unit->Locomotion);
+ if (clsid == ClassID_TunnelLocomotion) {
+ IPiggyback * piggy = Piggyback_Of(unit->Locomotion.get());
if (piggy != NULL && piggy->Is_Piggybacking()) {
- piggy->End_Piggyback(&unit->Locomotion);
+ unit->Locomotion = piggy->End_Piggyback();
}
- ILocomotionPtr walk(CLSID_DriveLocomotion);
+ std::unique_ptr walk = Create_Locomotor(ClassID_DriveLocomotion);
walk->Link_To_Object(unit);
- piggy = IPiggybackPtr(walk);
+ piggy = Piggyback_Of(walk.get());
if (piggy != NULL) {
piggy->Begin_Piggyback(unit->Locomotion);
- unit->Locomotion = walk;
+ unit->Locomotion = std::move(walk);
unit->Locomotion->Force_Track(DriveLocomotionClass::OUT_OF_WEAPON_FACTORY, coord);
} else {
int damage = unit->Strength;
unit->Take_Damage(damage, 0, Rule->C4Warhead, NULL, true);
}
- } else if (clsid != CLSID_DriveLocomotion) {
+ } else if (clsid != ClassID_DriveLocomotion) {
unit->Assign_Destination(&Map[Get_Cell() + Cell(3, 1)]);
} else {
Coord cs;
@@ -8845,9 +8840,8 @@ void BuildingClass::Clear_Occupy_Bit(Coord const & coord)
/// since the one it is about to be given is the one it was saved with. Post_Load enters it
/// again once that identity has arrived.
///
-/// Returns with S_OK if the building was read, or the failure code from the
-/// underlying stream.
-HRESULT STDMETHODCALLTYPE BuildingClass::Load(IStream *stream)
+/// bool; Was the record read whole?
+bool BuildingClass::Load(SaveStreamClass & stream)
{
TargetTracker.Remove_Index(Fetch_ID());
return(BASECLASS::Load(stream));
@@ -10348,18 +10342,9 @@ void BuildingClass::Discharge_Turret(void)
}
-///
-/// Fetches the persistent class identifier for this building.
-/// This routine is part of the persistence support. The save code writes this identifier
-/// ahead of the object so that the loader knows what kind of object to create.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE BuildingClass::GetClassID(CLSID * retval)
+ClassID BuildingClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_BuildingClass;
- return(S_OK);
+ return(ClassID_BuildingClass);
}
diff --git a/code/building.h b/code/building.h
index fd605bf5f..3dbbd841e 100644
--- a/code/building.h
+++ b/code/building.h
@@ -357,8 +357,8 @@ class BuildingClass : public TechnoClass
BuildingClass(BuildingTypeClass const * type = NULL, HouseClass * house = NULL);
virtual ~BuildingClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Load(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/builtype.cpp b/code/builtype.cpp
index 72fdc7395..478a06b25 100644
--- a/code/builtype.cpp
+++ b/code/builtype.cpp
@@ -55,7 +55,6 @@
* BuildingTypeClass::operator new -- Allocates a building type object from the special heap.*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "builtype.h"
@@ -1942,18 +1941,9 @@ void BuildingTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the save game system so that it knows what kind of object to
-/// construct when the stream is read back in.
-///
-/// Pointer to the class ID to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE BuildingTypeClass::GetClassID(CLSID * retval)
+ClassID BuildingTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_BuildingTypeClass;
- return(S_OK);
+ return(ClassID_BuildingTypeClass);
}
diff --git a/code/builtype.h b/code/builtype.h
index 00c63bcd9..4019ff5ad 100644
--- a/code/builtype.h
+++ b/code/builtype.h
@@ -855,7 +855,7 @@ class BuildingTypeClass : public TechnoTypeClass
BuildingTypeClass(char const * ininame = NULL);
virtual ~BuildingTypeClass() override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/bullet.cpp b/code/bullet.cpp
index fd5ef3660..40c62322f 100644
--- a/code/bullet.cpp
+++ b/code/bullet.cpp
@@ -47,7 +47,6 @@
* BulletClass::~BulletClass -- Destructor for bullet objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "bullet.h"
@@ -95,7 +94,6 @@
#include
-extern ULONG COMRefCount;
/***********************************************************************************************
@@ -1461,35 +1459,6 @@ void BulletClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Takes out a reference on this projectile.
-/// This is the IUnknown implementation used by the COM machinery that owns projectiles.
-///
-/// Returns with the number of references now outstanding.
-ULONG STDMETHODCALLTYPE BulletClass::AddRef(void)
-{
- COMRefCount++;
- return(InterlockedIncrement(&RefCount));
-}
-
-
-///
-/// Drops a reference to this projectile.
-/// This is the IUnknown implementation. The projectile deletes itself when the last
-/// reference to it is released.
-///
-/// Returns with the number of references still outstanding.
-ULONG STDMETHODCALLTYPE BulletClass::Release(void)
-{
- COMRefCount--;
- ULONG count = InterlockedDecrement(&RefCount);
- if (count == 0) {
- delete this;
- }
- return(count);
-}
-
-
///
/// Can this projectile steer toward its target?
/// The flight logic calls this routine to decide whether the projectile should be turned
@@ -1507,9 +1476,7 @@ bool BulletClass::Is_Homing(void) const
///
/// Creates a projectile and fills in the data for the shot.
-/// This routine is used by the weapon firing code in place of a bare new -- projectiles are
-/// COM objects, so the instance must come from the class factory. The projectile is inert
-/// until it is unlimboed with a starting position and velocity.
+/// The projectile is inert until it is unlimboed with a starting position and velocity.
///
/// The object that fired the shot. It receives credit for any kill.
/// The damage the projectile will inflict when it detonates.
@@ -1518,12 +1485,7 @@ bool BulletClass::Is_Homing(void) const
/// made.
BulletClass * Create_Bullet(BulletTypeClass const *type, AbstractClass *target, TechnoClass *payback, int strength, WarheadTypeClass const *warhead, int max_speed, int range, bool bright)
{
- LPVOID unk = NULL;
- if (FAILED(CoCreateInstance(CLSID_BulletClass, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER|CLSCTX_LOCAL_SERVER, IID_IUnknown, &unk))) {
- return(NULL);
- }
-
- BulletClass * bullet = (BulletClass *)unk;
+ BulletClass * bullet = new BulletClass;
bullet->Set_Bullet_Data(type, target, payback, strength, warhead, max_speed, range, bright);
return(bullet);
}
@@ -1570,17 +1532,9 @@ RTTIType BulletClass::Fetch_RTTI(void) const
}
-///
-/// Fetches the class identifier for this projectile.
-/// This is the IPersist implementation the save and load machinery uses to recognize which
-/// kind of object it is about to read back from the stream.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE BulletClass::GetClassID(CLSID * retval)
+ClassID BulletClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_BulletClass;
- return(S_OK);
+ return(ClassID_BulletClass);
}
diff --git a/code/bullet.h b/code/bullet.h
index a51519ad8..4f2f2aa2a 100644
--- a/code/bullet.h
+++ b/code/bullet.h
@@ -52,8 +52,6 @@ class BulletClass : public ObjectClass
typedef ObjectClass BASECLASS;
public:
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
- virtual ULONG STDMETHODCALLTYPE Release(void) override;
public:
@@ -78,7 +76,7 @@ class BulletClass : public ObjectClass
BulletClass(void);
virtual ~BulletClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/bullettype.cpp b/code/bullettype.cpp
index 4fd298dcc..0c19cd2db 100644
--- a/code/bullettype.cpp
+++ b/code/bullettype.cpp
@@ -38,7 +38,6 @@
* BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "bullettype.h"
@@ -356,15 +355,9 @@ void BulletTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier that the save game code stores for this object.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE BulletTypeClass::GetClassID(CLSID * retval)
+ClassID BulletTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_BulletTypeClass;
- return(S_OK);
+ return(ClassID_BulletTypeClass);
}
diff --git a/code/bullettype.h b/code/bullettype.h
index 9a8c96831..ecd3ceaec 100644
--- a/code/bullettype.h
+++ b/code/bullettype.h
@@ -237,7 +237,7 @@ class BulletTypeClass : public ObjectTypeClass
BulletTypeClass(char const * name = NULL);
virtual ~BulletTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/campaign.cpp b/code/campaign.cpp
index 2731a716a..8ac83dc87 100644
--- a/code/campaign.cpp
+++ b/code/campaign.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "campaign.h"
@@ -126,18 +125,9 @@ void Read_Battle_INI(CCINIClass const & ini)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is required of every persistent object so that the save game loader
-/// can recognize what to construct when the object is read back.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE CampaignClass::GetClassID(CLSID * retval)
+ClassID CampaignClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_CampaignClass;
- return(S_OK);
+ return(ClassID_CampaignClass);
}
diff --git a/code/campaign.h b/code/campaign.h
index a59c4da89..585345e8d 100644
--- a/code/campaign.h
+++ b/code/campaign.h
@@ -23,7 +23,7 @@ class CampaignClass : public AbstractTypeClass
CampaignClass(char const * name = NULL);
virtual ~CampaignClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/cell.cpp b/code/cell.cpp
index eca014b12..6218a1685 100644
--- a/code/cell.cpp
+++ b/code/cell.cpp
@@ -74,7 +74,6 @@
* CellClass::Wall_Update -- Updates the imagery for wall objects in cell. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "cell.h"
@@ -4366,6 +4365,13 @@ void CellClass::Serialize(SaveStreamClass & stream)
stream.Serialize(CellID);
+ // Post_Load installs the cell in the array slot this coordinate names, so a coordinate
+ // that names none is refused here, while the record can still be thrown away whole.
+ if (stream.Is_Loading() && Map.Cell_Slot(CellID) < 0) {
+ stream.Fail();
+ return;
+ }
+
/*
* The snapshot list is built only once something standing here has been fogged over,
* so whether the cell has one at all travels ahead of its contents.
@@ -4469,7 +4475,11 @@ void CellClass::Post_Load(void)
{
BASECLASS::Post_Load();
- int id = CellID.X + (CellID.Y << 9);
+ int id = Map.Cell_Slot(CellID);
+ if (id < 0) {
+ return;
+ }
+
if (Map.Array[id] != NULL) {
delete Map.Array[id];
Map.Array[id] = NULL;
@@ -5168,18 +5178,9 @@ void CellClass::Detach(AbstractClass const * target)
}
-///
-/// Fetches the class identifier of this object.
-/// This is the persistence requirement that lets the save system recognize a cell when a
-/// saved game is read back in.
-///
-/// Pointer to the location to store the class identifier in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE CellClass::GetClassID(CLSID * retval)
+ClassID CellClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_CellClass;
- return(S_OK);
+ return(ClassID_CellClass);
}
diff --git a/code/cell.h b/code/cell.h
index 790b5b7dc..c5bda32e7 100644
--- a/code/cell.h
+++ b/code/cell.h
@@ -517,7 +517,7 @@ class CellClass : public AbstractClass
CellClass(void);
virtual ~CellClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/classfactory.cpp b/code/classfactory.cpp
new file mode 100644
index 000000000..bf3c87adb
--- /dev/null
+++ b/code/classfactory.cpp
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * O P E N T S
+ *******************************************************************************
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2026 OpenTS contributors
+ *
+ * See LICENSE.md for applicable additional terms and warranty disclaimers.
+ ******************************************************************************/
+
+#include "always.h"
+
+#include "classfactory.h"
+#include "dbgprint.h"
+
+#include
+
+namespace {
+
+struct ClassEntryType {
+ ClassID Class;
+ ClassCreatorType Creator;
+};
+
+std::vector Classes;
+
+} // namespace
+
+
+// A later registration of the same identifier wins, as the last class object
+// published did before.
+void Register_Class(ClassID const & classid, ClassCreatorType creator)
+{
+ for (ClassEntryType & entry : Classes) {
+ if (entry.Class == classid) {
+ entry.Creator = creator;
+ return;
+ }
+ }
+ Classes.push_back({ classid, creator });
+}
+
+
+void Unregister_Classes(void)
+{
+ Classes.clear();
+}
+
+
+///
+/// Creates a new object of the registered class named by the identifier.
+///
+/// The object, owned by the caller, or nothing with a debug line naming the
+/// identifier when no class was registered for it.
+std::unique_ptr Create_Object(ClassID const & classid)
+{
+ for (ClassEntryType const & entry : Classes) {
+ if (entry.Class == classid) {
+ return(entry.Creator());
+ }
+ }
+
+ DebugString("No class is registered for {%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
+ (unsigned long)classid.Data1, (unsigned int)classid.Data2, (unsigned int)classid.Data3,
+ classid.Data4[0], classid.Data4[1], classid.Data4[2], classid.Data4[3],
+ classid.Data4[4], classid.Data4[5], classid.Data4[6], classid.Data4[7]);
+ return(nullptr);
+}
diff --git a/code/classfactory.h b/code/classfactory.h
index f11d77047..cebba65c9 100644
--- a/code/classfactory.h
+++ b/code/classfactory.h
@@ -9,113 +9,20 @@
#pragma once
-template
-class TClassFactory : public IClassFactory
-{
- public:
- TClassFactory(void);
-
- STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
- STDMETHOD_(ULONG, AddRef)(void);
- STDMETHOD_(ULONG, Release)(void);
-
- STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppbObj);
- STDMETHOD(LockServer)(BOOL fLock);
-
- private:
- /*
- * This is the number of outstanding references to this factory, counting both the
- * interface pointers handed out and any server locks taken. The factory deletes
- * itself once the count falls back to zero.
- */
- LONG RefCount;
-};
-
-
-template
-TClassFactory::TClassFactory(void) :
- RefCount(0)
-{
-}
-
-
-template
-STDMETHODIMP TClassFactory::QueryInterface(REFIID riid, void **ppvObj)
-{
- if (ppvObj == NULL) {
- return(E_POINTER);
- }
-
- *ppvObj = NULL;
-
- if (riid == IID_IUnknown) {
- *ppvObj = (void *)((IClassFactory *)this);
- } else if (riid == IID_IClassFactory) {
- *ppvObj = (void *)((IClassFactory *)this);
- }
-
- if (*ppvObj == NULL) {
- return(E_NOINTERFACE);
- }
-
- ((IClassFactory *)this)->AddRef();
-
- return(S_OK);
-}
-
-
-template
-ULONG TClassFactory::AddRef(void)
-{
- return(InterlockedIncrement(&RefCount));
-}
+#include "persist.h"
+#include
-template
-ULONG TClassFactory::Release(void)
-{
- int count = InterlockedDecrement(&RefCount);
- if (count == 0) {
- delete this;
- }
-
- return(count);
-}
-
-
-template
-STDMETHODIMP TClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)
-{
- if (ppvObj == NULL) {
- return(E_INVALIDARG);
- }
-
- *ppvObj = NULL;
- if (pUnkOuter != NULL) {
- return(CLASS_E_NOAGGREGATION);
- }
-
- T *obj = new T();
- if (obj == NULL) {
- return(E_OUTOFMEMORY);
- }
-
- HRESULT hr = obj->QueryInterface(riid, ppvObj);
- if (FAILED(hr)) {
- delete obj;
- }
-
- return(hr);
-}
+// The classes a saved game or a unit type can name by class identifier. Startup
+// registers each one; nothing is created for an identifier nobody registered.
+using ClassCreatorType = std::unique_ptr (*)(void);
+void Register_Class(ClassID const & classid, ClassCreatorType creator);
+void Unregister_Classes(void);
+std::unique_ptr Create_Object(ClassID const & classid);
template
-HRESULT STDMETHODCALLTYPE TClassFactory::LockServer(BOOL fLock)
+void Register_Class(ClassID const & classid)
{
- if (fLock) {
- RefCount++;
- } else {
- RefCount--;
- }
- return(S_OK);
+ Register_Class(classid, []() -> std::unique_ptr { return(std::make_unique()); });
}
diff --git a/code/classid.h b/code/classid.h
new file mode 100644
index 000000000..42474d286
--- /dev/null
+++ b/code/classid.h
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * O P E N T S
+ *******************************************************************************
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2026 OpenTS contributors
+ *
+ * See LICENSE.md for applicable additional terms and warranty disclaimers.
+ ******************************************************************************/
+
+#pragma once
+
+#include
+
+// The identity a persistent class is saved and named by. The sixteen bytes are those of
+// the COM class identifier the class once registered, kept as they are because saved
+// games and the Locomotor= key carry them.
+struct ClassID
+{
+ unsigned int Data1;
+ unsigned short Data2;
+ unsigned short Data3;
+ unsigned char Data4[8];
+};
+
+static_assert(sizeof(ClassID) == 16, "a class identifier is sixteen bytes on disk");
+
+inline bool operator==(ClassID const & left, ClassID const & right)
+{
+ return(std::memcmp(&left, &right, sizeof(ClassID)) == 0);
+}
+
+inline bool operator!=(ClassID const & left, ClassID const & right)
+{
+ return(!(left == right));
+}
diff --git a/code/classids.cpp b/code/classids.cpp
new file mode 100644
index 000000000..0b88b27d7
--- /dev/null
+++ b/code/classids.cpp
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * O P E N T S
+ *******************************************************************************
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2026 OpenTS contributors
+ *
+ * See LICENSE.md for applicable additional terms and warranty disclaimers.
+ ******************************************************************************/
+
+#include "classids.h"
+
+ClassID const ClassID_HouseClass = {0xD9D4A910,0x87C6,0x11D1,{0xB7,0x07,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_SuperWeaponTypeClass = {0x0CF2BCE7,0x36E4,0x11D2,{0xB8,0xD8,0x00,0x60,0x08,0xC8,0x09,0xED}};
+ClassID const ClassID_SuperWeaponClass = {0xD7F754C6,0x391C,0x11D2,{0x9B,0x64,0x00,0x10,0x4B,0x97,0x2F,0xE8}};
+ClassID const ClassID_UnitTypeClass = {0xDCBD42EA,0x0546,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_InfantryTypeClass = {0xAE8B33D8,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_AircraftTypeClass = {0xAE8B33D9,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_BuildingTypeClass = {0xAE8B33DB,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_BulletTypeClass = {0x5AF2CE77,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_TerrainTypeClass = {0x5AF2CE7B,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_IsometricTileTypeClass = {0x5AF2CE7A,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_OverlayTypeClass = {0x5AF2CE79,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_SmudgeTypeClass = {0x5AF2CE78,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_AnimTypeClass = {0xAE8B33DA,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_HouseTypeClass = {0x1DD43928,0x046B,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_IsometricTileClass = {0x0E272DC0,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_VoxelAnimClass = {0x0E272DC1,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_AircraftClass = {0x0E272DC2,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_AnimClass = {0x0E272DC3,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_InfantryClass = {0x0E272DC4,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_SmudgeClass = {0x0E272DC5,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_BuildingClass = {0x0E272DC6,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_OverlayClass = {0x0E272DC7,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_ParticleSystemClass = {0x0E272DC8,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_ParticleSystemTypeClass = {0x703E044A,0x0FB1,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_BulletClass = {0x0E272DC9,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_UnitClass = {0x0E272DCA,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_ParticleClass = {0x0E272DCC,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_ParticleTypeClass = {0x703E044B,0x0FB1,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_WaveClass = {0x0E272DCD,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_BuildingLightClass = {0x54822258,0xD8A8,0x11D1,{0xB4,0x62,0x00,0x60,0x97,0xC6,0xA9,0x79}};
+ClassID const ClassID_TerrainClass = {0x0E272DCE,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_TubeClass = {0x0B4CA41C,0xB3A7,0x11D1,{0xB4,0x57,0x00,0x60,0x97,0xC6,0xA9,0x79}};
+ClassID const ClassID_TeamClass = {0x0E272DCF,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_TaskForceClass = {0x61DE341E,0x0774,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_TeamTypeClass = {0xD1DBA64E,0x0778,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_VoxelAnimTypeClass = {0x2EBB6D66,0x0D4D,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_ScriptClass = {0x42F3A646,0x0789,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_ScriptTypeClass = {0x42F3A647,0x0789,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_TagClass = {0x54F6E432,0x09ED,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_TagTypeClass = {0x54F6E433,0x09ED,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_TriggerClass = {0xC02D1590,0x0A2A,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_TriggerTypeClass = {0xC02D1591,0x0A2A,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_ActionClass = {0x4F0EC392,0x0A55,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_EventClass = {0x4F0EC393,0x0A55,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_FactoryClass = {0x34ECD9A8,0x0AB0,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_WeaponTypeClass = {0x9FD219CA,0x0F7B,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_WarheadTypeClass = {0xA8C54DA4,0x0F7B,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_WaypointPath = {0xF73125BA,0x1054,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_LightSource = {0x6F9C48F0,0x1207,0x11D2,{0x81,0x74,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_CampaignClass = {0xFFDAC848,0x1517,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_SideClass = {0xC53DD372,0x151E,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_TiberiumClass = {0xC53DD373,0x151E,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_CellClass = {0xC1BF99CE,0x1A8C,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_EMPulseClass = {0xB825CB22,0x200E,0x11D2,{0x9F,0xA9,0x00,0x60,0x08,0x9A,0xD4,0x58}};
+ClassID const ClassID_TacticalMapClass = {0xCF56B38A,0x240D,0x11D2,{0x81,0x7C,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_AITriggerTypeClass = {0xBA093524,0x4CF4,0x11D2,{0xBC,0x26,0x00,0x10,0x4B,0x8F,0xB0,0x4D}};
+ClassID const ClassID_AITriggerClass = {0x03C4CE76,0x4CF5,0x11D2,{0xBC,0x26,0x00,0x10,0x4B,0x8F,0xB0,0x4D}};
+ClassID const ClassID_NeuronClass = {0x241AB316,0x4CF5,0x11D2,{0xBC,0x26,0x00,0x10,0x4B,0x8F,0xB0,0x4D}};
+ClassID const ClassID_FoggedObjectClass = {0x1C470B0E,0x69D7,0x11D2,{0xB8,0xF2,0x00,0x60,0x08,0xC8,0x09,0xED}};
+ClassID const ClassID_AlphaShapeClass = {0x623C7584,0x74E7,0x11D2,{0xB8,0xF5,0x00,0x60,0x08,0xC8,0x09,0xED}};
+ClassID const ClassID_VeinholeMonsterClass = {0x5192D06A,0xC632,0x11D2,{0xB9,0x0B,0x00,0x60,0x08,0xC8,0x09,0xED}};
+ClassID const ClassID_DriveLocomotion = {0x4A582741,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_HoverLocomotion = {0x4A582742,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_TunnelLocomotion = {0x4A582743,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_WalkLocomotion = {0x4A582744,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_BallisticLocomotion = {0x4A582745,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_FlyerLocomotion = {0x4A582746,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_TeleportLocomotion = {0x4A582747,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
+ClassID const ClassID_MechLocomotion = {0x55D141B8,0xDB94,0x11D1,{0xAC,0x98,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_JumpjetLocomotion = {0x92612C46,0xF71F,0x11D1,{0xAC,0x9F,0x00,0x60,0x08,0x05,0x5B,0xB5}};
+ClassID const ClassID_LevitateLocomotion = {0x3DC0B295,0x6546,0x11D3,{0x80,0xB0,0x00,0x90,0x27,0x92,0x49,0x4C}};
diff --git a/code/classids.h b/code/classids.h
new file mode 100644
index 000000000..e86803bed
--- /dev/null
+++ b/code/classids.h
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * O P E N T S
+ *******************************************************************************
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2026 OpenTS contributors
+ *
+ * See LICENSE.md for applicable additional terms and warranty disclaimers.
+ ******************************************************************************/
+
+#pragma once
+
+#include "classid.h"
+
+// The identifiers of every class a saved game or a Locomotor= key can name.
+extern ClassID const ClassID_HouseClass;
+extern ClassID const ClassID_SuperWeaponTypeClass;
+extern ClassID const ClassID_SuperWeaponClass;
+extern ClassID const ClassID_UnitTypeClass;
+extern ClassID const ClassID_InfantryTypeClass;
+extern ClassID const ClassID_AircraftTypeClass;
+extern ClassID const ClassID_BuildingTypeClass;
+extern ClassID const ClassID_BulletTypeClass;
+extern ClassID const ClassID_TerrainTypeClass;
+extern ClassID const ClassID_IsometricTileTypeClass;
+extern ClassID const ClassID_OverlayTypeClass;
+extern ClassID const ClassID_SmudgeTypeClass;
+extern ClassID const ClassID_AnimTypeClass;
+extern ClassID const ClassID_HouseTypeClass;
+extern ClassID const ClassID_IsometricTileClass;
+extern ClassID const ClassID_VoxelAnimClass;
+extern ClassID const ClassID_AircraftClass;
+extern ClassID const ClassID_AnimClass;
+extern ClassID const ClassID_InfantryClass;
+extern ClassID const ClassID_SmudgeClass;
+extern ClassID const ClassID_BuildingClass;
+extern ClassID const ClassID_OverlayClass;
+extern ClassID const ClassID_ParticleSystemClass;
+extern ClassID const ClassID_ParticleSystemTypeClass;
+extern ClassID const ClassID_BulletClass;
+extern ClassID const ClassID_UnitClass;
+extern ClassID const ClassID_ParticleClass;
+extern ClassID const ClassID_ParticleTypeClass;
+extern ClassID const ClassID_WaveClass;
+extern ClassID const ClassID_BuildingLightClass;
+extern ClassID const ClassID_TerrainClass;
+extern ClassID const ClassID_TubeClass;
+extern ClassID const ClassID_TeamClass;
+extern ClassID const ClassID_TaskForceClass;
+extern ClassID const ClassID_TeamTypeClass;
+extern ClassID const ClassID_VoxelAnimTypeClass;
+extern ClassID const ClassID_ScriptClass;
+extern ClassID const ClassID_ScriptTypeClass;
+extern ClassID const ClassID_TagClass;
+extern ClassID const ClassID_TagTypeClass;
+extern ClassID const ClassID_TriggerClass;
+extern ClassID const ClassID_TriggerTypeClass;
+extern ClassID const ClassID_ActionClass;
+extern ClassID const ClassID_EventClass;
+extern ClassID const ClassID_FactoryClass;
+extern ClassID const ClassID_WeaponTypeClass;
+extern ClassID const ClassID_WarheadTypeClass;
+extern ClassID const ClassID_WaypointPath;
+extern ClassID const ClassID_LightSource;
+extern ClassID const ClassID_CampaignClass;
+extern ClassID const ClassID_SideClass;
+extern ClassID const ClassID_TiberiumClass;
+extern ClassID const ClassID_CellClass;
+extern ClassID const ClassID_EMPulseClass;
+extern ClassID const ClassID_TacticalMapClass;
+extern ClassID const ClassID_AITriggerTypeClass;
+extern ClassID const ClassID_AITriggerClass;
+extern ClassID const ClassID_NeuronClass;
+extern ClassID const ClassID_FoggedObjectClass;
+extern ClassID const ClassID_AlphaShapeClass;
+extern ClassID const ClassID_VeinholeMonsterClass;
+extern ClassID const ClassID_DriveLocomotion;
+extern ClassID const ClassID_HoverLocomotion;
+extern ClassID const ClassID_TunnelLocomotion;
+extern ClassID const ClassID_WalkLocomotion;
+extern ClassID const ClassID_BallisticLocomotion;
+extern ClassID const ClassID_FlyerLocomotion;
+extern ClassID const ClassID_TeleportLocomotion;
+extern ClassID const ClassID_MechLocomotion;
+extern ClassID const ClassID_JumpjetLocomotion;
+extern ClassID const ClassID_LevitateLocomotion;
diff --git a/code/crc.cpp b/code/crc.cpp
index 60238085d..bd7f3a4eb 100644
--- a/code/crc.cpp
+++ b/code/crc.cpp
@@ -295,7 +295,7 @@ unsigned int CRC::_Table[ 256 ] =
/// The CRC value to accumulate onto. Pass the result of a previous
/// call in order to chain several blocks into one value.
/// Returns with the CRC of the block.
-unsigned int CRC::Memory( unsigned char *data, unsigned int length, unsigned int crc )
+unsigned int CRC::Memory( unsigned char const *data, unsigned int length, unsigned int crc )
{
crc ^= 0xFFFFFFFF; // invert previous CRC
while ( length-- ) {
diff --git a/code/crc.h b/code/crc.h
index 490f48cee..9fa56c6ab 100644
--- a/code/crc.h
+++ b/code/crc.h
@@ -50,7 +50,7 @@ class CRC {
public:
// get the CRC of a block of memory
- static unsigned int Memory( unsigned char *data, unsigned int length, unsigned int crc = 0 );
+ static unsigned int Memory( unsigned char const *data, unsigned int length, unsigned int crc = 0 );
// get the CRC of a null-terminated string
static unsigned int String( const char *string, unsigned int crc = 0 );
diff --git a/code/cstream.cpp b/code/cstream.cpp
deleted file mode 100644
index 65061d9da..000000000
--- a/code/cstream.cpp
+++ /dev/null
@@ -1,540 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#include "always.h"
-
-#include "cstream.h"
-
-#include
-
-extern ULONG COMRefCount;
-
-///
-/// Creates a compressing stream object.
-/// This routine prepares the working buffers that the LZO codec needs. The object starts
-/// out with no storage stream of its own to compress through.
-///
-/// Call Link_Stream to attach a storage stream before reading or writing.
-CStreamClass::CStreamClass(void) :
- StreamPtr(NULL),
- RefCount(0),
- IsReading(false),
- IsWriting(false),
- CurOffset(0),
- DataBuffer(new unsigned char[BUFFER_SIZE]),
- StreamBuffer(new unsigned char[STREAM_BUFFER_SIZE]),
- LZODictionary(new unsigned char[LZO1X_1_MEM_COMPRESS])
-{
- BlockHead.CompSize = BUFFER_SIZE - 1;
-}
-
-
-///
-/// Destroys the compressing stream object.
-/// Any storage stream still attached is unlinked first, so that whatever is left in the
-/// work buffer is compressed out rather than lost.
-///
-CStreamClass::~CStreamClass(void)
-{
- IUnknown **unk = NULL;
- if (StreamPtr) {
- Unlink_Stream(unk);
- }
-
- delete [] LZODictionary;
- LZODictionary = NULL;
- delete [] DataBuffer;
- DataBuffer = NULL;
- delete [] StreamBuffer;
- StreamBuffer = NULL;
-}
-
-
-///
-/// Takes out a reference on this stream object.
-///
-/// Returns with the new reference count.
-ULONG CStreamClass::AddRef(void)
-{
- COMRefCount++;
- return(InterlockedIncrement(&RefCount));
-}
-
-
-///
-/// Releases a reference to this stream object.
-/// This routine will destroy the object once the last outstanding reference has been
-/// given up.
-///
-/// Returns with the number of references that remain.
-ULONG CStreamClass::Release(void)
-{
- COMRefCount--;
- ULONG i = InterlockedDecrement(&RefCount);
-
- if (i == 0) {
- delete this;
- }
-
- return(i);
-}
-
-
-///
-/// Fetches an alternate interface to this stream object.
-/// The IUnknown, IStream and ILinkStream interfaces are the ones supported. A successful
-/// query takes out a reference on this object for the caller.
-///
-/// The identifier of the interface being asked for.
-/// Pointer to the interface pointer to fill in.
-/// Returns with S_OK, or E_NOINTERFACE if the interface is not supported.
-LONG CStreamClass::QueryInterface(REFIID riid, LPVOID * ppvObject)
-{
- if (ppvObject == NULL) {
- return(E_POINTER);
- }
-
- *ppvObject = NULL;
- if (riid == IID_IUnknown) {
- *ppvObject = this;
- }
- if (riid == IID_IStream) {
- *ppvObject = (IStream *)this;
- }
- if (riid == IID_ILinkStream) {
- *ppvObject = (ILinkStream *)this;
- }
- if (*ppvObject == NULL) {
- return(E_NOINTERFACE);
- }
- //reinterpret_cast(*ppvObject)->AddRef();
- this->AddRef();
- return(S_OK);
-}
-
-
-///
-/// Attaches the storage stream this object compresses through.
-/// Use this routine to bind the compressor to the real stream that the compressed blocks
-/// will be written to or read back from. Only one stream may be attached at a time.
-///
-/// Pointer to the object to fetch the storage stream from.
-/// Returns with S_OK, or E_FAIL if a stream is already attached.
-HRESULT CStreamClass::Link_Stream(IUnknown *stream)
-{
- if (stream == NULL) {
- return(E_POINTER);
- }
-
- if (StreamPtr != NULL) {
- return(E_FAIL);
- }
-
- HRESULT hr = stream->QueryInterface(__uuidof(IStream), (void **)&stream);
- if (FAILED(hr)) {
- /// &StreamPtr;
- StreamPtr.Attach(NULL, false);
- } else {
- StreamPtr.Attach((IStream *)stream, false);
- }
-
- if (FAILED(hr) && (hr != E_NOINTERFACE)) {
- _com_issue_error(hr);
- }
-
- return(S_OK);
-}
-
-
-///
-/// Detaches the underlying storage stream from this object.
-/// Anything still sitting in the work buffer is compressed out first and the storage
-/// stream is committed, so the caller gets back a complete stream.
-///
-/// Pointer to fill in with the released stream, or NULL if the caller
-/// does not want it.
-/// Returns with S_OK, or E_FAIL if no stream was attached. A commit failure is
-/// returned as it stands.
-HRESULT CStreamClass::Unlink_Stream(IUnknown **stream)
-{
- Compress();
-
- if (StreamPtr == NULL) {
- return(E_FAIL);
- }
-
- if (stream != NULL) {
- StreamPtr->AddRef();
- *stream = StreamPtr;
- }
-
- HRESULT hr = StreamPtr->Commit(0);
- if (SUCCEEDED(hr)) {
- StreamPtr.Release();
- } else {
- return(hr);
- }
-
- return(S_OK);
-}
-
-
-///
-/// Reads and decompresses data from the underlying stream.
-/// This routine pulls whole compressed blocks out of the storage stream and doles out
-/// pieces of the decompressed result until the caller's request has been satisfied.
-///
-/// Pointer to the buffer to fill with the data read.
-/// The number of bytes to read.
-/// Pointer to fill in with the number of bytes read, or NULL if the
-/// count is not wanted.
-/// Returns with S_OK, or an error code if the data could not be read.
-/// A stream that is being written cannot also be read.
-HRESULT CStreamClass::Read(void *pv, ULONG cb, ULONG *pcbRead)
-{
- int read_size;
- int left;
-
- read_size = cb;
- left = cb;
-
- if (pv == NULL) {
- return(E_POINTER);
- }
-
- if (read_size < 0) {
- return(E_INVALIDARG);
- }
-
- if (StreamPtr == NULL) {
- return(E_FAIL);
- }
-
- if (IsWriting) {
- return(E_FAIL);
- }
-
- IsReading = true;
-
- if (pcbRead != NULL) {
- *pcbRead = 0;
- }
-
- while (left > 0) {
-
- int offset = CurOffset;
- if (offset > 0) {
- int len = left;
- if (left >= offset) {
- len = CurOffset;
- }
- memmove(pv, (char *)DataBuffer + BlockHead.UncompSize - offset, len);
- pv = (char *)pv + len;
- left -= len;
- CurOffset -= len;
- }
-
- if (left == 0) {
- break;
- }
-
- ULONG read = 0;
-
- HRESULT hr = StreamPtr->Read(&BlockHead, sizeof(BlockHead), &read);
- if (FAILED(hr)) {
- return(hr);
- }
-
- if (read != sizeof(BlockHead)) {
- return(E_FAIL);
- }
-
- if (BlockHead.CompSize > STREAM_BUFFER_SIZE) {
- return(E_FAIL);
- }
-
- hr = StreamPtr->Read(StreamBuffer, BlockHead.CompSize, &read);
- if (FAILED(hr)) {
- return(hr);
- }
-
- unsigned int inlen = BlockHead.CompSize;
- if (read != inlen) {
- return(E_FAIL);
- }
- lzo_byte *out = (lzo_byte *)DataBuffer;
- lzo_byte *in = (lzo_byte *)StreamBuffer;
- lzo_uint out_len = BUFFER_SIZE;
- if (lzo1x_decompress_safe(in, inlen, out, &out_len, NULL) != LZO_E_OK) {
- return(E_FAIL);
- }
- // Compress records the whole buffer size rather than the block's own length, so only
- // the decompressor's count says how much of the buffer is real.
- BlockHead.UncompSize = out_len;
- CurOffset = out_len;
- }
-
- if (pcbRead != NULL) {
- *pcbRead = read_size;
- }
-
- return(S_OK);
-}
-
-
-///
-/// Compresses data out to the underlying stream.
-/// This routine gathers the caller's data into a work buffer and hands it to the
-/// compressor a block at a time, so what reaches the storage stream is a run of
-/// compressed blocks rather than the raw bytes.
-///
-/// Pointer to the data to write.
-/// The number of bytes to write.
-/// Pointer to fill in with the number of bytes accepted, or NULL
-/// if the count is not wanted.
-/// Returns with S_OK, or an error code if the data could not be written.
-/// A stream that is being read cannot also be written. The trailing partial
-/// block does not reach the storage stream until the object is flushed or unlinked.
-HRESULT CStreamClass::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
-{
- unsigned char *ptr;
- int write_size;
- int left;
- int result;
- int temp_size;
-
- ptr = (unsigned char *)pv;
- write_size = cb;
- left = cb;
-
- if (pv == NULL) {
- return(E_POINTER);
- }
-
- if (write_size < 0) {
- return(E_INVALIDARG);
- }
-
- if (StreamPtr == NULL) {
- return(E_FAIL);
- }
-
- if (IsReading) {
- return(E_FAIL);
- }
-
- IsWriting = true;
-
- if (cb != 0) {
- if (pcbWritten != NULL) {
- *pcbWritten = 0;
- }
-
- if (CurOffset > 0) {
- if (write_size >= BUFFER_SIZE - CurOffset) {
- write_size = BUFFER_SIZE - CurOffset;
- }
-
- memmove((unsigned char *)DataBuffer + CurOffset, ptr, write_size);
- temp_size = write_size + CurOffset;
-
- ptr += write_size;
- left -= write_size;
- CurOffset = temp_size;
-
- if (CurOffset == BUFFER_SIZE) {
- result = Compress(DataBuffer, CurOffset);
- if (result < 0) {
- return(result);
- }
- CurOffset = 0;
- }
-
- write_size = cb;
- }
- while (left >= BUFFER_SIZE) {
- result = Compress(ptr, BUFFER_SIZE);
- if (result < 0) {
- return(result);
- }
- left -= BUFFER_SIZE;
- ptr += BUFFER_SIZE;
- }
-
- if (left > 0) {
- memmove((unsigned char *)DataBuffer, ptr, left);
- write_size = cb;
- CurOffset = left;
- }
-
- if (pcbWritten) {
- *pcbWritten = write_size;
- }
- }
-
- return(S_OK);
-}
-
-
-///
-/// Moves the file pointer of the underlying stream.
-///
-/// Returns with S_OK, or E_FAIL if a transfer is already under way.
-/// Seeking is refused once reading or writing has begun, since the compressor
-/// keeps state that a seek would invalidate.
-HRESULT CStreamClass::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
-{
- if (IsReading || IsWriting) {
- return(E_FAIL);
- }
-
- return(StreamPtr->Seek(dlibMove, dwOrigin, plibNewPosition));
-}
-
-
-///
-/// Sets the size of the underlying stream.
-///
-/// Returns with S_OK, or E_FAIL if a transfer is already under way.
-/// Resizing is refused once reading or writing has begun.
-HRESULT CStreamClass::SetSize(ULARGE_INTEGER libNewSize)
-{
- if (IsReading || IsWriting) {
- return(E_FAIL);
- }
-
- return(StreamPtr->SetSize(libNewSize));
-}
-
-
-///
-/// Copies data from this stream over to another stream.
-/// The request is handed straight to the underlying stream, so it is the compressed
-/// bytes that get copied rather than the data they stand for.
-///
-/// Returns with the result of the underlying stream's copy request.
-HRESULT CStreamClass::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
-{
- return(StreamPtr->CopyTo(pstm, cb, pcbRead, pcbWritten));
-}
-
-
-///
-/// Commits any pending changes to the underlying stream.
-///
-/// Returns with the result of the underlying stream's commit request.
-HRESULT CStreamClass::Commit(DWORD grfCommitFlags)
-{
- return(StreamPtr->Commit(grfCommitFlags));
-}
-
-///
-/// Discards any uncommitted changes to the stream.
-///
-/// Returns with the result of the underlying stream's revert request.
-HRESULT CStreamClass::Revert(void)
-{
- return(StreamPtr->Revert());
-}
-
-
-///
-/// Locks a byte range of the underlying stream.
-///
-/// Returns with the result of the underlying stream's lock request.
-HRESULT CStreamClass::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
-{
- return(StreamPtr->LockRegion(libOffset, cb, dwLockType));
-}
-
-
-///
-/// Releases a lock on a byte range of the underlying stream.
-///
-/// Returns with the result of the underlying stream's unlock request.
-HRESULT CStreamClass::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
-{
- return(StreamPtr->UnlockRegion(libOffset, cb, dwLockType));
-}
-
-
-///
-/// Fetches the statistics of the underlying stream.
-///
-/// Returns with the result of the underlying stream's stat request.
-HRESULT CStreamClass::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
-{
- return(StreamPtr->Stat(pstatstg, grfStatFlag));
-}
-
-
-///
-/// Creates a second stream object over the same storage.
-/// The clone is made by the underlying stream, so it is a plain stream rather than a
-/// compressing one.
-///
-/// Returns with the result of the underlying stream's clone request.
-HRESULT CStreamClass::Clone(IStream **ppstm)
-{
- return(StreamPtr->Clone(ppstm));
-}
-
-
-///
-/// Compresses a buffer out as a single stream block.
-/// This is the low level routine that runs the buffer through the LZO compressor and
-/// writes the block header and the compressed bytes to the underlying stream.
-///
-/// Pointer to the data to compress.
-/// The number of bytes to compress.
-/// Returns with S_OK, or an error code if the block could not be written.
-HRESULT CStreamClass::Compress(void *in_buffer, ULONG length)
-{
- HRESULT hr;
- lzo_uint out_len = length;
- lzo1x_1_compress((lzo_byte *)in_buffer, length, (lzo_byte *)StreamBuffer, &out_len, (lzo_byte *)LZODictionary);
- BlockHead.UncompSize = BUFFER_SIZE;
- length = 0;
- BlockHead.CompSize = out_len;
-
- hr = StreamPtr->Write(&BlockHead, sizeof(BlockHead), &length);
-
- if (SUCCEEDED(hr)) {
- if (length != sizeof(BlockHead)) {
- return(E_FAIL);
- }
-
- hr = StreamPtr->Write(StreamBuffer, out_len, &length);
- if (SUCCEEDED(hr)) {
- hr = length != out_len ? (unsigned int)E_FAIL : 0;
- }
- }
-
- return(hr);
-}
-
-
-///
-/// Flushes any buffered data out as a compressed block.
-/// Use this routine to make sure the tail end of a write actually reaches the stream.
-/// It does nothing if there is nothing left over to flush.
-///
-/// Returns with S_OK, or an error code if the block could not be written.
-HRESULT CStreamClass::Compress(void)
-{
- if (IsWriting && CurOffset > 0) {
- if (StreamPtr == NULL) {
- return(E_FAIL);
- }
- if (CurOffset > 0) {
- return(Compress(DataBuffer, CurOffset));
- }
- }
- return(S_OK);
-}
diff --git a/code/cstream.h b/code/cstream.h
deleted file mode 100644
index e344687e9..000000000
--- a/code/cstream.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#pragma once
-
-#include "ilinkstm.h"
-
-#include
-#include
-
-class CStreamClass : public IStream, public ILinkStream
-{
- public:
- virtual LONG STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID * ppvObject) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
- virtual ULONG STDMETHODCALLTYPE Release(void) override;
-
- virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead) override;
- virtual HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten) override;
-
- virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) override;
- virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize) override;
- virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) override;
- virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags) override;
- virtual HRESULT STDMETHODCALLTYPE Revert() override;
- virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) override;
- virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) override;
- virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag) override;
- virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm) override;
-
- virtual HRESULT STDMETHODCALLTYPE Link_Stream(IUnknown *stream) override;
- virtual HRESULT STDMETHODCALLTYPE Unlink_Stream(IUnknown **stream) override;
-
- public:
- CStreamClass(void);
- virtual ~CStreamClass(void);
-
- HRESULT Compress(void *in_buffer, ULONG length);
- HRESULT Compress(void);
-
- enum {
- BUFFER_SIZE = 64*1024,
-
- /*
- * LZO1X-1 can expand a block rather than shrink it, so the compressed side is
- * sized for its worst case.
- */
- STREAM_BUFFER_SIZE = BUFFER_SIZE + BUFFER_SIZE/16 + 64 + 3,
- };
-
- private:
- /*
- * This points to the stream the compressed data actually travels over. Nothing can be
- * read or written until one has been linked in, and the link is broken again once the
- * stream has been committed.
- */
- IStreamPtr StreamPtr;
-
- /*
- * This is the COM reference count for this object. The stream destroys itself once
- * the last reference to it has been released.
- */
- LONG RefCount;
-
- /*
- * These flags record which direction the stream has been committed to. The first read
- * or write sets one of them, and from that point on the opposite operation is
- * refused, as are seeking and resizing.
- */
- bool IsReading;
- bool IsWriting;
-
- /*
- * This is how much of the data buffer is currently in play, expressed in bytes. While
- * reading it counts down the part of the decompressed block not yet handed out; while
- * writing it counts up the bytes waiting to be compressed.
- */
- int CurOffset;
-
- /*
- * This is the working buffer that holds the data in its uncompressed form, one
- * block's worth at a time.
- */
- void *DataBuffer;
-
- /*
- * This is the working buffer that holds a block in its compressed form, on its way to
- * or from the linked stream.
- */
- void *StreamBuffer;
-
- /*
- * This is the scratch memory the LZO compressor keeps its dictionary in. It is of no
- * interest outside the compression call itself.
- */
- void *LZODictionary;
-
- /*
- * This is the header of the block currently being read or written. Every block on the
- * stream is preceded by one, so the reader knows how much compressed data to pull in
- * and how far it will expand.
- */
- struct BlockHeader {
- /*
- * This is the number of bytes the block occupies on the stream, compressed.
- */
- unsigned int CompSize;
-
- /*
- * This is the number of bytes the block expands to once decompressed.
- */
- unsigned int UncompSize;
- } BlockHead;
-};
diff --git a/code/display.cpp b/code/display.cpp
index bd4712eb6..b9775bef5 100644
--- a/code/display.cpp
+++ b/code/display.cpp
@@ -3859,14 +3859,13 @@ LRESULT DisplayClass::Windows_Message_Proc(HWND hWnd, UINT Msg, WPARAM wParam, L
/// Loads the display layers from the save game stream.
///
/// The stream to read the layers from.
-/// Returns with S_OK if every layer was read, otherwise the failure code of the
-/// layer that could not be read.
-HRESULT DisplayClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool DisplayClass::Load(SaveStreamClass & stream)
{
- HRESULT result = S_OK;
+ bool result = true;
for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) {
result = Layer[layer].Load(stream);
- if (FAILED(result)) break;
+ if (!result) break;
}
return(result);
}
@@ -3876,14 +3875,13 @@ HRESULT DisplayClass::Load(IStream * stream)
/// Saves the display layers to the save game stream.
///
/// The stream to write the layers to.
-/// Returns with S_OK if every layer was written, otherwise the failure code of the
-/// layer that could not be written.
-HRESULT DisplayClass::Save(IStream * stream)
+/// bool; Was the record written whole?
+bool DisplayClass::Save(SaveStreamClass & stream)
{
- HRESULT result = S_OK;
+ bool result = true;
for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) {
result = Layer[layer].Save(stream);
- if (FAILED(result)) break;
+ if (!result) break;
}
return(result);
}
diff --git a/code/display.h b/code/display.h
index 3c346c1f7..a2ac6197c 100644
--- a/code/display.h
+++ b/code/display.h
@@ -65,8 +65,8 @@ class DisplayClass: public MapClass
friend class Tactical;
public:
- virtual HRESULT Load(IStream * stream);
- virtual HRESULT Save(IStream * stream);
+ virtual bool Load(SaveStreamClass & stream);
+ virtual bool Save(SaveStreamClass & stream);
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/drive.cpp b/code/drive.cpp
index 538c71bc9..ded8d082f 100644
--- a/code/drive.cpp
+++ b/code/drive.cpp
@@ -68,6 +68,7 @@
#include "inline.h"
#include "overtype.h"
#include "rules.h"
+#include "saveload.h"
#include "savestream.h"
#include "tube.h"
#include "unit.h"
@@ -116,8 +117,7 @@ DriveLocomotionClass::DriveLocomotionClass(void) :
SpeedAccum(0),
TargetSpeed(0),
TrackNumber(-1),
- TrackIndex(-1),
- Piggybacker(NULL)
+ TrackIndex(-1)
{
}
@@ -131,68 +131,10 @@ DriveLocomotionClass::~DriveLocomotionClass(void)
}
-///
-/// Fetches the class identifier of whichever locomotor is driving the unit.
-/// That is the identifier of the locomotor riding along on this driver when there is one,
-/// and the driver's own otherwise.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK if the identifier was supplied, E_FAIL if the locomotor
-/// could not be asked, or E_POINTER if no destination was supplied.
-HRESULT DriveLocomotionClass::Piggyback_CLSID(CLSID * classid)
-{
- if (classid == NULL) {
- return(E_POINTER);
- }
-
- if (Piggybacker != NULL) {
- IPersistPtr ptr(Piggybacker);
- if (ptr == NULL) {
- return(E_FAIL);
- }
- return(ptr->GetClassID(classid));
- }
-
- IPersistPtr ptr(this);
- if (ptr == NULL) {
- return(E_FAIL);
- }
- return(ptr->GetClassID(classid));
-}
-
-
-///
-/// Fetches an interface supported by this locomotor.
-/// The driver answers for the piggyback interface on top of whatever the base locomotor
-/// already supports.
-///
-/// The identifier of the interface asked for.
-/// Pointer to the interface pointer to fill in.
-/// Returns with S_OK if the interface was supplied, otherwise
-/// E_NOINTERFACE.
-HRESULT STDMETHODCALLTYPE DriveLocomotionClass::QueryInterface(REFIID riid, LPVOID * ppvObject)
-{
- HRESULT result = BASECLASS::QueryInterface(riid, ppvObject);
-
- if (result == E_NOINTERFACE) {
- if (riid == IID_IPiggyback) {
- *ppvObject = (IPiggyback*)this;
- }
- if (*ppvObject == NULL) {
- result = E_NOINTERFACE;
- } else {
- AddRef();
- result = S_OK;
- }
- }
- return(result);
-}
-
-
///
/// Lists the members this driver carries.
/// A locomotor riding along on this one is a separate persistent object rather than a
-/// member, so it still travels framed by OLE and is recreated as the class it was saved as.
+/// member, so it travels as a record of its own and is recreated as the class it was saved as.
///
/// The stream carrying the members.
void DriveLocomotionClass::Serialize(SaveStreamClass & stream)
@@ -220,10 +162,9 @@ void DriveLocomotionClass::Serialize(SaveStreamClass & stream)
if (haspiggy) {
if (stream.Is_Saving()) {
- IPersistStreamPtr persist(Piggybacker);
- OleSaveToStream(persist, stream.Get_Stream());
+ Save_Object(stream, Piggybacker.get());
} else {
- OleLoadFromStream(stream.Get_Stream(), IID_ILocomotion, (LPVOID *)&Piggybacker);
+ Piggybacker = Load_Locomotor(stream);
}
}
// TrackControl -- constant tables shared by every driver.
@@ -237,19 +178,15 @@ void DriveLocomotionClass::Serialize(SaveStreamClass & stream)
/// A unit that must travel in some special manner -- through a tunnel, or aboard a
/// carrier -- keeps its driver but lets the special locomotor move it for the duration.
///
-/// The locomotor that is to take over the unit.
-/// Returns with S_OK if the locomotor was taken on, E_FAIL if one is already
-/// riding, or E_POINTER if none was supplied.
-HRESULT STDMETHODCALLTYPE DriveLocomotionClass::Begin_Piggyback(ILocomotion *pointer)
+/// The locomotor that is to take over the unit.
+/// bool; Was the locomotor taken on? One already carrying a locomotor refuses.
+bool DriveLocomotionClass::Begin_Piggyback(std::unique_ptr & carried)
{
- if (pointer == NULL) {
- return(E_POINTER);
- }
- if (Piggybacker == NULL) {
- Piggybacker = pointer;
- return(S_OK);
+ if (carried == nullptr || Piggybacker != nullptr) {
+ return(false);
}
- return(E_FAIL);
+ Piggybacker = std::move(carried);
+ return(true);
}
@@ -258,20 +195,10 @@ HRESULT STDMETHODCALLTYPE DriveLocomotionClass::Begin_Piggyback(ILocomotion *poi
/// The riding locomotor is detached and given up, leaving this driver in sole charge of
/// the unit once more.
///
-/// Pointer to the locomotor pointer to fill in.
-/// Returns with S_OK if a locomotor was handed back, S_FALSE if there was none
-/// riding, or E_POINTER if no destination was supplied.
-HRESULT DriveLocomotionClass::End_Piggyback(ILocomotion **pointer)
+/// Returns with the locomotor that was riding, or nothing when none was.
+std::unique_ptr DriveLocomotionClass::End_Piggyback(void)
{
- if (pointer == NULL) {
- return(E_POINTER);
- }
- if (Piggybacker != NULL) {
- *pointer = Piggybacker;
- Piggybacker.Detach();
- return(S_OK);
- }
- return(S_FALSE);
+ return(std::move(Piggybacker));
}
@@ -282,7 +209,7 @@ HRESULT DriveLocomotionClass::End_Piggyback(ILocomotion **pointer)
/// back only once the unit has settled.
///
/// bool; Is it safe to end the piggyback?
-boolean DriveLocomotionClass::Is_Ok_To_End(void)
+bool DriveLocomotionClass::Is_Ok_To_End(void)
{
if (!Is_Moving() && (Piggybacker != NULL && IsLocomotorUnlocked)) {
return(true);
@@ -345,7 +272,7 @@ void DriveLocomotionClass::Set_Slope(int ramp)
/// when it is first placed on the map.
///
/// The ramp the unit is to be sitting on.
-void STDMETHODCALLTYPE DriveLocomotionClass::Force_New_Slope(int ramp)
+void DriveLocomotionClass::Force_New_Slope(int ramp)
{
PreviousRamp = ramp;
CurrentRamp = ramp;
@@ -359,7 +286,7 @@ void STDMETHODCALLTYPE DriveLocomotionClass::Force_New_Slope(int ramp)
/// between cells, even if it is not making any headway at this moment.
///
/// bool; Is the unit under way or owing a move?
-boolean STDMETHODCALLTYPE DriveLocomotionClass::Is_Moving(void)
+bool DriveLocomotionClass::Is_Moving(void)
{
if (DestinationCoord != COORD_NONE) {
return(true);
@@ -377,7 +304,7 @@ boolean STDMETHODCALLTYPE DriveLocomotionClass::Is_Moving(void)
/// has been given a destination but has not gotten rolling yet does not.
///
/// bool; Is the unit moving right now?
-boolean STDMETHODCALLTYPE DriveLocomotionClass::Is_Moving_Now(void)
+bool DriveLocomotionClass::Is_Moving_Now(void)
{
if (LinkedTo->PrimaryFacing.Is_Rotating()) {
return(true);
@@ -394,7 +321,7 @@ boolean STDMETHODCALLTYPE DriveLocomotionClass::Is_Moving_Now(void)
///
/// Returns with the destination coordinate, or COORD_NONE if the unit has
/// nowhere it must be.
-Coord STDMETHODCALLTYPE DriveLocomotionClass::Destination(void)
+Coord DriveLocomotionClass::Destination(void)
{
return(DestinationCoord);
}
@@ -405,7 +332,7 @@ Coord STDMETHODCALLTYPE DriveLocomotionClass::Destination(void)
///
/// Returns with the coordinate being driven toward. A unit that is not under
/// way returns its current position instead.
-Coord STDMETHODCALLTYPE DriveLocomotionClass::Head_To_Coord(void)
+Coord DriveLocomotionClass::Head_To_Coord(void)
{
if (HeadToCoord != COORD_NONE) {
return(HeadToCoord);
@@ -420,7 +347,7 @@ Coord STDMETHODCALLTYPE DriveLocomotionClass::Head_To_Coord(void)
/// raised to the deck, since that is where the vehicle will actually end up driving.
///
/// The location to drive to.
-void STDMETHODCALLTYPE DriveLocomotionClass::Move_To(Coord to)
+void DriveLocomotionClass::Move_To(Coord to)
{
if (LinkedTo->StunDuration <= 0) {
DestinationCoord = to;
@@ -438,7 +365,7 @@ void STDMETHODCALLTYPE DriveLocomotionClass::Move_To(Coord to)
/// The destination is given up and the driver begins slowing down. A train engine passes
/// the order back along the line so that every car it is pulling stops with it.
///
-void STDMETHODCALLTYPE DriveLocomotionClass::Stop_Moving(void)
+void DriveLocomotionClass::Stop_Moving(void)
{
if (HeadToCoord != COORD_NONE) {
if (LinkedTo->TClass->IsTrain) {
@@ -489,7 +416,7 @@ BOOL DriveLocomotionClass::Is_Angled(void) const
///
/// Pointer to the voxel cache key to be updated. May be NULL.
/// Returns with the matrix the unit is to be rendered through.
-Matrix3D STDMETHODCALLTYPE DriveLocomotionClass::Draw_Matrix(int *key)
+Matrix3D DriveLocomotionClass::Draw_Matrix(int *key)
{
Matrix3D m;
@@ -554,7 +481,7 @@ Matrix3D STDMETHODCALLTYPE DriveLocomotionClass::Draw_Matrix(int *key)
/// The driver adopts the slope of the cell the vehicle appears on straight away, so that
/// a unit unlimboed onto a ramp is never seen tilting itself into place.
///
-void STDMETHODCALLTYPE DriveLocomotionClass::Unlimbo(void)
+void DriveLocomotionClass::Unlimbo(void)
{
Force_New_Slope(LinkedTo->Get_Cell_Ptr()->Ramp);
}
@@ -584,7 +511,7 @@ void STDMETHODCALLTYPE DriveLocomotionClass::Unlimbo(void)
* 09/26/1993 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
-boolean STDMETHODCALLTYPE DriveLocomotionClass::Process(void)
+bool DriveLocomotionClass::Process(void)
{
Set_Slope(LinkedTo->Get_Cell_Ptr()->Ramp);
@@ -771,7 +698,7 @@ void DriveLocomotionClass::Mark_Track(Coord const & headto, MarkType type)
* HISTORY: *
* 03/17/1995 JLB : Created. *
*=============================================================================================*/
-void STDMETHODCALLTYPE DriveLocomotionClass::Force_Track(int track, Coord coord)
+void DriveLocomotionClass::Force_Track(int track, Coord coord)
{
assert(LinkedTo->IsActive);
@@ -2122,24 +2049,15 @@ bool DriveLocomotionClass::Incoming(Cell cell)
/// Fetches the display layer the driving unit belongs to.
///
/// Returns with LAYER_GROUND, since a driving unit travels on the ground.
-LayerType STDMETHODCALLTYPE DriveLocomotionClass::In_Which_Layer(void)
+LayerType DriveLocomotionClass::In_Which_Layer(void)
{
return(LAYER_GROUND);
}
-///
-/// Fetches the class identifier of this locomotor.
-/// The persistence system uses this to know which locomotor to create when the unit is
-/// loaded back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE DriveLocomotionClass::GetClassID(CLSID * retval)
+ClassID DriveLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_DriveLocomotion;
- return(S_OK);
+ return(ClassID_DriveLocomotion);
}
@@ -2148,7 +2066,7 @@ HRESULT STDMETHODCALLTYPE DriveLocomotionClass::GetClassID(CLSID * retval)
/// A driving vehicle sits at the depth of the ground it is standing on.
///
/// Returns with the adjustment to apply to the unit's draw depth.
-int STDMETHODCALLTYPE DriveLocomotionClass::Z_Adjust(void)
+int DriveLocomotionClass::Z_Adjust(void)
{
return(0);
}
@@ -2158,7 +2076,7 @@ int STDMETHODCALLTYPE DriveLocomotionClass::Z_Adjust(void)
/// Fetches the depth gradient the unit is to be drawn with.
///
/// Returns with the gradient the base locomotor asks for.
-ZGradientType STDMETHODCALLTYPE DriveLocomotionClass::Z_Gradient(void)
+ZGradientType DriveLocomotionClass::Z_Gradient(void)
{
return(BASECLASS::Z_Gradient());
}
@@ -2188,7 +2106,7 @@ bool DriveLocomotionClass::Abandon_Navigation(void)
/// be told about every cell of the track it is committed to.
///
/// The MarkType to apply to the cells occupied.
-void STDMETHODCALLTYPE DriveLocomotionClass::Mark_All_Occupation_Bits(int mark)
+void DriveLocomotionClass::Mark_All_Occupation_Bits(int mark)
{
if (HeadToCoord != COORD_NONE) {
Mark_Track(HeadToCoord, (MarkType)mark);
@@ -2204,7 +2122,7 @@ void STDMETHODCALLTYPE DriveLocomotionClass::Mark_All_Occupation_Bits(int mark)
///
/// The location to test against.
/// bool; Is the unit moving there?
-boolean STDMETHODCALLTYPE DriveLocomotionClass::Is_Moving_Here(Coord to)
+bool DriveLocomotionClass::Is_Moving_Here(Coord to)
{
Coord coord = Head_To_Coord();
@@ -2250,7 +2168,7 @@ boolean STDMETHODCALLTYPE DriveLocomotionClass::Is_Moving_Here(Coord to)
/// such a hop is due, but performs none of it.
///
/// bool; Will the driver jump tracks?
-boolean STDMETHODCALLTYPE DriveLocomotionClass::Will_Jump_Tracks(void)
+bool DriveLocomotionClass::Will_Jump_Tracks(void)
{
/// This repeats the track jump test that While_Moving performs.
assert(LinkedTo->IsActive);
@@ -2304,7 +2222,7 @@ boolean STDMETHODCALLTYPE DriveLocomotionClass::Will_Jump_Tracks(void)
/// While locked, this driver will not report itself ready to end a piggyback, so a
/// temporary locomotor riding on top of it keeps control of the unit.
///
-void STDMETHODCALLTYPE DriveLocomotionClass::Lock(void)
+void DriveLocomotionClass::Lock(void)
{
IsLocomotorUnlocked = false;
}
@@ -2315,7 +2233,7 @@ void STDMETHODCALLTYPE DriveLocomotionClass::Lock(void)
/// This is the counterpart to Lock. The driver may once again report itself ready to
/// end a piggyback.
///
-void STDMETHODCALLTYPE DriveLocomotionClass::Unlock(void)
+void DriveLocomotionClass::Unlock(void)
{
IsLocomotorUnlocked = true;
}
@@ -2326,7 +2244,7 @@ void STDMETHODCALLTYPE DriveLocomotionClass::Unlock(void)
///
/// Returns with the track control number, or -1 if the unit is not on a
/// track.
-int STDMETHODCALLTYPE DriveLocomotionClass::Get_Track_Number(void)
+int DriveLocomotionClass::Get_Track_Number(void)
{
return(TrackNumber);
}
@@ -2337,7 +2255,7 @@ int STDMETHODCALLTYPE DriveLocomotionClass::Get_Track_Number(void)
///
/// Returns with the index into the track the unit has reached, or -1 if the
/// unit is not following one.
-int STDMETHODCALLTYPE DriveLocomotionClass::Get_Track_Index(void)
+int DriveLocomotionClass::Get_Track_Index(void)
{
return(TrackIndex);
}
@@ -2347,32 +2265,12 @@ int STDMETHODCALLTYPE DriveLocomotionClass::Get_Track_Index(void)
/// Fetches the movement the driver has banked up along its track.
///
/// Returns with the accumulated movement not yet spent advancing the unit.
-int STDMETHODCALLTYPE DriveLocomotionClass::Get_Speed_Accum(void)
+int DriveLocomotionClass::Get_Speed_Accum(void)
{
return(SpeedAccum);
}
-///
-/// Adds a reference to this locomotor.
-///
-/// Returns with the reference count once the new reference is counted.
-ULONG STDMETHODCALLTYPE DriveLocomotionClass::AddRef(void)
-{
- return(BASECLASS::AddRef());
-}
-
-
-///
-/// Releases a reference to this locomotor.
-///
-/// Returns with the reference count remaining after the release.
-ULONG STDMETHODCALLTYPE DriveLocomotionClass::Release(void)
-{
- return(BASECLASS::Release());
-}
-
-
/***************************************************************************
** Smooth turn track tables. These are coordinate offsets from the center
** of the destination cell. These are the raw tracks that are modified
diff --git a/code/drive.h b/code/drive.h
index 5011c55ce..54b7d6d8c 100644
--- a/code/drive.h
+++ b/code/drive.h
@@ -40,6 +40,8 @@
#include "matrix3d.h"
#include "timer.h"
+#include
+
#include "mark.hh"
/****************************************************************************
@@ -58,43 +60,39 @@ class DriveLocomotionClass : public LocomotionClass, public IPiggyback
DriveLocomotionClass(void);
virtual ~DriveLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID * ppvObject) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
- virtual ULONG STDMETHODCALLTYPE Release(void) override;
-
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) override;
- virtual Matrix3D STDMETHODCALLTYPE Draw_Matrix(int *key) override;
- virtual int STDMETHODCALLTYPE Z_Adjust(void) override;
- virtual ZGradientType STDMETHODCALLTYPE Z_Gradient(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) override;
- virtual void STDMETHODCALLTYPE Unlimbo(void) override;
- virtual void STDMETHODCALLTYPE Force_Track(int track, Coord coord) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual void STDMETHODCALLTYPE Force_New_Slope(int ramp) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Here(Coord to) override;
- virtual boolean STDMETHODCALLTYPE Will_Jump_Tracks(void) override;
- virtual void STDMETHODCALLTYPE Lock(void) override;
- virtual void STDMETHODCALLTYPE Unlock(void) override;
- virtual int STDMETHODCALLTYPE Get_Track_Number(void) override;
- virtual int STDMETHODCALLTYPE Get_Track_Index(void) override;
- virtual int STDMETHODCALLTYPE Get_Speed_Accum(void) override;
-
- virtual HRESULT STDMETHODCALLTYPE Begin_Piggyback(ILocomotion * pointer) override;
- virtual HRESULT STDMETHODCALLTYPE End_Piggyback(ILocomotion ** pointer) override;
- virtual boolean STDMETHODCALLTYPE Is_Ok_To_End(void) override;
- virtual HRESULT STDMETHODCALLTYPE Piggyback_CLSID(GUID * classid) override;
- virtual boolean STDMETHODCALLTYPE Is_Piggybacking(void) override {return(Piggybacker != NULL);}
+
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual Coord Head_To_Coord(void) override;
+ virtual Matrix3D Draw_Matrix(int *key) override;
+ virtual int Z_Adjust(void) override;
+ virtual ZGradientType Z_Gradient(void) override;
+ virtual bool Process(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual void Do_Turn(DirType coord) override;
+ virtual void Unlimbo(void) override;
+ virtual void Force_Track(int track, Coord coord) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual void Force_New_Slope(int ramp) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual void Mark_All_Occupation_Bits(int mark) override;
+ virtual bool Is_Moving_Here(Coord to) override;
+ virtual bool Will_Jump_Tracks(void) override;
+ virtual void Lock(void) override;
+ virtual void Unlock(void) override;
+ virtual int Get_Track_Number(void) override;
+ virtual int Get_Track_Index(void) override;
+ virtual int Get_Speed_Accum(void) override;
+
+ virtual bool Begin_Piggyback(std::unique_ptr & carried) override;
+ virtual std::unique_ptr End_Piggyback(void) override;
+ virtual bool Is_Ok_To_End(void) override;
+ virtual bool Is_Piggybacking(void) override {return(Piggybacker != nullptr);}
/*---------------------------------------------------------------------
** Member function prototypes.
@@ -245,7 +243,7 @@ class DriveLocomotionClass : public LocomotionClass, public IPiggyback
* driver answers for it rather than for itself. If NULL, this driver is in sole
* charge of the unit.
*/
- ILocomotionPtr Piggybacker;
+ std::unique_ptr Piggybacker;
/*---------------------------------------------------------------------
** Member function prototypes.
diff --git a/code/droppod.cpp b/code/droppod.cpp
index 0e74e2fc8..d4eab0915 100644
--- a/code/droppod.cpp
+++ b/code/droppod.cpp
@@ -23,6 +23,7 @@
#include "house.h"
#include "map.h"
#include "rules.h"
+#include "saveload.h"
#include "savestream.h"
#include "sun.h"
#include "weapon.h"
@@ -37,8 +38,7 @@
DropPodLocomotionClass::DropPodLocomotionClass(void) :
BASECLASS(),
Direction(DPOD_DIR_NE),
- DestinationCoord(COORD_NONE),
- Piggybacker(NULL)
+ DestinationCoord(COORD_NONE)
{
}
@@ -55,7 +55,7 @@ DropPodLocomotionClass::~DropPodLocomotionClass(void)
/// Is the drop pod in motion?
/// A pod exists only for the duration of its fall, so it always reports movement.
///
-boolean STDMETHODCALLTYPE DropPodLocomotionClass::Is_Moving(void)
+bool DropPodLocomotionClass::Is_Moving(void)
{
return(true);
}
@@ -66,7 +66,7 @@ boolean STDMETHODCALLTYPE DropPodLocomotionClass::Is_Moving(void)
///
/// Returns with the landing coordinate, or COORD_NONE if no destination has
/// been assigned yet.
-Coord STDMETHODCALLTYPE DropPodLocomotionClass::Destination(void)
+Coord DropPodLocomotionClass::Destination(void)
{
return(DestinationCoord);
}
@@ -79,8 +79,13 @@ Coord STDMETHODCALLTYPE DropPodLocomotionClass::Destination(void)
/// passenger is unlimboed, or destroyed along with its surroundings if there is nowhere
/// for it to stand.
///
-boolean STDMETHODCALLTYPE DropPodLocomotionClass::Process(void)
+bool DropPodLocomotionClass::Process(void)
{
+ // Handing the carried locomotor back leaves this pod unowned, so it holds itself for
+ // the rest of the routine. The slot is declared here rather than beside the hand-back
+ // so that the pod outlives every member this routine still reads.
+ std::unique_ptr self;
+
Coord coord = LinkedTo->PositionCoord;
Coord smoke_coord = coord;
@@ -117,8 +122,12 @@ boolean STDMETHODCALLTYPE DropPodLocomotionClass::Process(void)
coord = linked->PositionCoord;
linked->Limbo();
- AddRef();
- End_Piggyback(&LinkedTo->Locomotion);
+ // A pod that carries nothing stays the object's locomotor.
+ std::unique_ptr carried = End_Piggyback();
+ if (carried != nullptr) {
+ self = std::move(LinkedTo->Locomotion);
+ LinkedTo->Locomotion = std::move(carried);
+ }
if (!linked->Unlimbo(coord, DIR_N)) {
Explosion_Damage(coord, 100, LinkedTo, Rule->C4Warhead);
@@ -132,7 +141,6 @@ boolean STDMETHODCALLTYPE DropPodLocomotionClass::Process(void)
linked->Commence();
linked->Scatter(COORD_NONE);
}
- Release();
} else {
LinkedTo->PositionCoord = coord;
WeaponTypeClass const * weapon = Rule->DropPodWeapon;
@@ -163,7 +171,7 @@ boolean STDMETHODCALLTYPE DropPodLocomotionClass::Process(void)
/// has a destination ignores any later request.
///
/// The coordinate the pod should land on.
-void STDMETHODCALLTYPE DropPodLocomotionClass::Move_To(Coord to)
+void DropPodLocomotionClass::Move_To(Coord to)
{
if (DestinationCoord == COORD_NONE) {
@@ -213,22 +221,16 @@ void STDMETHODCALLTYPE DropPodLocomotionClass::Move_To(Coord to)
}
-///
-/// Fetches the class ID that this locomotor is persisted under.
-///
-/// Returns with S_OK, or E_POINTER if no return pointer was supplied.
-HRESULT STDMETHODCALLTYPE DropPodLocomotionClass::GetClassID(CLSID * retval)
+ClassID DropPodLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_BallisticLocomotion;
- return(S_OK);
+ return(ClassID_BallisticLocomotion);
}
///
/// Lists the members this drop pod locomotor carries.
/// The locomotor set aside while the pod descends is a separate persistent object rather
-/// than a member, so it still travels framed by OLE and is recreated as the class it was
+/// than a member, so it travels as a record of its own and is recreated as the class it was
/// saved as.
///
/// The stream carrying the members.
@@ -244,10 +246,9 @@ void DropPodLocomotionClass::Serialize(SaveStreamClass & stream)
if (haspiggy) {
if (stream.Is_Saving()) {
- IPersistStreamPtr persist(Piggybacker);
- OleSaveToStream(persist, stream.Get_Stream());
+ Save_Object(stream, Piggybacker.get());
} else {
- OleLoadFromStream(stream.Get_Stream(), IID_ILocomotion, (LPVOID *)&Piggybacker);
+ Piggybacker = Load_Locomotor(stream);
}
}
}
@@ -257,7 +258,7 @@ void DropPodLocomotionClass::Serialize(SaveStreamClass & stream)
/// Stops the pod's descent.
/// A pod cannot be halted in mid air, so this request is quietly ignored.
///
-void STDMETHODCALLTYPE DropPodLocomotionClass::Stop_Moving(void)
+void DropPodLocomotionClass::Stop_Moving(void)
{
// empty
}
@@ -268,18 +269,15 @@ void STDMETHODCALLTYPE DropPodLocomotionClass::Stop_Moving(void)
/// The drop pod holds on to the locomotor it displaces so that the object can be given
/// it back when the pod touches down.
///
-/// The locomotor to carry.
-/// Returns with S_OK, or E_FAIL if something is already being carried.
-HRESULT STDMETHODCALLTYPE DropPodLocomotionClass::Begin_Piggyback(ILocomotion * pointer)
+/// The locomotor that is to take over the unit.
+/// bool; Was the locomotor taken on? One already carrying a locomotor refuses.
+bool DropPodLocomotionClass::Begin_Piggyback(std::unique_ptr & carried)
{
- if (pointer == NULL) {
- return(E_POINTER);
+ if (carried == nullptr || Piggybacker != nullptr) {
+ return(false);
}
- if (Piggybacker == NULL) {
- Piggybacker = pointer;
- return(S_OK);
- }
- return(E_FAIL);
+ Piggybacker = std::move(carried);
+ return(true);
}
@@ -288,19 +286,10 @@ HRESULT STDMETHODCALLTYPE DropPodLocomotionClass::Begin_Piggyback(ILocomotion *
/// The pod gives up its hold without destroying the locomotor, so the object can resume
/// using it once the pod has landed.
///
-/// Pointer to the location that receives the carried locomotor.
-/// Returns with S_OK, or S_FALSE if nothing was being carried.
-HRESULT STDMETHODCALLTYPE DropPodLocomotionClass::End_Piggyback(ILocomotion ** pointer)
+/// Returns with the locomotor that was riding, or nothing when none was.
+std::unique_ptr DropPodLocomotionClass::End_Piggyback(void)
{
- if (pointer == NULL) {
- return(E_POINTER);
- }
- if (Piggybacker != NULL) {
- *pointer = Piggybacker;
- Piggybacker.Detach();
- return(S_OK);
- }
- return(S_FALSE);
+ return(std::move(Piggybacker));
}
@@ -309,7 +298,7 @@ HRESULT STDMETHODCALLTYPE DropPodLocomotionClass::End_Piggyback(ILocomotion ** p
/// The carried locomotor may only be given control back once the pod has come to rest.
///
/// bool; May the carried locomotor take over again?
-boolean STDMETHODCALLTYPE DropPodLocomotionClass::Is_Ok_To_End(void)
+bool DropPodLocomotionClass::Is_Ok_To_End(void)
{
if (!Is_Moving() && Piggybacker != NULL) {
return(true);
@@ -318,77 +307,22 @@ boolean STDMETHODCALLTYPE DropPodLocomotionClass::Is_Ok_To_End(void)
}
-///
-/// Fetches an interface pointer from the drop pod locomotor.
-/// This routine extends the base locomotor's interface set with IPiggyback, which is how
-/// the pod carries the object's real locomotor while it falls.
-///
-/// Returns with S_OK, or E_NOINTERFACE if this object does not offer the
-/// interface asked for.
-HRESULT STDMETHODCALLTYPE DropPodLocomotionClass::QueryInterface(REFIID riid, LPVOID * ppvObject)
-{
- HRESULT result = BASECLASS::QueryInterface(riid, ppvObject);
-
- if (result == E_NOINTERFACE) {
- if (riid == IID_IPiggyback) {
- *ppvObject = (IPiggyback*)this;
- }
- if (*ppvObject == NULL) {
- result = E_NOINTERFACE;
- } else {
- AddRef();
- result = S_OK;
- }
- }
- return(result);
-}
-
-
///
/// Determines which display layer the pod belongs in.
/// A pod is always falling, so it draws along with the other airborne objects right up
/// until it lands and gives its object back.
///
-LayerType STDMETHODCALLTYPE DropPodLocomotionClass::In_Which_Layer(void)
+LayerType DropPodLocomotionClass::In_Which_Layer(void)
{
return(LAYER_AIR);
}
-///
-/// Fetches the class ID of the locomotor being carried.
-/// The save system uses this to record which locomotor is to be restored underneath the
-/// drop pod. When nothing is being carried, the pod supplies its own class ID instead.
-///
-/// Returns with S_OK, or an error code if the class ID could not be
-/// determined.
-HRESULT STDMETHODCALLTYPE DropPodLocomotionClass::Piggyback_CLSID(GUID * classid)
-{
- if (classid == NULL) {
- return(E_POINTER);
- }
-
- if (Piggybacker != NULL) {
- IPersistPtr ptr(Piggybacker);
- if (ptr == NULL) {
- return(E_FAIL);
- }
- return(ptr->GetClassID(classid));
- }
-
- IPersistPtr ptr(this);
- if (ptr == NULL) {
- return(E_FAIL);
- }
- return(ptr->GetClassID(classid));
-}
-
-
///
/// Fetches the drawing code for the drop pod.
/// The renderer uses this to choose the artwork that suits the pod's approach.
///
-int STDMETHODCALLTYPE DropPodLocomotionClass::Drawing_Code(void)
+int DropPodLocomotionClass::Drawing_Code(void)
{
return((unsigned)Direction % 2);
}
diff --git a/code/droppod.h b/code/droppod.h
index ecd9da0a7..de6c0b7cd 100644
--- a/code/droppod.h
+++ b/code/droppod.h
@@ -16,6 +16,8 @@
#include "ipiggy.h"
#include "loco.h"
+#include
+
class DropPodLocomotionClass : public LocomotionClass, public IPiggyback
{
@@ -29,27 +31,23 @@ class DropPodLocomotionClass : public LocomotionClass, public IPiggyback
DropPodLocomotionClass(void);
virtual ~DropPodLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID * ppvObject) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override {return(BASECLASS::AddRef());}
- virtual ULONG STDMETHODCALLTYPE Release(void) override {return(BASECLASS::Release());}
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual int STDMETHODCALLTYPE Drawing_Code(void) override;
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual bool Process(void) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual int Drawing_Code(void) override;
- virtual HRESULT STDMETHODCALLTYPE Begin_Piggyback(ILocomotion * pointer) override;
- virtual HRESULT STDMETHODCALLTYPE End_Piggyback(ILocomotion ** pointer) override;
- virtual boolean STDMETHODCALLTYPE Is_Ok_To_End(void) override;
- virtual HRESULT STDMETHODCALLTYPE Piggyback_CLSID(GUID * classid) override;
- virtual boolean STDMETHODCALLTYPE Is_Piggybacking(void) override {return(Piggybacker != NULL);}
+ virtual bool Begin_Piggyback(std::unique_ptr & carried) override;
+ virtual std::unique_ptr End_Piggyback(void) override;
+ virtual bool Is_Ok_To_End(void) override;
+ virtual bool Is_Piggybacking(void) override {return(Piggybacker != nullptr);}
private:
enum DropPodDirType {
@@ -78,5 +76,5 @@ class DropPodLocomotionClass : public LocomotionClass, public IPiggyback
* handed back the moment the pod touches ground, so that the object resumes moving
* the way its type normally does.
*/
- ILocomotionPtr Piggybacker;
+ std::unique_ptr Piggybacker;
};
diff --git a/code/empulse.cpp b/code/empulse.cpp
index 501892e45..964bd2293 100644
--- a/code/empulse.cpp
+++ b/code/empulse.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "empulse.h"
@@ -283,18 +282,9 @@ void EMPulseClass::Compute_CRC(CRCEngine &crc) const
}
-///
-/// Fetches the class identifier used to persist this object.
-/// The save system writes this identifier ahead of the object data so that the loader
-/// knows what kind of object to reconstruct.
-///
-/// Pointer to the buffer that will receive the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE EMPulseClass::GetClassID(CLSID * retval)
+ClassID EMPulseClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_EMPulseClass;
- return(S_OK);
+ return(ClassID_EMPulseClass);
}
diff --git a/code/empulse.h b/code/empulse.h
index c059e0d4a..d04dc713f 100644
--- a/code/empulse.h
+++ b/code/empulse.h
@@ -30,7 +30,7 @@ class EMPulseClass : public AbstractClass
virtual RTTIType Fetch_RTTI(void) const override {return(RTTI_EMPULSE);}
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/enviro.cpp b/code/enviro.cpp
index 2f6cfe9aa..ab82c1097 100644
--- a/code/enviro.cpp
+++ b/code/enviro.cpp
@@ -110,12 +110,11 @@ void EnvironmentClass::Restore(void)
/// restored, before the scenario itself is brought back.
///
/// Returns with the result reported by the stream read.
-HRESULT EnvironmentClass::Load(IStream * stream)
+bool EnvironmentClass::Load(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("EnvironmentClass");
- Serialize(savestream);
- return(savestream.Result());
+ stream.Set_Context("EnvironmentClass");
+ Serialize(stream);
+ return(!stream.Was_Error());
}
@@ -123,11 +122,10 @@ HRESULT EnvironmentClass::Load(IStream * stream)
/// Writes the carry over environment out to a save game.
///
/// Returns with the result reported by the stream write.
-HRESULT EnvironmentClass::Save(IStream * stream)
+bool EnvironmentClass::Save(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
- return(savestream.Result());
+ Serialize(stream);
+ return(!stream.Was_Error());
}
diff --git a/code/enviro.h b/code/enviro.h
index 01cf06eec..89b46fa4c 100644
--- a/code/enviro.h
+++ b/code/enviro.h
@@ -13,7 +13,6 @@
#include "diff.hh"
-#include
class SaveStreamClass;
@@ -26,8 +25,8 @@ class EnvironmentClass
void Store(void);
void Restore(void);
- HRESULT Load(IStream * stream);
- HRESULT Save(IStream * stream);
+ bool Load(SaveStreamClass & stream);
+ bool Save(SaveStreamClass & stream);
void Serialize(SaveStreamClass & stream);
diff --git a/code/factory.cpp b/code/factory.cpp
index 2ce156040..efe17eec9 100644
--- a/code/factory.cpp
+++ b/code/factory.cpp
@@ -47,7 +47,6 @@
* FactoryClass::~FactoryClass -- Default destructor for factory objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "factory.h"
@@ -632,17 +631,9 @@ bool FactoryClass::Completed(void)
}
-///
-/// Fetches the class identifier for a factory.
-/// This routine is part of the persistence interface. The save game loader uses the
-/// identifier to know what kind of object to create before handing it the stream.
-///
-/// Returns with S_OK, or E_POINTER if no return location was supplied.
-HRESULT STDMETHODCALLTYPE FactoryClass::GetClassID(CLSID * retval)
+ClassID FactoryClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_FactoryClass;
- return(S_OK);
+ return(ClassID_FactoryClass);
}
diff --git a/code/factory.h b/code/factory.h
index 7886611ef..473c80021 100644
--- a/code/factory.h
+++ b/code/factory.h
@@ -54,7 +54,7 @@ class FactoryClass : public AbstractClass, private StageClass
FactoryClass(void);
~FactoryClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/fly.cpp b/code/fly.cpp
index 0b64fe905..2cf7d741f 100644
--- a/code/fly.cpp
+++ b/code/fly.cpp
@@ -117,7 +117,7 @@ FlyLocomotionClass::~FlyLocomotionClass(void)
/// an aircraft that has been told to go somewhere but has yet to build up any speed.
///
/// bool; Is the aircraft moving or trying to?
-boolean STDMETHODCALLTYPE FlyLocomotionClass::Is_Moving(void)
+bool FlyLocomotionClass::Is_Moving(void)
{
return(IsMoving || LinkedTo->PitchAngle > 0);
}
@@ -129,7 +129,7 @@ boolean STDMETHODCALLTYPE FlyLocomotionClass::Is_Moving(void)
/// the aircraft has any speed at all.
///
/// bool; Is the aircraft moving right now?
-boolean STDMETHODCALLTYPE FlyLocomotionClass::Is_Moving_Now(void)
+bool FlyLocomotionClass::Is_Moving_Now(void)
{
if (CurrentSpeed == 0) {
return(false);
@@ -143,7 +143,7 @@ boolean STDMETHODCALLTYPE FlyLocomotionClass::Is_Moving_Now(void)
///
/// Returns with the destination coordinate. If the aircraft is not going
/// anywhere, COORD_NONE is returned.
-Coord STDMETHODCALLTYPE FlyLocomotionClass::Destination(void)
+Coord FlyLocomotionClass::Destination(void)
{
if (Is_Moving()) {
return(DestinationCoord);
@@ -159,7 +159,7 @@ Coord STDMETHODCALLTYPE FlyLocomotionClass::Destination(void)
/// disposed of if it has wandered off the edge of the world.
///
/// bool; Is the aircraft still under way?
-boolean STDMETHODCALLTYPE FlyLocomotionClass::Process(void)
+bool FlyLocomotionClass::Process(void)
{
if (!IsLanding && !IsTakingOff && TargetSpeed >= 1.0 && FlightLevel == 0) {
FlightLevel = LinkedTo->TClass->Flight_Level();
@@ -234,7 +234,7 @@ boolean STDMETHODCALLTYPE FlyLocomotionClass::Process(void)
/// destination is a request to stop, which brings a flying aircraft down to land.
///
/// The coordinate to head for, or COORD_NONE to stop and land.
-void STDMETHODCALLTYPE FlyLocomotionClass::Move_To(Coord to)
+void FlyLocomotionClass::Move_To(Coord to)
{
if (((Coord)to).As_Cell() != DestinationCoord.As_Cell() || !IsLanding) {
@@ -242,7 +242,7 @@ void STDMETHODCALLTYPE FlyLocomotionClass::Move_To(Coord to)
if ((Coord)to == COORD_NONE) {
int landing_altitude = 0;
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
if (flyctrl != NULL) {
landing_altitude = flyctrl->Landing_Altitude();
}
@@ -261,7 +261,7 @@ void STDMETHODCALLTYPE FlyLocomotionClass::Move_To(Coord to)
DestinationCoord.Z = LinkedTo->TClass->Flight_Level() + Map.Get_Height_GL(to);
}
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
int landing_altitude = 0;
if (flyctrl != NULL) {
landing_altitude = flyctrl->Landing_Altitude();
@@ -286,7 +286,7 @@ void STDMETHODCALLTYPE FlyLocomotionClass::Move_To(Coord to)
/// assigns that as the new destination. An aircraft with nowhere at all to go is destroyed
/// rather than left loitering in an illegal spot.
///
-void STDMETHODCALLTYPE FlyLocomotionClass::Stop_Moving(void)
+void FlyLocomotionClass::Stop_Moving(void)
{
if (Is_Moving()) {
@@ -608,7 +608,7 @@ void FlyLocomotionClass::Movement_AI(void)
if (current_height < FlightLevel && LinkedTo->Strength > 0) {
bool is_loaded = false;
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
if (flyctrl != NULL) {
is_loaded = flyctrl->Is_Loaded() != 0;
}
@@ -698,7 +698,7 @@ void FlyLocomotionClass::Movement_AI(void)
}
if (LinkedTo->Strength > 0 && Is_In_Flight() && DestinationCoord != COORD_NONE) {
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
if (!Needs_To_Land()) {
TargetSpeed = 1.0;
@@ -881,7 +881,7 @@ bool FlyLocomotionClass::Process_Take_Off(void)
height -= BRIDGE_LEPTON_HEIGHT;
}
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
int landing_altitude = 0;
if (flyctrl) {
landing_altitude = flyctrl->Landing_Altitude();
@@ -962,7 +962,7 @@ bool FlyLocomotionClass::Process_Landing(void)
TargetSpeed = 0;
int landing_altitude = 0;
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
if (flyctrl) {
landing_altitude = flyctrl->Landing_Altitude();
}
@@ -1072,7 +1072,7 @@ bool FlyLocomotionClass::Process_Landing(void)
/// Returns with the distance remaining to the destination.
int FlyLocomotionClass::Nearing_Target(bool stage_approach, Coord coord)
{
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
/*
* A strafing aircraft that is over an ammo-bearing attack run should ignore the
@@ -1303,7 +1303,7 @@ int FlyLocomotionClass::Nearing_Target(bool stage_approach, Coord coord)
/// Optional cache key for the resulting orientation. It may be NULL, and
/// is set to -1 for an attitude that is not worth caching.
/// Returns with the matrix to draw the aircraft with.
-Matrix3D STDMETHODCALLTYPE FlyLocomotionClass::Draw_Matrix(int * key)
+Matrix3D FlyLocomotionClass::Draw_Matrix(int * key)
{
Matrix3D mtx;
mtx.Make_Identity();
@@ -1398,10 +1398,10 @@ Matrix3D STDMETHODCALLTYPE FlyLocomotionClass::Draw_Matrix(int * key)
/// look pinned in place while it hovers. Dropships and grounded aircraft do not bob.
///
/// Returns with the pixel offset to shift the aircraft by.
-Point2D STDMETHODCALLTYPE FlyLocomotionClass::Draw_Point(void)
+Point2D FlyLocomotionClass::Draw_Point(void)
{
int y = 0;
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
int landing_altitude = 0;
if (flyctrl) {
@@ -1421,7 +1421,7 @@ Point2D STDMETHODCALLTYPE FlyLocomotionClass::Draw_Point(void)
/// The shadow is drawn where the aircraft's position puts it, so no adjustment is needed.
///
/// Returns with the pixel offset to shift the shadow by.
-Point2D STDMETHODCALLTYPE FlyLocomotionClass::Shadow_Point(void)
+Point2D FlyLocomotionClass::Shadow_Point(void)
{
return(Point2D(0, 0));
}
@@ -1470,7 +1470,7 @@ void FlyLocomotionClass::Land(void)
/// Optional cache key for the shadow orientation. It may be NULL, and a
/// value of -1 marks the shadow as not worth caching.
/// Returns with the matrix to draw the shadow with.
-Matrix3D STDMETHODCALLTYPE FlyLocomotionClass::Shadow_Matrix(int * key)
+Matrix3D FlyLocomotionClass::Shadow_Matrix(int * key)
{
int ramp = Map[(Coord const &)LinkedTo->PositionCoord].Ramp;
if (LinkedTo->TClass->IsDropship) {
@@ -1493,7 +1493,7 @@ Matrix3D STDMETHODCALLTYPE FlyLocomotionClass::Shadow_Matrix(int * key)
/// This routine snaps the body around immediately rather than rotating it over time.
///
/// The facing to set the aircraft body to.
-void STDMETHODCALLTYPE FlyLocomotionClass::Do_Turn(DirType coord)
+void FlyLocomotionClass::Do_Turn(DirType coord)
{
LinkedTo->SecondaryFacing.Set(coord);
}
@@ -1515,18 +1515,9 @@ bool FlyLocomotionClass::Is_In_Flight(void)
}
-///
-/// Fetches the class identifier of this locomotor.
-/// This routine is used by the save and load machinery so that it knows which locomotor to
-/// create when the owning object is restored.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE FlyLocomotionClass::GetClassID(CLSID * retval)
+ClassID FlyLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_FlyerLocomotion;
- return(S_OK);
+ return(ClassID_FlyerLocomotion);
}
@@ -1559,7 +1550,7 @@ void FlyLocomotionClass::Serialize(SaveStreamClass & stream)
///
/// Returns with LAYER_GROUND while the aircraft is on the deck, or LAYER_TOP once
/// it is above it.
-LayerType STDMETHODCALLTYPE FlyLocomotionClass::In_Which_Layer(void)
+LayerType FlyLocomotionClass::In_Which_Layer(void)
{
return(LinkedTo->HeightAGL <= 0 ? LAYER_GROUND : LAYER_TOP);
}
@@ -1572,7 +1563,7 @@ LayerType STDMETHODCALLTYPE FlyLocomotionClass::In_Which_Layer(void)
/// stop, so that it falls out of the sky rather than coasting on to its objective.
///
/// bool; Was the power successfully cut?
-boolean STDMETHODCALLTYPE FlyLocomotionClass::Power_Off(void)
+bool FlyLocomotionClass::Power_Off(void)
{
if (Is_Moving()) {
Tumble();
@@ -1587,7 +1578,7 @@ boolean STDMETHODCALLTYPE FlyLocomotionClass::Power_Off(void)
/// Is the aircraft still under power?
///
/// bool; Does the aircraft still have power?
-boolean STDMETHODCALLTYPE FlyLocomotionClass::Is_Powered(void)
+bool FlyLocomotionClass::Is_Powered(void)
{
return(BASECLASS::Is_Powered());
}
@@ -1599,7 +1590,7 @@ boolean STDMETHODCALLTYPE FlyLocomotionClass::Is_Powered(void)
/// by one.
///
/// bool; Does an ion storm affect this aircraft?
-boolean STDMETHODCALLTYPE FlyLocomotionClass::Is_Ion_Sensitive(void)
+bool FlyLocomotionClass::Is_Ion_Sensitive(void)
{
return(!LinkedTo->TClass->IsHunterSeeker);
}
@@ -1625,7 +1616,7 @@ void FlyLocomotionClass::Tumble(void)
/// Fetches the speed the aircraft is currently traveling at.
///
/// Returns with the distance the aircraft will cover in one game frame.
-int STDMETHODCALLTYPE FlyLocomotionClass::Apparent_Speed(void)
+int FlyLocomotionClass::Apparent_Speed(void)
{
return(LinkedTo->TClass->MaxSpeed * CurrentSpeed);
}
@@ -1638,7 +1629,7 @@ int STDMETHODCALLTYPE FlyLocomotionClass::Apparent_Speed(void)
///
/// Returns with the status code for taking off, landing, moving, or sitting
/// idle.
-int STDMETHODCALLTYPE FlyLocomotionClass::Get_Status(void)
+int FlyLocomotionClass::Get_Status(void)
{
if (IsLanding) {
return(1);
@@ -1660,7 +1651,7 @@ int STDMETHODCALLTYPE FlyLocomotionClass::Get_Status(void)
/// targets in a multiplay game so that the drone makes a nuisance of itself where it will
/// be noticed.
///
-void STDMETHODCALLTYPE FlyLocomotionClass::Acquire_Hunter_Seeker_Target(void)
+void FlyLocomotionClass::Acquire_Hunter_Seeker_Target(void)
{
if (LinkedTo->TarCom == NULL) {
@@ -1722,7 +1713,7 @@ bool FlyLocomotionClass::Needs_To_Land(void)
return(true);
}
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
if (flyctrl != NULL && !flyctrl->Is_Strafe()) {
return(true);
}
@@ -1751,7 +1742,7 @@ bool FlyLocomotionClass::Is_Locked_To_Straight_Flight(void)
return(true);
}
- IFlyControlPtr flyctrl(LinkedTo);
+ IFlyControl * const flyctrl = dynamic_cast(LinkedTo);
if (flyctrl) {
if (flyctrl->Is_Locked()) {
return(true);
diff --git a/code/fly.h b/code/fly.h
index 96ffe8cc3..3981beb9d 100644
--- a/code/fly.h
+++ b/code/fly.h
@@ -58,28 +58,28 @@ class FlyLocomotionClass : public LocomotionClass
FlyLocomotionClass(void);
virtual ~FlyLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual Matrix3D STDMETHODCALLTYPE Draw_Matrix(int *key) override;
- virtual Point2D STDMETHODCALLTYPE Draw_Point(void) override;
- virtual Point2D STDMETHODCALLTYPE Shadow_Point(void) override;
- virtual Matrix3D STDMETHODCALLTYPE Shadow_Matrix(int *key) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual boolean STDMETHODCALLTYPE Power_Off(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Powered(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Ion_Sensitive(void) override;
- virtual int STDMETHODCALLTYPE Apparent_Speed(void) override;
- virtual int STDMETHODCALLTYPE Get_Status(void) override;
- virtual void STDMETHODCALLTYPE Acquire_Hunter_Seeker_Target(void) override;
+ virtual bool Is_Moving(void) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual Coord Destination(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual bool Process(void) override;
+ virtual Matrix3D Draw_Matrix(int *key) override;
+ virtual Point2D Draw_Point(void) override;
+ virtual Point2D Shadow_Point(void) override;
+ virtual Matrix3D Shadow_Matrix(int *key) override;
+ virtual void Do_Turn(DirType coord) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual bool Power_Off(void) override;
+ virtual bool Is_Powered(void) override;
+ virtual bool Is_Ion_Sensitive(void) override;
+ virtual int Apparent_Speed(void) override;
+ virtual int Get_Status(void) override;
+ virtual void Acquire_Hunter_Seeker_Target(void) override;
/*---------------------------------------------------------------------
** Member function prototypes.
diff --git a/code/fog.cpp b/code/fog.cpp
index 76acc1e3c..b1dbdb91e 100644
--- a/code/fog.cpp
+++ b/code/fog.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "fog.h"
@@ -593,18 +592,9 @@ RTTIType FoggedObjectClass::Fetch_RTTI(void) const
}
-///
-/// Fetches the class ID of this object.
-/// This routine is part of the persistence interface the save game system uses to
-/// recreate objects of the right kind when a game is loaded.
-///
-/// Pointer to the class ID to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE FoggedObjectClass::GetClassID(CLSID * retval)
+ClassID FoggedObjectClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_FoggedObjectClass;
- return(S_OK);
+ return(ClassID_FoggedObjectClass);
}
diff --git a/code/fog.h b/code/fog.h
index 873145049..938207fd1 100644
--- a/code/fog.h
+++ b/code/fog.h
@@ -40,7 +40,7 @@ class FoggedObjectClass : public AbstractClass
FoggedObjectClass(TerrainClass * object);
virtual ~FoggedObjectClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/foot.cpp b/code/foot.cpp
index 0d4349512..412526c3b 100644
--- a/code/foot.cpp
+++ b/code/foot.cpp
@@ -115,6 +115,7 @@
#include "partsys.h"
#include "revent.h"
#include "rules.h"
+#include "saveload.h"
#include "savestream.h"
#include "session.h"
#include "swizzle.h"
@@ -604,7 +605,6 @@ void FootClass::Advance_Path(int count)
}
-
/***********************************************************************************************
* FootClass::Mission_Move -- AI process for moving a vehicle to its destination. *
* *
@@ -1132,10 +1132,8 @@ void FootClass::Approach_Target(void)
*/
bool flyer = (RTTI == RTTI_AIRCRAFT);
- CLSID clsid;
- IPersistPtr persist(Locomotion);
- persist->GetClassID(&clsid);
- if (clsid == CLSID_JumpjetLocomotion) {
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
+ if (clsid == ClassID_JumpjetLocomotion) {
flyer = true;
}
@@ -1875,9 +1873,9 @@ bool FootClass::Enter_Idle_Mode(bool, bool resume_waypoint)
}
bool was_piggybacking = false;
- IPiggybackPtr piggy(Locomotion);
+ IPiggyback * piggy = Piggyback_Of(Locomotion.get());
if (piggy != NULL && piggy->Is_Ok_To_End()) {
- piggy->End_Piggyback(&Locomotion);
+ Locomotion = piggy->End_Piggyback();
was_piggybacking = true;
}
@@ -2333,9 +2331,9 @@ int FootClass::Do_MISSION_ENTER(void)
Enter_Idle_Mode();
} else {
if (NavCom == NULL && RouteQueue.Count() > 0 ) {
- IPiggybackPtr piggy(Locomotion);
+ IPiggyback * piggy = Piggyback_Of(Locomotion.get());
if (piggy != NULL && piggy->Is_Ok_To_End()) {
- piggy->End_Piggyback(&Locomotion);
+ Locomotion = piggy->End_Piggyback();
}
if (RouteQueue.Count() > 0) {
Assign_Destination(RouteQueue[0], false);
@@ -2387,11 +2385,9 @@ void FootClass::Assign_Destination(AbstractClass * target, bool)
ParticleSystems[ATTACHED_PARTICLE_FIRE] = NULL;
}
- CLSID locoid;
- IPersistPtr persist(Locomotion);
- persist->GetClassID(&locoid);
+ ClassID const locoid = Locomotion_Class_ID(Locomotion.get());
- if (locoid == CLSID_HoverLocomotion && PathDelay == 0) {
+ if (locoid == ClassID_HoverLocomotion && PathDelay == 0) {
PathDelay = 1;
}
@@ -3317,10 +3313,10 @@ void FootClass::AI(void)
Scatter(Coord(0,0,0), true);
}
- IPiggybackPtr piggy(Locomotion);
+ IPiggyback * piggy = Piggyback_Of(Locomotion.get());
if (piggy != NULL) {
if (piggy->Is_Ok_To_End()) {
- piggy->End_Piggyback(&Locomotion);
+ Locomotion = piggy->End_Piggyback();
}
}
@@ -3412,7 +3408,7 @@ ZGradientType FootClass::Get_Z_Gradient(void) const
///
void FootClass::Draw_Voxel_Shadow(VoxelDataStruct const & voxeldata, int layer_index, int key, VoxelIndexClass * cache, Rect const & cliprect, Point2D const & point, Matrix3D const & matrix, bool force_cache) const
{
- if (Locomotion != NULL && Locomotion->Is_To_Have_Shadow() == (boolean)true) {
+ if (Locomotion != nullptr && Locomotion->Is_To_Have_Shadow() == (bool)true) {
Point2D drawpoint = point;
if (Locomotion != NULL) {
drawpoint = Point2D(Locomotion->Shadow_Point()) + point;
@@ -3519,19 +3515,13 @@ void FootClass::Serialize(SaveStreamClass & stream)
stream.Serialize(BlockagePathDelay);
/*
- * The locomotor is a COM sub-object rather than a member, so it persists itself onto
- * the raw stream through OLE. The one being replaced is released first, since loading
- * hands back a fresh interface pointer rather than filling this one in.
+ * The locomotor is a sub-object rather than a member, so it travels as a record of
+ * its own.
*/
if (stream.Is_Saving()) {
- IPersistStreamPtr persist(Locomotion);
- OleSaveToStream(persist, stream.Get_Stream());
+ Save_Object(stream, Locomotion.get());
} else {
- if (Locomotion != NULL) {
- ((ILocomotion *)Locomotion)->Release();
- }
- Locomotion.Detach();
- OleLoadFromStream(stream.Get_Stream(), IID_ILocomotion, (LPVOID *)&Locomotion);
+ Locomotion = Load_Locomotor(stream);
}
stream.Serialize(HeadToCoord);
@@ -3595,12 +3585,12 @@ void FootClass::Set_Coord(Coord const & coord)
///
void FootClass::Link_DropPod(void)
{
- ILocomotionPtr locomotion = Locomotion;
- ILocomotionPtr ballistic(CLSID_BallisticLocomotion);
+ std::unique_ptr locomotion = std::move(Locomotion);
+ std::unique_ptr ballistic = Create_Locomotor(ClassID_BallisticLocomotion);
ballistic->Link_To_Object(this);
- IPiggybackPtr piggy(ballistic);
+ IPiggyback * piggy = Piggyback_Of(ballistic.get());
piggy->Begin_Piggyback(locomotion);
- Locomotion = ballistic;
+ Locomotion = std::move(ballistic);
}
@@ -4766,12 +4756,9 @@ void FootClass::Delete_Me(void)
/// bool; Is the object in the air?
bool FootClass::In_Air(void) const
{
- IPersistPtr loco(Locomotion);
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
- CLSID clsid;
- loco->GetClassID(&clsid);
-
- if (clsid == CLSID_HoverLocomotion) {
+ if (clsid == ClassID_HoverLocomotion) {
return(false);
}
@@ -4790,10 +4777,7 @@ bool FootClass::On_Ground(void) const
if (BASECLASS::On_Ground()) {
return(true);
}
- IPersistPtr loco(Locomotion);
-
- CLSID clsid;
- loco->GetClassID(&clsid);
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
- return(IsDown && clsid == CLSID_HoverLocomotion);
+ return(IsDown && clsid == ClassID_HoverLocomotion);
}
diff --git a/code/foot.h b/code/foot.h
index 246b96e08..75638b977 100644
--- a/code/foot.h
+++ b/code/foot.h
@@ -38,6 +38,7 @@
#include "techno.h"
#include
+#include
class UnitClass;
class BuildingClass;
@@ -210,7 +211,7 @@ class FootClass : public TechnoClass
* handed to a ballistic locomotor and a unit crossing a tunnel walks -- so all
* movement is asked of this interface rather than of the type's setting.
*/
- ILocomotionPtr Locomotion;
+ std::unique_ptr Locomotion;
/*
** This is the coordinate that the unit is heading to
diff --git a/code/globals.cpp b/code/globals.cpp
index 5b6a9a89a..1c61a8f0e 100644
--- a/code/globals.cpp
+++ b/code/globals.cpp
@@ -29,23 +29,10 @@
*---------------------------------------------------------------------------------------------*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
-/// create all com interfaces here
-#include "iblowfish.h"
-#include "iblowfish_i.c"
#include "sun.h"
-#include "isun_i.c"
-#include "ilocos.h"
-#include "ilocos_i.c"
-#include "ipiggy.h"
-#include "ipiggy_i.c"
-#include "iblockci.h"
-#include "iblockci_i.c"
-#include "iflyctrl.h"
-#include "iflyctrl_i.c"
-#undef INCLUDE_COM
+#include "classids.h"
#include "_voxel.h"
#include "globals.h"
diff --git a/code/house.cpp b/code/house.cpp
index a288589d9..a1bf5b687 100644
--- a/code/house.cpp
+++ b/code/house.cpp
@@ -128,7 +128,6 @@
* HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "house.h"
@@ -690,8 +689,15 @@ HouseClass::~HouseClass (void)
}
SuperWeapon.Clear();
+ // A tag removes itself from this list as it dies; a slot a failed load left empty is
+ // removed here, or the list would never drain.
while (HouseTags.Count() > 0) {
- delete HouseTags[0];
+ TagClass * const tag = HouseTags[0];
+ if (tag == nullptr) {
+ HouseTags.Delete_Index(0);
+ } else {
+ delete tag;
+ }
}
AbstractTypePtrTracker.Delete(this);
@@ -6432,8 +6438,8 @@ void HouseClass::Compute_CRC(CRCEngine & crc) const
/// record, so they are disposed of before the saved members are read over the top of them.
///
/// The stream to read the house from.
-/// Returns with S_OK, or the failure code reported by the stream.
-HRESULT STDMETHODCALLTYPE HouseClass::Load(IStream *stream)
+/// bool; Was the record read whole?
+bool HouseClass::Load(SaveStreamClass & stream)
{
while (SuperWeapon.Count()) {
delete SuperWeapon[0];
@@ -6623,18 +6629,9 @@ void HouseClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence contract. The load system uses the identifier to
-/// discover which class to build when the object is read back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE HouseClass::GetClassID(CLSID * retval)
+ClassID HouseClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_HouseClass;
- return(S_OK);
+ return(ClassID_HouseClass);
}
@@ -9206,30 +9203,6 @@ void HouseClass::AI_Drop_Pods(SuperClass * super)
}
-///
-/// Adds a reference to this house.
-/// Houses are permanent heap objects rather than reference counted ones, so this routine
-/// exists only to satisfy the IUnknown contract.
-///
-/// Returns with the reference count, which is always one.
-ULONG STDMETHODCALLTYPE HouseClass::AddRef(void)
-{
- return(1);
-}
-
-
-///
-/// Releases a reference to this house.
-/// Houses are permanent heap objects rather than reference counted ones, so this routine
-/// exists only to satisfy the IUnknown contract. It never destroys the house.
-///
-/// Returns with the reference count, which is always one.
-ULONG STDMETHODCALLTYPE HouseClass::Release(void)
-{
- return(1);
-}
-
-
///
/// Fetches the RTTI type of this object.
///
diff --git a/code/house.h b/code/house.h
index 7f4e50526..fa42696b2 100644
--- a/code/house.h
+++ b/code/house.h
@@ -735,13 +735,11 @@ class HouseClass : public AbstractClass
HouseClass(HouseTypeClass const * type = NULL);
virtual ~HouseClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Load(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
- virtual ULONG STDMETHODCALLTYPE Release(void) override;
int Available_Money(void);
int Available_Storage(void);
@@ -1060,8 +1058,6 @@ class HouseClass : public AbstractClass
BuildChoiceClass(UrgencyType urgency=URGENCY_NONE, StructType structure=STRUCT_NONE) : Urgency(urgency), Structure(structure) {};
bool operator==(BuildChoiceClass const & ) const {return(false);}
bool operator!=(BuildChoiceClass const & ) const {return(true);}
- HRESULT Save(IStream *) const {return(S_OK);};
- HRESULT Load(IStream *) {return(S_OK);};
};
static DynamicVectorClass BuildChoice;
diff --git a/code/houstype.cpp b/code/houstype.cpp
index 593e2be76..25721afd6 100644
--- a/code/houstype.cpp
+++ b/code/houstype.cpp
@@ -39,7 +39,6 @@
* HouseTypeClass::operator new -- Allocates a house type class object from special heap. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "houstype.h"
@@ -231,17 +230,6 @@ void HouseTypeClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Determines if this house type has been changed since it was last saved.
-/// House types are written out wholesale rather than on demand, so the answer never varies.
-///
-/// Returns with S_OK.
-HRESULT STDMETHODCALLTYPE HouseTypeClass::IsDirty(void)
-{
- return(false);
-}
-
-
///
/// Lists the members this house type carries.
///
@@ -270,49 +258,9 @@ void HouseTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the requested interface from this house type.
-/// House types serve up the persistence and RTTI interfaces that the save game system asks
-/// them for.
-///
-/// Returns with S_OK, or E_NOINTERFACE if the interface is not supported.
-HRESULT STDMETHODCALLTYPE HouseTypeClass::QueryInterface(REFIID riid, LPVOID * ppvObject)
+ClassID HouseTypeClass::Class_ID(void) const
{
- if (ppvObject == NULL) {
- return(E_POINTER);
- }
-
- *ppvObject = NULL;
-
- if (riid == IID_IUnknown) {
- *ppvObject = (IUnknown *)(IPersistStream *)this;
- }
- if (riid == IID_IPersist) {
- *ppvObject = (IPersistStream *)this;
- }
- if (riid == IID_IPersistStream) {
- *ppvObject = (IPersist *)this;
- }
- if (*ppvObject == NULL) {
- return(E_NOINTERFACE);
- }
-
- AddRef();
- return(S_OK);
-}
-
-
-///
-/// Fetches the class identifier of this object.
-/// The save game system stores this identifier so that the object can be recreated as the
-/// correct class when the game is loaded.
-///
-/// Returns with S_OK, or E_POINTER if no return pointer was supplied.
-HRESULT STDMETHODCALLTYPE HouseTypeClass::GetClassID(CLSID * retval)
-{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_HouseTypeClass;
- return(S_OK);
+ return(ClassID_HouseTypeClass);
}
@@ -347,27 +295,3 @@ int HouseTypeClass::Fetch_Heap_ID(void) const
{
return(HeapID);
}
-
-
-///
-/// Adds a reference to this house type.
-/// House types are not reference counted -- they live for the duration of the game, so this
-/// routine exists only to satisfy the IUnknown contract.
-///
-/// Returns with the reference count, which is always one.
-ULONG STDMETHODCALLTYPE HouseTypeClass::AddRef(void)
-{
- return(1);
-}
-
-
-///
-/// Releases a reference to this house type.
-/// House types are not reference counted -- they live for the duration of the game, so this
-/// routine exists only to satisfy the IUnknown contract.
-///
-/// Returns with the reference count, which is always one.
-ULONG STDMETHODCALLTYPE HouseTypeClass::Release(void)
-{
- return(1);
-}
diff --git a/code/houstype.h b/code/houstype.h
index ef3cb3d95..0020ea187 100644
--- a/code/houstype.h
+++ b/code/houstype.h
@@ -102,12 +102,8 @@ class HouseTypeClass : public AbstractTypeClass
HouseTypeClass(char const * ininame = NULL);
virtual ~HouseTypeClass() override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE IsDirty(void) override;
+ virtual ClassID Class_ID(void) const override;
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID * ppvObject) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
- virtual ULONG STDMETHODCALLTYPE Release(void) override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/hover.cpp b/code/hover.cpp
index b108e633d..369b36fc2 100644
--- a/code/hover.cpp
+++ b/code/hover.cpp
@@ -66,12 +66,11 @@ HoverLocomotionClass::HoverLocomotionClass(void) :
///
/// Pointer to the object this locomotor will drive.
/// Returns with the result of the attach operation.
-HRESULT STDMETHODCALLTYPE HoverLocomotionClass::Link_To_Object(void *pointer)
+void HoverLocomotionClass::Link_To_Object(void *pointer)
{
- HRESULT res = BASECLASS::Link_To_Object(pointer);
+ BASECLASS::Link_To_Object(pointer);
FacingClass face(2 * LinkedTo->TClass->ROT);
Facing = face;
- return(res);
}
@@ -141,7 +140,7 @@ void HoverLocomotionClass::Gravity_AI(void)
///
/// Pointer to the render cache key to update; may be NULL.
/// Returns with the matrix to render the object with.
-Matrix3D STDMETHODCALLTYPE HoverLocomotionClass::Draw_Matrix(int *key)
+Matrix3D HoverLocomotionClass::Draw_Matrix(int *key)
{
if (!Is_Powered()) {
int ramp = Map[(Coord const &)(LinkedTo->PositionCoord)].Ramp;
@@ -164,7 +163,7 @@ Matrix3D STDMETHODCALLTYPE HoverLocomotionClass::Draw_Matrix(int *key)
/// water, and applies the bob and sag of the hover cushion.
///
/// bool; Is the object still moving?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Process(void)
+bool HoverLocomotionClass::Process(void)
{
if (Is_Moving() && Is_Moving1()) {
Motion_AI();
@@ -288,7 +287,7 @@ boolean STDMETHODCALLTYPE HoverLocomotionClass::Process(void)
/// Does the object have a move order outstanding?
///
/// bool; Is the object either headed somewhere or bound for a destination?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Moving(void)
+bool HoverLocomotionClass::Is_Moving(void)
{
return(DestinationCoord != COORD_NONE || HeadToCoord != COORD_NONE);
}
@@ -300,7 +299,7 @@ boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Moving(void)
/// moving, but it is not moving now.
///
/// bool; Is the object traveling at this moment?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Moving_Now(void)
+bool HoverLocomotionClass::Is_Moving_Now(void)
{
return(Is_Moving() && Height != 0.0);
}
@@ -311,7 +310,7 @@ boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Moving_Now(void)
///
/// Returns with the destination coordinate, or COORD_NONE if the object has no
/// move order outstanding.
-Coord STDMETHODCALLTYPE HoverLocomotionClass::Destination(void)
+Coord HoverLocomotionClass::Destination(void)
{
if (DestinationCoord != COORD_NONE) {
return(DestinationCoord);
@@ -325,7 +324,7 @@ Coord STDMETHODCALLTYPE HoverLocomotionClass::Destination(void)
///
/// Returns with the intermediate destination, or with the object's current
/// position if it is not headed anywhere.
-Coord STDMETHODCALLTYPE HoverLocomotionClass::Head_To_Coord(void)
+Coord HoverLocomotionClass::Head_To_Coord(void)
{
if (HeadToCoord != COORD_NONE) {
return(HeadToCoord);
@@ -341,7 +340,7 @@ Coord STDMETHODCALLTYPE HoverLocomotionClass::Head_To_Coord(void)
/// the drive is started if the object is not already under way.
///
/// The coordinate to move to.
-void STDMETHODCALLTYPE HoverLocomotionClass::Move_To(Coord to)
+void HoverLocomotionClass::Move_To(Coord to)
{
DestinationCoord = to;
if (Is_Powered() && Is_Ion_Sensitive() && IonStormClass::Is_Ion_Storm_Active()) {
@@ -678,7 +677,7 @@ void HoverLocomotionClass::Motion_AI(void)
/// The object will still coast into the spot it has already reserved, but it will not
/// carry on toward its former destination once it arrives.
///
-void STDMETHODCALLTYPE HoverLocomotionClass::Stop_Moving(void)
+void HoverLocomotionClass::Stop_Moving(void)
{
if (DestinationCoord != HeadToCoord) {
DestinationCoord = COORD_NONE;
@@ -918,7 +917,7 @@ void HoverLocomotionClass::Start_Of_Move(int num)
/// and slews as it sinks rather than dropping neatly in place.
///
/// bool; Was the power turned off?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Power_Off(void)
+bool HoverLocomotionClass::Power_Off(void)
{
if (Is_Powered() && LinkedTo->CurrentMission != MISSION_SLEEP) {
Do_Shove();
@@ -937,7 +936,7 @@ boolean STDMETHODCALLTYPE HoverLocomotionClass::Power_Off(void)
/// way onto the ground, so it is treated as powered for as long as it has height to lose.
///
/// bool; Is the object still under power?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Powered(void)
+bool HoverLocomotionClass::Is_Powered(void)
{
if (!BASECLASS::Is_Powered() && LinkedTo->HeightAGL <= 0) {
return(false);
@@ -953,7 +952,7 @@ boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Powered(void)
/// units across the factory doorway.
///
/// bool; Should an ion storm cut this object's power?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Ion_Sensitive(void)
+bool HoverLocomotionClass::Is_Ion_Sensitive(void)
{
BuildingClass *bptr;
if (LinkedTo->In_Radio_Contact()) {
@@ -997,7 +996,7 @@ boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Ion_Sensitive(void)
///
/// The direction to push the object toward.
/// bool; Was the object pushed?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Push(DirType dir)
+bool HoverLocomotionClass::Push(DirType dir)
{
if (Is_Powered() && !WasPushed) {
@@ -1042,7 +1041,7 @@ boolean STDMETHODCALLTYPE HoverLocomotionClass::Push(DirType dir)
///
/// The direction to shove the object toward.
/// bool; Was the object shoved?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Shove(DirType dir)
+bool HoverLocomotionClass::Shove(DirType dir)
{
if (Push(dir)) {
Do_Shove();
@@ -1067,18 +1066,9 @@ void HoverLocomotionClass::Do_Shove(void)
}
-///
-/// Fetches the class identifier of this locomotor.
-/// The persistence system uses this identifier to create a locomotor of the right kind
-/// when the object it drives is loaded back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE HoverLocomotionClass::GetClassID(CLSID * retval)
+ClassID HoverLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_HoverLocomotion;
- return(S_OK);
+ return(ClassID_HoverLocomotion);
}
@@ -1109,7 +1099,7 @@ void HoverLocomotionClass::Serialize(SaveStreamClass & stream)
/// layer with ordinary vehicles.
///
/// Returns with the layer this object belongs in.
-LayerType STDMETHODCALLTYPE HoverLocomotionClass::In_Which_Layer(void)
+LayerType HoverLocomotionClass::In_Which_Layer(void)
{
return(LAYER_GROUND);
}
@@ -1140,7 +1130,7 @@ void HoverLocomotionClass::Start(void)
///
/// The marking operation to perform; MARK_UP releases the cell,
/// anything else reserves it.
-void STDMETHODCALLTYPE HoverLocomotionClass::Mark_All_Occupation_Bits(int mark)
+void HoverLocomotionClass::Mark_All_Occupation_Bits(int mark)
{
if (mark == MARK_UP) {
Coord coord = Head_To_Coord();
@@ -1159,7 +1149,7 @@ void STDMETHODCALLTYPE HoverLocomotionClass::Mark_All_Occupation_Bits(int mark)
///
/// The coordinate to compare the current destination against.
/// bool; Is the object moving to this location?
-boolean STDMETHODCALLTYPE HoverLocomotionClass::Is_Moving_Here(Coord to)
+bool HoverLocomotionClass::Is_Moving_Here(Coord to)
{
Coord coord = Head_To_Coord();
diff --git a/code/hover.h b/code/hover.h
index ab7bce672..4b22c0869 100644
--- a/code/hover.h
+++ b/code/hover.h
@@ -36,28 +36,28 @@ class HoverLocomotionClass : public LocomotionClass
HoverLocomotionClass(void);
virtual ~HoverLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual HRESULT STDMETHODCALLTYPE Link_To_Object(void *pointer) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) override;
- virtual Matrix3D STDMETHODCALLTYPE Draw_Matrix(int *key) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) override;
- virtual boolean STDMETHODCALLTYPE Power_Off(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Powered(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Ion_Sensitive(void) override;
- virtual boolean STDMETHODCALLTYPE Push(DirType dir) override;
- virtual boolean STDMETHODCALLTYPE Shove(DirType dir) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Here(Coord to) override;
+ virtual void Link_To_Object(void *pointer) override;
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual Coord Head_To_Coord(void) override;
+ virtual Matrix3D Draw_Matrix(int *key) override;
+ virtual bool Process(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual void Do_Turn(DirType coord) override;
+ virtual bool Power_Off(void) override;
+ virtual bool Is_Powered(void) override;
+ virtual bool Is_Ion_Sensitive(void) override;
+ virtual bool Push(DirType dir) override;
+ virtual bool Shove(DirType dir) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual void Mark_All_Occupation_Bits(int mark) override;
+ virtual bool Is_Moving_Here(Coord to) override;
private:
diff --git a/code/iblockci.h b/code/iblockci.h
deleted file mode 100644
index 0240dffd4..000000000
--- a/code/iblockci.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#pragma once
-
-#include
-
-/// Names and comments from TLBs
-
-EXTERN_C const IID IID_IBlockCipher;
-
-MIDL_INTERFACE("E0113100-6A7C-11D1-B6F9-00A024DDAFD1")
-IBlockCipher : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Set_Key(LONG keylength, const void *key) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Max_Key_Length(LONG *length) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Block_Size(LONG *length) = 0;
- virtual HRESULT STDMETHODCALLTYPE Encrypt(LONG length, const void *plaintext, void *cyphertext) = 0;
- virtual HRESULT STDMETHODCALLTYPE Decrypt(LONG length, const void *cyphertext, void *plaintext) = 0;
-};
diff --git a/code/iblockci_i.c b/code/iblockci_i.c
deleted file mode 100644
index 750d2597f..000000000
--- a/code/iblockci_i.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-/* this file contains the actual definitions of */
-/* the IIDs and CLSIDs */
-
-/* link this file in with the server and any clients */
-
-
-/* File created by MIDL compiler version X.XX.XXXX */
-/* at XXX XXX XX XX:XX:XX XXXX
- */
-/* Compiler settings for XXXX.idl:
- Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: none
-*/
-//@@MIDL_FILE_HEADING( )
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef struct _IID
-{
- unsigned long x;
- unsigned short s1;
- unsigned short s2;
- unsigned char c[8];
-} IID;
-
-#endif // __IID_DEFINED__
-
-#ifndef CLSID_DEFINED
-#define CLSID_DEFINED
-typedef IID CLSID;
-#endif // CLSID_DEFINED
-
-const IID IID_IBlockCipher = {0xE0113100,0x6A7C,0x11D1,{0xB6,0xF9,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/code/iblowfish.h b/code/iblowfish.h
deleted file mode 100644
index 4c01ee694..000000000
--- a/code/iblowfish.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#pragma once
-
-#include
-
-/// Names and comments from TLBs
-
-EXTERN_C const IID LIBID_BlowfishLibrary;
-EXTERN_C const CLSID CLSID_BlowfishObject;
diff --git a/code/iblowfish_i.c b/code/iblowfish_i.c
deleted file mode 100644
index 71ad0728b..000000000
--- a/code/iblowfish_i.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-/* this file contains the actual definitions of */
-/* the IIDs and CLSIDs */
-
-/* link this file in with the server and any clients */
-
-
-/* File created by MIDL compiler version X.XX.XXXX */
-/* at XXX XXX XX XX:XX:XX XXXX
- */
-/* Compiler settings for XXXX.idl:
- Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: none
-*/
-//@@MIDL_FILE_HEADING( )
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef struct _IID
-{
- unsigned long x;
- unsigned short s1;
- unsigned short s2;
- unsigned char c[8];
-} IID;
-
-#endif // __IID_DEFINED__
-
-#ifndef CLSID_DEFINED
-#define CLSID_DEFINED
-typedef IID CLSID;
-#endif // CLSID_DEFINED
-
-const IID LIBID_BlowfishLibrary = {0xE7F91750,0x8861,0x11d1,{0xB7,0x07,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_BlowfishObject = {0x1440ad10,0x6aa8,0x11d1,{0xb6,0xf9,0x00,0xa0,0x24,0xdd,0xaf,0xd1}};
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/code/iflyctrl.h b/code/iflyctrl.h
index 958e59d47..8a2415ee6 100644
--- a/code/iflyctrl.h
+++ b/code/iflyctrl.h
@@ -9,43 +9,34 @@
#pragma once
-#include
+#include "win.h"
-/// Names and comments from TLBs
-EXTERN_C const IID IID_IFlyControl;
-MIDL_INTERFACE("820F501C-4F39-11D2-9B70-00104B972FE8")
-IFlyControl : public IUnknown
+struct IFlyControl
{
-public:
/*
* Landing altitude
*/
- virtual LONG STDMETHODCALLTYPE Landing_Altitude(void) = 0;
+ virtual LONG Landing_Altitude(void) = 0;
/*
* Lading direction
*/
- virtual LONG STDMETHODCALLTYPE Landing_Direction(void) = 0;
+ virtual LONG Landing_Direction(void) = 0;
/*
* Loaded with cargo?
*/
- virtual BOOL STDMETHODCALLTYPE Is_Loaded(void) = 0;
+ virtual BOOL Is_Loaded(void) = 0;
/*
* Does it strafe over the target rather than hover?
*/
- virtual LONG STDMETHODCALLTYPE Is_Strafe(void) = 0;
+ virtual LONG Is_Strafe(void) = 0;
/*
* Is the aircraft locked into straight flight?
*/
- virtual LONG STDMETHODCALLTYPE Is_Locked(void) = 0;
+ virtual LONG Is_Locked(void) = 0;
};
-
-/*
- * IFlyControl com smart pointer declaration.
- */
-_COM_SMARTPTR_TYPEDEF(IFlyControl, __uuidof(IFlyControl));
diff --git a/code/iflyctrl_i.c b/code/iflyctrl_i.c
deleted file mode 100644
index 994c2ab42..000000000
--- a/code/iflyctrl_i.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-/* this file contains the actual definitions of */
-/* the IIDs and CLSIDs */
-
-/* link this file in with the server and any clients */
-
-
-/* File created by MIDL compiler version X.XX.XXXX */
-/* at XXX XXX XX XX:XX:XX XXXX
- */
-/* Compiler settings for XXXX.idl:
- Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: none
-*/
-//@@MIDL_FILE_HEADING( )
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef struct _IID
-{
- unsigned long x;
- unsigned short s1;
- unsigned short s2;
- unsigned char c[8];
-} IID;
-
-#endif // __IID_DEFINED__
-
-#ifndef CLSID_DEFINED
-#define CLSID_DEFINED
-typedef IID CLSID;
-#endif // CLSID_DEFINED
-
-const IID IID_IFlyControl = {0x820F501C,0x4F39,0x11D2,{0x9B,0x70,0x00,0x10,0x4B,0x97,0x2F,0xE8}};
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/code/ilinkstm.h b/code/ilinkstm.h
deleted file mode 100644
index 055b91573..000000000
--- a/code/ilinkstm.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#pragma once
-
-#include
-
-/// Names and comments from TLBs
-
-EXTERN_C const IID IID_ILinkStream;
-
-MIDL_INTERFACE("0D5CD78E-6470-11D2-9B74-00104B972FE8")
-ILinkStream : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Link_Stream(IUnknown *stream) = 0;
- virtual HRESULT STDMETHODCALLTYPE Unlink_Stream(IUnknown **stream) = 0;
-};
-
-/*
- * ILinkStream com smart pointer declaration.
- */
-//_COM_SMARTPTR_TYPEDEF(ILinkStream, __uuidof(ILinkStream));
diff --git a/code/iloco.h b/code/iloco.h
index 1d43d5e35..d83e775cf 100644
--- a/code/iloco.h
+++ b/code/iloco.h
@@ -20,251 +20,243 @@
#include "visual.hh"
#include "zgrad.hh"
-#include
-/// Names and comments from TLBs
-
-EXTERN_C const IID IID_ILocomotion;
/*
* Game object locomotion handler.
*/
-MIDL_INTERFACE("070F3290-9841-11D1-B709-00A024DDAFD1")
-ILocomotion : public IUnknown
+struct ILocomotion
{
-public:
+ virtual ~ILocomotion(void) {}
+
/*
* Links object to locomotor.
*/
- virtual HRESULT STDMETHODCALLTYPE Link_To_Object(void *pointer) = 0;
+ virtual void Link_To_Object(void *pointer) = 0;
/*
* Sees if object is moving.
*/
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) = 0;
+ virtual bool Is_Moving(void) = 0;
/*
* Fetches destination coordinate.
*/
- virtual Coord STDMETHODCALLTYPE Destination(void) = 0;
+ virtual Coord Destination(void) = 0;
/*
* Fetches immediate (next cell) destination coordinate.
*/
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) = 0;
+ virtual Coord Head_To_Coord(void) = 0;
/*
* Determine if specific cell can be entered.
*/
- virtual MoveType STDMETHODCALLTYPE Can_Enter_Cell(Cell cell) = 0;
+ virtual MoveType Can_Enter_Cell(Cell cell) = 0;
/*
* Should object cast a shadow?
*/
- virtual boolean STDMETHODCALLTYPE Is_To_Have_Shadow(void) = 0;
+ virtual bool Is_To_Have_Shadow(void) = 0;
/*
* Fetch voxel draw matrix.
*/
- virtual Matrix3D STDMETHODCALLTYPE Draw_Matrix(int *key) = 0;
+ virtual Matrix3D Draw_Matrix(int *key) = 0;
/*
* Fetch shadow draw matrix.
*/
- virtual Matrix3D STDMETHODCALLTYPE Shadow_Matrix(int *key) = 0;
+ virtual Matrix3D Shadow_Matrix(int *key) = 0;
/*
* Draw point center location.
*/
- virtual Point2D STDMETHODCALLTYPE Draw_Point(void) = 0;
+ virtual Point2D Draw_Point(void) = 0;
/*
* Shadow draw point center location.
*/
- virtual Point2D STDMETHODCALLTYPE Shadow_Point(void) = 0;
+ virtual Point2D Shadow_Point(void) = 0;
/*
* Visual character for drawing.
*/
- virtual VisualType STDMETHODCALLTYPE Visual_Character(boolean flag) = 0;
+ virtual VisualType Visual_Character(bool flag) = 0;
/*
* Z adjust control value.
*/
- virtual int STDMETHODCALLTYPE Z_Adjust(void) = 0;
+ virtual int Z_Adjust(void) = 0;
/*
* Z gradient control value.
*/
- virtual ZGradientType STDMETHODCALLTYPE Z_Gradient(void) = 0;
+ virtual ZGradientType Z_Gradient(void) = 0;
/*
* Process movement of object.
*/
- virtual boolean STDMETHODCALLTYPE Process(void) = 0;
+ virtual bool Process(void) = 0;
/*
* Instruct to move to location specified.
*/
- virtual void STDMETHODCALLTYPE Move_To(Coord to) = 0;
+ virtual void Move_To(Coord to) = 0;
/*
* Stop moving at first opportunity.
*/
- virtual void STDMETHODCALLTYPE Stop_Moving(void) = 0;
+ virtual void Stop_Moving(void) = 0;
/*
* Try to face direction specified.
*/
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) = 0;
+ virtual void Do_Turn(DirType coord) = 0;
/*
* Object is appearing in the world.
*/
- virtual void STDMETHODCALLTYPE Unlimbo(void) = 0;
+ virtual void Unlimbo(void) = 0;
/*
* Special tilting AI function.
*/
- virtual void STDMETHODCALLTYPE Tilt_Pitch_AI(void) = 0;
+ virtual void Tilt_Pitch_AI(void) = 0;
/*
* Locomotor becomes powered.
*/
- virtual boolean STDMETHODCALLTYPE Power_On(void) = 0;
+ virtual bool Power_On(void) = 0;
/*
* Locomotor loses power.
*/
- virtual boolean STDMETHODCALLTYPE Power_Off(void) = 0;
+ virtual bool Power_Off(void) = 0;
/*
* Is locomotor powered?
*/
- virtual boolean STDMETHODCALLTYPE Is_Powered(void) = 0;
+ virtual bool Is_Powered(void) = 0;
/*
* Is locomotor sensitive to ion storms?
*/
- virtual boolean STDMETHODCALLTYPE Is_Ion_Sensitive(void) = 0;
+ virtual bool Is_Ion_Sensitive(void) = 0;
/*
* Push object in direction specified.
*/
- virtual boolean STDMETHODCALLTYPE Push(DirType dir) = 0;
+ virtual bool Push(DirType dir) = 0;
/*
* Shove object (with spin) in direction specified.
*/
- virtual boolean STDMETHODCALLTYPE Shove(DirType dir) = 0;
+ virtual bool Shove(DirType dir) = 0;
/*
* Force drive track -- special case only.
*/
- virtual void STDMETHODCALLTYPE Force_Track(int track, Coord coord) = 0;
+ virtual void Force_Track(int track, Coord coord) = 0;
/*
* What display layer is it located in.
*/
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) = 0;
+ virtual LayerType In_Which_Layer(void) = 0;
/*
* Don't use this function.
*/
- virtual void STDMETHODCALLTYPE Force_Immediate_Destination(Coord coord) = 0;
+ virtual void Force_Immediate_Destination(Coord coord) = 0;
/*
* Force a voxel unit to a given slope. Used in cratering.
*/
- virtual void STDMETHODCALLTYPE Force_New_Slope(int ramp) = 0;
+ virtual void Force_New_Slope(int ramp) = 0;
/*
* Is it actually moving across the ground this very second?
*/
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) = 0;
+ virtual bool Is_Moving_Now(void) = 0;
/*
* Actual current speed of object expressed as leptons per game frame.
*/
- virtual int STDMETHODCALLTYPE Apparent_Speed(void) = 0;
+ virtual int Apparent_Speed(void) = 0;
/*
* Special drawing feedback code (locomotor specific meaning)
*/
- virtual int STDMETHODCALLTYPE Drawing_Code(void) = 0;
+ virtual int Drawing_Code(void) = 0;
/*
* Queries if any locomotor specific state prevents the object from firing.
*/
- virtual FireErrorType STDMETHODCALLTYPE Can_Fire(void) = 0;
+ virtual FireErrorType Can_Fire(void) = 0;
/*
* Queries the general state of the locomotor.
*/
- virtual int STDMETHODCALLTYPE Get_Status(void) = 0;
+ virtual int Get_Status(void) = 0;
/*
* Forces a hunter seeker droid to find a target.
*/
- virtual void STDMETHODCALLTYPE Acquire_Hunter_Seeker_Target(void) = 0;
+ virtual void Acquire_Hunter_Seeker_Target(void) = 0;
/*
* Is this object surfacing?
*/
- virtual boolean STDMETHODCALLTYPE Is_Surfacing(void) = 0;
+ virtual bool Is_Surfacing(void) = 0;
/*
* Lifts all occupation bits associated with the object off the map
*/
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) = 0;
+ virtual void Mark_All_Occupation_Bits(int mark) = 0;
/*
* Is this object in the process of moving into this coord.
*/
- virtual boolean STDMETHODCALLTYPE Is_Moving_Here(Coord to) = 0;
+ virtual bool Is_Moving_Here(Coord to) = 0;
/*
* Will this object jump tracks?
*/
- virtual boolean STDMETHODCALLTYPE Will_Jump_Tracks(void) = 0;
+ virtual bool Will_Jump_Tracks(void) = 0;
/*
* Infantry moving query function
*/
- virtual boolean STDMETHODCALLTYPE Is_Really_Moving_Now(void) = 0;
+ virtual bool Is_Really_Moving_Now(void) = 0;
/*
* Falsifies the IsReallyMoving flag in WalkLocomotionClass
*/
- virtual void STDMETHODCALLTYPE Stop_Movement_Animation(void) = 0;
+ virtual void Stop_Movement_Animation(void) = 0;
/*
- * Locks the locomotor from being deleted
+ * Locks the locomotor against being handed back, so that one piggybacking on it keeps
+ * control of the object.
*/
- virtual void STDMETHODCALLTYPE Lock(void) = 0;
+ virtual void Lock(void) = 0;
/*
- * Unlocks the locomotor from being deleted
+ * Unlocks the locomotor, so that a piggyback riding on it may end.
*/
- virtual void STDMETHODCALLTYPE Unlock(void) = 0;
+ virtual void Unlock(void) = 0;
/*
* Queries internal variables
*/
- virtual int STDMETHODCALLTYPE Get_Track_Number(void) = 0;
+ virtual int Get_Track_Number(void) = 0;
/*
* Queries internal variables
*/
- virtual int STDMETHODCALLTYPE Get_Track_Index(void) = 0;
+ virtual int Get_Track_Index(void) = 0;
/*
* Queries internal variables
*/
- virtual int STDMETHODCALLTYPE Get_Speed_Accum(void) = 0;
+ virtual int Get_Speed_Accum(void) = 0;
};
-
-/*
- * ILocomtion com smart pointer declaration.
- */
-_COM_SMARTPTR_TYPEDEF(ILocomotion, __uuidof(ILocomotion));
diff --git a/code/iloco_i.c b/code/iloco_i.c
deleted file mode 100644
index cecc3d209..000000000
--- a/code/iloco_i.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-/* this file contains the actual definitions of */
-/* the IIDs and CLSIDs */
-
-/* link this file in with the server and any clients */
-
-
-/* File created by MIDL compiler version X.XX.XXXX */
-/* at XXX XXX XX XX:XX:XX XXXX
- */
-/* Compiler settings for XXXX.idl:
- Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: none
-*/
-//@@MIDL_FILE_HEADING( )
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef struct _IID
-{
- unsigned long x;
- unsigned short s1;
- unsigned short s2;
- unsigned char c[8];
-} IID;
-
-#endif // __IID_DEFINED__
-
-#ifndef CLSID_DEFINED
-#define CLSID_DEFINED
-typedef IID CLSID;
-#endif // CLSID_DEFINED
-
-const IID IID_ILocomotion = {0x070F3290,0x9841,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/code/ilocos.h b/code/ilocos.h
deleted file mode 100644
index ee939a7e0..000000000
--- a/code/ilocos.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#pragma once
-
-#include "iloco.h"
-
-/// Names and comments from TLBs
-
-EXTERN_C const IID LIBID_LocomotionLibrary;
-
-EXTERN_C const CLSID CLSID_DriveLocomotion;
-EXTERN_C const CLSID CLSID_HoverLocomotion;
-EXTERN_C const CLSID CLSID_TunnelLocomotion;
-EXTERN_C const CLSID CLSID_WalkLocomotion;
-EXTERN_C const CLSID CLSID_BallisticLocomotion;
-EXTERN_C const CLSID CLSID_FlyerLocomotion;
-EXTERN_C const CLSID CLSID_TeleportLocomotion;
-EXTERN_C const CLSID CLSID_MechLocomotion;
-EXTERN_C const CLSID CLSID_JumpjetLocomotion;
-EXTERN_C const CLSID CLSID_LevitateLocomotion;
diff --git a/code/ilocos_i.c b/code/ilocos_i.c
deleted file mode 100644
index a1fb1faf0..000000000
--- a/code/ilocos_i.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-/* this file contains the actual definitions of */
-/* the IIDs and CLSIDs */
-
-/* link this file in with the server and any clients */
-
-
-/* File created by MIDL compiler version X.XX.XXXX */
-/* at XXX XXX XX XX:XX:XX XXXX
- */
-/* Compiler settings for XXXX.idl:
- Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: none
-*/
-//@@MIDL_FILE_HEADING( )
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef struct _IID
-{
- unsigned long x;
- unsigned short s1;
- unsigned short s2;
- unsigned char c[8];
-} IID;
-
-#endif // __IID_DEFINED__
-
-#ifndef CLSID_DEFINED
-#define CLSID_DEFINED
-typedef IID CLSID;
-#endif // CLSID_DEFINED
-
-
-const IID LIBID_LocomotionLibrary = {0x4A582740,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_DriveLocomotion = {0x4A582741,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_HoverLocomotion = {0x4A582742,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_TunnelLocomotion = {0x4A582743,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_WalkLocomotion = {0x4A582744,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_BallisticLocomotion = {0x4A582745,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_FlyerLocomotion = {0x4A582746,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_TeleportLocomotion = {0x4A582747,0x9839,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_MechLocomotion = {0x55D141B8,0xDB94,0x11D1,{0xAC,0x98,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_JumpjetLocomotion = {0x92612C46,0xF71F,0x11D1,{0xAC,0x9F,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_LevitateLocomotion = {0x3DC0B295,0x6546,0x11D3,{0x80,0xB0,0x00,0x90,0x27,0x92,0x49,0x4C}};
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/code/infantry.cpp b/code/infantry.cpp
index f8f11f6ae..1a6103742 100644
--- a/code/infantry.cpp
+++ b/code/infantry.cpp
@@ -79,7 +79,6 @@
* InfantryClass::~InfantryClass -- Default destructor for infantry units. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "infantry.h"
@@ -100,6 +99,7 @@
#include "builtype.h"
#include "ccrand.h"
#include "cell.h"
+#include "classids.h"
#include "combat.h"
#include "data.h"
#include "draw.h"
@@ -108,7 +108,6 @@
#include "goptions.h"
#include "house.h"
#include "houstype.h"
-#include "ilocos.h"
#include "incdec.h"
#include "infatype.h"
#include "inline.h"
@@ -250,7 +249,7 @@ InfantryClass::InfantryClass(InfantryTypeClass const * type, HouseClass * house)
Init();
if (Class != NULL) {
- Locomotion.CreateInstance(Class->Locomotor, NULL, CLSCTX_ALL);
+ Locomotion = Create_Locomotor(Class->Locomotor);
Locomotion->Link_To_Object(this);
}
@@ -632,11 +631,9 @@ void InfantryClass::Draw_It(Point2D const & xpoint, Rect const & cliprect) const
Cell cell = Get_Target_Cell();
if (CurrentTube == -1) {
- IPersistPtr persist = Locomotion;
- CLSID clsid;
- persist->GetClassID(&clsid);
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
- if (HeightAGL > 0 && clsid == CLSID_BallisticLocomotion) {
+ if (HeightAGL > 0 && clsid == ClassID_BallisticLocomotion) {
ShapeSet const * shapefile = (ShapeSet const *)MFCD::Retrieve("POD.SHP");
Point2D spoint = xpoint + Point2D(Locomotion->Shadow_Point());
Draw_Shape(
@@ -1174,10 +1171,8 @@ void InfantryClass::Assign_Destination(AbstractClass * target, bool immediate)
}
if (target != NULL && Class->IsJumpJet && Locomotion->Is_Moving()) {
- IPersistPtr persist(Locomotion);
- CLSID clsid;
- persist->GetClassID(&clsid);
- if (clsid == CLSID_WalkLocomotion) {
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
+ if (clsid == ClassID_WalkLocomotion) {
NavQueue.Add_Head(target);
target = Get_Target_Cell_Ptr();
if (target != NULL && ((CellClass *)target)->IsUnderBridge) {
@@ -1190,26 +1185,26 @@ void InfantryClass::Assign_Destination(AbstractClass * target, bool immediate)
bool should_fly = Should_JumpJet_Fly(Destination_Coord().As_Cell(), target->Center_Coord().As_Cell());
if (Is_JumpJet()) {
if (!should_fly) {
- IPiggybackPtr piggy(Locomotion);
+ IPiggyback * piggy = Piggyback_Of(Locomotion.get());
if (piggy != NULL) {
if (piggy->Is_Piggybacking() && piggy->Is_Ok_To_End()) {
- piggy->End_Piggyback(&Locomotion);
+ Locomotion = piggy->End_Piggyback();
}
}
- ILocomotionPtr walk(CLSID_WalkLocomotion);
+ std::unique_ptr walk = Create_Locomotor(ClassID_WalkLocomotion);
walk->Link_To_Object(this);
- piggy = IPiggybackPtr(walk);
+ piggy = Piggyback_Of(walk.get());
if (piggy != NULL) {
piggy->Begin_Piggyback(Locomotion);
- Locomotion = walk;
+ Locomotion = std::move(walk);
}
}
} else {
if (should_fly) {
- IPiggybackPtr piggy(Locomotion);
+ IPiggyback * piggy = Piggyback_Of(Locomotion.get());
if (piggy != NULL) {
if (piggy->Is_Piggybacking() && piggy->Is_Ok_To_End()) {
- piggy->End_Piggyback(&Locomotion);
+ Locomotion = piggy->End_Piggyback();
}
}
}
@@ -3943,8 +3938,8 @@ void InfantryClass::Clear_Occupy_Bit(Coord const & coord)
/// since the one it is about to be given is the one it was saved with. Post_Load enters it
/// again once that identity has arrived.
///
-/// Returns with S_OK if the object was read successfully.
-HRESULT STDMETHODCALLTYPE InfantryClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool InfantryClass::Load(SaveStreamClass & stream)
{
TargetTracker.Remove_Index(Fetch_ID());
return(BASECLASS::Load(stream));
@@ -4154,15 +4149,15 @@ bool InfantryClass::JumpJet_To_Walk(void)
if (path_length >= 4) return(false);
if (Is_JumpJet()) {
- IPiggybackPtr piggy(Locomotion);
+ IPiggyback * piggy = Piggyback_Of(Locomotion.get());
if (piggy != NULL && !piggy->Is_Piggybacking()) {
- ILocomotionPtr walk(CLSID_WalkLocomotion);
+ std::unique_ptr walk = Create_Locomotor(ClassID_WalkLocomotion);
walk->Link_To_Object(this);
- piggy = IPiggybackPtr(walk);
+ piggy = Piggyback_Of(walk.get());
if (piggy != NULL) {
Path[0] = FACING_NONE;
piggy->Begin_Piggyback(Locomotion);
- Locomotion = walk;
+ Locomotion = std::move(walk);
Locomotion->Move_To(NavCom->Center_Coord());
return(true);
}
@@ -4197,10 +4192,8 @@ bool InfantryClass::Is_JumpJet(void) const
return(false);
}
- IPersistPtr persist(Locomotion);
- CLSID clsid;
- persist->GetClassID(&clsid);
- return((clsid == CLSID_JumpjetLocomotion) ? true : false);
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
+ return((clsid == ClassID_JumpjetLocomotion) ? true : false);
}
@@ -4303,18 +4296,9 @@ int InfantryClass::Do_MISSION_GUARD(void)
}
-///
-/// Fetches the class identifier used to persist this object.
-/// The save system records this identifier alongside the object data so that the
-/// correct kind of object can be created again when the stream is read back.
-///
-/// Pointer to the buffer to fill in with the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE InfantryClass::GetClassID(CLSID * retval)
+ClassID InfantryClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_InfantryClass;
- return(S_OK);
+ return(ClassID_InfantryClass);
}
diff --git a/code/infantry.h b/code/infantry.h
index fc6183008..1540aa8e8 100644
--- a/code/infantry.h
+++ b/code/infantry.h
@@ -126,8 +126,8 @@ class InfantryClass : public FootClass
InfantryClass(InfantryTypeClass const * type = NULL, HouseClass * house = NULL);
virtual ~InfantryClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Load(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/infatype.cpp b/code/infatype.cpp
index e4c42f96b..9d707ff34 100644
--- a/code/infatype.cpp
+++ b/code/infatype.cpp
@@ -45,7 +45,6 @@
* InfantryTypeClass::operator new -- Allocate an infanty type class object. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "infatype.h"
@@ -513,17 +512,9 @@ void InfantryTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// The save game system stores this identifier so that the object can be recreated as the
-/// correct class when the game is loaded.
-///
-/// Returns with S_OK, or E_POINTER if no return pointer was supplied.
-HRESULT STDMETHODCALLTYPE InfantryTypeClass::GetClassID(CLSID * retval)
+ClassID InfantryTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_InfantryTypeClass;
- return(S_OK);
+ return(ClassID_InfantryTypeClass);
}
diff --git a/code/infatype.h b/code/infatype.h
index 340f22dcc..e1b717d6c 100644
--- a/code/infatype.h
+++ b/code/infatype.h
@@ -172,7 +172,7 @@ class InfantryTypeClass : public TechnoTypeClass
InfantryTypeClass(char const * ininame = NULL);
virtual ~InfantryTypeClass() override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/ini.cpp b/code/ini.cpp
index 4db0ee6f2..64a8536f7 100644
--- a/code/ini.cpp
+++ b/code/ini.cpp
@@ -957,6 +957,86 @@ int INIClass::Get_Int(char const * section, char const * entry, int defvalue) co
}
+// One field of a class identifier, exactly the many hexadecimal digits it is written with.
+static bool Parse_Hex_Field(char const * & ptr, int digits, unsigned int & value)
+{
+ value = 0;
+
+ for (int index = 0; index < digits; index++) {
+ char const letter = *ptr++;
+ unsigned int digit;
+
+ if (letter >= '0' && letter <= '9') {
+ digit = (unsigned int)(letter - '0');
+ } else if (letter >= 'A' && letter <= 'F') {
+ digit = (unsigned int)(letter - 'A') + 10;
+ } else if (letter >= 'a' && letter <= 'f') {
+ digit = (unsigned int)(letter - 'a') + 10;
+ } else {
+ return(false);
+ }
+
+ value = (value << 4) | digit;
+ }
+
+ return(true);
+}
+
+
+// A class identifier as the registry writes it, the surrounding braces optional: eight,
+// four, four, four and twelve hexadecimal digits separated by hyphens, and nothing else.
+static bool Parse_ClassID(char const * text, ClassID & clsid)
+{
+ char const * ptr = text;
+ std::size_t length = strlen(text);
+
+ if (length == 38 && ptr[0] == '{' && ptr[37] == '}') {
+ ptr++;
+ length -= 2;
+ }
+ if (length != 36) {
+ return(false);
+ }
+
+ unsigned int data1;
+ unsigned int data2;
+ unsigned int data3;
+ if (!Parse_Hex_Field(ptr, 8, data1) || *ptr++ != '-') return(false);
+ if (!Parse_Hex_Field(ptr, 4, data2) || *ptr++ != '-') return(false);
+ if (!Parse_Hex_Field(ptr, 4, data3) || *ptr++ != '-') return(false);
+
+ unsigned char data4[8];
+ for (int index = 0; index < ARRAY_SIZE(data4); index++) {
+ unsigned int byte;
+ if (!Parse_Hex_Field(ptr, 2, byte)) {
+ return(false);
+ }
+ data4[index] = (unsigned char)byte;
+ if (index == 1 && *ptr++ != '-') {
+ return(false);
+ }
+ }
+
+ clsid.Data1 = data1;
+ clsid.Data2 = (unsigned short)data2;
+ clsid.Data3 = (unsigned short)data3;
+ for (int index = 0; index < ARRAY_SIZE(data4); index++) {
+ clsid.Data4[index] = data4[index];
+ }
+ return(true);
+}
+
+
+// The buffer holds the 38 characters of the braced form and its terminator.
+static void Format_ClassID(ClassID const & clsid, char * text)
+{
+ sprintf(text, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+ (unsigned long)clsid.Data1, (unsigned int)clsid.Data2, (unsigned int)clsid.Data3,
+ clsid.Data4[0], clsid.Data4[1], clsid.Data4[2], clsid.Data4[3],
+ clsid.Data4[4], clsid.Data4[5], clsid.Data4[6], clsid.Data4[7]);
+}
+
+
///
/// Fetches a class identifier from the specified section.
/// This routine will fetch the printable form of a class identifier from the entry and
@@ -968,15 +1048,13 @@ int INIClass::Get_Int(char const * section, char const * entry, int defvalue) co
/// The default identifier to use if the entry could not be found.
/// Returns with the class identifier specified in the INI database or else returns
/// the default value.
-CLSID const INIClass::Get_CLSID(char const * section, char const * entry, CLSID defvalue) const
+ClassID const INIClass::Get_ClassID(char const * section, char const * entry, ClassID defvalue) const
{
char buffer[128];
if (Get_String(section, entry, "", buffer, sizeof(buffer))) {
- wchar_t olestr[128];
- MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buffer, -1, olestr, ARRAY_SIZE(olestr));
- CLSID clsid;
- if (SUCCEEDED(CLSIDFromString(olestr, &clsid))) {
+ ClassID clsid;
+ if (Parse_ClassID(buffer, clsid)) {
return(clsid);
}
}
@@ -993,17 +1071,10 @@ CLSID const INIClass::Get_CLSID(char const * section, char const * entry, CLSID
/// The entry identifier to tag to the class identifier specified.
/// The class identifier to store.
/// bool; Was the class identifier placed into the INI database?
-bool INIClass::Put_CLSID(char const * section, char const * entry, CLSID const & value)
+bool INIClass::Put_ClassID(char const * section, char const * entry, ClassID const & value)
{
- char buffer[128];
- LPOLESTR olestr = NULL;
-
- StringFromCLSID(value, &olestr);
- if (WideCharToMultiByte(CP_ACP, 0, olestr, -1, buffer, sizeof(buffer), NULL, NULL) == 0) {
- /// BUG, return not used
- GetLastError();
- }
- SysFreeString(olestr);
+ char buffer[40];
+ Format_ClassID(value, buffer);
return(Put_String(section, entry, buffer));
}
diff --git a/code/ini.h b/code/ini.h
index a4f33badd..ac28a63f9 100644
--- a/code/ini.h
+++ b/code/ini.h
@@ -34,7 +34,7 @@
#include "crc.h"
#include "index.h"
-#include
+#include "classid.h"
#include
#include
#include
@@ -113,7 +113,7 @@ class INIClass {
TPoint3D const Get_Point(char const * section, char const * entry, TPoint3D const & defvalue) const;
TPoint2D const Get_Point(char const * section, char const * entry, TPoint2D const & defvalue) const;
TPoint3D const Get_Point(char const * section, char const * entry, TPoint3D const & defvalue) const;
- CLSID const Get_CLSID(char const * section, char const * entry, CLSID defvalue) const;
+ ClassID const Get_ClassID(char const * section, char const * entry, ClassID defvalue) const;
/*
** Put a data type to the section and entry specified.
@@ -130,7 +130,7 @@ class INIClass {
bool Put_Point(char const * section, char const * entry, TPoint3D const & value);
bool Put_Point(char const * section, char const * entry, TPoint3D const & value);
bool Put_Point(char const * section, char const * entry, TPoint2D const & value);
- bool Put_CLSID(char const * section, char const * entry, CLSID const & value);
+ bool Put_ClassID(char const * section, char const * entry, ClassID const & value);
// Callers size the buffers they hand to Get_String from this. It does not bound a line
// of the file; the reader keeps a line of any length.
diff --git a/code/init.cpp b/code/init.cpp
index d99c3b88c..a6994a892 100644
--- a/code/init.cpp
+++ b/code/init.cpp
@@ -6025,7 +6025,7 @@ void Delete_All_Objects(void)
}
Process_Deferred_Deletion();
while (Bullets.Count()) {
- Bullets[0]->Release();
+ delete Bullets[0];
}
Process_Deferred_Deletion();
while (Objects.Count()) {
diff --git a/code/ion.cpp b/code/ion.cpp
index c4ce335a1..4348c797e 100644
--- a/code/ion.cpp
+++ b/code/ion.cpp
@@ -78,11 +78,10 @@ void IonStormClass::Init(void)
/// Saves the ion storm state to the save game stream.
///
/// Returns with the result reported by the stream write.
-HRESULT IonStormClass::Save(IStream * stream)
+bool IonStormClass::Save(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
- return(savestream.Result());
+ Serialize(stream);
+ return(!stream.Was_Error());
}
@@ -92,12 +91,11 @@ HRESULT IonStormClass::Save(IStream * stream)
/// Returns with the result reported by the stream read.
/// Only the bookkeeping is restored here. Post_Load_Game must still call
/// Apply_Secondary_Effect to put the world back into its storm bound state.
-HRESULT IonStormClass::Load(IStream * stream)
+bool IonStormClass::Load(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("IonStormClass");
- Serialize(savestream);
- return(savestream.Result());
+ stream.Set_Context("IonStormClass");
+ Serialize(stream);
+ return(!stream.Was_Error());
}
diff --git a/code/ion.h b/code/ion.h
index b3dab97f2..149f25c5e 100644
--- a/code/ion.h
+++ b/code/ion.h
@@ -13,17 +13,18 @@
#include "theme.hh"
-#include
class SaveStreamClass;
+#include "win.h"
+
class ShapeSet;
class IonStormClass
{
public:
static void Init(void);
- static HRESULT Save(IStream * stream);
- static HRESULT Load(IStream * stream);
+ static bool Save(SaveStreamClass & stream);
+ static bool Load(SaveStreamClass & stream);
static void Serialize(SaveStreamClass & stream);
diff --git a/code/ipiggy.h b/code/ipiggy.h
index 74c1d8216..39cdd1d1f 100644
--- a/code/ipiggy.h
+++ b/code/ipiggy.h
@@ -11,43 +11,36 @@
#include "iloco.h"
-#include
+#include
-/// Names and comments from TLBs
-EXTERN_C const IID IID_IPiggyback;
-
-MIDL_INTERFACE("92FEA800-A184-11D1-B70A-00A024DDAFD1")
-IPiggyback : public IUnknown
+struct IPiggyback
{
-public:
/*
- * Piggybacks a locomotor onto this one.
+ * Piggybacks a locomotor onto this one. The locomotor is taken only when the answer
+ * is true; a refusal leaves it with the caller rather than destroying it.
*/
- virtual HRESULT STDMETHODCALLTYPE Begin_Piggyback(ILocomotion * pointer) = 0;
+ virtual bool Begin_Piggyback(std::unique_ptr & carried) = 0;
/*
- * End piggyback process and restore locomotor interface pointer.
+ * Hands the carried locomotor back, or nothing when none is carried.
*/
- virtual HRESULT STDMETHODCALLTYPE End_Piggyback(ILocomotion ** pointer) = 0;
+ virtual std::unique_ptr End_Piggyback(void) = 0;
/*
* Is it ok to end the piggyback process?
*/
- virtual boolean STDMETHODCALLTYPE Is_Ok_To_End(void) = 0;
-
- /*
- * Fetches piggybacked locomotor class ID.
- */
- virtual HRESULT STDMETHODCALLTYPE Piggyback_CLSID(GUID * classid) = 0;
+ virtual bool Is_Ok_To_End(void) = 0;
/*
* Is it currently piggy backing another locomotor?
*/
- virtual boolean STDMETHODCALLTYPE Is_Piggybacking(void) = 0;
+ virtual bool Is_Piggybacking(void) = 0;
};
-/*
- * IPiggyback com smart pointer declaration.
- */
-_COM_SMARTPTR_TYPEDEF(IPiggyback, __uuidof(IPiggyback));
+
+// The piggyback side of a locomotor, or null when it cannot carry one.
+inline IPiggyback * Piggyback_Of(ILocomotion * locomotion)
+{
+ return(dynamic_cast(locomotion));
+}
diff --git a/code/ipiggy_i.c b/code/ipiggy_i.c
deleted file mode 100644
index 46006be43..000000000
--- a/code/ipiggy_i.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-/* this file contains the actual definitions of */
-/* the IIDs and CLSIDs */
-
-/* link this file in with the server and any clients */
-
-
-/* File created by MIDL compiler version X.XX.XXXX */
-/* at XXX XXX XX XX:XX:XX XXXX
- */
-/* Compiler settings for XXXX.idl:
- Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: none
-*/
-//@@MIDL_FILE_HEADING( )
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef struct _IID
-{
- unsigned long x;
- unsigned short s1;
- unsigned short s2;
- unsigned char c[8];
-} IID;
-
-#endif // __IID_DEFINED__
-
-#ifndef CLSID_DEFINED
-#define CLSID_DEFINED
-typedef IID CLSID;
-#endif // CLSID_DEFINED
-
-const IID IID_IPiggyback = {0x92FEA800,0xA184,0x11D1,{0xB7,0x0A,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/code/isotile.cpp b/code/isotile.cpp
index 709b3c71b..8f67c3d9d 100644
--- a/code/isotile.cpp
+++ b/code/isotile.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "isotile.h"
@@ -227,18 +226,9 @@ RTTIType IsometricTileClass::Fetch_RTTI(void) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence contract and is called by the save system
-/// when it must record what kind of object it is about to write out.
-///
-/// Pointer to the buffer that will receive the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE IsometricTileClass::GetClassID(CLSID * retval)
+ClassID IsometricTileClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_IsometricTileClass;
- return(S_OK);
+ return(ClassID_IsometricTileClass);
}
diff --git a/code/isotile.h b/code/isotile.h
index f99cc2683..6833b02d1 100644
--- a/code/isotile.h
+++ b/code/isotile.h
@@ -15,7 +15,6 @@
#include "isotype.hh"
-#include
class IsometricTileTypeClass;
@@ -26,7 +25,7 @@ class IsometricTileClass : public ObjectClass
IsometricTileClass(IsometricTileType type, Cell const &cell);
virtual ~IsometricTileClass() override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/isotype.cpp b/code/isotype.cpp
index ed7132a33..45d6a9920 100644
--- a/code/isotype.cpp
+++ b/code/isotype.cpp
@@ -11,7 +11,6 @@
* disclaimers apply; see LICENSE.md.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "isotype.h"
@@ -2792,16 +2791,9 @@ void IsometricTileTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier that this tile type persists under.
-///
-/// Receives the class identifier.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE IsometricTileTypeClass::GetClassID(CLSID * retval)
+ClassID IsometricTileTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_IsometricTileTypeClass;
- return(S_OK);
+ return(ClassID_IsometricTileTypeClass);
}
diff --git a/code/isotype.h b/code/isotype.h
index 70693e2d0..41a9e64b8 100644
--- a/code/isotype.h
+++ b/code/isotype.h
@@ -208,7 +208,7 @@ class IsometricTileTypeClass : public ObjectTypeClass
IsometricTileTypeClass(IsometricTileType type = ISOTILE_CLEAR, int unknown1 = 0, unsigned char unknown2 = 0, char const *ininame = NULL, bool skip_registration = false);
virtual ~IsometricTileTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/isun.h b/code/isun.h
deleted file mode 100644
index c46215471..000000000
--- a/code/isun.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#pragma once
-
-#include
-
-/// Names and comments from TLBs
-
-#define GAME_VERNAME TEXT("Tiberian Sun")
-
-EXTERN_C const IID IID_ILinkStream;
-EXTERN_C const CLSID CLSID_CompressStream;
-EXTERN_C const CLSID CLSID_HouseClass;
-EXTERN_C const CLSID CLSID_SuperWeaponTypeClass;
-EXTERN_C const CLSID CLSID_SuperWeaponClass;
-EXTERN_C const CLSID CLSID_UnitTypeClass;
-EXTERN_C const CLSID CLSID_InfantryTypeClass;
-EXTERN_C const CLSID CLSID_AircraftTypeClass;
-EXTERN_C const CLSID CLSID_BuildingTypeClass;
-EXTERN_C const CLSID CLSID_BulletTypeClass;
-EXTERN_C const CLSID CLSID_TerrainTypeClass;
-EXTERN_C const CLSID CLSID_IsometricTileTypeClass;
-EXTERN_C const CLSID CLSID_OverlayTypeClass;
-EXTERN_C const CLSID CLSID_SmudgeTypeClass;
-EXTERN_C const CLSID CLSID_AnimTypeClass;
-EXTERN_C const CLSID CLSID_HouseTypeClass;
-EXTERN_C const CLSID CLSID_IsometricTileClass;
-EXTERN_C const CLSID CLSID_VoxelAnimClass;
-EXTERN_C const CLSID CLSID_AircraftClass;
-EXTERN_C const CLSID CLSID_AnimClass;
-EXTERN_C const CLSID CLSID_InfantryClass;
-EXTERN_C const CLSID CLSID_SmudgeClass;
-EXTERN_C const CLSID CLSID_BuildingClass;
-EXTERN_C const CLSID CLSID_OverlayClass;
-EXTERN_C const CLSID CLSID_ParticleSystemClass;
-EXTERN_C const CLSID CLSID_ParticleSystemTypeClass;
-EXTERN_C const CLSID CLSID_BulletClass;
-EXTERN_C const CLSID CLSID_UnitClass;
-EXTERN_C const CLSID CLSID_ParticleClass;
-EXTERN_C const CLSID CLSID_ParticleTypeClass;
-EXTERN_C const CLSID CLSID_WaveClass;
-EXTERN_C const CLSID CLSID_BuildingLightClass;
-EXTERN_C const CLSID CLSID_TerrainClass;
-EXTERN_C const CLSID CLSID_TubeClass;
-EXTERN_C const CLSID CLSID_TeamClass;
-EXTERN_C const CLSID CLSID_TaskForceClass;
-EXTERN_C const CLSID CLSID_TeamTypeClass;
-EXTERN_C const CLSID CLSID_VoxelAnimTypeClass;
-EXTERN_C const CLSID CLSID_ScriptClass;
-EXTERN_C const CLSID CLSID_ScriptTypeClass;
-EXTERN_C const CLSID CLSID_TagClass;
-EXTERN_C const CLSID CLSID_TagTypeClass;
-EXTERN_C const CLSID CLSID_TriggerClass;
-EXTERN_C const CLSID CLSID_TriggerTypeClass;
-EXTERN_C const CLSID CLSID_ActionClass;
-EXTERN_C const CLSID CLSID_EventClass;
-EXTERN_C const CLSID CLSID_FactoryClass;
-EXTERN_C const CLSID CLSID_WeaponTypeClass;
-EXTERN_C const CLSID CLSID_WarheadTypeClass;
-EXTERN_C const CLSID CLSID_WaypointPath;
-EXTERN_C const CLSID CLSID_LightSource;
-EXTERN_C const CLSID CLSID_CampaignClass;
-EXTERN_C const CLSID CLSID_SideClass;
-EXTERN_C const CLSID CLSID_TiberiumClass;
-EXTERN_C const CLSID CLSID_CellClass;
-EXTERN_C const CLSID CLSID_EMPulseClass;
-EXTERN_C const CLSID CLSID_TacticalMapClass;
-EXTERN_C const CLSID CLSID_AITriggerTypeClass;
-EXTERN_C const CLSID CLSID_AITriggerClass;
-EXTERN_C const CLSID CLSID_NeuronClass;
-EXTERN_C const CLSID CLSID_FoggedObjectClass;
-EXTERN_C const CLSID CLSID_AlphaShapeClass;
-EXTERN_C const CLSID CLSID_VeinholeMonsterClass;
diff --git a/code/isun_i.c b/code/isun_i.c
deleted file mode 100644
index d667c8d25..000000000
--- a/code/isun_i.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-/* this file contains the actual definitions of */
-/* the IIDs and CLSIDs */
-
-/* link this file in with the server and any clients */
-
-
-/* File created by MIDL compiler version X.XX.XXXX */
-/* at XXX XXX XX XX:XX:XX XXXX
- */
-/* Compiler settings for XXXX.idl:
- Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: none
-*/
-//@@MIDL_FILE_HEADING( )
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef struct _IID
-{
- unsigned long x;
- unsigned short s1;
- unsigned short s2;
- unsigned char c[8];
-} IID;
-
-#endif // __IID_DEFINED__
-
-#ifndef CLSID_DEFINED
-#define CLSID_DEFINED
-typedef IID CLSID;
-#endif // CLSID_DEFINED
-
-const IID IID_ILinkStream = {0x0D5CD78E,0x6470,0x11D2,{0x9B,0x74,0x00,0x10,0x4B,0x97,0x2F,0xE8}};
-
-
-const CLSID CLSID_CompressStream = {0xB48FA168,0x646F,0x11D2,{0x9B,0x74,0x00,0x10,0x4B,0x97,0x2F,0xE8}};
-
-
-const CLSID CLSID_HouseClass = {0xD9D4A910,0x87C6,0x11D1,{0xB7,0x07,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_SuperWeaponTypeClass = {0x0CF2BCE7,0x36E4,0x11D2,{0xB8,0xD8,0x00,0x60,0x08,0xC8,0x09,0xED}};
-
-
-const CLSID CLSID_SuperWeaponClass = {0xD7F754C6,0x391C,0x11D2,{0x9B,0x64,0x00,0x10,0x4B,0x97,0x2F,0xE8}};
-
-
-const CLSID CLSID_UnitTypeClass = {0xDCBD42EA,0x0546,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_InfantryTypeClass = {0xAE8B33D8,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_AircraftTypeClass = {0xAE8B33D9,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_BuildingTypeClass = {0xAE8B33DB,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_BulletTypeClass = {0x5AF2CE77,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_TerrainTypeClass = {0x5AF2CE7B,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_IsometricTileTypeClass = {0x5AF2CE7A,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_OverlayTypeClass = {0x5AF2CE79,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_SmudgeTypeClass = {0x5AF2CE78,0x0634,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_AnimTypeClass = {0xAE8B33DA,0x061C,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_HouseTypeClass = {0x1DD43928,0x046B,0x11D2,{0xAC,0xA4,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_IsometricTileClass = {0x0E272DC0,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_VoxelAnimClass = {0x0E272DC1,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_AircraftClass = {0x0E272DC2,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_AnimClass = {0x0E272DC3,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_InfantryClass = {0x0E272DC4,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_SmudgeClass = {0x0E272DC5,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_BuildingClass = {0x0E272DC6,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_OverlayClass = {0x0E272DC7,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_ParticleSystemClass = {0x0E272DC8,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_ParticleSystemTypeClass = {0x703E044A,0x0FB1,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_BulletClass = {0x0E272DC9,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_UnitClass = {0x0E272DCA,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_ParticleClass = {0x0E272DCC,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_ParticleTypeClass = {0x703E044B,0x0FB1,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_WaveClass = {0x0E272DCD,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_BuildingLightClass = {0x54822258,0xD8A8,0x11D1,{0xB4,0x62,0x00,0x60,0x97,0xC6,0xA9,0x79}};
-
-
-const CLSID CLSID_TerrainClass = {0x0E272DCE,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_TubeClass = {0x0B4CA41C,0xB3A7,0x11D1,{0xB4,0x57,0x00,0x60,0x97,0xC6,0xA9,0x79}};
-
-
-const CLSID CLSID_TeamClass = {0x0E272DCF,0x9C0F,0x11D1,{0xB7,0x09,0x00,0xA0,0x24,0xDD,0xAF,0xD1}};
-
-
-const CLSID CLSID_TaskForceClass = {0x61DE341E,0x0774,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_TeamTypeClass = {0xD1DBA64E,0x0778,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_VoxelAnimTypeClass = {0x2EBB6D66,0x0D4D,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_ScriptClass = {0x42F3A646,0x0789,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_ScriptTypeClass = {0x42F3A647,0x0789,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_TagClass = {0x54F6E432,0x09ED,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_TagTypeClass = {0x54F6E433,0x09ED,0x11D2,{0xAC,0xA5,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_TriggerClass = {0xC02D1590,0x0A2A,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_TriggerTypeClass = {0xC02D1591,0x0A2A,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_ActionClass = {0x4F0EC392,0x0A55,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_EventClass = {0x4F0EC393,0x0A55,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_FactoryClass = {0x34ECD9A8,0x0AB0,0x11D2,{0xAC,0xA7,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_WeaponTypeClass = {0x9FD219CA,0x0F7B,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_WarheadTypeClass = {0xA8C54DA4,0x0F7B,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_WaypointPath = {0xF73125BA,0x1054,0x11D2,{0x81,0x72,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_LightSource = {0x6F9C48F0,0x1207,0x11D2,{0x81,0x74,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_CampaignClass = {0xFFDAC848,0x1517,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_SideClass = {0xC53DD372,0x151E,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_TiberiumClass = {0xC53DD373,0x151E,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_CellClass = {0xC1BF99CE,0x1A8C,0x11D2,{0x81,0x75,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_EMPulseClass = {0xB825CB22,0x200E,0x11D2,{0x9F,0xA9,0x00,0x60,0x08,0x9A,0xD4,0x58}};
-
-
-const CLSID CLSID_TacticalMapClass = {0xCF56B38A,0x240D,0x11D2,{0x81,0x7C,0x00,0x60,0x08,0x05,0x5B,0xB5}};
-
-
-const CLSID CLSID_AITriggerTypeClass = {0xBA093524,0x4CF4,0x11D2,{0xBC,0x26,0x00,0x10,0x4B,0x8F,0xB0,0x4D}};
-
-
-const CLSID CLSID_AITriggerClass = {0x03C4CE76,0x4CF5,0x11D2,{0xBC,0x26,0x00,0x10,0x4B,0x8F,0xB0,0x4D}};
-
-
-const CLSID CLSID_NeuronClass = {0x241AB316,0x4CF5,0x11D2,{0xBC,0x26,0x00,0x10,0x4B,0x8F,0xB0,0x4D}};
-
-
-const CLSID CLSID_FoggedObjectClass = {0x1C470B0E,0x69D7,0x11D2,{0xB8,0xF2,0x00,0x60,0x08,0xC8,0x09,0xED}};
-
-
-const CLSID CLSID_AlphaShapeClass = {0x623C7584,0x74E7,0x11D2,{0xB8,0xF5,0x00,0x60,0x08,0xC8,0x09,0xED}};
-
-
-const CLSID CLSID_VeinholeMonsterClass = {0x5192D06A,0xC632,0x11D2,{0xB9,0x0B,0x00,0x60,0x08,0xC8,0x09,0xED}};
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/code/jumpjet.cpp b/code/jumpjet.cpp
index 717456b26..61fd2c653 100644
--- a/code/jumpjet.cpp
+++ b/code/jumpjet.cpp
@@ -64,7 +64,7 @@ JumpjetLocomotionClass::~JumpjetLocomotionClass(void)
/// This asks whether the unit has a destination, not whether it happens to be in the air.
///
/// bool; Does the jumpjet have somewhere to be?
-boolean STDMETHODCALLTYPE JumpjetLocomotionClass::Is_Moving(void)
+bool JumpjetLocomotionClass::Is_Moving(void)
{
return(IsMoving);
}
@@ -75,7 +75,7 @@ boolean STDMETHODCALLTYPE JumpjetLocomotionClass::Is_Moving(void)
///
/// Returns with the destination coordinate. Otherwise, COORD_NONE is
/// returned.
-Coord STDMETHODCALLTYPE JumpjetLocomotionClass::Destination(void)
+Coord JumpjetLocomotionClass::Destination(void)
{
if (Is_Moving()) {
return(HeadToCoord);
@@ -92,7 +92,7 @@ Coord STDMETHODCALLTYPE JumpjetLocomotionClass::Destination(void)
/// and resubmits the object to the map when its display layer changes. An ion storm will
/// bring down anything caught off the ground.
///
-boolean STDMETHODCALLTYPE JumpjetLocomotionClass::Process(void)
+bool JumpjetLocomotionClass::Process(void)
{
LayerType layer = In_Which_Layer();
@@ -167,7 +167,7 @@ boolean STDMETHODCALLTYPE JumpjetLocomotionClass::Process(void)
///
/// The coordinate to fly to, or COORD_NONE to give the unit no
/// destination at all.
-void STDMETHODCALLTYPE JumpjetLocomotionClass::Move_To(Coord to)
+void JumpjetLocomotionClass::Move_To(Coord to)
{
if (HeadToCoord != COORD_NONE && CurrentState != GROUNDED && IsLanding) {
LinkedTo->Clear_Occupy_Bit(HeadToCoord);
@@ -200,7 +200,7 @@ void STDMETHODCALLTYPE JumpjetLocomotionClass::Move_To(Coord to)
/// it could put down in. A unit with nowhere at all to land is destroyed rather than left
/// hanging in the air.
///
-void STDMETHODCALLTYPE JumpjetLocomotionClass::Stop_Moving(void)
+void JumpjetLocomotionClass::Stop_Moving(void)
{
if (IsMoving) {
if (HeadToCoord != COORD_NONE && CurrentState != GROUNDED && IsLanding) {
@@ -230,23 +230,15 @@ void STDMETHODCALLTYPE JumpjetLocomotionClass::Stop_Moving(void)
/// through the locomotor's own facing tracker.
///
/// The direction the unit should be facing.
-void STDMETHODCALLTYPE JumpjetLocomotionClass::Do_Turn(DirType coord)
+void JumpjetLocomotionClass::Do_Turn(DirType coord)
{
LinkedTo->PrimaryFacing.Set(coord);
}
-///
-/// Fetches the class identifier of this locomotor.
-/// The persistence machinery uses the identifier to build the right kind of locomotor back
-/// when a save game is loaded.
-///
-/// Returns with S_OK, or E_POINTER if there is nowhere to put the answer.
-HRESULT STDMETHODCALLTYPE JumpjetLocomotionClass::GetClassID(CLSID * retval)
+ClassID JumpjetLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_JumpjetLocomotion;
- return(S_OK);
+ return(ClassID_JumpjetLocomotion);
}
@@ -277,7 +269,7 @@ void JumpjetLocomotionClass::Serialize(SaveStreamClass & stream)
/// measured from the bridge deck rather than from the ground.
///
/// Returns with the layer this object should be drawn in.
-LayerType STDMETHODCALLTYPE JumpjetLocomotionClass::In_Which_Layer(void)
+LayerType JumpjetLocomotionClass::In_Which_Layer(void)
{
int height = LinkedTo->HeightAGL;
if (!LinkedTo->IsOnBridge) {
@@ -479,7 +471,7 @@ void JumpjetLocomotionClass::Process_Unknown(void)
/// not it has been given a destination.
///
/// bool; Is the jumpjet in flight toward somewhere?
-boolean STDMETHODCALLTYPE JumpjetLocomotionClass::Is_Moving_Now(void)
+bool JumpjetLocomotionClass::Is_Moving_Now(void)
{
if (CurrentState != GROUNDED && CurrentState != HOVERING) {
return(true);
@@ -684,7 +676,7 @@ int JumpjetLocomotionClass::Desired_Flight_Level(void) const
/// that reservation is given up when the object is lifted off the map.
///
/// The marking operation being performed, such as MARK_UP.
-void STDMETHODCALLTYPE JumpjetLocomotionClass::Mark_All_Occupation_Bits(int mark)
+void JumpjetLocomotionClass::Mark_All_Occupation_Bits(int mark)
{
if (mark == MARK_UP) {
Coord headto = Head_To_Coord();
@@ -702,7 +694,7 @@ void STDMETHODCALLTYPE JumpjetLocomotionClass::Mark_All_Occupation_Bits(int mark
/// destination.
///
/// Returns with the coordinate being flown to.
-Coord STDMETHODCALLTYPE JumpjetLocomotionClass::Head_To_Coord(void)
+Coord JumpjetLocomotionClass::Head_To_Coord(void)
{
if (CurrentState == GROUNDED) {
return(LinkedTo->PositionCoord);
diff --git a/code/jumpjet.h b/code/jumpjet.h
index e8bc9d07e..5a3872031 100644
--- a/code/jumpjet.h
+++ b/code/jumpjet.h
@@ -25,20 +25,20 @@ class JumpjetLocomotionClass : public LocomotionClass
JumpjetLocomotionClass(void);
virtual ~JumpjetLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) override;
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual Coord Head_To_Coord(void) override;
+ virtual bool Process(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual void Do_Turn(DirType coord) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual void Mark_All_Occupation_Bits(int mark) override;
/*---------------------------------------------------------------------
** Member function prototypes.
diff --git a/code/layer.cpp b/code/layer.cpp
index 13ef01533..4c91cf6d9 100644
--- a/code/layer.cpp
+++ b/code/layer.cpp
@@ -151,17 +151,12 @@ int LayerClass::Sorted_Add(ObjectClass const * const object)
/// are written by their own owners -- only the layer's object pointers are recorded here,
/// to be swizzled back into real addresses when the game is loaded.
///
-/// Returns with S_OK if the layer was written. Otherwise, the failure code from
-/// the stream is returned.
-HRESULT LayerClass::Save(IStream * stream)
+/// bool; Was the record written whole?
+bool LayerClass::Save(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(E_POINTER);
- }
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- DynamicVectorClass::Serialize(savestream);
- return(savestream.Result());
+ DynamicVectorClass::Serialize(stream);
+ return(!stream.Was_Error());
}
@@ -171,16 +166,11 @@ HRESULT LayerClass::Save(IStream * stream)
/// reconstructed. Whatever the layer was holding is discarded and the object pointers are
/// read back, so they do not become usable until the swizzle pass has run.
///
-/// Returns with S_OK if the layer was read. Otherwise, the failure code from the
-/// stream is returned.
-HRESULT LayerClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool LayerClass::Load(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(E_POINTER);
- }
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("LayerClass");
- DynamicVectorClass::Serialize(savestream);
- return(savestream.Result());
+ stream.Set_Context("LayerClass");
+ DynamicVectorClass::Serialize(stream);
+ return(!stream.Was_Error());
}
diff --git a/code/layer.h b/code/layer.h
index ffa129bc1..25dc07af0 100644
--- a/code/layer.h
+++ b/code/layer.h
@@ -40,8 +40,8 @@ class ObjectClass;
class LayerClass : public DynamicVectorClass
{
public:
- HRESULT Load(IStream * stream);
- HRESULT Save(IStream * stream);
+ bool Load(SaveStreamClass & stream);
+ bool Save(SaveStreamClass & stream);
public:
diff --git a/code/levitate.cpp b/code/levitate.cpp
index 28836e322..616718351 100644
--- a/code/levitate.cpp
+++ b/code/levitate.cpp
@@ -77,9 +77,9 @@ LevitateLocomotionClass::LevitateLocomotionClass(void) :
///
/// Pointer to the object this locomotor will drive.
/// Returns with the result of the attach operation.
-HRESULT LevitateLocomotionClass::Link_To_Object(void *pointer)
+void LevitateLocomotionClass::Link_To_Object(void *pointer)
{
- return(BASECLASS::Link_To_Object(pointer));
+ BASECLASS::Link_To_Object(pointer);
}
@@ -825,7 +825,7 @@ bool LevitateLocomotionClass::Needs_New_Target(void)
/// the vertical hover (Hover_AI).
///
/// True while the unit is still moving.
-boolean LevitateLocomotionClass::Process(void)
+bool LevitateLocomotionClass::Process(void)
{
State_AI();
@@ -846,7 +846,7 @@ boolean LevitateLocomotionClass::Process(void)
/// Reports whether the locomotor is in any state other than STATE_IDLE.
///
/// True while moving.
-boolean LevitateLocomotionClass::Is_Moving(void)
+bool LevitateLocomotionClass::Is_Moving(void)
{
return(State != STATE_IDLE);
}
@@ -856,7 +856,7 @@ boolean LevitateLocomotionClass::Is_Moving(void)
/// Reports whether the locomotor is in any state other than STATE_IDLE (identical to Is_Moving).
///
/// True while moving.
-boolean LevitateLocomotionClass::Is_Moving_Now(void)
+bool LevitateLocomotionClass::Is_Moving_Now(void)
{
return(State != STATE_IDLE);
}
@@ -892,18 +892,9 @@ void LevitateLocomotionClass::Stop(void)
}
-///
-/// Fetches the class identifier of this locomotor.
-/// The persistence system uses this identifier to create a locomotor of the right kind
-/// when the object it drives is loaded back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT LevitateLocomotionClass::GetClassID(CLSID * retval)
+ClassID LevitateLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_LevitateLocomotion;
- return(S_OK);
+ return(ClassID_LevitateLocomotion);
}
diff --git a/code/levitate.h b/code/levitate.h
index bd273ba6b..e5bd84473 100644
--- a/code/levitate.h
+++ b/code/levitate.h
@@ -28,18 +28,18 @@ class LevitateLocomotionClass : public LocomotionClass
LevitateLocomotionClass(void);
virtual ~LevitateLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual HRESULT STDMETHODCALLTYPE Link_To_Object(void *pointer) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) override;
+ virtual void Link_To_Object(void *pointer) override;
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual Coord Head_To_Coord(void) override;
+ virtual bool Process(void) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual void Mark_All_Occupation_Bits(int mark) override;
/*---------------------------------------------------------------------
diff --git a/code/light.cpp b/code/light.cpp
index 8ff5eacec..40c6bb860 100644
--- a/code/light.cpp
+++ b/code/light.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "light.h"
@@ -278,16 +277,9 @@ void LightSourceClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier this object is persisted under.
-///
-/// Destination for the class identifier.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE LightSourceClass::GetClassID(CLSID * retval)
+ClassID LightSourceClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_LightSource;
- return(S_OK);
+ return(ClassID_LightSource);
}
diff --git a/code/light.h b/code/light.h
index 0e875e507..9585de25c 100644
--- a/code/light.h
+++ b/code/light.h
@@ -25,7 +25,7 @@ class LightSourceClass : public AbstractClass
LightSourceClass(void);
virtual ~LightSourceClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/loco.cpp b/code/loco.cpp
index 7030a98fd..f75059f61 100644
--- a/code/loco.cpp
+++ b/code/loco.cpp
@@ -14,10 +14,12 @@
#include "_map.h"
#include "_tactica.h"
#include "cell.h"
+#include "classfactory.h"
#include "coord.h"
#include "foot.h"
#include "globals.h"
#include "map.h"
+#include "saveload.h"
#include "savestream.h"
#include "swizzle.h"
#include "tactical.h"
@@ -28,8 +30,8 @@
#include "zgrad.hh"
#include
+#include
-extern ULONG COMRefCount;
///
@@ -41,8 +43,7 @@ extern ULONG COMRefCount;
LocomotionClass::LocomotionClass(void) :
LinkedTo(NULL),
IsPowered(true),
- Dirty(true),
- RefCount(0)
+ Dirty(true)
{
}
@@ -62,11 +63,9 @@ LocomotionClass::~LocomotionClass(void)
/// offers depends on it having been called first.
///
/// Pointer to the foot class object this locomotor will carry about.
-/// Returns with S_OK, since the attachment cannot fail.
-HRESULT STDMETHODCALLTYPE LocomotionClass::Link_To_Object(void *pointer)
+void LocomotionClass::Link_To_Object(void *pointer)
{
LinkedTo = (FootClass *)pointer;
- return(S_OK);
}
@@ -79,7 +78,7 @@ HRESULT STDMETHODCALLTYPE LocomotionClass::Link_To_Object(void *pointer)
/// Optional cache key for the voxel renderer, which the facing is folded
/// into. May be NULL, and a key of -1 means the drawing is not to be cached.
/// Returns with the matrix to transform the object by.
-Matrix3D STDMETHODCALLTYPE LocomotionClass::Draw_Matrix(int *key)
+Matrix3D LocomotionClass::Draw_Matrix(int *key)
{
Matrix3D draw_matrix(true);
@@ -101,7 +100,7 @@ Matrix3D STDMETHODCALLTYPE LocomotionClass::Draw_Matrix(int *key)
/// Optional cache key for the voxel renderer, which the slope and facing are
/// folded into. May be NULL, and a key of -1 means the shadow is not to be cached.
/// Returns with the matrix to transform the shadow by.
-Matrix3D STDMETHODCALLTYPE LocomotionClass::Shadow_Matrix(int *key)
+Matrix3D LocomotionClass::Shadow_Matrix(int *key)
{
int ramp = Map[LinkedTo->Get_Coord()].Ramp;
@@ -122,7 +121,7 @@ Matrix3D STDMETHODCALLTYPE LocomotionClass::Shadow_Matrix(int *key)
/// down by however far the object is flying above the terrain.
///
/// Returns with the pixel offset to shift the shadow by when drawing.
-Point2D STDMETHODCALLTYPE LocomotionClass::Shadow_Point(void)
+Point2D LocomotionClass::Shadow_Point(void)
{
Point2D pt;
@@ -139,7 +138,7 @@ Point2D STDMETHODCALLTYPE LocomotionClass::Shadow_Point(void)
/// more.
///
/// bool; Is the locomotor powered after the change?
-boolean STDMETHODCALLTYPE LocomotionClass::Power_On(void)
+bool LocomotionClass::Power_On(void)
{
IsPowered = true;
return(Is_Powered());
@@ -152,7 +151,7 @@ boolean STDMETHODCALLTYPE LocomotionClass::Power_On(void)
/// by an EMP pulse or its owner loses base power.
///
/// bool; Is the locomotor powered after the change?
-boolean STDMETHODCALLTYPE LocomotionClass::Power_Off(void)
+bool LocomotionClass::Power_Off(void)
{
IsPowered = false;
return(Is_Powered());
@@ -165,7 +164,7 @@ boolean STDMETHODCALLTYPE LocomotionClass::Power_Off(void)
/// loss of base power leaves a unit stranded.
///
/// bool; Is the locomotor powered?
-boolean STDMETHODCALLTYPE LocomotionClass::Is_Powered(void)
+bool LocomotionClass::Is_Powered(void)
{
return(IsPowered);
}
@@ -177,79 +176,33 @@ boolean STDMETHODCALLTYPE LocomotionClass::Is_Powered(void)
/// this routine so that a storm can bring their objects down.
///
/// bool; Is the locomotor sensitive to ion storms?
-boolean STDMETHODCALLTYPE LocomotionClass::Is_Ion_Sensitive(void)
+bool LocomotionClass::Is_Ion_Sensitive(void)
{
return(false);
}
-///
-/// Adds a reference to this locomotor.
-/// Anything that holds on to a locomotor takes a reference first, which keeps the
-/// locomotor alive until that holder releases it again.
-///
-/// Returns with the number of references now outstanding.
-ULONG STDMETHODCALLTYPE LocomotionClass::AddRef(void)
+std::unique_ptr Create_Locomotor(ClassID const & classid)
{
- ++COMRefCount;
- return(InterlockedIncrement(&RefCount));
+ std::unique_ptr object = Create_Object(classid);
+ ILocomotion * const locomotion = dynamic_cast(object.get());
+ if (locomotion != nullptr) {
+ object.release();
+ }
+ return(std::unique_ptr(locomotion));
}
-///
-/// Releases a reference to this locomotor.
-/// When the last reference goes away the locomotor destroys itself, so the caller must
-/// not touch its pointer afterward.
-///
-/// Returns with the number of references still outstanding.
-ULONG STDMETHODCALLTYPE LocomotionClass::Release(void)
+std::unique_ptr Load_Locomotor(SaveStreamClass & stream)
{
- --COMRefCount;
-
- ULONG count = InterlockedDecrement(&RefCount);
- if (count == 0) {
- delete this;
- }
- return(count);
+ return(Load_Object_As(stream));
}
-///
-/// Fetches one of the interfaces this locomotor implements.
-/// A locomotor answers to IUnknown, IPersist, IPersistStream, and ILocomotion. Any other
-/// interface asked for is refused.
-///
-/// The identifier of the interface being asked for.
-/// Pointer to the location to store the interface pointer in.
-/// Returns with S_OK, or E_NOINTERFACE if the interface is not supported.
-/// An interface fetched successfully carries a reference. The caller must release
-/// it when finished with it.
-LONG STDMETHODCALLTYPE LocomotionClass::QueryInterface(REFIID riid, LPVOID *ppvObject)
+ClassID Locomotion_Class_ID(ILocomotion * locomotion)
{
- if (ppvObject == NULL) {
- return(E_POINTER);
- }
-
- *ppvObject = NULL;
-
- if (riid == IID_IUnknown) {
- *ppvObject = (IUnknown *)(ILocomotion *)this;
- }
- if (riid == IID_IPersistStream) {
- *ppvObject = (IPersistStream *)this;
- }
- if (riid == IID_ILocomotion) {
- *ppvObject = (ILocomotion *)this;
- }
- if (riid == IID_IPersist) {
- *ppvObject = (IPersist *)this;
- }
- if (*ppvObject == NULL) {
- return(E_NOINTERFACE);
- }
-
- AddRef();
- return(S_OK);
+ IPersistent const * const persist = dynamic_cast(locomotion);
+ return(persist != nullptr ? persist->Class_ID() : ClassID());
}
@@ -259,32 +212,15 @@ LONG STDMETHODCALLTYPE LocomotionClass::QueryInterface(REFIID riid, LPVOID *ppvO
/// swizzle manager remap every pointer to it when the game is loaded again.
///
/// Should the locomotor be marked as no longer needing a save?
-/// Returns with the result of the write, or E_POINTER if no stream was supplied.
-HRESULT STDMETHODCALLTYPE LocomotionClass::Save(IStream * stream, BOOL cleardirty)
+/// Returns with the result of the write.
+bool LocomotionClass::Save(SaveStreamClass & stream, bool cleardirty)
{
- if (stream == NULL) {
- return(E_POINTER); /// E_INVALIDARG
- }
-
return(Save_Members(stream, cleardirty));
}
-///
-/// Loads the locomotor back from a save game stream.
-/// The locomotor announces its new address to the swizzle manager before its data is
-/// read in, so that every saved pointer to it can be remapped and its link back to the
-/// object it drives can be restored. The reference count belongs to the running session
-/// rather than to the saved state, so it survives the load untouched.
-///
-/// The stream to read the locomotor back from.
-/// Returns with the result of the read, or E_POINTER if no stream was supplied.
-HRESULT STDMETHODCALLTYPE LocomotionClass::Load(IStream * stream)
+bool LocomotionClass::Load(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(E_POINTER); /// E_INVALIDARG
- }
-
return(Load_Members(stream));
}
@@ -296,77 +232,44 @@ HRESULT STDMETHODCALLTYPE LocomotionClass::Load(IStream * stream)
///
/// The stream to write to.
/// Should the locomotor be marked clean once it has been written?
-/// Returns with S_OK when the record was written, otherwise a failure code.
-HRESULT LocomotionClass::Save_Members(IStream * stream, BOOL cleardirty)
+/// bool; Was the record written whole?
+bool LocomotionClass::Save_Members(SaveStreamClass & stream, bool cleardirty)
{
- if (stream == NULL) {
- return(E_POINTER);
- }
-
SwizzleIDType id = Swizzler.ID_Of(this);
-
- HRESULT result = stream->Write(&id, sizeof(id), NULL);
- if (FAILED(result)) {
- return(result);
- }
-
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
-
- if (SUCCEEDED(savestream.Result()) && cleardirty) {
+ stream.Serialize(id);
+ Serialize(stream);
+ if (!stream.Was_Error() && cleardirty) {
Dirty = false;
}
-
- return(savestream.Result());
+ return(!stream.Was_Error());
}
-///
-/// Reads the members this locomotor describes back from the save stream.
-/// The saved identity is handed to the swizzle system so that pointers elsewhere in the
-/// save game can be remapped onto this locomotor, and the members follow.
-///
-/// The stream to read from.
-/// Returns with S_OK when the record was read, otherwise a failure code.
-HRESULT LocomotionClass::Load_Members(IStream * stream)
+bool LocomotionClass::Load_Members(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(E_POINTER);
- }
-
- SwizzleIDType id;
-
- HRESULT result = stream->Read(&id, sizeof(id), NULL);
- if (FAILED(result)) {
- return(result);
+ SwizzleIDType id = 0;
+ stream.Serialize(id);
+ if (stream.Was_Error()) {
+ return(false);
}
-
assert(id != 0);
Swizzle_Here_I_Am(id, this);
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context(typeid(*this).name(), id);
- Serialize(savestream);
+ char const * const outertype = stream.Context_Type();
+ SwizzleIDType const outerid = stream.Context_ID();
+ stream.Set_Context(typeid(*this).name(), id);
+ Serialize(stream);
+ stream.Set_Context(outertype, outerid);
- if (SUCCEEDED(savestream.Result())) {
- Post_Load();
- }
-
- return(savestream.Result());
+ return(!stream.Was_Error());
}
-///
-/// Lists the members every locomotor carries.
-///
-/// The stream carrying the members.
void LocomotionClass::Serialize(SaveStreamClass & stream)
{
stream.Serialize(LinkedTo);
stream.Serialize(IsPowered);
stream.Serialize(Dirty);
-
- // RefCount -- belongs to the running session rather than the record.
}
@@ -379,27 +282,13 @@ void LocomotionClass::Post_Load(void)
}
-///
-/// Fetches the number of bytes needed to save this locomotor.
-/// A record is as long as the members a class names, so the count is not known before
-/// the members have been written. Nothing in the game asks for it, so rather than
-/// walk the locomotor twice this reports that the size cannot be supplied.
-///
-/// Pointer to the value to fill in with the required byte count.
-/// Returns with E_NOTIMPL.
-LONG STDMETHODCALLTYPE LocomotionClass::GetSizeMax(ULARGE_INTEGER *pcbSize)
-{
- return(E_NOTIMPL);
-}
-
-
///
/// Asks the object to step out of the way in the direction specified.
/// This routine is used when another object needs the cell this one happens to be
/// occupying. The base locomotor cannot be moved and declines.
///
/// bool; Did the object step out of the way?
-boolean STDMETHODCALLTYPE LocomotionClass::Push(DirType dir)
+bool LocomotionClass::Push(DirType dir)
{
return(false);
}
@@ -411,7 +300,7 @@ boolean STDMETHODCALLTYPE LocomotionClass::Push(DirType dir)
/// displaced to clear the way. The base locomotor will not budge.
///
/// bool; Was the object shoved out of the way?
-boolean STDMETHODCALLTYPE LocomotionClass::Shove(DirType dir)
+bool LocomotionClass::Shove(DirType dir)
{
return(false);
}
@@ -422,7 +311,7 @@ boolean STDMETHODCALLTYPE LocomotionClass::Shove(DirType dir)
/// Locomotors that rock their object about -- over bumps, on landing, or when it takes a
/// hit -- use this routine to ease the body back toward level.
///
-void STDMETHODCALLTYPE LocomotionClass::Tilt_Pitch_AI(void)
+void LocomotionClass::Tilt_Pitch_AI(void)
{
}
@@ -433,7 +322,7 @@ void STDMETHODCALLTYPE LocomotionClass::Tilt_Pitch_AI(void)
/// against the terrain it is traveling over. The base locomotor needs no such favor.
///
/// Returns with the depth adjustment to apply when drawing the object.
-int STDMETHODCALLTYPE LocomotionClass::Z_Adjust(void)
+int LocomotionClass::Z_Adjust(void)
{
return(0);
}
@@ -445,7 +334,7 @@ int STDMETHODCALLTYPE LocomotionClass::Z_Adjust(void)
/// shape. The base locomotor reports the upright case.
///
/// Returns with the Z gradient to render the object with.
-ZGradientType STDMETHODCALLTYPE LocomotionClass::Z_Gradient(void)
+ZGradientType LocomotionClass::Z_Gradient(void)
{
return(ZGRAD_90DEG);
}
@@ -457,7 +346,7 @@ ZGradientType STDMETHODCALLTYPE LocomotionClass::Z_Gradient(void)
/// otherwise leaves plain sight. The base locomotor never alters the appearance.
///
/// Returns with the visual character to render the object with.
-VisualType STDMETHODCALLTYPE LocomotionClass::Visual_Character(boolean flag)
+VisualType LocomotionClass::Visual_Character(bool flag)
{
return(VISUAL_NORMAL);
}
@@ -469,7 +358,7 @@ VisualType STDMETHODCALLTYPE LocomotionClass::Visual_Character(boolean flag)
/// locomotor makes its object bob, hop, or sink. The base locomotor draws in place.
///
/// Returns with the pixel offset to shift the object by when drawing.
-Point2D STDMETHODCALLTYPE LocomotionClass::Draw_Point(void)
+Point2D LocomotionClass::Draw_Point(void)
{
Point2D pt;
pt.X = 0;
@@ -484,7 +373,7 @@ Point2D STDMETHODCALLTYPE LocomotionClass::Draw_Point(void)
/// otherwise hidden -- will override this routine to suppress the shadow.
///
/// bool; Should a shadow be drawn for the object?
-boolean STDMETHODCALLTYPE LocomotionClass::Is_To_Have_Shadow(void)
+bool LocomotionClass::Is_To_Have_Shadow(void)
{
return(true);
}
@@ -497,7 +386,7 @@ boolean STDMETHODCALLTYPE LocomotionClass::Is_To_Have_Shadow(void)
/// unrestricted and welcomes every cell.
///
/// Returns with the move legality of the cell.
-MoveType STDMETHODCALLTYPE LocomotionClass::Can_Enter_Cell(Cell cell)
+MoveType LocomotionClass::Can_Enter_Cell(Cell cell)
{
return(MOVE_OK);
}
@@ -509,7 +398,7 @@ MoveType STDMETHODCALLTYPE LocomotionClass::Can_Enter_Cell(Cell cell)
/// outside code must dictate exactly where the object ends up next.
///
/// The coordinate the object should head to immediately.
-void STDMETHODCALLTYPE LocomotionClass::Force_Immediate_Destination(Coord coord)
+void LocomotionClass::Force_Immediate_Destination(Coord coord)
{
}
@@ -521,7 +410,7 @@ void STDMETHODCALLTYPE LocomotionClass::Force_Immediate_Destination(Coord coord)
///
/// The track number the object should be placed onto.
/// The coordinate to treat as the start of the track.
-void STDMETHODCALLTYPE LocomotionClass::Force_Track(int track, Coord coord)
+void LocomotionClass::Force_Track(int track, Coord coord)
{
}
@@ -531,7 +420,7 @@ void STDMETHODCALLTYPE LocomotionClass::Force_Track(int track, Coord coord)
/// This gives derived locomotors their chance to pick up a starting facing, slope, or
/// altitude from the ground the object has just arrived on.
///
-void STDMETHODCALLTYPE LocomotionClass::Unlimbo(void)
+void LocomotionClass::Unlimbo(void)
{
}
@@ -541,7 +430,7 @@ void STDMETHODCALLTYPE LocomotionClass::Unlimbo(void)
/// The base locomotor has no body of its own to rotate, so the request goes unheeded.
///
/// The direction that the object should come to face.
-void STDMETHODCALLTYPE LocomotionClass::Do_Turn(DirType coord)
+void LocomotionClass::Do_Turn(DirType coord)
{
}
@@ -551,7 +440,7 @@ void STDMETHODCALLTYPE LocomotionClass::Do_Turn(DirType coord)
/// This routine is called when the object must give up on wherever it was going. Derived
/// locomotors use it to abandon their journey and bring the object to a legal rest.
///
-void STDMETHODCALLTYPE LocomotionClass::Stop_Moving(void)
+void LocomotionClass::Stop_Moving(void)
{
}
@@ -561,7 +450,7 @@ void STDMETHODCALLTYPE LocomotionClass::Stop_Moving(void)
/// This is how the object hands its locomotor a new place to go. The base locomotor
/// cannot move anything, so the request is quietly ignored.
///
-void STDMETHODCALLTYPE LocomotionClass::Move_To(Coord to)
+void LocomotionClass::Move_To(Coord to)
{
}
@@ -573,7 +462,7 @@ void STDMETHODCALLTYPE LocomotionClass::Move_To(Coord to)
/// is already at rest.
///
/// bool; Is the locomotor at rest, with nothing further to do?
-boolean STDMETHODCALLTYPE LocomotionClass::Process(void)
+bool LocomotionClass::Process(void)
{
return(true);
}
@@ -585,7 +474,7 @@ boolean STDMETHODCALLTYPE LocomotionClass::Process(void)
/// destination at all.
///
/// Returns with the destination coordinate, or COORD_NONE if there is none.
-Coord STDMETHODCALLTYPE LocomotionClass::Destination(void)
+Coord LocomotionClass::Destination(void)
{
Coord coord;
coord.X = COORD_NONE.X;
@@ -601,7 +490,7 @@ Coord STDMETHODCALLTYPE LocomotionClass::Destination(void)
/// has nowhere to go, it reports the object's own position.
///
/// Returns with the coordinate currently being moved toward.
-Coord STDMETHODCALLTYPE LocomotionClass::Head_To_Coord(void)
+Coord LocomotionClass::Head_To_Coord(void)
{
return(LinkedTo->PositionCoord);
}
@@ -613,7 +502,7 @@ Coord STDMETHODCALLTYPE LocomotionClass::Head_To_Coord(void)
/// The base locomotor never carries its object anywhere, so it always answers no.
///
/// bool; Is the object moving?
-boolean STDMETHODCALLTYPE LocomotionClass::Is_Moving(void)
+bool LocomotionClass::Is_Moving(void)
{
return(false);
}
@@ -625,7 +514,7 @@ boolean STDMETHODCALLTYPE LocomotionClass::Is_Moving(void)
/// Only locomotors that tilt their object with the ground need to act on it.
///
/// The ramp type of the slope the object should now conform to.
-void STDMETHODCALLTYPE LocomotionClass::Force_New_Slope(int ramp)
+void LocomotionClass::Force_New_Slope(int ramp)
{
}
@@ -636,7 +525,7 @@ void STDMETHODCALLTYPE LocomotionClass::Force_New_Slope(int ramp)
/// currently traveling. The base locomotor has no preference.
///
/// Returns with the drawing code, or zero for the ordinary presentation.
-int STDMETHODCALLTYPE LocomotionClass::Drawing_Code(void)
+int LocomotionClass::Drawing_Code(void)
{
return(0);
}
@@ -648,7 +537,7 @@ int STDMETHODCALLTYPE LocomotionClass::Drawing_Code(void)
/// will override this routine. The base locomotor never stands in the way.
///
/// Returns with the reason firing is disallowed, or FIRE_OK if it is permitted.
-FireErrorType STDMETHODCALLTYPE LocomotionClass::Can_Fire(void)
+FireErrorType LocomotionClass::Can_Fire(void)
{
return(FIRE_OK);
}
@@ -660,14 +549,7 @@ FireErrorType STDMETHODCALLTYPE LocomotionClass::Can_Fire(void)
/// visible motion differs from the object's logical speed will override this routine.
///
/// Returns with the apparent speed of the linked object.
-int STDMETHODCALLTYPE LocomotionClass::Apparent_Speed(void)
+int LocomotionClass::Apparent_Speed(void)
{
return(LinkedTo->Current_Speed());
}
-
-
-/// Unlike the other interface identifiers, this one is defined in the locomotion module.
-#define INITGUID
-#undef DEFINE_GUID
-#include
-#include "iloco_i.c"
diff --git a/code/loco.h b/code/loco.h
index ba9f5f627..dc00c0c92 100644
--- a/code/loco.h
+++ b/code/loco.h
@@ -9,72 +9,83 @@
#pragma once
+#include "classids.h"
#include "coord.h"
-#include "ilocos.h"
+#include "iloco.h"
+#include "persist.h"
+
+#include
class FootClass;
class SaveStreamClass;
-class LocomotionClass : public IPersistStream, public ILocomotion
+// The class identifier of a locomotor reached through its locomotion interface, or
+// all zero when it is not one of ours.
+ClassID Locomotion_Class_ID(ILocomotion * locomotion);
+
+// A new, unlinked locomotor of the registered class, or nothing when the identifier
+// names no locomotor.
+std::unique_ptr Create_Locomotor(ClassID const & classid);
+
+// The locomotor whose record is next in the stream, or nothing when the record names
+// something that is not one, which fails the stream.
+std::unique_ptr Load_Locomotor(SaveStreamClass & stream);
+
+
+class LocomotionClass : public IPersistent, public ILocomotion
{
public:
LocomotionClass(void);
virtual ~LocomotionClass(void);
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) override;
- virtual ULONG STDMETHODCALLTYPE AddRef() override;
- virtual ULONG STDMETHODCALLTYPE Release() override;
-
- virtual LONG STDMETHODCALLTYPE IsDirty(void) override {return(Dirty ? S_OK : S_FALSE);}
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
- virtual HRESULT STDMETHODCALLTYPE Save(IStream * stream, BOOL cleardirty) override;
- virtual LONG STDMETHODCALLTYPE GetSizeMax(ULARGE_INTEGER *pcbSize) override;
-
- virtual HRESULT STDMETHODCALLTYPE Link_To_Object(void *object) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) override;
- virtual MoveType STDMETHODCALLTYPE Can_Enter_Cell(Cell cell) override;
- virtual boolean STDMETHODCALLTYPE Is_To_Have_Shadow(void) override;
- virtual Matrix3D STDMETHODCALLTYPE Draw_Matrix(int *key) override;
- virtual Matrix3D STDMETHODCALLTYPE Shadow_Matrix(int *key) override;
- virtual Point2D STDMETHODCALLTYPE Draw_Point(void) override;
- virtual Point2D STDMETHODCALLTYPE Shadow_Point(void) override;
- virtual VisualType STDMETHODCALLTYPE Visual_Character(boolean flag) override;
- virtual int STDMETHODCALLTYPE Z_Adjust(void) override;
- virtual ZGradientType STDMETHODCALLTYPE Z_Gradient(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) override;
- virtual void STDMETHODCALLTYPE Unlimbo(void) override;
- virtual void STDMETHODCALLTYPE Tilt_Pitch_AI(void) override;
- virtual boolean STDMETHODCALLTYPE Power_On(void) override;
- virtual boolean STDMETHODCALLTYPE Power_Off(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Powered(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Ion_Sensitive(void) override;
- virtual boolean STDMETHODCALLTYPE Push(DirType dir) override;
- virtual boolean STDMETHODCALLTYPE Shove(DirType dir) override;
- virtual void STDMETHODCALLTYPE Force_Track(int track, Coord coord) override;
- virtual void STDMETHODCALLTYPE Force_Immediate_Destination(Coord coord) override;
- virtual void STDMETHODCALLTYPE Force_New_Slope(int ramp) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override {return(Is_Moving());}
- virtual int STDMETHODCALLTYPE Apparent_Speed(void) override;
- virtual int STDMETHODCALLTYPE Drawing_Code(void) override;
- virtual FireErrorType STDMETHODCALLTYPE Can_Fire(void) override;
- virtual int STDMETHODCALLTYPE Get_Status() override {return(0);}
- virtual void STDMETHODCALLTYPE Acquire_Hunter_Seeker_Target(void) override {}
- virtual boolean STDMETHODCALLTYPE Is_Surfacing() override {return(false);}
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) override {}
- virtual boolean STDMETHODCALLTYPE Is_Moving_Here(Coord to) override {return(false);}
- virtual boolean STDMETHODCALLTYPE Will_Jump_Tracks(void) override {return(false);}
- virtual boolean STDMETHODCALLTYPE Is_Really_Moving_Now(void) override {return(Is_Moving_Now());}
- virtual void STDMETHODCALLTYPE Stop_Movement_Animation(void) override {}
- virtual void STDMETHODCALLTYPE Lock(void) override {}
- virtual void STDMETHODCALLTYPE Unlock(void) override {}
- virtual int STDMETHODCALLTYPE Get_Track_Number(void) override {return(-1);}
- virtual int STDMETHODCALLTYPE Get_Track_Index(void) override {return(-1);}
- virtual int STDMETHODCALLTYPE Get_Speed_Accum(void) override {return(-1);}
+ virtual bool Load(SaveStreamClass & stream) override;
+ virtual bool Save(SaveStreamClass & stream, bool cleardirty) override;
+
+ virtual void Link_To_Object(void *object) override;
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual Coord Head_To_Coord(void) override;
+ virtual MoveType Can_Enter_Cell(Cell cell) override;
+ virtual bool Is_To_Have_Shadow(void) override;
+ virtual Matrix3D Draw_Matrix(int *key) override;
+ virtual Matrix3D Shadow_Matrix(int *key) override;
+ virtual Point2D Draw_Point(void) override;
+ virtual Point2D Shadow_Point(void) override;
+ virtual VisualType Visual_Character(bool flag) override;
+ virtual int Z_Adjust(void) override;
+ virtual ZGradientType Z_Gradient(void) override;
+ virtual bool Process(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual void Do_Turn(DirType coord) override;
+ virtual void Unlimbo(void) override;
+ virtual void Tilt_Pitch_AI(void) override;
+ virtual bool Power_On(void) override;
+ virtual bool Power_Off(void) override;
+ virtual bool Is_Powered(void) override;
+ virtual bool Is_Ion_Sensitive(void) override;
+ virtual bool Push(DirType dir) override;
+ virtual bool Shove(DirType dir) override;
+ virtual void Force_Track(int track, Coord coord) override;
+ virtual void Force_Immediate_Destination(Coord coord) override;
+ virtual void Force_New_Slope(int ramp) override;
+ virtual bool Is_Moving_Now(void) override {return(Is_Moving());}
+ virtual int Apparent_Speed(void) override;
+ virtual int Drawing_Code(void) override;
+ virtual FireErrorType Can_Fire(void) override;
+ virtual int Get_Status() override {return(0);}
+ virtual void Acquire_Hunter_Seeker_Target(void) override {}
+ virtual bool Is_Surfacing() override {return(false);}
+ virtual void Mark_All_Occupation_Bits(int mark) override {}
+ virtual bool Is_Moving_Here(Coord to) override {return(false);}
+ virtual bool Will_Jump_Tracks(void) override {return(false);}
+ virtual bool Is_Really_Moving_Now(void) override {return(Is_Moving_Now());}
+ virtual void Stop_Movement_Animation(void) override {}
+ virtual void Lock(void) override {}
+ virtual void Unlock(void) override {}
+ virtual int Get_Track_Number(void) override {return(-1);}
+ virtual int Get_Track_Index(void) override {return(-1);}
+ virtual int Get_Speed_Accum(void) override {return(-1);}
/*
@@ -85,9 +96,9 @@ class LocomotionClass : public IPersistStream, public ILocomotion
virtual void Serialize(SaveStreamClass & stream);
/*
- * Restores whatever the record could not carry. Load_Members calls this once the
- * members are in place, so a base class fixup runs even when the load was entered
- * through a derived class.
+ * Restores whatever the record could not carry. Load_Object calls this once the
+ * record has been checked, so a locomotor never takes its place while its record
+ * is still in doubt.
*/
virtual void Post_Load(void);
@@ -98,8 +109,8 @@ class LocomotionClass : public IPersistStream, public ILocomotion
* from its Load and Save; the record is the swizzle identity followed by whatever
* members the class names.
*/
- HRESULT Save_Members(IStream * stream, BOOL cleardirty);
- HRESULT Load_Members(IStream * stream);
+ bool Save_Members(SaveStreamClass & stream, bool cleardirty);
+ bool Load_Members(SaveStreamClass & stream);
protected:
/*
@@ -121,11 +132,4 @@ class LocomotionClass : public IPersistStream, public ILocomotion
* persistence machinery never assumes a locomotor is already safely on disk.
*/
bool Dirty;
-
- /*
- * This is the number of outstanding references to this locomotor. Releasing the
- * last one destroys the locomotor, which is how its lifetime is managed through
- * the COM interfaces it presents.
- */
- LONG RefCount;
};
diff --git a/code/logic.cpp b/code/logic.cpp
index 2935b2a1e..3d1129fe3 100644
--- a/code/logic.cpp
+++ b/code/logic.cpp
@@ -79,12 +79,6 @@
#include
-/*
- * Global COM reference count.
- */
-ULONG COMRefCount = 0;
-
-
unsigned FramesThisSecond=0;
unsigned LastFramesPerSecond=0;
unsigned TotalFrames=0;
diff --git a/code/map.cpp b/code/map.cpp
index 7c824e39b..45441ad4a 100644
--- a/code/map.cpp
+++ b/code/map.cpp
@@ -299,6 +299,16 @@ void MapClass::Serialize(SaveStreamClass & stream)
stream.Serialize(XSize);
stream.Serialize(YSize);
stream.Serialize(Size);
+
+ // The cell array is reallocated to Size and every cell then installs itself in it by
+ // coordinate, so a saved extent other than the one this build lays out is refused
+ // before anything is sized from it.
+ if (stream.Is_Loading()
+ && (XSize != MAP_CELL_W || YSize != MAP_CELL_H || Size != MAP_CELL_TOTAL)) {
+ stream.Fail();
+ return;
+ }
+
stream.Serialize(Crates);
stream.Serialize(Redraws);
stream.Serialize(TaggedCells);
@@ -482,6 +492,26 @@ bool MapClass::Is_Valid(Cell const & cell)
}
+///
+/// Fetches the slot a cell coordinate names in the cell array.
+/// A cell coordinate arrives from a saved game as two signed shorts, so this is what tells
+/// a coordinate that names a slot from one that does not.
+///
+/// Returns with the index into Array, or -1 when the coordinate names no slot.
+int MapClass::Cell_Slot(Cell const & cell) const
+{
+ if (cell.X < 0 || cell.X >= MAP_CELL_W || cell.Y < 0 || cell.Y >= MAP_CELL_H) {
+ return(-1);
+ }
+
+ int const cellnum = cell.X + cell.Y * MAP_CELL_W;
+ if (cellnum >= Array.Length()) {
+ return(-1);
+ }
+ return(cellnum);
+}
+
+
/***********************************************************************************************
* MapClass::One_Time -- Performs special one time initializations for the map. *
* *
diff --git a/code/map.h b/code/map.h
index af96ee643..ce521d7a5 100644
--- a/code/map.h
+++ b/code/map.h
@@ -87,6 +87,7 @@ class MapClass: public GScreenClass
int ID(CellClass * ptr) {return(Array.ID(ptr));};
int ID(CellClass & ptr) {return(Array.ID(&ptr));};
bool Is_Valid(Cell const & cell);
+ int Cell_Slot(Cell const & cell) const;
/*
** Initialization
diff --git a/code/mech.cpp b/code/mech.cpp
index ee1d17cd5..0ddf517a6 100644
--- a/code/mech.cpp
+++ b/code/mech.cpp
@@ -56,7 +56,7 @@ MechLocomotionClass::~MechLocomotionClass(void)
/// Has the mech been given somewhere to walk to?
///
/// bool; Is the mech under movement orders?
-boolean STDMETHODCALLTYPE MechLocomotionClass::Is_Moving(void)
+bool MechLocomotionClass::Is_Moving(void)
{
return(IsMoving);
}
@@ -67,7 +67,7 @@ boolean STDMETHODCALLTYPE MechLocomotionClass::Is_Moving(void)
///
/// Returns with the destination assigned, or COORD_NONE if the unit has not been
/// given one.
-Coord STDMETHODCALLTYPE MechLocomotionClass::Destination(void)
+Coord MechLocomotionClass::Destination(void)
{
if (Is_Moving()) {
return(DestinationCoord);
@@ -81,7 +81,7 @@ Coord STDMETHODCALLTYPE MechLocomotionClass::Destination(void)
///
/// Returns with the location being stepped into, or the unit's own location if it
/// is not part way between cells.
-Coord STDMETHODCALLTYPE MechLocomotionClass::Head_To_Coord(void)
+Coord MechLocomotionClass::Head_To_Coord(void)
{
if (HeadToCoord != COORD_NONE) {
return(HeadToCoord);
@@ -95,7 +95,7 @@ Coord STDMETHODCALLTYPE MechLocomotionClass::Head_To_Coord(void)
/// This is the locomotor's entry point from the owning unit's AI.
///
/// bool; Does the mech still have somewhere to walk to?
-boolean STDMETHODCALLTYPE MechLocomotionClass::Process(void)
+bool MechLocomotionClass::Process(void)
{
Movement_AI(true);
return(Is_Moving());
@@ -108,7 +108,7 @@ boolean STDMETHODCALLTYPE MechLocomotionClass::Process(void)
/// raised to the deck above it, since that is where a walking unit can actually get to.
///
/// The location to walk to.
-void STDMETHODCALLTYPE MechLocomotionClass::Move_To(Coord to)
+void MechLocomotionClass::Move_To(Coord to)
{
if (LinkedTo->StunDuration <= 0) {
Coord coord = to;
@@ -126,7 +126,7 @@ void STDMETHODCALLTYPE MechLocomotionClass::Move_To(Coord to)
/// A unit caught part way between cells is left in motion so that it finishes the step it
/// is taking before coming to rest.
///
-void STDMETHODCALLTYPE MechLocomotionClass::Stop_Moving(void)
+void MechLocomotionClass::Stop_Moving(void)
{
DestinationCoord = COORD_NONE;
if (HeadToCoord == COORD_NONE) {
@@ -155,7 +155,7 @@ void MechLocomotionClass::Do_Turn(DirType coord)
/// destination -- it will walk there and then pick its path up again.
///
/// The location to step into immediately.
-void STDMETHODCALLTYPE MechLocomotionClass::Force_Immediate_Destination(Coord coord)
+void MechLocomotionClass::Force_Immediate_Destination(Coord coord)
{
HeadToCoord = coord;
}
@@ -651,18 +651,9 @@ bool MechLocomotionClass::Mark_Head_To(Coord const & coord)
}
-///
-/// Fetches the class identifier of this locomotor.
-/// The persistence layer uses this identifier to create a locomotor of the right kind
-/// when a saved game is loaded.
-///
-/// Pointer to the buffer to fill in with the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE MechLocomotionClass::GetClassID(CLSID * retval)
+ClassID MechLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_MechLocomotion;
- return(S_OK);
+ return(ClassID_MechLocomotion);
}
@@ -684,7 +675,7 @@ void MechLocomotionClass::Serialize(SaveStreamClass & stream)
/// Fetches the display layer that the mech is rendered in.
///
/// Returns with the layer appropriate to a unit that walks on the ground.
-LayerType STDMETHODCALLTYPE MechLocomotionClass::In_Which_Layer(void)
+LayerType MechLocomotionClass::In_Which_Layer(void)
{
return(LAYER_GROUND);
}
@@ -696,7 +687,7 @@ LayerType STDMETHODCALLTYPE MechLocomotionClass::In_Which_Layer(void)
/// standing still -- blocked, or waiting on a path -- is not moving now.
///
/// bool; Is the mech turning or walking right now?
-boolean STDMETHODCALLTYPE MechLocomotionClass::Is_Moving_Now(void)
+bool MechLocomotionClass::Is_Moving_Now(void)
{
if (LinkedTo->PrimaryFacing.Is_Rotating()) {
return(true);
@@ -714,7 +705,7 @@ boolean STDMETHODCALLTYPE MechLocomotionClass::Is_Moving_Now(void)
/// it is walking toward stays reserved for it.
///
/// The marking operation to perform; MARK_UP releases the cell.
-void STDMETHODCALLTYPE MechLocomotionClass::Mark_All_Occupation_Bits(int mark)
+void MechLocomotionClass::Mark_All_Occupation_Bits(int mark)
{
if (mark == MARK_UP) {
LinkedTo->Clear_Occupy_Bit((Coord)Head_To_Coord());
@@ -731,7 +722,7 @@ void STDMETHODCALLTYPE MechLocomotionClass::Mark_All_Occupation_Bits(int mark)
///
/// The location to test against.
/// bool; Is the mech heading into that location?
-boolean STDMETHODCALLTYPE MechLocomotionClass::Is_Moving_Here(Coord to)
+bool MechLocomotionClass::Is_Moving_Here(Coord to)
{
Coord coord = Head_To_Coord();
diff --git a/code/mech.h b/code/mech.h
index 9a0a1cab6..cb7a6b6ec 100644
--- a/code/mech.h
+++ b/code/mech.h
@@ -27,22 +27,22 @@ class MechLocomotionClass : public LocomotionClass
MechLocomotionClass(void);
virtual ~MechLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual void STDMETHODCALLTYPE Force_Immediate_Destination(Coord coord) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Here(Coord to) override;
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual Coord Head_To_Coord(void) override;
+ virtual bool Process(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual void Do_Turn(DirType coord) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual void Force_Immediate_Destination(Coord coord) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual void Mark_All_Occupation_Bits(int mark) override;
+ virtual bool Is_Moving_Here(Coord to) override;
/*---------------------------------------------------------------------
** Member function prototypes.
diff --git a/code/mouse.cpp b/code/mouse.cpp
index 32a5718ce..92b046afc 100644
--- a/code/mouse.cpp
+++ b/code/mouse.cpp
@@ -51,6 +51,7 @@
#include "mixfile.h"
#include "overtype.h"
#include "rawfile.h"
+#include "saveload.h"
#include "savestream.h"
#include "scenario.h"
#include "shapeset.h"
@@ -58,6 +59,8 @@
#include "terrtype.h"
#include "xmouse.h"
+#include
+
#define MOUSE_HOTSPOT_MIN 0
#define MOUSE_HOTSPOT_CENTER 12345
@@ -391,17 +394,17 @@ void MouseClass::Init_Clear(void)
/// back into it. Object pointers within the restored state are remapped by the swizzle
/// manager, and the theater specific type data is reinitialized to match the scenario.
///
-/// Returns with S_OK if the map was loaded, otherwise the stream error.
-HRESULT MouseClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool MouseClass::Load(SaveStreamClass & stream)
{
int i;
- HRESULT result = BASECLASS::Load(stream);
- if (SUCCEEDED(result)) {
+ bool result = BASECLASS::Load(stream);
+ if (result) {
int theater;
- result = stream->Read(&theater, sizeof(theater), NULL);
- if (FAILED(result)) {
- return(result);
+ stream.Serialize(theater);
+ if (stream.Was_Error()) {
+ return(false);
}
LastTheater = THEATER_NONE;
@@ -433,12 +436,10 @@ HRESULT MouseClass::Load(IStream * stream)
Array.Clear();
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("MouseClass");
- Serialize(savestream);
- result = savestream.Result();
- if (FAILED(result)) {
- return(result);
+ stream.Set_Context("MouseClass");
+ Serialize(stream);
+ if (stream.Was_Error()) {
+ return(false);
}
/*
@@ -465,32 +466,24 @@ HRESULT MouseClass::Load(IStream * stream)
/*
* These blocks are read raw, so a file whose records are a different size would drag
- * the rest of the stream out of step. A short read reports S_FALSE, not a failure.
+ * the rest of the stream out of step.
*/
- ULONG readcount = 0;
- result = stream->Read(CellZones, sizeof(*CellZones) * CellZoneCount, &readcount);
- if (FAILED(result)) {
- return(result);
- }
- if (readcount != sizeof(*CellZones) * CellZoneCount) {
- return(E_FAIL);
+ stream.Serialize_Bytes(CellZones, (int)(sizeof(*CellZones) * CellZoneCount));
+ if (stream.Was_Error()) {
+ return(false);
}
for (i = 0; i < MZONE_COUNT; i++) {
Zones[i] = new int[ZoneCount];
- result = stream->Read(Zones[i], sizeof(*Zones[i]) * ZoneCount, &readcount);
- if (FAILED(result)) {
- return(result);
- }
- if (readcount != sizeof(*Zones[i]) * ZoneCount) {
- return(E_FAIL);
+ stream.Serialize_Bytes(Zones[i], (int)(sizeof(*Zones[i]) * ZoneCount));
+ if (stream.Was_Error()) {
+ return(false);
}
}
- savestream.Serialize(ZoneConnections);
- result = savestream.Result();
- if (FAILED(result)) {
- return(result);
+ stream.Serialize(ZoneConnections);
+ if (stream.Was_Error()) {
+ return(false);
}
for (i = 0; i < Array.Length(); i++) {
@@ -498,13 +491,18 @@ HRESULT MouseClass::Load(IStream * stream)
Array[i] = NULL;
}
int count;
- result = stream->Read(&count, sizeof(count), NULL);
- if (FAILED(result)) {
- return(result);
+ stream.Serialize(count);
+ if (stream.Was_Error()) {
+ return(false);
}
for (i = 0; i < count; i++) {
- LPVOID ptr;
- OleLoadFromStream(stream, IID_IUnknown, &ptr);
+ std::unique_ptr cell = Load_Object_As(stream);
+ if (cell == nullptr) {
+ return(false);
+ }
+ // The cell put itself into the map's array as it finished loading, and the map
+ // is what deletes it from here on.
+ cell.release();
}
TerrainTypeClass::Init(Scen->Theater);
@@ -521,7 +519,7 @@ HRESULT MouseClass::Load(IStream * stream)
DraggedWaypoint = NULL;
LastTheater = Scen->Theater;
- result = S_OK;
+ result = true;
}
return(result);
}
@@ -531,45 +529,42 @@ HRESULT MouseClass::Load(IStream * stream)
/// Saves the map layer to a save game stream.
/// This routine writes the theater, the members of the whole display chain, the zone tables
/// and zone connections, and then every valid cell, in the order that Load expects to find
-/// them. The cells persist themselves through OLE, so each one writes its own contents.
+/// them. Each cell writes its own contents as a record of its own.
///
-/// Returns with S_OK if the map was written, otherwise the stream error.
-HRESULT MouseClass::Save(IStream * stream)
+/// bool; Was the record written whole?
+bool MouseClass::Save(SaveStreamClass & stream)
{
int i;
int count;
- HRESULT result = BASECLASS::Save(stream);
- if (SUCCEEDED(result)) {
+ bool result = BASECLASS::Save(stream);
+ if (result) {
int theater = Scen->Theater;
- result = stream->Write(&theater, sizeof(theater), NULL);
- if (FAILED(result)) {
- return(result);
+ stream.Serialize(theater);
+ if (stream.Was_Error()) {
+ return(false);
}
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
- result = savestream.Result();
- if (FAILED(result)) {
- return(result);
+ Serialize(stream);
+ if (stream.Was_Error()) {
+ return(false);
}
- result = stream->Write(CellZones, sizeof(*CellZones) * CellZoneCount, NULL);
- if (FAILED(result)) {
- return(result);
+ stream.Serialize_Bytes(CellZones, (int)(sizeof(*CellZones) * CellZoneCount));
+ if (stream.Was_Error()) {
+ return(false);
}
for (i = 0; i < MZONE_COUNT; i++) {
- result = stream->Write(Zones[i], sizeof(*Zones[i]) * ZoneCount, NULL);
- if (FAILED(result)) {
- return(result);
+ stream.Serialize_Bytes(Zones[i], (int)(sizeof(*Zones[i]) * ZoneCount));
+ if (stream.Was_Error()) {
+ return(false);
}
}
- savestream.Serialize(ZoneConnections);
- result = savestream.Result();
- if (FAILED(result)) {
- return(result);
+ stream.Serialize(ZoneConnections);
+ if (stream.Was_Error()) {
+ return(false);
}
count = 0;
@@ -582,25 +577,28 @@ HRESULT MouseClass::Save(IStream * stream)
}
cptr = Iterate();
}
- result = stream->Write(&count, sizeof(count), NULL);
- if (FAILED(result)) {
- return(result);
+ stream.Serialize(count);
+ if (stream.Was_Error()) {
+ return(false);
}
Reset_Iterator();
cptr = Iterate();
while (cptr != NULL) {
Cell cell = cptr->CellID;
if (Is_Valid(cell)) {
- OleSaveToStream(cptr, stream);
+ Save_Object(stream, cptr);
count--;
}
cptr = Iterate();
}
+ // The count was written before the cells, so a second pass that disagrees with it
+ // has already written a map no load can read back.
if (count != 0) {
- return(result);
+ stream.Fail();
+ return(false);
}
- result = S_OK;
+ result = true;
}
return(result);
}
diff --git a/code/mouse.h b/code/mouse.h
index 9a2da1d17..09d260c65 100644
--- a/code/mouse.h
+++ b/code/mouse.h
@@ -42,8 +42,8 @@ class MouseClass: public ScrollClass
typedef ScrollClass BASECLASS;
public:
- virtual HRESULT Load(IStream * stream) override;
- virtual HRESULT Save(IStream * stream) override;
+ virtual bool Load(SaveStreamClass & stream) override;
+ virtual bool Save(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/overlay.cpp b/code/overlay.cpp
index 2000810a1..2de57e657 100644
--- a/code/overlay.cpp
+++ b/code/overlay.cpp
@@ -36,7 +36,6 @@
* OverlayClass::new -- Allocates a overlay object from pool *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "overlay.h"
diff --git a/code/overlay.h b/code/overlay.h
index 7ee7f1254..bf5a122a3 100644
--- a/code/overlay.h
+++ b/code/overlay.h
@@ -32,7 +32,7 @@
#pragma once
-#include "isun.h"
+#include "classids.h"
#include "object.h"
#include "overlay.hh"
@@ -63,7 +63,7 @@ class OverlayClass : public ObjectClass
OverlayClass(OverlayTypeClass const * ttype, Cell const & pos = CELL_NONE, HousesType = HOUSE_NONE);
virtual ~OverlayClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override {if (retval == NULL) return(E_POINTER);*retval = CLSID_OverlayClass;return(S_OK);}
+ virtual ClassID Class_ID(void) const override {return(ClassID_OverlayClass);}
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/overtype.cpp b/code/overtype.cpp
index c00b1614a..8ce1f2cdd 100644
--- a/code/overtype.cpp
+++ b/code/overtype.cpp
@@ -46,7 +46,6 @@
* OverlayTypeClass::operator new -- Allocate an overlay type class object from pool. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "overtype.h"
@@ -482,17 +481,9 @@ void OverlayTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// The save system asks for this so that it knows which class to construct when the object
-/// is read back out of a save file.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE OverlayTypeClass::GetClassID(CLSID * retval)
+ClassID OverlayTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_OverlayTypeClass;
- return(S_OK);
+ return(ClassID_OverlayTypeClass);
}
diff --git a/code/overtype.h b/code/overtype.h
index 1920dd7f3..b80e1f56b 100644
--- a/code/overtype.h
+++ b/code/overtype.h
@@ -159,7 +159,7 @@ class OverlayTypeClass: public ObjectTypeClass
OverlayTypeClass(char const * ininame = NULL);
~OverlayTypeClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/particle.cpp b/code/particle.cpp
index 6aeefabef..f5e660884 100644
--- a/code/particle.cpp
+++ b/code/particle.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "particle.h"
@@ -929,10 +928,10 @@ void ParticleClass::Serialize(SaveStreamClass & stream)
///
/// The stream to write this particle to.
/// Should the modified flag be cleared once written?
-/// Returns with S_OK if the particle was written successfully.
-HRESULT STDMETHODCALLTYPE ParticleClass::Save(IStream * stream, BOOL cleardirty)
+/// bool; Was the record written whole?
+bool ParticleClass::Save(SaveStreamClass & stream, bool cleardirty)
{
- HRESULT result = BASECLASS::Save(stream, cleardirty);
+ bool result = BASECLASS::Save(stream, cleardirty);
WasSaved = true;
return(result);
}
@@ -993,18 +992,9 @@ int ParticleClass::Shape_Number(void) const
}
-///
-/// Fetches the class identifier of this object.
-/// The persistence code uses this identifier to recreate the correct object when the
-/// save file is loaded back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE ParticleClass::GetClassID(CLSID * retval)
+ClassID ParticleClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_ParticleClass;
- return(S_OK);
+ return(ClassID_ParticleClass);
}
diff --git a/code/particle.h b/code/particle.h
index b7b32a9c9..a8c16b9e0 100644
--- a/code/particle.h
+++ b/code/particle.h
@@ -31,8 +31,8 @@ class ParticleClass : public ObjectClass
ParticleClass(ParticleTypeClass const * type, Coord const & origin, Coord const & target = COORD_NONE, ParticleSystemClass * partsys = NULL);
virtual ~ParticleClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Save(IStream * stream, BOOL cleardirty) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Save(SaveStreamClass & stream, bool cleardirty) override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/partsys.cpp b/code/partsys.cpp
index cf5f1d608..9629812fe 100644
--- a/code/partsys.cpp
+++ b/code/partsys.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "partsys.h"
@@ -872,18 +871,9 @@ void ParticleSystemClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier for this object.
-/// This routine is part of the persistence support. The save process records the
-/// identifier so that the load process knows what kind of object to build.
-///
-/// Pointer to the class identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE ParticleSystemClass::GetClassID(CLSID * retval)
+ClassID ParticleSystemClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_ParticleSystemClass;
- return(S_OK);
+ return(ClassID_ParticleSystemClass);
}
diff --git a/code/partsys.h b/code/partsys.h
index 91db45788..41bc50380 100644
--- a/code/partsys.h
+++ b/code/partsys.h
@@ -31,7 +31,7 @@ class ParticleSystemClass : public ObjectClass
ParticleSystemClass(void);
virtual ~ParticleSystemClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/persist.h b/code/persist.h
new file mode 100644
index 000000000..1b9524367
--- /dev/null
+++ b/code/persist.h
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * O P E N T S
+ *******************************************************************************
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2026 OpenTS contributors
+ *
+ * See LICENSE.md for applicable additional terms and warranty disclaimers.
+ ******************************************************************************/
+
+#pragma once
+
+#include "win.h"
+
+#include "classid.h"
+
+class SaveStreamClass;
+
+// What a saved game asks of an object it carries: the class identifier the record is
+// tagged with, and the record itself.
+struct IPersistent
+{
+ virtual ~IPersistent(void) {}
+
+ virtual ClassID Class_ID(void) const = 0;
+ virtual bool Load(SaveStreamClass & stream) = 0;
+ // Restores what the record could not carry, once the record has been checked; an object
+ // takes its place in the map or a side table here, never while its record is still in doubt.
+ virtual void Post_Load(void) {}
+ virtual bool Save(SaveStreamClass & stream, bool cleardirty) = 0;
+};
diff --git a/code/psystype.cpp b/code/psystype.cpp
index 4a6cae08f..439a4fdd9 100644
--- a/code/psystype.cpp
+++ b/code/psystype.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "psystype.h"
@@ -181,18 +180,9 @@ void ParticleSystemTypeClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence interface. The save system records the class
-/// ID so that the right kind of object can be manufactured when the game is reloaded.
-///
-/// Pointer to the class ID to be filled in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE ParticleSystemTypeClass::GetClassID(CLSID * retval)
+ClassID ParticleSystemTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_ParticleSystemTypeClass;
- return(S_OK);
+ return(ClassID_ParticleSystemTypeClass);
}
diff --git a/code/psystype.h b/code/psystype.h
index 3e1b60e9f..c9d1fb379 100644
--- a/code/psystype.h
+++ b/code/psystype.h
@@ -25,7 +25,7 @@ class ParticleSystemTypeClass : public ObjectTypeClass
ParticleSystemTypeClass(char const * ininame = NULL);
virtual ~ParticleSystemTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/ptype.cpp b/code/ptype.cpp
index 4ff73a7d5..5644fea15 100644
--- a/code/ptype.cpp
+++ b/code/ptype.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "ptype.h"
@@ -220,18 +219,9 @@ void ParticleTypeClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// The save game system uses this identifier to know which kind of object to build
-/// when the stream is read back in.
-///
-/// Pointer to the location to store the class identifier.
-/// Returns with S_OK, or E_POINTER if no storage location was supplied.
-HRESULT STDMETHODCALLTYPE ParticleTypeClass::GetClassID(CLSID * retval)
+ClassID ParticleTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_ParticleTypeClass;
- return(S_OK);
+ return(ClassID_ParticleTypeClass);
}
diff --git a/code/ptype.h b/code/ptype.h
index fd5f29303..23599468b 100644
--- a/code/ptype.h
+++ b/code/ptype.h
@@ -28,7 +28,7 @@ class ParticleTypeClass : public ObjectTypeClass
ParticleTypeClass(char const * ininame = NULL);
virtual ~ParticleTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/reinf.cpp b/code/reinf.cpp
index 540a86469..d27e1be6d 100644
--- a/code/reinf.cpp
+++ b/code/reinf.cpp
@@ -47,10 +47,10 @@
#include "airctype.h"
#include "building.h"
#include "cell.h"
+#include "classids.h"
#include "foot.h"
#include "globals.h"
#include "house.h"
-#include "ilocos.h"
#include "incdec.h"
#include "inline.h"
#include "mouse.h"
@@ -533,7 +533,7 @@ inline bool _Can_Burrow(FootClass * object)
{
while (object != NULL) {
TechnoTypeClass const * tclass = object->TClass;
- if (tclass->Locomotor != CLSID_TunnelLocomotion) {
+ if (tclass->Locomotor != ClassID_TunnelLocomotion) {
return(false);
}
object = (FootClass *)object->Next;
diff --git a/code/revent.cpp b/code/revent.cpp
index 8d0a51dee..ff4c07403 100644
--- a/code/revent.cpp
+++ b/code/revent.cpp
@@ -366,20 +366,19 @@ void RadarEventClass::Get_Event_Rect(Point2D (& event_rect)[4]) const
///
/// The stream to write the radar events to.
/// bool; Were the events written successfully?
-bool RadarEventClass::Save(IStream * stream)
+bool RadarEventClass::Save(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
int count = RadarEvents.Count();
- savestream.Serialize(count);
+ stream.Serialize(count);
for (int index = 0; index < count; index++) {
- RadarEvents[index]->Serialize(savestream);
+ RadarEvents[index]->Serialize(stream);
}
- savestream.Serialize(LastRadarEventCell);
+ stream.Serialize(LastRadarEventCell);
- return(SUCCEEDED(savestream.Result()));
+ return(!stream.Was_Error());
}
@@ -390,27 +389,29 @@ bool RadarEventClass::Save(IStream * stream)
///
/// The stream to read the radar events from.
/// bool; Were the events read successfully?
-bool RadarEventClass::Load(IStream * stream)
+bool RadarEventClass::Load(SaveStreamClass & stream)
{
+ // The destructor takes the event off the list, so the list drains as they are deleted.
for (int i = RadarEvents.Count() - 1; i >= 0; i--) {
delete RadarEvents[i];
- RadarEvents.Delete_Index(i);
}
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("RadarEventClass");
+ stream.Set_Context("RadarEventClass");
int count = 0;
- savestream.Serialize(count);
+ stream.Serialize(count);
+ if (!stream.Fits(count, 1)) {
+ return(false);
+ }
- for (int index = 0; index < count; index++) {
+ for (int index = 0; index < count && !stream.Was_Error(); index++) {
RadarEventClass * event = new RadarEventClass(RADAREVENT_NONE, Cell(0, 0));
- event->Serialize(savestream);
+ event->Serialize(stream);
}
- savestream.Serialize(LastRadarEventCell);
+ stream.Serialize(LastRadarEventCell);
- return(SUCCEEDED(savestream.Result()));
+ return(!stream.Was_Error());
}
diff --git a/code/revent.h b/code/revent.h
index 4c3264376..e27232e87 100644
--- a/code/revent.h
+++ b/code/revent.h
@@ -18,15 +18,14 @@
#include "revent.hh"
-struct IStream;
class SaveStreamClass;
template class DynamicVectorClass;
class RadarEventClass
{
public:
- static bool Save(IStream * stream);
- static bool Load(IStream * stream);
+ static bool Save(SaveStreamClass & stream);
+ static bool Load(SaveStreamClass & stream);
public:
RadarEventClass(RadarEventType event, Cell cell);
diff --git a/code/rules.cpp b/code/rules.cpp
index 2979154b7..f3cf6a7cb 100644
--- a/code/rules.cpp
+++ b/code/rules.cpp
@@ -2092,10 +2092,9 @@ bool RulesClass::Do_Movies(CCINIClass const & ini)
///
/// Writes the rule data out to a save game stream.
///
-void RulesClass::Save(IStream * stream)
+void RulesClass::Save(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
+ Serialize(stream);
}
@@ -2104,11 +2103,10 @@ void RulesClass::Save(IStream * stream)
///
/// Be sure the object heaps have been loaded before calling this routine, since
/// the pointer swizzle needs them.
-void RulesClass::Load(IStream * stream)
+void RulesClass::Load(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("RulesClass");
- Serialize(savestream);
+ stream.Set_Context("RulesClass");
+ Serialize(stream);
}
diff --git a/code/rules.h b/code/rules.h
index 1b8eaa80c..f05b06927 100644
--- a/code/rules.h
+++ b/code/rules.h
@@ -138,8 +138,8 @@ class RulesClass
bool Do_Movies(CCINIClass const & ini);
bool Objects(CCINIClass const & ini);
- void Save(IStream * stream);
- void Load(IStream * stream);
+ void Save(SaveStreamClass & stream);
+ void Load(SaveStreamClass & stream);
void Serialize(SaveStreamClass & stream);
diff --git a/code/savefile.cpp b/code/savefile.cpp
new file mode 100644
index 000000000..ae47dfcb7
--- /dev/null
+++ b/code/savefile.cpp
@@ -0,0 +1,539 @@
+/*******************************************************************************
+ * O P E N T S
+ *******************************************************************************
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2026 OpenTS contributors
+ *
+ * See LICENSE.md for applicable additional terms and warranty disclaimers.
+ ******************************************************************************/
+
+#include "savefile.h"
+
+#include "crc.h"
+
+#include
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+unsigned char const Signature[4] = { 'O', 'T', 'S', 'V' };
+
+constexpr std::uint32_t FLAG_LZO = 0x0001;
+constexpr std::uint32_t FIELD_HEADER_SIZE = 8;
+constexpr std::uint32_t MAX_FIELD_LENGTH = 0x10000;
+// No game state comes near this, and a header asking for more is asking for memory.
+constexpr std::uint32_t MAX_CONTENT_LENGTH = 0x10000000;
+// A listing is a dozen short fields; a table beyond this is not one.
+constexpr std::uint32_t MAX_TABLE_LENGTH = 0x100000;
+
+
+std::uint32_t Get_U16(unsigned char const * from)
+{
+ return((std::uint32_t)from[0] | ((std::uint32_t)from[1] << 8));
+}
+
+
+std::uint32_t Get_U32(unsigned char const * from)
+{
+ return((std::uint32_t)from[0] | ((std::uint32_t)from[1] << 8)
+ | ((std::uint32_t)from[2] << 16) | ((std::uint32_t)from[3] << 24));
+}
+
+
+void Put_U16(unsigned char * into, std::uint32_t value)
+{
+ into[0] = (unsigned char)(value & 0xFF);
+ into[1] = (unsigned char)((value >> 8) & 0xFF);
+}
+
+
+void Put_U32(unsigned char * into, std::uint32_t value)
+{
+ into[0] = (unsigned char)(value & 0xFF);
+ into[1] = (unsigned char)((value >> 8) & 0xFF);
+ into[2] = (unsigned char)((value >> 16) & 0xFF);
+ into[3] = (unsigned char)((value >> 24) & 0xFF);
+}
+
+
+void Append(std::vector & into, void const * data, std::size_t length)
+{
+ unsigned char const * bytes = (unsigned char const *)data;
+ into.insert(into.end(), bytes, bytes + length);
+}
+
+
+// Sizes a buffer the header asked for, and says so rather than throw when the process
+// cannot hold it.
+bool Reserve(std::vector & buffer, std::size_t length)
+{
+ try {
+ buffer.resize(length);
+ } catch (std::bad_alloc const &) {
+ buffer.clear();
+ return(false);
+ }
+ return(true);
+}
+
+
+bool Read_Range(HANDLE file, void * into, std::uint32_t length)
+{
+ unsigned char * cursor = (unsigned char *)into;
+
+ while (length > 0) {
+ DWORD got = 0;
+ if (!ReadFile(file, cursor, length, &got, nullptr) || got == 0) return(false);
+ cursor += got;
+ length -= got;
+ }
+
+ return(true);
+}
+
+
+bool Write_Range(HANDLE file, void const * data, std::uint32_t length)
+{
+ unsigned char const * cursor = (unsigned char const *)data;
+
+ while (length > 0) {
+ DWORD const block = (length > 0x100000) ? 0x100000 : length;
+ DWORD written = 0;
+ if (!WriteFile(file, cursor, block, &written, nullptr) || written != block) return(false);
+ cursor += written;
+ length -= written;
+ }
+
+ return(true);
+}
+
+
+struct HeaderType {
+ std::uint32_t Version;
+ std::uint32_t Flags;
+ std::uint32_t TableLength;
+ std::uint32_t ContentOffset;
+ std::uint32_t StoredLength;
+ std::uint32_t ContentLength;
+ std::uint32_t ContentCRC;
+ std::uint32_t HeaderCRC;
+};
+
+
+// The header checksum continues over the field table, so a listing can verify what it
+// reads without touching the content.
+std::uint32_t Header_CRC(unsigned char const * header, unsigned char const * table, std::uint32_t length)
+{
+ return(SaveFileClass::Checksum(table, length, SaveFileClass::Checksum(header, SaveFileClass::HEADER_SIZE - 4)));
+}
+
+
+// Decides everything the first 32 bytes can decide, in the order a caller wants to
+// hear about it: not ours, a version we do not read, or damage.
+SaveFileClass::ResultType Parse_Header(unsigned char const * bytes, std::uint32_t available, HeaderType & header)
+{
+ if (available < sizeof(Signature) || memcmp(bytes, Signature, sizeof(Signature)) != 0) {
+ return(SaveFileClass::RESULT_NOT_A_SAVE);
+ }
+ if (available < SaveFileClass::HEADER_SIZE) {
+ return(SaveFileClass::RESULT_CORRUPT);
+ }
+
+ header.Version = Get_U16(bytes + 4);
+ header.Flags = Get_U16(bytes + 6);
+ header.TableLength = Get_U32(bytes + 8);
+ header.ContentOffset = Get_U32(bytes + 12);
+ header.StoredLength = Get_U32(bytes + 16);
+ header.ContentLength = Get_U32(bytes + 20);
+ header.ContentCRC = Get_U32(bytes + 24);
+ header.HeaderCRC = Get_U32(bytes + 28);
+
+ if (header.Version == 0 || header.Version > SaveFileClass::FORMAT_VERSION) {
+ return(SaveFileClass::RESULT_UNSUPPORTED_VERSION);
+ }
+ if ((header.Flags & ~FLAG_LZO) != 0) {
+ return(SaveFileClass::RESULT_UNSUPPORTED_VERSION);
+ }
+ if (header.TableLength > MAX_TABLE_LENGTH) {
+ return(SaveFileClass::RESULT_CORRUPT);
+ }
+ if (header.ContentOffset != SaveFileClass::HEADER_SIZE + header.TableLength) {
+ return(SaveFileClass::RESULT_CORRUPT);
+ }
+ if (header.StoredLength > MAX_CONTENT_LENGTH || header.ContentLength > MAX_CONTENT_LENGTH) {
+ return(SaveFileClass::RESULT_CORRUPT);
+ }
+
+ return(SaveFileClass::RESULT_OK);
+}
+
+} // namespace
+
+
+SaveFileClass::SaveFileClass(void)
+{
+}
+
+
+std::uint32_t SaveFileClass::Checksum(unsigned char const * data, std::uint32_t length, std::uint32_t seed)
+{
+ return(CRC::Memory(data, length, seed));
+}
+
+
+char const * SaveFileClass::Result_Text(ResultType result)
+{
+ switch (result) {
+ case RESULT_OK: return("ok");
+ case RESULT_MISSING: return("the file is missing");
+ case RESULT_NOT_A_SAVE: return("the file is not a saved game");
+ case RESULT_UNSUPPORTED_VERSION: return("the file uses a format version this build does not read");
+ case RESULT_CORRUPT: return("the file is damaged");
+ case RESULT_WRITE_FAILED: return("the file could not be written");
+ case RESULT_NO_MEMORY: return("there is not enough memory to read the file");
+ case RESULT_TOO_LARGE: return("the game state is larger than a saved game can hold");
+ }
+ return("unknown");
+}
+
+
+SaveFileClass::FieldType const * SaveFileClass::Find(int id, int kind) const
+{
+ for (FieldType const & field : Fields) {
+ if (field.ID == id && field.Kind == kind) return(&field);
+ }
+ return(nullptr);
+}
+
+
+void SaveFileClass::Set(int id, int kind, void const * data, std::size_t length)
+{
+ for (FieldType & field : Fields) {
+ if (field.ID == id && field.Kind == kind) {
+ field.Bytes.assign((unsigned char const *)data, (unsigned char const *)data + length);
+ return;
+ }
+ }
+
+ FieldType field;
+ field.ID = id;
+ field.Kind = kind;
+ field.Bytes.assign((unsigned char const *)data, (unsigned char const *)data + length);
+ Fields.push_back(field);
+}
+
+
+void SaveFileClass::Set_String(int id, char const * text)
+{
+ if (text == nullptr) text = "";
+ Set(id, FIELD_STRING, text, strlen(text));
+}
+
+
+void SaveFileClass::Set_Int(int id, int value)
+{
+ unsigned char bytes[4];
+ Put_U32(bytes, (std::uint32_t)value);
+ Set(id, FIELD_INT, bytes, sizeof(bytes));
+}
+
+
+void SaveFileClass::Set_Time(int id, FILETIME const & time)
+{
+ unsigned char bytes[8];
+ Put_U32(bytes, time.dwLowDateTime);
+ Put_U32(bytes + 4, time.dwHighDateTime);
+ Set(id, FIELD_TIME, bytes, sizeof(bytes));
+}
+
+
+// A string that does not fit is truncated to what does; the result is always terminated.
+bool SaveFileClass::Get_String(int id, char * text, int size) const
+{
+ if (text == nullptr || size <= 0) return(false);
+
+ FieldType const * const field = Find(id, FIELD_STRING);
+ if (field == nullptr) {
+ text[0] = '\0';
+ return(false);
+ }
+
+ std::size_t length = field->Bytes.size();
+ if (length > (std::size_t)(size - 1)) {
+ // A cut never splits a UTF-8 sequence, so a shortened description stays text.
+ length = (std::size_t)(size - 1);
+ while (length > 0 && (field->Bytes[length] & 0xC0) == 0x80) length--;
+ }
+ memcpy(text, field->Bytes.data(), length);
+ text[length] = '\0';
+
+ return(true);
+}
+
+
+bool SaveFileClass::Get_Int(int id, int * value) const
+{
+ FieldType const * const field = Find(id, FIELD_INT);
+ if (field == nullptr || field->Bytes.size() != 4) return(false);
+
+ if (value != nullptr) *value = (int)Get_U32(field->Bytes.data());
+ return(true);
+}
+
+
+bool SaveFileClass::Get_Time(int id, FILETIME * time) const
+{
+ FieldType const * const field = Find(id, FIELD_TIME);
+ if (field == nullptr || field->Bytes.size() != 8) return(false);
+
+ if (time != nullptr) {
+ time->dwLowDateTime = Get_U32(field->Bytes.data());
+ time->dwHighDateTime = Get_U32(field->Bytes.data() + 4);
+ }
+ return(true);
+}
+
+
+void SaveFileClass::Clear_Fields(void)
+{
+ Fields.clear();
+}
+
+
+void SaveFileClass::Serialize_Fields(std::vector & table) const
+{
+ table.clear();
+
+ for (FieldType const & field : Fields) {
+ unsigned char head[FIELD_HEADER_SIZE];
+ Put_U16(head, (std::uint32_t)field.ID);
+ Put_U16(head + 2, (std::uint32_t)field.Kind);
+ Put_U32(head + 4, (std::uint32_t)field.Bytes.size());
+ Append(table, head, sizeof(head));
+ Append(table, field.Bytes.data(), field.Bytes.size());
+ }
+}
+
+
+SaveFileClass::ResultType SaveFileClass::Parse_Fields(unsigned char const * table, std::uint32_t length)
+{
+ Fields.clear();
+
+ std::uint32_t offset = 0;
+ while (offset < length) {
+ if (length - offset < FIELD_HEADER_SIZE) return(RESULT_CORRUPT);
+
+ FieldType field;
+ field.ID = (int)Get_U16(table + offset);
+ field.Kind = (int)Get_U16(table + offset + 2);
+ std::uint32_t const bytes = Get_U32(table + offset + 4);
+ offset += FIELD_HEADER_SIZE;
+
+ if (bytes > MAX_FIELD_LENGTH || bytes > length - offset) return(RESULT_CORRUPT);
+ field.Bytes.assign(table + offset, table + offset + bytes);
+ offset += bytes;
+
+ Fields.push_back(field);
+ }
+
+ return(RESULT_OK);
+}
+
+
+// The file lands under its final name only once every byte is on disk, so a save
+// interrupted at any point leaves the previous file untouched.
+SaveFileClass::ResultType SaveFileClass::Write(char const * path) const
+{
+ if (path == nullptr) return(RESULT_WRITE_FAILED);
+
+ // The reader's limits bind the writer too, so a save this build writes is one it reads,
+ // and one it cannot write leaves the file on disk alone.
+ if (Content.size() > MAX_CONTENT_LENGTH) return(RESULT_TOO_LARGE);
+ for (FieldType const & field : Fields) {
+ if (field.Bytes.size() > MAX_FIELD_LENGTH) return(RESULT_TOO_LARGE);
+ }
+
+ std::vector table;
+ Serialize_Fields(table);
+ if (table.size() > MAX_TABLE_LENGTH) return(RESULT_TOO_LARGE);
+
+ // The compressed block is kept only when it is smaller than the content; otherwise
+ // the content is written where it already sits, rather than copied to be written.
+ std::vector compressed;
+ unsigned char const * payload = Content.data();
+ std::uint32_t payload_length = (std::uint32_t)Content.size();
+ std::uint32_t flags = 0;
+
+ if (!Content.empty()) {
+ std::vector work;
+ if (!Reserve(work, LZO1X_MEM_COMPRESS)
+ || !Reserve(compressed, Content.size() + Content.size() / 16 + 64 + 3)) {
+ return(RESULT_NO_MEMORY);
+ }
+
+ lzo_uint packed = 0;
+ int const status = lzo1x_1_compress(Content.data(), (lzo_uint)Content.size(),
+ compressed.data(), &packed, work.data());
+
+ if (status == LZO_E_OK && packed < Content.size()) {
+ payload = compressed.data();
+ payload_length = (std::uint32_t)packed;
+ flags |= FLAG_LZO;
+ }
+ }
+
+ unsigned char header[HEADER_SIZE];
+ memcpy(header, Signature, sizeof(Signature));
+ Put_U16(header + 4, FORMAT_VERSION);
+ Put_U16(header + 6, flags);
+ Put_U32(header + 8, (std::uint32_t)table.size());
+ Put_U32(header + 12, HEADER_SIZE + (std::uint32_t)table.size());
+ Put_U32(header + 16, payload_length);
+ Put_U32(header + 20, (std::uint32_t)Content.size());
+ Put_U32(header + 24, Checksum(payload, payload_length));
+ // The header checksum covers everything before itself, so it is filled in last.
+ Put_U32(header + 28, Header_CRC(header, table.data(), (std::uint32_t)table.size()));
+
+ std::string const temporary = std::string(path) + ".tmp";
+
+ HANDLE const file = CreateFileA(temporary.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE) return(RESULT_WRITE_FAILED);
+
+ bool ok = Write_Range(file, header, HEADER_SIZE);
+ if (ok && !table.empty()) ok = Write_Range(file, table.data(), (std::uint32_t)table.size());
+ if (ok && payload_length > 0) ok = Write_Range(file, payload, payload_length);
+ if (ok) ok = (FlushFileBuffers(file) != FALSE);
+ if (!CloseHandle(file)) ok = false;
+
+ if (ok) ok = (MoveFileExA(temporary.c_str(), path, MOVEFILE_REPLACE_EXISTING) != FALSE);
+
+ if (!ok) {
+ DeleteFileA(temporary.c_str());
+ return(RESULT_WRITE_FAILED);
+ }
+
+ return(RESULT_OK);
+}
+
+
+SaveFileClass::ResultType SaveFileClass::Read(char const * path)
+{
+ Fields.clear();
+ Content.clear();
+
+ if (path == nullptr) return(RESULT_MISSING);
+
+ HANDLE const file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE) return(RESULT_MISSING);
+
+ // The header is judged before anything the file's size could ask for is allocated.
+ unsigned char head[HEADER_SIZE];
+ DWORD got = 0;
+ bool const ok = (ReadFile(file, head, HEADER_SIZE, &got, nullptr) != FALSE);
+
+ HeaderType header;
+ ResultType result = ok ? Parse_Header(head, got, header) : RESULT_CORRUPT;
+
+ std::vector image;
+ if (result == RESULT_OK) {
+ DWORD const size = GetFileSize(file, nullptr);
+ if (size == INVALID_FILE_SIZE || size != header.ContentOffset + header.StoredLength) {
+ result = RESULT_CORRUPT;
+ } else if (!Reserve(image, size)) {
+ result = RESULT_NO_MEMORY;
+ } else {
+ memcpy(image.data(), head, HEADER_SIZE);
+ if (!Read_Range(file, image.data() + HEADER_SIZE, size - HEADER_SIZE)) result = RESULT_CORRUPT;
+ }
+ }
+ CloseHandle(file);
+ if (result != RESULT_OK) return(result);
+
+ if (Header_CRC(image.data(), image.data() + HEADER_SIZE, header.TableLength) != header.HeaderCRC) {
+ return(RESULT_CORRUPT);
+ }
+
+ result = Parse_Fields(image.data() + HEADER_SIZE, header.TableLength);
+ if (result != RESULT_OK) return(result);
+
+ unsigned char const * const stored = image.data() + header.ContentOffset;
+ if (Checksum(stored, header.StoredLength) != header.ContentCRC) {
+ Fields.clear();
+ return(RESULT_CORRUPT);
+ }
+
+ if ((header.Flags & FLAG_LZO) != 0) {
+ if (!Reserve(Content, header.ContentLength)) {
+ Fields.clear();
+ return(RESULT_NO_MEMORY);
+ }
+
+ lzo_uint unpacked = (lzo_uint)Content.size();
+ int const status = lzo1x_decompress_safe(stored, (lzo_uint)header.StoredLength,
+ Content.data(), &unpacked, nullptr);
+
+ if (status != LZO_E_OK || unpacked != header.ContentLength) {
+ Fields.clear();
+ Content.clear();
+ return(RESULT_CORRUPT);
+ }
+ } else {
+ if (header.StoredLength != header.ContentLength) {
+ Fields.clear();
+ return(RESULT_CORRUPT);
+ }
+ if (!Reserve(Content, header.StoredLength)) {
+ Fields.clear();
+ return(RESULT_NO_MEMORY);
+ }
+ memcpy(Content.data(), stored, header.StoredLength);
+ }
+
+ return(RESULT_OK);
+}
+
+
+// Reads the header and the field table only, so listing a folder of saves touches a
+// few hundred bytes of each file.
+SaveFileClass::ResultType SaveFileClass::Read_Fields(char const * path)
+{
+ Fields.clear();
+ Content.clear();
+
+ if (path == nullptr) return(RESULT_MISSING);
+
+ HANDLE const file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE) return(RESULT_MISSING);
+
+ unsigned char head[HEADER_SIZE];
+ DWORD got = 0;
+ bool ok = (ReadFile(file, head, HEADER_SIZE, &got, nullptr) != FALSE);
+
+ HeaderType header;
+ ResultType result = ok ? Parse_Header(head, got, header) : RESULT_CORRUPT;
+
+ std::vector table;
+ if (result == RESULT_OK && header.TableLength > 0) {
+ DWORD const size = GetFileSize(file, nullptr);
+ if (size == INVALID_FILE_SIZE || header.TableLength > size - HEADER_SIZE) {
+ result = RESULT_CORRUPT;
+ } else if (!Reserve(table, header.TableLength)) {
+ result = RESULT_NO_MEMORY;
+ } else {
+ if (!Read_Range(file, table.data(), header.TableLength)) result = RESULT_CORRUPT;
+ }
+ }
+ CloseHandle(file);
+
+ if (result != RESULT_OK) return(result);
+ if (Header_CRC(head, table.data(), (std::uint32_t)table.size()) != header.HeaderCRC) return(RESULT_CORRUPT);
+
+ return(Parse_Fields(table.data(), (std::uint32_t)table.size()));
+}
diff --git a/code/savefile.h b/code/savefile.h
new file mode 100644
index 000000000..2a25968e0
--- /dev/null
+++ b/code/savefile.h
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * O P E N T S
+ *******************************************************************************
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2026 OpenTS contributors
+ *
+ * See LICENSE.md for applicable additional terms and warranty disclaimers.
+ ******************************************************************************/
+
+#pragma once
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include
+
+#include
+#include
+
+// The file a saved game is kept in: a fixed header, a table of listing fields, and one
+// compressed block of game state. docs/SAVE-FORMAT.md records the layout.
+class SaveFileClass
+{
+ public:
+ enum ResultType {
+ RESULT_OK,
+ RESULT_MISSING, // No file under that name.
+ RESULT_NOT_A_SAVE, // The file does not begin with the signature.
+ RESULT_UNSUPPORTED_VERSION, // A format version, or a header flag, this build does not read.
+ RESULT_CORRUPT, // A length, checksum or block that does not add up.
+ RESULT_WRITE_FAILED, // The file could not be written or moved into place.
+ RESULT_NO_MEMORY, // The file is within its limits but the process cannot hold it.
+ RESULT_TOO_LARGE, // The content or a listing field is more than a save can hold.
+ };
+
+ enum {
+ FORMAT_VERSION = 1,
+ HEADER_SIZE = 32,
+ };
+
+ SaveFileClass(void);
+
+ void Set_String(int id, char const * text);
+ void Set_Int(int id, int value);
+ void Set_Time(int id, FILETIME const & time);
+ bool Get_String(int id, char * text, int size) const;
+ bool Get_Int(int id, int * value) const;
+ bool Get_Time(int id, FILETIME * time) const;
+ void Clear_Fields(void);
+
+ ResultType Write(char const * path) const;
+ ResultType Read(char const * path);
+ ResultType Read_Fields(char const * path);
+
+ static char const * Result_Text(ResultType result);
+ static std::uint32_t Checksum(unsigned char const * data, std::uint32_t length, std::uint32_t seed = 0);
+
+ std::vector Content;
+
+ private:
+ enum FieldKind {
+ FIELD_STRING = 1,
+ FIELD_INT = 2,
+ FIELD_TIME = 3,
+ };
+
+ struct FieldType {
+ int ID;
+ int Kind;
+ std::vector Bytes;
+ };
+
+ FieldType const * Find(int id, int kind) const;
+ void Set(int id, int kind, void const * data, std::size_t length);
+ void Serialize_Fields(std::vector & table) const;
+ ResultType Parse_Fields(unsigned char const * table, std::uint32_t length);
+
+ std::vector Fields;
+};
diff --git a/code/saveload.cpp b/code/saveload.cpp
index 1353bf15b..df3ff9062 100644
--- a/code/saveload.cpp
+++ b/code/saveload.cpp
@@ -42,7 +42,6 @@
* Save_Misc_Values -- saves miscellaneous variables *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "saveload.h"
@@ -68,6 +67,7 @@
#include "builtype.h"
#include "bullet.h"
#include "bullettype.h"
+#include "classfactory.h"
#include "data.h"
#include "dbgprint.h"
#include "deploymentconfig.h"
@@ -79,7 +79,6 @@
#include "globals.h"
#include "goptions.h"
#include "houstype.h"
-#include "ilinkstm.h"
#include "infantry.h"
#include "infatype.h"
#include "init.h"
@@ -93,10 +92,12 @@
#include "ovrlight.h"
#include "particle.h"
#include "partsys.h"
+#include "persist.h"
#include "psystype.h"
#include "ptype.h"
#include "revent.h"
#include "rules.h"
+#include "savefile.h"
#include "savemgr.h"
#include "savestream.h"
#include "savever.h"
@@ -143,6 +144,9 @@
#include "objheaps.hh"
+#include
+#include
+#include
#include
//#define SAVE_BLOCK_SIZE 512
@@ -154,68 +158,165 @@
*/
unsigned int ExpectedGameVersion = LoadOptionsClass::GAMEVER_OPENTS;
-_COM_SMARTPTR_TYPEDEF(ILinkStream, __uuidof(ILinkStream));
+
+///
+/// Writes one object to the save stream as a record of its own. A reader that does not
+/// consume exactly the record's length has read a record of another shape than was
+/// written, and a missing object fails the stream rather than leaving a gap where the
+/// reader expects one.
+///
+/// bool; Was the record written whole?
+bool Save_Object(SaveStreamClass & stream, IPersistent * persist)
+{
+ if (persist == nullptr) {
+ stream.Fail();
+ return(false);
+ }
+
+ ClassID classid = persist->Class_ID();
+ stream.Serialize_Bytes(&classid, sizeof(classid));
+ unsigned int const lengthat = stream.Offset();
+ unsigned int length = 0;
+ stream.Serialize(length);
+ unsigned int const start = stream.Offset();
+
+ bool result = persist->Save(stream, true);
+ if (!result) {
+ return(false);
+ }
+
+ length = stream.Offset() - start;
+ stream.Overwrite_Bytes(lengthat, &length, sizeof(length));
+ return(!stream.Was_Error());
+}
+
+
+bool Save_Object(SaveStreamClass & stream, ILocomotion * locomotion)
+{
+ IPersistent * const persist = dynamic_cast(locomotion);
+ if (persist == nullptr) {
+ stream.Fail();
+ return(false);
+ }
+ return(Save_Object(stream, persist));
+}
+
+
+///
+/// Recreates one object from the save stream. It reattaches itself to its own heap as it
+/// is constructed, so the caller is handed it only to keep or to refuse.
+///
+/// Asked whether the object is of the class the caller expects, once
+/// the record has been read and before the object takes its place. May be null when any
+/// class will do.
+/// The object, owned by the caller, or nothing with the stream failed when the
+/// identifier names no registered class, the object could not read its record, the record's
+/// length does not match what the object consumed, or the class is not the one asked
+/// for.
+std::unique_ptr Load_Object(SaveStreamClass & stream, bool (*accepts)(IPersistent const * object))
+{
+ ClassID classid;
+ unsigned int length = 0;
+ stream.Serialize_Bytes(&classid, sizeof(classid));
+ stream.Serialize(length);
+ if (stream.Was_Error()) {
+ return(nullptr);
+ }
+
+ unsigned int const start = stream.Offset();
+ if (length > stream.Size() - start) {
+ DebugString("Save record at %u claims %u bytes, past the end of the save\n", start, length);
+ stream.Fail();
+ return(nullptr);
+ }
+
+ SwizzleManagerClass::MarkType const mark = Swizzler.Mark();
+ std::unique_ptr persist = Create_Object(classid);
+ if (persist == nullptr) {
+ DebugString("Save record at %u names a class this build does not register\n", start);
+ stream.Fail();
+ return(nullptr);
+ }
+
+ bool ok;
+ {
+ SaveStreamClass::BoundScope const bound(stream, start + length);
+ ok = persist->Load(stream);
+ }
+ if (ok && stream.Offset() != start + length) {
+ DebugString("Save record of %s at %u is %u bytes but %u were read\n",
+ typeid(*persist).name(), start, length, stream.Offset() - start);
+ ok = false;
+ }
+ if (ok && accepts != nullptr && !accepts(persist.get())) {
+ DebugString("Save record of %s at %u is not the class expected there\n",
+ typeid(*persist).name(), start);
+ ok = false;
+ }
+ if (!ok) {
+ Swizzler.Abandon(mark);
+ stream.Fail();
+ return(nullptr);
+ }
+
+ persist->Post_Load();
+ return(persist);
+}
///
/// Loads a vector of persistent objects from the save game stream.
-/// This routine reads the element count and then recreates each object through OLE. The
-/// objects are not handed back -- each one reattaches itself to its own heap as it is
-/// constructed, which is what refills the game's vectors.
+/// The objects are not handed back -- each one reattaches itself to its own heap as it is
+/// constructed, which is what refills the game's vectors. A record naming any class other
+/// than the heap's fails the load, since nothing else belongs in that heap.
///
-/// Returns with S_OK, or the failure code of the read that went wrong.
-__forceinline HRESULT Load_Vector(IStream * stream)
+/// bool; Was the record read whole?
+template
+static bool Load_Vector(SaveStreamClass & stream)
{
- int count;
- int index;
- LPVOID obj;
-
- HRESULT result = stream->Read(&count, sizeof(count), NULL);
- if (FAILED(result)) {
- return(result);
- }
- for (index = 0; index < count; index++) {
- result = OleLoadFromStream(stream, IID_IUnknown, &obj);
- if (FAILED(result)) {
- return(result);
+ int count = 0;
+ stream.Serialize(count);
+ if (stream.Was_Error()) {
+ return(false);
+ }
+ if (count < 0) {
+ stream.Fail();
+ return(false);
+ }
+
+ for (int index = 0; index < count; index++) {
+ std::unique_ptr object = Load_Object_As(stream);
+ if (object == nullptr) {
+ return(false);
}
+ // The object attached itself to its own heap as it was constructed, and the heap
+ // is what deletes it from here on.
+ object.release();
}
- return(S_OK);
+ return(true);
}
///
/// Saves a vector of persistent objects to the save game stream.
-/// This routine writes the element count and then streams out each object in turn through
-/// its IPersistStream interface.
///
-/// Returns with S_OK, or the failure code of the first object that refused to
-/// save.
+/// bool; Was the record read whole?
template
-__forceinline HRESULT Save_Vector(IStream * stream, const DynamicVectorClass &list)
+static bool Save_Vector(SaveStreamClass & stream, const DynamicVectorClass &list)
{
int count = list.Count();
- HRESULT result = stream->Write(&count, sizeof(count), NULL);
- if (SUCCEEDED(result)) {
- for (int index = 0; index < count; index++) {
- LPPERSISTSTREAM lpPS = NULL;
- result = list[index]->QueryInterface(IID_IPersistStream, (LPVOID *)&lpPS);
- if (FAILED(result)) {
- return(result);
- }
- result = OleSaveToStream(lpPS, stream);
- if (FAILED(result)) {
- return(result);
- }
- result = lpPS->Release();
- if (FAILED(result)) {
- return(result);
- }
+ stream.Serialize(count);
+
+ for (int index = 0; index < count; index++) {
+ bool const result = Save_Object(stream, list[index]);
+ if (!result) {
+ return(false);
}
- result = S_OK;
}
- return(result);
+ return(!stream.Was_Error());
}
+
+
///
/// Builds a checksum over the whole of the game object state.
/// This routine walks the scenario and every object and type heap, folding each one's own
@@ -301,7 +402,7 @@ void Print_Heap_CRCs(FILE * fp)
* HISTORY: *
* 07/08/1996 JLB : Created. *
*=============================================================================================*/
-static bool Put_All(IStream *stream, int save_net)
+static bool Put_All(SaveStreamClass & stream, int save_net)
{
/*
** Save the scenario global information.
@@ -311,7 +412,7 @@ static bool Put_All(IStream *stream, int save_net)
Rule->Save(stream);
DebugString("Saving AnimTypes\n");
- if (FAILED(Save_Vector(stream, AnimTypes))) {
+ if (!Save_Vector(stream, AnimTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
@@ -320,13 +421,13 @@ static bool Put_All(IStream *stream, int save_net)
** Save the map. The map must be saved first, since it saves the Theater.
*/
DebugString("Saving Map\n");
- if (FAILED(Map.Save(stream))) {
+ if (!Map.Save(stream)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Tunnels\n");
- if (FAILED(Save_Vector(stream, Tubes))) {
+ if (!Save_Vector(stream, Tubes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
@@ -335,7 +436,7 @@ static bool Put_All(IStream *stream, int save_net)
** Save miscellaneous variables.
*/
DebugString("Saving Misc. Values\n");
- if (FAILED(Save_Misc_Values(stream))) {
+ if (!Save_Misc_Values(stream)) {
DebugString("\t***** FAILED!\n");
return(false);
}
@@ -344,13 +445,13 @@ static bool Put_All(IStream *stream, int save_net)
** Save the Logic & Map layers
*/
DebugString("Saving Logic\n");
- if (FAILED(Logic.Save(stream))) {
+ if (!Logic.Save(stream)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving TacticalMap\n");
- if (FAILED(OleSaveToStream(TacticalMap, stream))) {
+ if (!Save_Object(stream, TacticalMap)) {
DebugString("\t***** FAILED!\n");
return(false);
}
@@ -360,248 +461,248 @@ static bool Put_All(IStream *stream, int save_net)
** TFixedIHeap class.
*/
DebugString("Saving HouseTypes\n");
- if (FAILED(Save_Vector(stream, HouseTypes))) {
+ if (!Save_Vector(stream, HouseTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Houses\n");
- if (FAILED(Save_Vector(stream, Houses))) {
+ if (!Save_Vector(stream, Houses)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Units\n");
- if (FAILED(Save_Vector(stream, Units))) {
+ if (!Save_Vector(stream, Units)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving UnitTypes\n");
- if (FAILED(Save_Vector(stream, UnitTypes))) {
+ if (!Save_Vector(stream, UnitTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving InfantryTypes\n");
- if (FAILED(Save_Vector(stream, InfantryTypes))) {
+ if (!Save_Vector(stream, InfantryTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Infantry\n");
- if (FAILED(Save_Vector(stream, Infantry))) {
+ if (!Save_Vector(stream, Infantry)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving BuildingTypes\n");
- if (FAILED(Save_Vector(stream, BuildingTypes))) {
+ if (!Save_Vector(stream, BuildingTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Buildings\n");
- if (FAILED(Save_Vector(stream, Buildings))) {
+ if (!Save_Vector(stream, Buildings)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving AircraftTypes\n");
- if (FAILED(Save_Vector(stream, AircraftTypes))) {
+ if (!Save_Vector(stream, AircraftTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Aircraft\n");
- if (FAILED(Save_Vector(stream, Aircraft))) {
+ if (!Save_Vector(stream, Aircraft)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Anims\n");
- if (FAILED(Save_Vector(stream, Anims))) {
+ if (!Save_Vector(stream, Anims)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving TaskForces\n");
- if (FAILED(Save_Vector(stream, TaskForces))) {
+ if (!Save_Vector(stream, TaskForces)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving TeamTypes\n");
- if (FAILED(Save_Vector(stream, TeamTypes))) {
+ if (!Save_Vector(stream, TeamTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Teams\n");
- if (FAILED(Save_Vector(stream, Teams))) {
+ if (!Save_Vector(stream, Teams)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving ScriptTypes\n");
- if (FAILED(Save_Vector(stream, ScriptTypes))) {
+ if (!Save_Vector(stream, ScriptTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Scripts\n");
- if (FAILED(Save_Vector(stream, Scripts))) {
+ if (!Save_Vector(stream, Scripts)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving TagTypes\n");
- if (FAILED(Save_Vector(stream, TagTypes))) {
+ if (!Save_Vector(stream, TagTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Tags\n");
- if (FAILED(Save_Vector(stream, Tags))) {
+ if (!Save_Vector(stream, Tags)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving TriggerTypes\n");
- if (FAILED(Save_Vector(stream, TriggerTypes))) {
+ if (!Save_Vector(stream, TriggerTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Triggers\n");
- if (FAILED(Save_Vector(stream, Triggers))) {
+ if (!Save_Vector(stream, Triggers)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving AITriggerTypes\n");
- if (FAILED(Save_Vector(stream, AITriggerTypes))) {
+ if (!Save_Vector(stream, AITriggerTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Actions\n");
- if (FAILED(Save_Vector(stream, Actions))) {
+ if (!Save_Vector(stream, Actions)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Events\n");
- if (FAILED(Save_Vector(stream, Events))) {
+ if (!Save_Vector(stream, Events)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Factories\n");
- if (FAILED(Save_Vector(stream, Factories))) {
+ if (!Save_Vector(stream, Factories)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving VoxelAnimTypes\n");
- if (FAILED(Save_Vector(stream, VoxelAnimTypes))) {
+ if (!Save_Vector(stream, VoxelAnimTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving VoxelAnims\n");
- if (FAILED(Save_Vector(stream, VoxelAnims))) {
+ if (!Save_Vector(stream, VoxelAnims)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Warheads\n");
- if (FAILED(Save_Vector(stream, Warheads))) {
+ if (!Save_Vector(stream, Warheads)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Weapons\n");
- if (FAILED(Save_Vector(stream, Weapons))) {
+ if (!Save_Vector(stream, Weapons)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving ParticleTypes\n");
- if (FAILED(Save_Vector(stream, ParticleTypes))) {
+ if (!Save_Vector(stream, ParticleTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Particles\n");
- if (FAILED(Save_Vector(stream, Particles))) {
+ if (!Save_Vector(stream, Particles)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving ParticleSystemTypes\n");
- if (FAILED(Save_Vector(stream, ParticleSystemTypes))) {
+ if (!Save_Vector(stream, ParticleSystemTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving ParticleSystems\n");
- if (FAILED(Save_Vector(stream, ParticleSystems))) {
+ if (!Save_Vector(stream, ParticleSystems)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving BulletTypes\n");
- if (FAILED(Save_Vector(stream, BulletTypes))) {
+ if (!Save_Vector(stream, BulletTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Bullets\n");
- if (FAILED(Save_Vector(stream, Bullets))) {
+ if (!Save_Vector(stream, Bullets)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving WaypointPaths\n");
- if (FAILED(Save_Vector(stream, WaypointPaths))) {
+ if (!Save_Vector(stream, WaypointPaths)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving SmudgeTypes\n");
- if (FAILED(Save_Vector(stream, SmudgeTypes))) {
+ if (!Save_Vector(stream, SmudgeTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving OverlayTypes\n");
- if (FAILED(Save_Vector(stream, OverlayTypes))) {
+ if (!Save_Vector(stream, OverlayTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving LightSources\n");
- if (FAILED(Save_Vector(stream, LightSources))) {
+ if (!Save_Vector(stream, LightSources)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving BuildingLights\n");
- if (FAILED(Save_Vector(stream, BuildingLights))) {
+ if (!Save_Vector(stream, BuildingLights)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Sides\n");
- if (FAILED(Save_Vector(stream, Sides))) {
+ if (!Save_Vector(stream, Sides)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Tiberiums\n");
- if (FAILED(Save_Vector(stream, Tiberiums))) {
+ if (!Save_Vector(stream, Tiberiums)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Empulses\n");
- if (FAILED(Save_Vector(stream, EMPulseClass::EMPulses))) {
+ if (!Save_Vector(stream, EMPulseClass::EMPulses)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving SuperWeaponTypes\n");
- if (FAILED(Save_Vector(stream, SuperWeaponTypes))) {
+ if (!Save_Vector(stream, SuperWeaponTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving SuperWeapons\n");
- if (FAILED(Save_Vector(stream, SuperWeapons))) {
+ if (!Save_Vector(stream, SuperWeapons)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving TerrianTypes\n");
- if (FAILED(Save_Vector(stream, TerrainTypes))) {
+ if (!Save_Vector(stream, TerrainTypes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Terrains\n");
- if (FAILED(Save_Vector(stream, Terrains))) {
+ if (!Save_Vector(stream, Terrains)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving FoggedObjects\n");
- if (FAILED(Save_Vector(stream, FoggedObjectClass::FoggyObjects))) {
+ if (!Save_Vector(stream, FoggedObjectClass::FoggyObjects)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving AlphaShapes\n");
- if (FAILED(Save_Vector(stream, AlphaShapes))) {
+ if (!Save_Vector(stream, AlphaShapes)) {
DebugString("\t***** FAILED!\n");
return(false);
}
DebugString("Saving Waves\n");
- if (FAILED(Save_Vector(stream, Waves))) {
+ if (!Save_Vector(stream, Waves)) {
DebugString("\t***** FAILED!\n");
return(false);
}
@@ -628,7 +729,7 @@ static bool Put_All(IStream *stream, int save_net)
}
}
- return(true);
+ return(!stream.Was_Error());
}
@@ -640,7 +741,7 @@ static bool Put_All(IStream *stream, int save_net)
/// order they were written out.
///
/// bool; Was the game state restored?
-static bool Get_All(IStream *stream, bool save_net)
+static bool Get_All(SaveStreamClass & stream, bool save_net)
{
Clear_Scenario();
Scen->Load(stream);
@@ -685,177 +786,184 @@ static bool Get_All(IStream *stream, bool save_net)
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// AnimTypes
+ if (!Load_Vector(stream)) { /// AnimTypes
return(false);
}
- Map.Load(stream);
+ if (!Map.Load(stream)) {
+ return(false);
+ }
- if (FAILED(Load_Vector(stream))) { /// Tubes
+ if (!Load_Vector(stream)) { /// Tubes
return(false);
}
- if (FAILED(Load_Misc_Values(stream))) {
+ if (!Load_Misc_Values(stream)) {
return(false);
}
Map.Reset_All_Subzones();
- Logic.Load(stream);
+ if (!Logic.Load(stream)) {
+ return(false);
+ }
if (TacticalMap != NULL) {
delete TacticalMap;
TacticalMap = NULL;
}
- Tactical * old_tactical;
- if (FAILED(OleLoadFromStream(stream, IID_IUnknown, (LPVOID *)&old_tactical))) {
+ std::unique_ptr tactical = Load_Object_As(stream);
+ if (tactical == nullptr) {
return(false);
}
+ // The map installed itself in TacticalMap as it was constructed, and that global is
+ // what deletes it from here on.
+ tactical.release();
- if (FAILED(Load_Vector(stream))) { /// HouseTypes
+ if (!Load_Vector(stream)) { /// HouseTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Houses
+ if (!Load_Vector(stream)) { /// Houses
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Units
+ if (!Load_Vector(stream)) { /// Units
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// UnitTypes
+ if (!Load_Vector(stream)) { /// UnitTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// InfantryTypes
+ if (!Load_Vector(stream)) { /// InfantryTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Infantry
+ if (!Load_Vector(stream)) { /// Infantry
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// BuildingTypes
+ if (!Load_Vector(stream)) { /// BuildingTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Buildings
+ if (!Load_Vector(stream)) { /// Buildings
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// AircraftTypes
+ if (!Load_Vector(stream)) { /// AircraftTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Aircraft
+ if (!Load_Vector(stream)) { /// Aircraft
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Anims
+ if (!Load_Vector(stream)) { /// Anims
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// TaskForces
+ if (!Load_Vector(stream)) { /// TaskForces
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// TeamTypes
+ if (!Load_Vector(stream)) { /// TeamTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Teams
+ if (!Load_Vector(stream)) { /// Teams
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// ScriptTypes
+ if (!Load_Vector(stream)) { /// ScriptTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Scripts
+ if (!Load_Vector(stream)) { /// Scripts
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// TagTypes
+ if (!Load_Vector(stream)) { /// TagTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Tags
+ if (!Load_Vector(stream)) { /// Tags
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// TriggerTypes
+ if (!Load_Vector(stream)) { /// TriggerTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Triggers
+ if (!Load_Vector(stream)) { /// Triggers
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// AITriggerTypes
+ if (!Load_Vector(stream)) { /// AITriggerTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Actions
+ if (!Load_Vector(stream)) { /// Actions
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Events
+ if (!Load_Vector(stream)) { /// Events
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Factories
+ if (!Load_Vector(stream)) { /// Factories
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// VoxelAnimTypes
+ if (!Load_Vector(stream)) { /// VoxelAnimTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// VoxelAnims
+ if (!Load_Vector(stream)) { /// VoxelAnims
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Warheads
+ if (!Load_Vector(stream)) { /// Warheads
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Weapons
+ if (!Load_Vector(stream)) { /// Weapons
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// ParticleTypes
+ if (!Load_Vector(stream)) { /// ParticleTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Particles
+ if (!Load_Vector(stream)) { /// Particles
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// ParticleSystemTypes
+ if (!Load_Vector(stream)) { /// ParticleSystemTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// ParticleSystems
+ if (!Load_Vector(stream)) { /// ParticleSystems
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// BulletTypes
+ if (!Load_Vector(stream)) { /// BulletTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Bullets
+ if (!Load_Vector(stream)) { /// Bullets
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// WaypointPaths
+ if (!Load_Vector(stream)) { /// WaypointPaths
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// SmudgeTypes
+ if (!Load_Vector(stream)) { /// SmudgeTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// OverlayTypes
+ if (!Load_Vector(stream)) { /// OverlayTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// LightSources
+ if (!Load_Vector(stream)) { /// LightSources
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// BuildingLights
+ if (!Load_Vector(stream)) { /// BuildingLights
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Sides
+ if (!Load_Vector(stream)) { /// Sides
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Tiberiums
+ if (!Load_Vector(stream)) { /// Tiberiums
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// EMPulseClass::EMPulses
+ if (!Load_Vector(stream)) { /// EMPulseClass::EMPulses
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// SuperWeaponTypes
+ if (!Load_Vector(stream)) { /// SuperWeaponTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// SuperWeapons
+ if (!Load_Vector(stream)) { /// SuperWeapons
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// TerrainTypes
+ if (!Load_Vector(stream)) { /// TerrainTypes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Terrains
+ if (!Load_Vector(stream)) { /// Terrains
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// FoggedObjectClass::FoggyObjects
+ if (!Load_Vector(stream)) { /// FoggedObjectClass::FoggyObjects
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// AlphaShapes
+ if (!Load_Vector(stream)) { /// AlphaShapes
return(false);
}
- if (FAILED(Load_Vector(stream))) { /// Waves
+ if (!Load_Vector(stream)) { /// Waves
return(false);
}
if (!VeinholeMonsterClass::Load_All(stream)) {
@@ -875,7 +983,7 @@ static bool Get_All(IStream *stream, bool save_net)
Map.Flag_To_Redraw(GS_REDRAW_ALL);
- return(true);
+ return(!stream.Was_Error());
}
/***************************************************************************
@@ -919,34 +1027,10 @@ static bool Get_All(IStream *stream, bool save_net)
*=========================================================================*/
bool Save_Game(const char *file_name, char const * descr)
{
- WCHAR name[MAX_PATH];
-
DebugString("\nSAVING GAME [%s - %s]\n", file_name, descr);
Swizzler.Begin_Save();
- MultiByteToWideChar(0,0, Saved_Game_Name(file_name).c_str(), -1, name, sizeof(name)/sizeof(WCHAR));
-
- /*
- ** Open the file
- */
- DebugString("Creating DocFile\n");
- IStoragePtr storage;
- if (FAILED(StgCreateDocfile(name, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &storage))) {
- DebugString("\t***** FAILED!\n");
- 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.)
- */
SaveVersionInfo info;
info.Set_Internal_Version(ExpectedGameVersion);
info.Set_Scenario_Description(descr);
@@ -956,66 +1040,37 @@ bool Save_Game(const char *file_name, char const * descr)
info.Set_Scenario_Number(Scen->Scenario);
info.Set_Executable_Name("SUN.EXE");
info.Set_Game_Type(Session.Type);
+
FILETIME FileTime;
- CoFileTimeNow(&FileTime);
+ GetSystemTimeAsFileTime(&FileTime);
info.Set_Last_Time(FileTime);
info.Set_Start_Time(FileTime);
info.Set_Play_Time(FileTime);
- /*
- ** Save the save-game version, for loading verification
- */
- DebugString("Saving version information\n");
- if (FAILED(info.Save(storage))) {
- DebugString("\t***** FAILED!\n");
- return(false);
- }
-
- DebugString("Creating content stream\n");
- IStreamPtr content;
- if (FAILED(storage->CreateStream(L"CONTENTS", STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0, &content))) {
- DebugString("\t***** FAILED!\n");
- return(false);
- }
+ SaveFileClass file;
+ info.Save(file);
- DebugString("Linking content stream to compressor\n");
- ILinkStreamPtr link;
- link.CreateInstance(CLSID_CompressStream, NULL, CLSCTX_INPROC|CLSCTX_LOCAL_SERVER);
- if (FAILED(link->Link_Stream(content))) {
- DebugString("\t***** FAILED!\n");
- return(false);
- }
- IStreamPtr stream(link);
-
- /*
- ** Dump the save game data to the file. The data is compressed
- ** and then encrypted. The message digest is calculated in the
- ** process by using the data just as it is written to disk.
- */
DebugString("Calling Put_All()\n");
- bool res = Put_All(stream,0);
-
- DebugString("Unlinking content stream from compressor\n");
- if (FAILED(link->Unlink_Stream(NULL))) {
+ SaveStreamClass stream(file.Content, SaveStreamClass::MODE_SAVE);
+ bool res = Put_All(stream, 0);
+ if (!res) {
DebugString("\t***** FAILED!\n");
- return(false);
}
- DebugString("Releasing content stream\n");
- content.Release();
-
- DebugString("Closing DocFile\n");
- if (FAILED(storage->Commit(0))) {
- DebugString("\t***** FAILED!\n");
- return(false);
+ if (res) {
+ DebugString("Writing %s\n", file_name);
+ SaveFileClass::ResultType const result = file.Write(Saved_Game_Name(file_name).c_str());
+ if (result != SaveFileClass::RESULT_OK) {
+ DebugString("\t***** FAILED! (%s)\n", SaveFileClass::Result_Text(result));
+ res = false;
+ }
}
- DebugString("SAVING GAME [%s - %s] - Complete\n\n", file_name, descr);
+ DebugString("SAVING GAME [%s - %s] - %s\n\n", file_name, descr, res ? "Complete" : "Failed");
if (res) {
SaveManager.Autosave.Schedule(Frame);
}
-
return(res);
}
@@ -1062,62 +1117,54 @@ bool Save_Game(const char *file_name, char const * descr)
*=========================================================================*/
bool Load_Game(const char *file_name)
{
- WCHAR name[MAX_PATH];
-
DebugString("\nLOADING GAME [%s]\n", file_name);
- /*
- ** Read & discard the save-game's header info
- */
- SaveVersionInfo info;
- if (!Get_Savefile_Info(file_name, &info)) {
+ // The whole file is checked before the running game is torn down, so a damaged
+ // save costs nothing. The listing fields come back with it, so the version this
+ // build will not read is judged on the same read rather than on a second one.
+ SaveFileClass file;
+ SaveFileClass::ResultType const result = file.Read(Saved_Game_Name(file_name).c_str());
+ if (result != SaveFileClass::RESULT_OK) {
+ DebugString("\t***** FAILED! (%s)\n", SaveFileClass::Result_Text(result));
return(false);
}
- /*
- * The load dialog screens the saves it lists, but a network save reaches this routine
- * without passing through it, so the stamp is checked here as well.
- */
+ SaveVersionInfo info;
+ if (!info.Load(file)) {
+ return(false);
+ }
if (info.Get_Internal_Version() != ExpectedGameVersion) {
return(false);
}
- LoadedSaveVersion = info.Get_Internal_Version();
+ LoadedSaveVersion = info.Get_Internal_Version();
Session.Type = (GameType)info.Get_Game_Type();
- Swizzler.Discard();
-
- /*
- ** Open the file
- */
- IStoragePtr storage;
-
- // Structured storage goes straight to Windows, so the saved game is named in full first.
- MultiByteToWideChar(0,0,Saved_Game_Name(file_name).c_str(), -1, name, (sizeof(name)/sizeof(WCHAR)));
- if (FAILED(StgOpenStorage(name, 0, STGM_SHARE_DENY_WRITE, 0, 0, &storage))) {
- return(false);
- }
-
- IStreamPtr content;
- if (FAILED(storage->OpenStream(L"CONTENTS", 0, STGM_SHARE_EXCLUSIVE, 0, &content))) {
- return(false);
- }
+ Swizzler.Discard();
- IUnknown *pUnknown = NULL;
- ILinkStreamPtr link;
- link.CreateInstance(CLSID_CompressStream, pUnknown,CLSCTX_INPROC|CLSCTX_LOCAL_SERVER);
- if (FAILED(link->Link_Stream(content))) {
- return(false);
+ SaveStreamClass stream(file.Content, SaveStreamClass::MODE_LOAD);
+ bool res = false;
+ // The catch sits here rather than around the whole routine because what was already
+ // loaded still has to be abandoned below. Both of the ways a count read from the file
+ // can end an allocation are refused here; anything else still raises.
+ try {
+ res = Get_All(stream, false);
+ } catch (std::bad_alloc const &) {
+ DebugString("\t***** FAILED! (out of memory at %u of %u bytes)\n", stream.Offset(), stream.Size());
+ } catch (std::length_error const &) {
+ DebugString("\t***** FAILED! (a count no container can hold at %u of %u bytes)\n",
+ stream.Offset(), stream.Size());
}
- IStreamPtr stream(link);
-
- bool res = Get_All(stream, false);
-
- link->Unlink_Stream(NULL);
-
if (!res) {
+ DebugString("\t***** FAILED! (at %u of %u bytes)\n", stream.Offset(), stream.Size());
+ // What was loaded stays in the heaps until the next teardown, so the requests it
+ // registered must not be answered into it once the game that follows has moved on.
+ Swizzler.Discard();
return(false);
}
+ if (stream.Offset() != stream.Size()) {
+ DebugString("Save carries %u bytes past its last record\n", stream.Size() - stream.Offset());
+ }
Swizzler.Resolve();
@@ -1211,11 +1258,10 @@ static void Serialize_Misc_Values(SaveStreamClass & stream)
}
-int Save_Misc_Values(IStream * stream)
+int Save_Misc_Values(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize_Misc_Values(savestream);
- return(savestream.Result());
+ Serialize_Misc_Values(stream);
+ return(!stream.Was_Error());
}
@@ -1232,12 +1278,11 @@ int Save_Misc_Values(IStream * stream)
* 06/24/1995 BRR : Created. *
* 03/12/1996 JLB : Simplified. *
*=============================================================================================*/
-int Load_Misc_Values(IStream * stream)
+int Load_Misc_Values(SaveStreamClass & stream)
{
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("Load_Misc_Values");
- Serialize_Misc_Values(savestream);
- return(savestream.Result());
+ stream.Set_Context("Load_Misc_Values");
+ Serialize_Misc_Values(stream);
+ return(!stream.Was_Error());
}
@@ -1261,23 +1306,20 @@ int Load_Misc_Values(IStream * stream)
*=========================================================================*/
bool Get_Savefile_Info(char const * name, SaveVersionInfo * info)
{
- IStoragePtr storage;
- WCHAR wname[MAX_PATH];
-
- // Structured storage goes straight to Windows, so the saved game is named in full first.
- MultiByteToWideChar(0, 0, Saved_Game_Name(name).c_str(), -1, wname, sizeof(wname) / sizeof(WCHAR));
-
- HRESULT result = StgOpenStorage(wname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &storage);
- if (FAILED(result)) {
+ if (name == nullptr || info == nullptr) {
return(false);
}
- result = info->Load(storage);
- if (FAILED(result)) {
+ SaveFileClass file;
+ SaveFileClass::ResultType const result = file.Read_Fields(Saved_Game_Name(name).c_str());
+ if (result != SaveFileClass::RESULT_OK) {
+ if (result != SaveFileClass::RESULT_MISSING) {
+ DebugString("Saved game %s: %s\n", name, SaveFileClass::Result_Text(result));
+ }
return(false);
}
- return(true);
+ return(info->Load(file));
}
diff --git a/code/saveload.h b/code/saveload.h
index c92bc5df8..556fe04d9 100644
--- a/code/saveload.h
+++ b/code/saveload.h
@@ -13,16 +13,49 @@
#pragma once
+#include "persist.h"
+
#include
+#include
-struct IStream;
+class SaveStreamClass;
class SaveVersionInfo;
+struct ILocomotion;
/*
** SAVELOAD.CPP
*/
-int Load_Misc_Values(IStream * stream);
-int Save_Misc_Values(IStream * stream);
+int Load_Misc_Values(SaveStreamClass & stream);
+int Save_Misc_Values(SaveStreamClass & stream);
+
+// A loaded object is handed back owned; one that belongs to a heap is released there by
+// the caller that puts it in one. docs/SAVE-FORMAT.md records what a record holds.
+bool Save_Object(SaveStreamClass & stream, IPersistent * object);
+bool Save_Object(SaveStreamClass & stream, ILocomotion * locomotion);
+std::unique_ptr Load_Object(SaveStreamClass & stream,
+ bool (*accepts)(IPersistent const * object) = nullptr);
+
+///
+/// Loads the record next in the stream and requires it to be of the class asked for.
+///
+/// The object, owned by the caller, or nothing with the stream failed when the
+/// record holds another class. A record of the wrong class is destroyed before it can take
+/// its place, so the test happens while the object is still only the reader's.
+template
+std::unique_ptr Load_Object_As(SaveStreamClass & stream)
+{
+ std::unique_ptr object = Load_Object(stream, [](IPersistent const * candidate) {
+ return(dynamic_cast(candidate) != nullptr);
+ });
+
+ // The record was accepted only if it holds a T, so this cast answers for what was loaded.
+ T * const wanted = dynamic_cast(object.get());
+ if (wanted != nullptr) {
+ object.release();
+ }
+ return(std::unique_ptr(wanted));
+}
+
bool Get_Savefile_Info(char const * name, SaveVersionInfo * info);
bool Save_Game(const char *file_name, char const * descr);
bool Load_Game(const char *file_name);
diff --git a/code/savestream.cpp b/code/savestream.cpp
index fd15e72f6..ca5e7d1d3 100644
--- a/code/savestream.cpp
+++ b/code/savestream.cpp
@@ -7,26 +7,30 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "savestream.h"
#include "saveload.h"
+#include
+
unsigned int LoadedSaveVersion = 0;
///
-/// Builds a save stream over the stream given.
+/// Builds a save stream over the buffer given, appending to it when saving and reading
+/// it from the start when loading.
///
-/// The stream the members are to be read from or written to.
+/// The bytes of the saved game, which must outlive this stream.
/// Is this stream saving or loading?
-SaveStreamClass::SaveStreamClass(IStream * stream, ModeType mode) :
- Stream(stream),
+SaveStreamClass::SaveStreamClass(std::vector & buffer, ModeType mode) :
+ Buffer(&buffer),
+ Cursor(mode == MODE_SAVE ? (unsigned int)buffer.size() : 0),
+ Limit((unsigned int)buffer.size()),
Mode(mode),
- ErrorCode(stream != NULL ? S_OK : E_POINTER),
+ Failed(false),
FormatVersion(mode == MODE_LOAD ? LoadedSaveVersion : ExpectedGameVersion),
OwnerType(NULL),
OwnerID(0)
@@ -34,51 +38,59 @@ SaveStreamClass::SaveStreamClass(IStream * stream, ModeType mode) :
}
-///
-/// Stops the pass, as though the stream itself had failed.
-/// This is for a record that reads back as something no save could hold -- a length that
-/// is negative, or one that does not fit the object waiting for it. An earlier failure is
-/// left in place, since it is the one that explains the rest.
-///
void SaveStreamClass::Fail(void)
{
- if (SUCCEEDED(ErrorCode)) {
- ErrorCode = E_FAIL;
- }
+ Failed = true;
}
///
-/// Moves a block of bytes between the object and the stream.
-/// Every other Serialize reaches the stream through this one. Once something has gone
-/// wrong the block is left alone and the failure is kept, so the rest of the pass runs
-/// harmlessly and the caller finds out at the end.
+/// Moves the bytes of one value between the caller and the stream.
+/// A load that runs out of stream in the middle of a value fails the stream rather than
+/// hand back a partly read value, and every later call is ignored. A negative length is
+/// a count that wrapped, and fails the same way.
///
-/// The bytes to write, or the place to read them back into.
-/// The number of bytes to move.
void SaveStreamClass::Serialize_Bytes(void * data, int length)
{
- if (FAILED(ErrorCode)) {
+ if (Failed) {
+ return;
+ }
+ if (length < 0) {
+ Failed = true;
+ return;
+ }
+ if (length == 0) {
return;
}
- ULONG moved = 0;
- HRESULT result;
+ unsigned char * const bytes = (unsigned char *)data;
if (Mode == MODE_SAVE) {
- result = Stream->Write(data, length, &moved);
+ Buffer->insert(Buffer->end(), bytes, bytes + length);
+ Cursor = (unsigned int)Buffer->size();
} else {
- result = Stream->Read(data, length, &moved);
+ // The bound is the record being read rather than the whole stream, so a member that
+ // reads more than its own is refused instead of quietly spending the bytes of the
+ // record after it.
+ if ((unsigned int)length > Limit - Cursor) {
+ Failed = true;
+ return;
+ }
+ memcpy(bytes, Buffer->data() + Cursor, (std::size_t)length);
+ Cursor += (unsigned int)length;
}
+}
- /*
- * A stream that stops early has run out in the middle of an object, which leaves
- * the rest of the members holding whatever they held before. Treat it as a failure
- * rather than let a half-read object reach the game.
- */
- if (SUCCEEDED(result) && moved != (ULONG)length) {
- result = E_FAIL;
- }
- ErrorCode = result;
+// A saver patches a length it could not know until the record was written.
+void SaveStreamClass::Overwrite_Bytes(unsigned int offset, void const * data, int length)
+{
+ if (Failed || Mode != MODE_SAVE || length <= 0) {
+ return;
+ }
+ if (offset > Buffer->size() || (unsigned int)length > Buffer->size() - offset) {
+ Failed = true;
+ return;
+ }
+ memcpy(Buffer->data() + offset, data, (std::size_t)length);
}
diff --git a/code/savestream.h b/code/savestream.h
index 0d7fc56c2..9655cbde3 100644
--- a/code/savestream.h
+++ b/code/savestream.h
@@ -15,7 +15,9 @@
#include "win.h"
#include
+#include
#include
+#include
#include
#include
#include
@@ -73,7 +75,7 @@ class SaveStreamClass
MODE_LOAD
};
- SaveStreamClass(IStream * stream, ModeType mode);
+ SaveStreamClass(std::vector & buffer, ModeType mode);
bool Is_Saving(void) const {return(Mode == MODE_SAVE);}
bool Is_Loading(void) const {return(Mode == MODE_LOAD);}
@@ -83,8 +85,7 @@ class SaveStreamClass
* does nothing, so a class lists its members without checking each one and the
* caller asks once whether the whole pass worked.
*/
- HRESULT Result(void) const {return(ErrorCode);}
- bool Was_Error(void) const {return(FAILED(ErrorCode));}
+ bool Was_Error(void) const {return(Failed);}
/*
* Stops the pass here. A container that reads back a length no honest save could
@@ -99,11 +100,6 @@ class SaveStreamClass
*/
unsigned int Version(void) const {return(FormatVersion);}
- /*
- * The stream underneath, for the sub-objects that are still framed by OLE.
- */
- IStream * Get_Stream(void) const {return(Stream);}
-
/*
* Names the record this stream is carrying, so that a pointer which nothing
* answers for can be reported against the object that asked for it.
@@ -113,9 +109,86 @@ class SaveStreamClass
OwnerType = ownertype;
OwnerID = ownerid;
}
+ char const * Context_Type(void) const {return(OwnerType);}
+ SwizzleIDType Context_ID(void) const {return(OwnerID);}
+
+ /*
+ * Where the next byte goes or comes from, so a record can be framed by its length.
+ */
+ unsigned int Offset(void) const {return(Cursor);}
+
+ /*
+ * Holds a load to one record while it is read, so that a member reading more than
+ * its record holds is refused rather than spending the bytes of the record after
+ * it. A record nested in another leaves the outer one bounded as it was.
+ */
+ class BoundScope
+ {
+ public:
+ BoundScope(SaveStreamClass & stream, unsigned int end)
+ : Stream(stream), Previous(stream.Limit)
+ {
+ if (end <= Stream.Limit) {
+ Stream.Limit = end;
+ }
+ }
+
+ ~BoundScope(void) {Stream.Limit = Previous;}
+
+ BoundScope(BoundScope const &) = delete;
+ BoundScope & operator=(BoundScope const &) = delete;
+
+ private:
+ SaveStreamClass & Stream;
+ unsigned int Previous;
+ };
+ unsigned int Size(void) const {return((unsigned int)Buffer->size());}
+ void Overwrite_Bytes(unsigned int offset, void const * data, int length);
void Serialize_Bytes(void * data, int length);
+ /*
+ * Refuses a count that the bytes left in the stream could not hold, so a damaged
+ * count fails the load before anything is allocated for it. Nothing serializes
+ * an element in less than a byte.
+ */
+ bool Fits(int count, std::size_t each)
+ {
+ if (Is_Loading()) {
+ std::size_t const room = (std::size_t)(Limit - Cursor) / (each > 0 ? each : 1);
+ if (count < 0 || (std::size_t)count > room) {
+ Fail();
+ return(false);
+ }
+ }
+ return(true);
+ }
+
+ /*
+ * Sizes a container the count asked for, failing the pass rather than throwing when
+ * the process cannot hold it. A count within the bytes remaining still asks for that
+ * many elements, which is more memory than the stream itself occupies, and for a
+ * wide element more than the container itself will hold.
+ */
+ template
+ bool Reserve(C & container, int count)
+ {
+ if (count < 0 || (std::size_t)count > container.max_size()) {
+ Fail();
+ return(false);
+ }
+
+ try {
+ container.clear();
+ container.resize((std::size_t)count);
+ } catch (std::bad_alloc const &) {
+ container.clear();
+ Fail();
+ return(false);
+ }
+ return(true);
+ }
+
/*
* Numbers and enumerations travel as their declared width.
*/
@@ -175,6 +248,39 @@ class SaveStreamClass
}
}
+ // How much room a buffer keeps for its text is this build's business rather than
+ // the file's, so only the text travels. The last byte stays the terminator, since
+ // every reader of these buffers treats them as C strings. This claims every
+ // char[N], so one holding bytes rather than text would be cut at its first zero.
+ template
+ void Serialize(char (&value)[N], std::source_location const & = std::source_location::current())
+ {
+ int count = 0;
+
+ if (Is_Saving()) {
+ while (count < N - 1 && value[count] != '\0') {
+ count++;
+ }
+ }
+
+ Serialize(count);
+
+ if (Is_Loading() && (count < 0 || count >= N)) {
+ Fail();
+ return;
+ }
+
+ if (count > 0) {
+ Serialize_Bytes(value, count);
+ }
+
+ if (Is_Loading()) {
+ for (int index = count; index < N; index++) {
+ value[index] = '\0';
+ }
+ }
+ }
+
/*
* The standard library's fixed size array travels as the built in one does.
*/
@@ -203,12 +309,12 @@ class SaveStreamClass
Serialize(count);
if (Is_Loading()) {
- if (count < 0) {
- Fail();
+ if (!Fits(count, (std::is_arithmetic_v || std::is_enum_v) ? sizeof(T) : 1)) {
+ return;
+ }
+ if (!Reserve(value, count)) {
return;
}
- value.clear();
- value.resize(count);
}
if constexpr (std::is_arithmetic_v || std::is_enum_v) {
@@ -254,12 +360,12 @@ class SaveStreamClass
Serialize(count);
if (Is_Loading()) {
- if (count < 0) {
- Fail();
+ if (!Fits(count, 1)) {
+ return;
+ }
+ if (!Reserve(value, count)) {
return;
}
- value.clear();
- value.resize(count);
}
for (int index = 0; index < count; index++) {
@@ -278,11 +384,9 @@ class SaveStreamClass
Serialize(count);
if (Is_Loading()) {
- if (count < 0) {
- Fail();
+ if (!Fits(count, 1) || !Reserve(value, count)) {
return;
}
- value.assign((std::size_t)count, false);
}
for (int index = 0; index < count; index++) {
@@ -301,11 +405,9 @@ class SaveStreamClass
Serialize(count);
if (Is_Loading()) {
- if (count < 0) {
- Fail();
+ if (!Fits(count, 1) || !Reserve(value, count)) {
return;
}
- value.resize(count);
}
if (count > 0) {
@@ -338,9 +440,13 @@ class SaveStreamClass
}
}
- IStream * Stream;
+ std::vector * Buffer;
+ unsigned int Cursor;
+
+ // A read is judged against the record being loaded rather than the whole stream.
+ unsigned int Limit;
ModeType Mode;
- HRESULT ErrorCode;
+ bool Failed;
unsigned int FormatVersion;
/*
@@ -365,8 +471,7 @@ class SaveStreamClass
/*
- * The version stamp of the save game currently being read. Each object builds its own
- * stream inside IPersistStream::Load, which has no way to be told, so the value is left
- * here by the load as a whole.
+ * The version stamp of the save game currently being read, left here by the load as a
+ * whole so every stream built during it reports the same version.
*/
extern unsigned int LoadedSaveVersion;
diff --git a/code/savever.cpp b/code/savever.cpp
index 414bb7ce6..68424fb9b 100644
--- a/code/savever.cpp
+++ b/code/savever.cpp
@@ -11,13 +11,11 @@
#include "savever.h"
-#include "utf8.h"
+#include "savefile.h"
#include "dbgprint.h"
#include "session.h"
-#include
-
///
/// Creates an empty save file information block.
@@ -33,8 +31,6 @@ SaveVersionInfo::SaveVersionInfo(void) :
{
ScenarioDescription[0] = '\0';
PlayerHouse[0] = '\0';
- UnknownString[0] = '\0';
- PlayerName[0] = '\0';
ExecutableName[0] = '\0';
StartTime.dwLowDateTime = 0;
@@ -177,50 +173,6 @@ int SaveVersionInfo::Get_Scenario_Number(void)
}
-///
-/// Records the spare string kept with the save information.
-/// The string is truncated if it will not fit the buffer it is kept in.
-///
-void SaveVersionInfo::Set_Unknown_String(const char * str)
-{
- UnknownString[sizeof(UnknownString) - 1] = 0;
- strncpy(UnknownString, str, sizeof(UnknownString) - 1);
-}
-
-
-///
-/// Fetches the spare string kept with the save information.
-/// Neither the save nor the load routine records this string, so it only ever holds what
-/// the current session put there.
-///
-/// Returns with the string most recently set.
-const char * SaveVersionInfo::Get_Unknown_String(void)
-{
- return(UnknownString);
-}
-
-
-///
-/// Records the name of the player making the save.
-/// The name is truncated if it will not fit the buffer it is kept in.
-///
-void SaveVersionInfo::Set_Player_Name(const char * name)
-{
- PlayerName[sizeof(PlayerName) - 1] = 0;
- strncpy(PlayerName, name, sizeof(PlayerName) - 1);
-}
-
-
-///
-/// Fetches the name of the player who made the save.
-///
-/// Returns with the player name recorded in the save.
-const char * SaveVersionInfo::Get_Player_Name(void)
-{
- return(PlayerName);
-}
-
-
///
/// Records the name of the program writing the save.
/// The name is truncated if it will not fit the buffer it is kept in.
@@ -320,796 +272,40 @@ int SaveVersionInfo::Get_Game_Type(void)
///
-/// Saves the version information into a save file.
-/// This routine is called while a save game is being written. It records every value into
-/// the summary information property set and then again as one stream per value, so that a
-/// reader which knows only the older layout can still identify the save.
+/// Writes every listing field into the file's field table.
///
-/// Returns with S_OK once every value has been written, otherwise the failure code
-/// from the storage layer.
-HRESULT SaveVersionInfo::Save(IStorage *storage)
+void SaveVersionInfo::Save(SaveFileClass & file) const
{
- if (storage == NULL) {
- return(E_POINTER);
- }
-
- DebugString("Attempting to obtain PropertySetStorage interface\n");
-
- IPropertySetStoragePtr storageset;
- HRESULT res;
-
- res = storage->QueryInterface(IID_IPropertySetStorage, (void **)&storageset);
- if (SUCCEEDED(res)) {
-
- DebugString("Saving version information the NEW way.\n");
-
- res = Save_String_Set(storageset, PIDSI_SCEN_DESCRIP, ScenarioDescription);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String_Set(storageset, PIDSI_PLAYER_HOUSE, PlayerHouse);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int_Set(storageset, PIDSI_G_VERSION, Version);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int_Set(storageset, PIDSI_INTERNAL_VER, InternalVersion);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Time_Set(storageset, PIDSI_G_START_TIME, &StartTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Time_Set(storageset, PIDSI_LAST_SAVE_TIME, &LastSaveTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Time_Set(storageset, PIDSI_G_PLAY_TIME, &PlayTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String_Set(storageset, PIDSI_EXEC_NAME, ExecutableName);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String_Set(storageset, PIDSI_PLAYER_NAME1, PlayerName);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String_Set(storageset, PIDSI_PLAYER_NAME2, PlayerName);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int_Set(storageset, PIDSI_SCENARIO_NUM, ScenarioNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int_Set(storageset, PIDSI_CAMPAIGN_NUM, CampaignNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int_Set(storageset, PIDSI_GAME_TYPE, GameType);
- if (FAILED(res)) {
- return(res);
- }
-
- } else {
- DebugString("\t***** FAILED!\n");
- }
-
- DebugString("Saving version information the old way.\n");
-
- res = Save_String(storage, PIDSI_SCEN_DESCRIP, ScenarioDescription);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String(storage, PIDSI_PLAYER_HOUSE, PlayerHouse);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int(storage, PIDSI_G_VERSION, Version);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int(storage, PIDSI_INTERNAL_VER, InternalVersion);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Time(storage, PIDSI_G_START_TIME, &StartTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Time(storage, PIDSI_LAST_SAVE_TIME, &LastSaveTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Time(storage, PIDSI_G_PLAY_TIME, &PlayTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String(storage, PIDSI_EXEC_NAME, ExecutableName);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String(storage, PIDSI_PLAYER_NAME1, PlayerName);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_String(storage, PIDSI_PLAYER_NAME2, PlayerName);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int(storage, PIDSI_SCENARIO_NUM, ScenarioNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int(storage, PIDSI_CAMPAIGN_NUM, CampaignNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Save_Int(storage, PIDSI_GAME_TYPE, GameType);
- if (FAILED(res)) {
- return(res);
- }
-
- return(S_OK);
+ file.Set_String(PIDSI_SCEN_DESCRIP, ScenarioDescription);
+ file.Set_String(PIDSI_PLAYER_HOUSE, PlayerHouse);
+ file.Set_Int(PIDSI_G_VERSION, Version);
+ file.Set_Int(PIDSI_INTERNAL_VER, InternalVersion);
+ file.Set_Time(PIDSI_G_START_TIME, StartTime);
+ file.Set_Time(PIDSI_LAST_SAVE_TIME, LastSaveTime);
+ file.Set_Time(PIDSI_G_PLAY_TIME, PlayTime);
+ file.Set_String(PIDSI_EXEC_NAME, ExecutableName);
+ file.Set_Int(PIDSI_SCENARIO_NUM, ScenarioNumber);
+ file.Set_Int(PIDSI_CAMPAIGN_NUM, CampaignNumber);
+ file.Set_Int(PIDSI_GAME_TYPE, GameType);
}
///
-/// Loads the version information out of a save file.
-/// This routine is called when a save game is scanned or restored. It prefers the property
-/// set that the current game writes and falls back to the one stream per value layout that
-/// older save files use, so that both generations of save file stay readable.
+/// Reads the listing fields the file carries; a field the file lacks keeps its default.
///
-/// Returns with S_OK once every value has been recovered, otherwise the failure
-/// code from the storage layer.
-HRESULT SaveVersionInfo::Load(IStorage *storage)
+/// bool; Does the file record an internal version at all?
+bool SaveVersionInfo::Load(SaveFileClass const & file)
{
- if (storage == NULL) {
- return(E_POINTER);
- }
-
- IPropertySetStoragePtr storageset;
- HRESULT res;
-
- if (SUCCEEDED(storage->QueryInterface(IID_IPropertySetStorage, (void **)&storageset))
- && SUCCEEDED(Load_String_Set(storageset, PIDSI_SCEN_DESCRIP, ScenarioDescription, sizeof(ScenarioDescription)))) {
-
-
- res = Load_String_Set(storageset, PIDSI_PLAYER_HOUSE, PlayerHouse, sizeof(PlayerHouse));
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int_Set(storageset, PIDSI_G_VERSION, &Version);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int_Set(storageset, PIDSI_INTERNAL_VER, &InternalVersion);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Time_Set(storageset, PIDSI_G_START_TIME, &StartTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Time_Set(storageset, PIDSI_LAST_SAVE_TIME, &LastSaveTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Time_Set(storageset, PIDSI_G_PLAY_TIME, &PlayTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_String_Set(storageset, PIDSI_EXEC_NAME, ExecutableName, sizeof(ExecutableName));
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_String_Set(storageset, PIDSI_PLAYER_NAME1, PlayerName, sizeof(PlayerName));
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int_Set(storageset, PIDSI_SCENARIO_NUM, &ScenarioNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int_Set(storageset, PIDSI_CAMPAIGN_NUM, &CampaignNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int_Set(storageset, PIDSI_GAME_TYPE, &GameType);
- if (FAILED(res)) {
- return(res);
- }
-
- } else {
-
- res = Load_String(storage, PIDSI_SCEN_DESCRIP, ScenarioDescription, sizeof(ScenarioDescription));
- if (FAILED(res)) {
- return(res);
- }
-
-
- res = Load_String(storage, PIDSI_PLAYER_HOUSE, PlayerHouse, sizeof(PlayerHouse));
- if (FAILED(res)) {
- return(res);
- }
-
-
- res = Load_Int(storage, PIDSI_G_VERSION, &Version);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int(storage, PIDSI_INTERNAL_VER, &InternalVersion);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Time(storage, PIDSI_G_START_TIME, &StartTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Time(storage, PIDSI_LAST_SAVE_TIME, &LastSaveTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Time(storage, PIDSI_G_PLAY_TIME, &PlayTime);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_String(storage, PIDSI_EXEC_NAME, ExecutableName, sizeof(ExecutableName));
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_String(storage, PIDSI_PLAYER_NAME1, PlayerName, sizeof(PlayerName));
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int(storage, PIDSI_SCENARIO_NUM, &ScenarioNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int(storage, PIDSI_CAMPAIGN_NUM, &CampaignNumber);
- if (FAILED(res)) {
- return(res);
- }
-
- res = Load_Int(storage, PIDSI_GAME_TYPE, &GameType);
- if (FAILED(res)) {
- return(res);
- }
- }
-
- return(S_OK);
-}
-
-
-///
-/// Reads a string from a stream of its own.
-/// The wide text held in the stream is narrowed into the caller's buffer, which is emptied
-/// first. This is the old style counterpart of Load_String_Set, used for save files written
-/// before the version information moved into a property set.
-///
-/// The property identifier naming the stream to open.
-/// Returns with the result of the read. A failure means the stream is absent or
-/// ended before the text was terminated.
-/// The capacity of string; longer text is cut on a character boundary.
-HRESULT SaveVersionInfo::Load_String(IStorage *storage, int id, char *string, int size)
-{
- *string = '\0';
-
- HRESULT res;
- IStreamPtr stm;
-
- res = storage->OpenStream(Stream_Name_From_ID(id), NULL, STGM_SHARE_EXCLUSIVE, 0, &stm);
- if (FAILED(res)) {
- return(res);
- }
-
- WCHAR buf[128];
- ULONG count;
-
- int i = 0;
- for (; i < ARRAY_SIZE(buf); i++) {
- res = stm->Read(&buf[i], sizeof(buf[i]), &count);
- if (FAILED(res)) {
- return(res);
- }
- if (res != S_OK || count != sizeof(buf[i])) {
- return(E_FAIL);
- }
- if (buf[i] == '\0') {
- break;
- }
- }
-
- if (i == ARRAY_SIZE(buf)) {
- return(E_FAIL);
- }
-
- char text[512];
- if (WideCharToMultiByte(CP_ACP, 0, buf, -1, text, sizeof(text), 0, 0) == 0) {
- text[0] = '\0';
- }
- UTF8::Copy(string, size, text);
-
- return(S_OK);
-}
-
-
-///
-/// Reads a string from the save file's property set.
-/// The wide text held in the property is narrowed back into the caller's buffer. That buffer
-/// is emptied before the read is attempted, so a missing property yields an empty string.
-///
-/// The summary information property identifier to read.
-/// Returns with the result of the read. A failure means the property set could not
-/// be opened.
-/// The capacity of string; longer text is cut on a character boundary.
-HRESULT SaveVersionInfo::Load_String_Set(IPropertySetStorage *storageset, int id, char *string, int size)
-{
- *string = '\0';
-
- HRESULT res;
- IPropertyStoragePtr storage;
-
- res = storageset->Open(FMTID_SummaryInformation, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &storage);
- if (FAILED(res)) {
- return(res);
- }
-
- PROPSPEC propsec;
- propsec.ulKind = PRSPEC_PROPID;
- propsec.propid = id;
- PROPVARIANT propvar;
-
- res = storage->ReadMultiple(1, &propsec, &propvar);
- if (FAILED(res)) {
- return(res);
- }
-
- if (propvar.vt == VT_LPWSTR) {
- char text[1024];
- if (WideCharToMultiByte(CP_ACP, 0, propvar.pwszVal, -1, text, sizeof(text), 0, 0) == 0) {
- text[0] = '\0';
- }
- UTF8::Copy(string, size, text);
- }
-
- return(res);
-}
-
-
-///
-/// Reads an integer from a stream of its own.
-/// This is the old style counterpart of Load_Int_Set, used for save files written before
-/// the version information moved into a property set. The value is cleared first.
-///
-/// The property identifier naming the stream to open.
-/// Returns with the result of the read. A failure means the stream is absent.
-HRESULT SaveVersionInfo::Load_Int(IStorage *storage, int id, int *integer)
-{
- *integer = 0;
-
- HRESULT res;
- IStreamPtr stm;
-
- res = storage->OpenStream(Stream_Name_From_ID(id), NULL, STGM_SHARE_EXCLUSIVE, 0, &stm);
- if (FAILED(res)) {
- return(res);
- }
-
- res = stm->Read(integer, sizeof(*integer), NULL);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Reads an integer from the save file's property set.
-/// The value is cleared before the read is attempted, so a save file that does not carry
-/// the property leaves the caller with zero.
-///
-/// The summary information property identifier to read.
-/// Returns with the result of the read. A failure means the property set could not
-/// be opened.
-HRESULT SaveVersionInfo::Load_Int_Set(IPropertySetStorage *storageset, int id, int *integer)
-{
- *integer = 0;
-
- HRESULT res;
- IPropertyStoragePtr storage;
-
- res = storageset->Open(FMTID_SummaryInformation, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &storage);
- if (FAILED(res)) {
- return(res);
- }
-
- PROPSPEC propsec;
- propsec.ulKind = PRSPEC_PROPID;
- propsec.propid = id;
- PROPVARIANT propvar;
-
- res = storage->ReadMultiple(1, &propsec, &propvar);
- if (FAILED(res)) {
- return(res);
- }
-
- if (propvar.vt == VT_I4) {
- *integer = propvar.lVal;
- }
-
- return(res);
-}
-
-
-///
-/// Writes a string to a stream of its own.
-/// The text is widened before it is written. This is the old style counterpart of
-/// Save_String_Set, kept for readers that do not understand property sets.
-///
-/// The property identifier naming the stream to create.
-/// Returns with the result of the write. A failure means the stream was never
-/// committed.
-HRESULT SaveVersionInfo::Save_String(IStorage *storage, int id, char *string)
-{
- WCHAR buf[260];
-
- if (MultiByteToWideChar(CP_ACP, 0, string, -1, buf, ARRAY_SIZE(buf)) == 0) {
- buf[0] = L'\0';
- }
-
- IStreamPtr stm(NULL);
-
- HRESULT res = storage->CreateStream(Stream_Name_From_ID(id), STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0, &stm);
- if (FAILED(res)) {
- return(res);
- }
-
- res = stm->Write(buf, sizeof(WCHAR) * wcslen(buf) + 2, NULL);
- if (FAILED(res)) {
- return(res);
- }
- res = stm->Commit(0);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Writes a string into the save file's property set.
-/// The text is widened before it is stored, since the summary information properties are
-/// held as wide characters. The property set is created if the save file has none yet.
-///
-/// The summary information property identifier to write.
-/// Returns with the result of the write. A failure means the property set could
-/// neither be opened nor created.
-HRESULT SaveVersionInfo::Save_String_Set(IPropertySetStorage *storageset, int id, const char *string)
-{
- WCHAR buf[260];
-
- if (MultiByteToWideChar(CP_ACP, 0, string, -1, buf, ARRAY_SIZE(buf)) == 0) {
- buf[0] = L'\0';
- }
-
- HRESULT res;
- IPropertyStoragePtr storage;
-
- res = storageset->Open(FMTID_SummaryInformation, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &storage);
- if (FAILED(res)) {
- res = storageset->Create(FMTID_SummaryInformation, NULL, PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE|STGM_READWRITE|STGM_CREATE, &storage);
- if (FAILED(res)) {
- return(res);
- }
- }
-
- PROPSPEC propsec;
- propsec.ulKind = PRSPEC_PROPID;
- propsec.propid = id;
- PROPVARIANT propvar;
-
- propvar.vt = VT_LPWSTR;
- propvar.pwszVal = buf;
-
- res = storage->WriteMultiple(1, &propsec, &propvar, PID_FIRST_USABLE);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Writes an integer to a stream of its own.
-/// This is the old style counterpart of Save_Int_Set, kept so that a reader which does not
-/// understand property sets can still recover the value.
-///
-/// The property identifier naming the stream to create.
-/// Returns with the result of the write. A failure means the stream was never
-/// committed.
-HRESULT SaveVersionInfo::Save_Int(IStorage *storage, int id, int integer)
-{
- IStreamPtr stm(NULL);
-
- HRESULT res = storage->CreateStream(Stream_Name_From_ID(id), STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0, &stm);
- if (FAILED(res)) {
- return(res);
- }
-
- res = stm->Write(&integer, sizeof(integer), NULL);
- if (FAILED(res)) {
- return(res);
- }
- res = stm->Commit(STGM_READ);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Writes an integer into the save file's property set.
-/// This routine stores the value as a summary information property, creating the property
-/// set first if the save file does not carry one yet.
-///
-/// The summary information property identifier to write.
-/// Returns with the result of the write. A failure means the property set could
-/// neither be opened nor created.
-HRESULT SaveVersionInfo::Save_Int_Set(IPropertySetStorage *storageset, int id, int integer)
-{
- HRESULT res;
- IPropertyStoragePtr storage;
-
- res = storageset->Open(FMTID_SummaryInformation, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &storage);
- if (FAILED(res)) {
- res = storageset->Create(FMTID_SummaryInformation, NULL, PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE|STGM_READWRITE|STGM_CREATE, &storage);
- if (FAILED(res)) {
- return(res);
- }
- }
-
- PROPSPEC propsec;
- propsec.ulKind = PRSPEC_PROPID;
- propsec.propid = id;
- PROPVARIANT propvar;
-
- propvar.vt = VT_I4;
- propvar.lVal = integer;
-
- res = storage->WriteMultiple(1, &propsec, &propvar, PID_FIRST_USABLE);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Reads a time stamp from a stream of its own.
-/// This is the old style counterpart of Load_Time_Set, used for save files written before
-/// the version information moved into a property set. The time is cleared first.
-///
-/// The property identifier naming the stream to open.
-/// Returns with the result of the read. A failure means the stream is absent.
-HRESULT SaveVersionInfo::Load_Time(IStorage *storage, int id, FILETIME *time)
-{
- time->dwLowDateTime = 0;
- time->dwHighDateTime = 0;
-
- HRESULT res;
- IStreamPtr stm;
-
- res = storage->OpenStream(Stream_Name_From_ID(id), NULL, STGM_SHARE_EXCLUSIVE, 0, &stm);
- if (FAILED(res)) {
- return(res);
- }
-
- res = stm->Read(time, sizeof(*time), NULL);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Reads a time stamp from the save file's property set.
-/// The time is cleared before the read is attempted, so a save file that does not carry the
-/// property leaves the caller with a zero time rather than with garbage.
-///
-/// The summary information property identifier to read.
-/// Returns with the result of the read. A failure means the property set could not
-/// be opened.
-HRESULT SaveVersionInfo::Load_Time_Set(IPropertySetStorage *storageset, int id, FILETIME *time)
-{
- time->dwLowDateTime = 0;
- time->dwHighDateTime = 0;
-
- HRESULT res;
- IPropertyStoragePtr storage;
-
- res = storageset->Open(FMTID_SummaryInformation, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &storage);
- if (FAILED(res)) {
- return(res);
- }
-
- PROPSPEC propsec;
- propsec.ulKind = PRSPEC_PROPID;
- propsec.propid = id;
- PROPVARIANT propvar;
-
- res = storage->ReadMultiple(1, &propsec, &propvar);
- if (FAILED(res)) {
- return(res);
- }
-
- if (propvar.vt == VT_FILETIME) {
- *time = propvar.filetime;
- }
-
- return(res);
-}
-
-
-///
-/// Writes a time stamp to a stream of its own.
-/// This is the old style counterpart of Save_Time_Set. The save routine records every value
-/// this way as well, so that a reader which does not understand property sets can still
-/// recover it.
-///
-/// The property identifier naming the stream to create.
-/// Returns with the result of the write. A failure means the stream was never
-/// committed.
-HRESULT SaveVersionInfo::Save_Time(IStorage *storage, int id, FILETIME *time)
-{
- IStreamPtr stm(NULL);
-
- HRESULT res = storage->CreateStream(Stream_Name_From_ID(id), STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0, &stm);
- if (FAILED(res)) {
- return(res);
- }
-
- res = stm->Write(time, sizeof(*time), NULL);
- if (FAILED(res)) {
- return(res);
- }
- res = stm->Commit(STGM_READ);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Writes a time stamp into the save file's property set.
-/// This routine stores the time as a summary information property, creating the property
-/// set first if the save file does not carry one yet.
-///
-/// The summary information property identifier to write.
-/// Returns with the result of the write. A failure means the property set could
-/// neither be opened nor created.
-HRESULT SaveVersionInfo::Save_Time_Set(IPropertySetStorage *storageset, int id, FILETIME *time)
-{
- HRESULT res;
- IPropertyStoragePtr storage;
-
- res = storageset->Open(FMTID_SummaryInformation, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &storage);
- if (FAILED(res)) {
- res = storageset->Create(FMTID_SummaryInformation, NULL, PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE|STGM_READWRITE|STGM_CREATE, &storage);
- if (FAILED(res)) {
- return(res);
- }
- }
-
- PROPSPEC propsec;
- propsec.ulKind = PRSPEC_PROPID;
- propsec.propid = id;
- PROPVARIANT propvar;
-
- propvar.vt = VT_FILETIME;
- propvar.filetime = *time;
-
- res = storage->WriteMultiple(1, &propsec, &propvar, PID_FIRST_USABLE);
- if (FAILED(res)) {
- return(res);
- }
-
- return(res);
-}
-
-
-///
-/// Fetches the stream name that a save version property is stored under.
-/// This routine maps the summary information property identifiers onto the wide names used
-/// by the old style save format, where every value lives in a stream of its own. The stream
-/// based save and load helpers call it to name the stream they are about to open.
-///
-/// The summary information property identifier to look up.
-/// Returns with the stream name for the property, or NULL if the identifier is not
-/// one of the recorded version properties.
-const WCHAR *Stream_Name_From_ID(int id)
-{
- static struct pidsiStruct {
- int ID;
- WCHAR const *Name;
- } _ids[] = {
- {PIDSI_SCEN_DESCRIP, L"Scenario Description"},
- {PIDSI_PLAYER_HOUSE, L"Player House"},
- {PIDSI_G_VERSION, L"Version"},
- {PIDSI_INTERNAL_VER, L"Internal Version"},
- {PIDSI_G_START_TIME, L"Start Time"},
- {PIDSI_LAST_SAVE_TIME, L"Last Save Time"},
- {PIDSI_G_PLAY_TIME, L"Play Time"},
- {PIDSI_EXEC_NAME, L"Executable Name"},
- {PIDSI_PLAYER_NAME1, L"Player Name"},
- {PIDSI_PLAYER_NAME2, L"Player Name2"},
- {PIDSI_SCENARIO_NUM, L"Scenario Number"},
- {PIDSI_CAMPAIGN_NUM, L"Campaign"},
- {PIDSI_GAME_TYPE, L"GameType"},
- };
-
- for (int i = 0; i < ARRAY_SIZE(_ids); i++) {
- if (_ids[i].ID == id) {
- return(_ids[i].Name);
- }
- }
+ file.Get_String(PIDSI_SCEN_DESCRIP, ScenarioDescription, sizeof(ScenarioDescription));
+ file.Get_String(PIDSI_PLAYER_HOUSE, PlayerHouse, sizeof(PlayerHouse));
+ file.Get_Int(PIDSI_G_VERSION, &Version);
+ file.Get_Time(PIDSI_G_START_TIME, &StartTime);
+ file.Get_Time(PIDSI_LAST_SAVE_TIME, &LastSaveTime);
+ file.Get_Time(PIDSI_G_PLAY_TIME, &PlayTime);
+ file.Get_String(PIDSI_EXEC_NAME, ExecutableName, sizeof(ExecutableName));
+ file.Get_Int(PIDSI_SCENARIO_NUM, &ScenarioNumber);
+ file.Get_Int(PIDSI_CAMPAIGN_NUM, &CampaignNumber);
+ file.Get_Int(PIDSI_GAME_TYPE, &GameType);
- return(NULL);
+ return(file.Get_Int(PIDSI_INTERNAL_VER, &InternalVersion));
}
diff --git a/code/savever.h b/code/savever.h
index 291dd7bde..337ef0eb6 100644
--- a/code/savever.h
+++ b/code/savever.h
@@ -11,12 +11,13 @@
#include "win.h"
-struct IStorage;
-struct IPropertySetStorage;
+class SaveFileClass;
enum {
PIDSI_SCEN_DESCRIP = 2,
PIDSI_PLAYER_HOUSE = 3,
+ // Nothing writes a player name; the identifiers stay reserved because the property set
+ // this table replaced numbered them.
PIDSI_PLAYER_NAME1 = 4,
PIDSI_PLAYER_NAME2 = 8,
PIDSI_G_VERSION = 9,
@@ -54,12 +55,6 @@ class SaveVersionInfo
void Set_Scenario_Number(int num);
int Get_Scenario_Number(void);
- void Set_Unknown_String(const char * name);
- const char * Get_Unknown_String(void);
-
- void Set_Player_Name(const char * name);
- const char * Get_Player_Name(void);
-
void Set_Executable_Name(const char * name);
const char * Get_Executable_Name(void);
@@ -75,27 +70,8 @@ class SaveVersionInfo
void Set_Game_Type(int id);
int Get_Game_Type(void);
- HRESULT Save(IStorage *storage);
- HRESULT Load(IStorage *storage);
-
- private:
- HRESULT Load_String(IStorage *storage, int id, char *string, int size);
- HRESULT Load_String_Set(IPropertySetStorage *storageset, int id, char *string, int size);
-
- HRESULT Load_Int(IStorage *storage, int id, int *integer);
- HRESULT Load_Int_Set(IPropertySetStorage *storageset, int id, int *integer);
-
- HRESULT Save_String(IStorage *storage, int id, char *string);
- HRESULT Save_String_Set(IPropertySetStorage *storageset, int id, const char *string);
-
- HRESULT Save_Int(IStorage *storage, int id, int integer);
- HRESULT Save_Int_Set(IPropertySetStorage *storageset, int id, int integer);
-
- HRESULT Load_Time(IStorage *storage, int id, FILETIME *time);
- HRESULT Load_Time_Set(IPropertySetStorage *storageset, int id, FILETIME *time);
-
- HRESULT Save_Time(IStorage *storage, int id, FILETIME *time);
- HRESULT Save_Time_Set(IPropertySetStorage *storageset, int id, FILETIME *time);
+ void Save(SaveFileClass & file) const;
+ bool Load(SaveFileClass const & file);
private:
/*
@@ -128,18 +104,6 @@ class SaveVersionInfo
int CampaignNumber;
int ScenarioNumber;
- /*
- * This is a spare string carried with the save information, reachable only through
- * its own accessors. Neither the save nor the load routine records it.
- */
- char UnknownString[260];
-
- /*
- * This is the name of the player who made the save, which is recorded separately
- * from the house so that the person and the side are both known.
- */
- char PlayerName[64];
-
/*
* This is the name of the program that wrote the save, so a file can be traced back
* to what produced it rather than merely to a version number.
@@ -159,5 +123,3 @@ class SaveVersionInfo
*/
int GameType;
};
-
-const WCHAR *Stream_Name_From_ID(int id);
diff --git a/code/scenario.cpp b/code/scenario.cpp
index 2e651211a..7019c5fc8 100644
--- a/code/scenario.cpp
+++ b/code/scenario.cpp
@@ -1075,11 +1075,7 @@ void Clear_Scenario(void)
LightSourceClass::Recalc = false;
while (Objects.Count()) {
- if (Objects[0]->RTTI == RTTI_BULLET) {
- Objects[0]->Release();
- } else {
- delete Objects[0];
- }
+ delete Objects[0];
}
LightSourceClass::Recalc = true;
@@ -3334,18 +3330,17 @@ static Cell const Clip_Move(Cell const & cell, FacingType facing, int dist)
/// The elapsed mission clock is halted across the write so that the time recorded is the
/// one the player will be given back when the game is resumed.
///
-void ScenarioClass::Save(IStream * stream) const
+void ScenarioClass::Save(SaveStreamClass & stream) const
{
DebugString("Scenario Save: ElapsedTimer = %d\n", (int)ElapsedTimer);
ElapsedTimer.Stop();
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
/*
* One member list serves both directions, so it cannot be declared const even though
* writing changes nothing.
*/
- const_cast(this)->Serialize(savestream);
+ const_cast(this)->Serialize(stream);
ElapsedTimer.Start();
}
@@ -3356,13 +3351,12 @@ void ScenarioClass::Save(IStream * stream) const
/// The elapsed mission clock is halted across the read for the same reason it is halted
/// across the write, so that it does not advance over the value coming back in.
///
-void ScenarioClass::Load(IStream * stream)
+void ScenarioClass::Load(SaveStreamClass & stream)
{
ElapsedTimer.Stop();
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("ScenarioClass");
- Serialize(savestream);
+ stream.Set_Context("ScenarioClass");
+ Serialize(stream);
ElapsedTimer.Start();
DebugString("Scenario Load: ElapsedTimer = %d\n", (int)ElapsedTimer);
diff --git a/code/scenario.h b/code/scenario.h
index f1bb213d6..eea2cb16b 100644
--- a/code/scenario.h
+++ b/code/scenario.h
@@ -101,8 +101,8 @@ class ScenarioClass {
bool Read_INI(CCINIClass const & ini);
bool Write_INI(CCINIClass & ini, bool mplayer=false) const;
- void Save(IStream * stream) const;
- void Load(IStream * stream);
+ void Save(SaveStreamClass & stream) const;
+ void Load(SaveStreamClass & stream);
void Serialize(SaveStreamClass & stream);
diff --git a/code/script.cpp b/code/script.cpp
index 977dad004..c1be22e65 100644
--- a/code/script.cpp
+++ b/code/script.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "script.h"
@@ -151,18 +150,9 @@ bool ScriptClass::Has_Missions_Remaining(void)
}
-///
-/// Fetches the class identifier used to persist this script.
-/// This routine is part of the IPersistStream contract that the save game system relies
-/// on to recreate objects when a game is loaded.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE ScriptClass::GetClassID(CLSID * retval)
+ClassID ScriptClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_ScriptClass;
- return(S_OK);
+ return(ClassID_ScriptClass);
}
@@ -368,18 +358,9 @@ ScriptTypeClass * ScriptTypeClass::Find_Or_Make(char const * name)
}
-///
-/// Fetches the class identifier used to persist this script type.
-/// This routine is part of the IPersistStream contract that the save game system relies
-/// on to recreate objects when a game is loaded.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE ScriptTypeClass::GetClassID(CLSID * retval)
+ClassID ScriptTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_ScriptTypeClass;
- return(S_OK);
+ return(ClassID_ScriptTypeClass);
}
diff --git a/code/script.h b/code/script.h
index f13faa0d1..cdabf2457 100644
--- a/code/script.h
+++ b/code/script.h
@@ -29,7 +29,7 @@ class ScriptClass : public AbstractClass
ScriptClass(ScriptTypeClass *type = NULL);
virtual ~ScriptClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
@@ -76,7 +76,7 @@ class ScriptTypeClass : public AbstractTypeClass
static ScriptTypeClass * Find_Or_Make(char const * ininame = NULL);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
static void Read_All(CCINIClass const & ini, INIScopeType scope);
static void Write_All(CCINIClass & ini, INIScopeType scope);
diff --git a/code/session.cpp b/code/session.cpp
index 3164bb476..c3e10e3e9 100644
--- a/code/session.cpp
+++ b/code/session.cpp
@@ -1333,15 +1333,11 @@ void SessionClass::Init_Fixed_Alliances(void)
/// Saves the game options to a save game.
///
/// bool; Were the options written successfully?
-bool GameOptionsType::Save(IStream * stream)
+bool GameOptionsType::Save(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(false);
- }
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- Serialize(savestream);
- return(SUCCEEDED(savestream.Result()));
+ Serialize(stream);
+ return(!stream.Was_Error());
}
@@ -1351,17 +1347,13 @@ bool GameOptionsType::Save(IStream * stream)
/// scenario with it.
///
/// bool; Were the options read back successfully?
-bool GameOptionsType::Load(IStream * stream)
+bool GameOptionsType::Load(SaveStreamClass & stream)
{
- if (stream == NULL) {
- return(false);
- }
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context("GameOptionsType");
- Serialize(savestream);
+ stream.Set_Context("GameOptionsType");
+ Serialize(stream);
ScenarioIndex = -1;
- return(SUCCEEDED(savestream.Result()));
+ return(!stream.Was_Error());
}
diff --git a/code/session.h b/code/session.h
index 98b724ab2..b29202d04 100644
--- a/code/session.h
+++ b/code/session.h
@@ -462,8 +462,8 @@ struct GameOptionsType {
bool ScrapMetal; // A wreck leaves the animations its type names in ScrapExplosion.
char ScenarioDescription [DESCRIP_MAX]; //Used on client machines only
- bool Save(IStream * stream);
- bool Load(IStream * stream);
+ bool Save(SaveStreamClass & stream);
+ bool Load(SaveStreamClass & stream);
void Serialize(SaveStreamClass & stream);
};
diff --git a/code/side.cpp b/code/side.cpp
index 1e081a06b..679ba158a 100644
--- a/code/side.cpp
+++ b/code/side.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "side.h"
@@ -132,18 +131,9 @@ bool SideClass::Read_INI(CCINIClass const & ini)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the IPersist interface. It is used by the save and load
-/// system to recognize what kind of object it is about to create.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE SideClass::GetClassID(CLSID * retval)
+ClassID SideClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_SideClass;
- return(S_OK);
+ return(ClassID_SideClass);
}
diff --git a/code/side.h b/code/side.h
index be97d6b84..f93d10c45 100644
--- a/code/side.h
+++ b/code/side.h
@@ -30,7 +30,7 @@ class SideClass : public AbstractTypeClass
SideClass(char const * ininame = NULL);
virtual ~SideClass() override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
/*
** Query functions.
diff --git a/code/smudge.cpp b/code/smudge.cpp
index dd77b643d..ef0a2c89d 100644
--- a/code/smudge.cpp
+++ b/code/smudge.cpp
@@ -38,7 +38,6 @@
* SmudgeClass::operator new -- Creator of smudge objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "smudge.h"
@@ -301,16 +300,7 @@ RTTIType SmudgeClass::Fetch_RTTI(void) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the IPersist interface. It is used by the save and load
-/// system to recognize what kind of object it is about to create.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE SmudgeClass::GetClassID(CLSID * retval)
+ClassID SmudgeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_SmudgeClass;
- return(S_OK);
+ return(ClassID_SmudgeClass);
}
diff --git a/code/smudge.h b/code/smudge.h
index 3fb659f24..f37359599 100644
--- a/code/smudge.h
+++ b/code/smudge.h
@@ -59,7 +59,7 @@ class SmudgeClass : public ObjectClass
SmudgeClass(SmudgeTypeClass const * type, Coord const & pos = COORD_NONE, HousesType = HOUSE_NONE);
virtual ~SmudgeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/smudtype.cpp b/code/smudtype.cpp
index afbdf5090..8f3e12cb2 100644
--- a/code/smudtype.cpp
+++ b/code/smudtype.cpp
@@ -44,7 +44,6 @@
* SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "smudtype.h"
@@ -337,17 +336,9 @@ void SmudgeTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// The save system asks for this so that it knows which class to construct when the object
-/// is read back out of a save file.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE SmudgeTypeClass::GetClassID(CLSID * retval)
+ClassID SmudgeTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_SmudgeTypeClass;
- return(S_OK);
+ return(ClassID_SmudgeTypeClass);
}
diff --git a/code/smudtype.h b/code/smudtype.h
index 373a0bda7..ad920b460 100644
--- a/code/smudtype.h
+++ b/code/smudtype.h
@@ -59,7 +59,7 @@ class SmudgeTypeClass : public ObjectTypeClass
SmudgeTypeClass(char const * ininame = NULL);
virtual ~SmudgeTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/startup.cpp b/code/startup.cpp
index 43ab863af..ee17b98dc 100644
--- a/code/startup.cpp
+++ b/code/startup.cpp
@@ -32,7 +32,6 @@
* main -- Initial startup routine (preps library systems). *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "_alpha.h"
@@ -64,7 +63,6 @@
#include "classfactory.h"
#include "command.h"
#include "conquer.h"
-#include "cstream.h"
#include "data.h"
#include "dbgprint.h"
#include "deploymentconfig.h"
@@ -82,7 +80,6 @@
#include "house.h"
#include "houstype.h"
#include "hover.h"
-#include "iblowfish.h"
#include "infantry.h"
#include "infatype.h"
#include "init.h"
@@ -174,20 +171,9 @@ extern HINSTANCE LanguageResources;
#define AUTOPLAY_GUID "b350c6d2-2f36-11d3-a72c-0090272fa661"
-#ifndef NO_BLOWFISH_DLL
-const struct RegStruct {
- const GUID *clsid;
- const char *name;
-} RegisterTheseDLLs[] = {
- { &CLSID_BlowfishObject, "blowfish.dll" }
-};
-#endif
-
HANDLE AppMutex;
HANDLE AutoPlayMutex;
-DynamicVectorClass RegisteredClasses;
-
//WinTimerClass * WinTimer;
///
@@ -234,130 +220,80 @@ void Reset_Surfaces(void)
}
///
-/// Registers the game's COM classes with OLE.
-/// This routine is called during startup, before anything that lives in the object
-/// database can be created. It first ensures the support DLLs are present, asking any
-/// that OLE cannot yet instantiate to register themselves, and then publishes a class
-/// factory for every persistent game class so that objects can be created by CLSID. The
-/// player is told by way of a message box if a support DLL could not be prepared.
+/// Registers every class a saved game or a unit type can name by class identifier.
+/// This runs during startup, before anything that lives in the object database can be
+/// created.
///
-/// bool; Did the preparation fail? Note the sense -- true means trouble.
-static bool RegisterClasses(void)
+static void RegisterClasses(void)
{
-
- bool failed = false;
-#ifndef NO_BLOWFISH_DLL
- for (int i = 0; i < ARRAY_SIZE(RegisterTheseDLLs); i++) {
- IUnknownPtr ptr;
- HRESULT result = ptr.CreateInstance(*RegisterTheseDLLs[i].clsid, NULL, CLSCTX_ALL);
- failed = FAILED(result);
- if (failed) {
- failed = false;
- HINSTANCE hModule = LoadLibrary(RegisterTheseDLLs[i].name);
- if (hModule != NULL) {
- FARPROC fprocDllReg = (FARPROC)GetProcAddress(hModule, "DllRegisterServer");
- if (!fprocDllReg || (fprocDllReg(), FAILED(ptr.CreateInstance(*RegisterTheseDLLs[i].clsid, NULL, CLSCTX_ALL)))) {
- failed = true;
- }
- FreeLibrary(hModule);
- } else {
- failed = true;
- }
- }
- if (failed) {
- break;
- }
- ptr.Release();
- }
-#endif
-
- DWORD dwRegister;
- IClassFactory *t;
-
- /// Handy macros to easily register the class factories.
-
- /// Register a class-object with OLE.
- #define REGISTER_CLASS(_class, _clsid) \
- { \
- t = new TClassFactory<_class>; \
- CoRegisterClassObject(_clsid, t, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &dwRegister); \
- RegisteredClasses.Add(dwRegister); \
- } \
-
- REGISTER_CLASS(CStreamClass, CLSID_CompressStream);
- REGISTER_CLASS(WaveClass, CLSID_WaveClass);
- REGISTER_CLASS(TerrainTypeClass, CLSID_TerrainTypeClass);
- REGISTER_CLASS(TerrainClass, CLSID_TerrainClass);
- REGISTER_CLASS(SuperWeaponTypeClass, CLSID_SuperWeaponTypeClass);
- REGISTER_CLASS(SuperClass, CLSID_SuperWeaponClass);
- REGISTER_CLASS(Tactical, CLSID_TacticalMapClass);
- REGISTER_CLASS(CellClass, CLSID_CellClass);
- REGISTER_CLASS(EMPulseClass, CLSID_EMPulseClass);
- REGISTER_CLASS(LightSourceClass, CLSID_LightSource);
- REGISTER_CLASS(SideClass, CLSID_SideClass);
- REGISTER_CLASS(TiberiumClass, CLSID_TiberiumClass);
- REGISTER_CLASS(TubeClass, CLSID_TubeClass);
- REGISTER_CLASS(CampaignClass, CLSID_CampaignClass);
- REGISTER_CLASS(BuildingLightClass, CLSID_BuildingLightClass);
- REGISTER_CLASS(WaypointPathClass, CLSID_WaypointPath);
- REGISTER_CLASS(TEventClass, CLSID_EventClass);
- REGISTER_CLASS(VoxelAnimTypeClass, CLSID_VoxelAnimTypeClass);
- REGISTER_CLASS(VoxelAnimClass, CLSID_VoxelAnimClass);
- REGISTER_CLASS(TActionClass, CLSID_ActionClass);
- REGISTER_CLASS(TriggerClass, CLSID_TriggerClass);
- REGISTER_CLASS(TriggerTypeClass, CLSID_TriggerTypeClass);
- REGISTER_CLASS(ScriptClass, CLSID_ScriptClass);
- REGISTER_CLASS(ScriptTypeClass, CLSID_ScriptTypeClass);
- REGISTER_CLASS(TagClass, CLSID_TagClass);
- REGISTER_CLASS(TagTypeClass, CLSID_TagTypeClass);
- REGISTER_CLASS(TeamClass, CLSID_TeamClass);
- REGISTER_CLASS(TeamTypeClass, CLSID_TeamTypeClass);
- REGISTER_CLASS(TaskForceClass, CLSID_TaskForceClass);
- REGISTER_CLASS(UnitTypeClass, CLSID_UnitTypeClass);
- REGISTER_CLASS(BuildingTypeClass, CLSID_BuildingTypeClass);
- REGISTER_CLASS(AircraftTypeClass, CLSID_AircraftTypeClass);
- REGISTER_CLASS(InfantryTypeClass, CLSID_InfantryTypeClass);
- REGISTER_CLASS(BulletTypeClass, CLSID_BulletTypeClass);
- REGISTER_CLASS(IsometricTileTypeClass, CLSID_IsometricTileTypeClass);
- REGISTER_CLASS(OverlayTypeClass, CLSID_OverlayTypeClass);
- REGISTER_CLASS(SmudgeTypeClass, CLSID_SmudgeTypeClass);
- REGISTER_CLASS(UnitClass, CLSID_UnitClass);
- REGISTER_CLASS(BuildingClass, CLSID_BuildingClass);
- REGISTER_CLASS(AircraftClass, CLSID_AircraftClass);
- REGISTER_CLASS(InfantryClass, CLSID_InfantryClass);
- REGISTER_CLASS(AnimClass, CLSID_AnimClass);
- REGISTER_CLASS(AnimTypeClass, CLSID_AnimTypeClass);
- REGISTER_CLASS(HouseTypeClass, CLSID_HouseTypeClass);
- REGISTER_CLASS(HouseClass, CLSID_HouseClass);
- REGISTER_CLASS(DriveLocomotionClass, CLSID_DriveLocomotion);
- REGISTER_CLASS(JumpjetLocomotionClass, CLSID_JumpjetLocomotion);
- REGISTER_CLASS(HoverLocomotionClass, CLSID_HoverLocomotion);
- REGISTER_CLASS(TunnelLocomotionClass, CLSID_TunnelLocomotion);
- REGISTER_CLASS(WalkLocomotionClass, CLSID_WalkLocomotion);
- REGISTER_CLASS(DropPodLocomotionClass, CLSID_BallisticLocomotion);
- REGISTER_CLASS(FlyLocomotionClass, CLSID_FlyerLocomotion);
- REGISTER_CLASS(TeleportLocomotionClass, CLSID_TeleportLocomotion);
- REGISTER_CLASS(MechLocomotionClass, CLSID_MechLocomotion);
- REGISTER_CLASS(LevitateLocomotionClass, CLSID_LevitateLocomotion);
- REGISTER_CLASS(BulletClass, CLSID_BulletClass);
- REGISTER_CLASS(FactoryClass, CLSID_FactoryClass);
- REGISTER_CLASS(WarheadTypeClass, CLSID_WarheadTypeClass);
- REGISTER_CLASS(WeaponTypeClass, CLSID_WeaponTypeClass);
- REGISTER_CLASS(ParticleClass, CLSID_ParticleClass);
- REGISTER_CLASS(ParticleTypeClass, CLSID_ParticleTypeClass);
- REGISTER_CLASS(ParticleSystemClass, CLSID_ParticleSystemClass);
- REGISTER_CLASS(ParticleSystemTypeClass, CLSID_ParticleSystemTypeClass);
- REGISTER_CLASS(AITriggerTypeClass, CLSID_AITriggerTypeClass);
- REGISTER_CLASS(NeuronClass, CLSID_NeuronClass);
- REGISTER_CLASS(FoggedObjectClass, CLSID_FoggedObjectClass);
- REGISTER_CLASS(AlphaShapeClass, CLSID_AlphaShapeClass);
-
- if (failed) {
- MessageBox(NULL, Fetch_String(TXT_PREPARECOM_FAILED), Fetch_String(TXT_SHORT_TITLE), MB_ICONEXCLAMATION);
- }
-
- return(failed);
-
+ #define REGISTER_CLASS(_class, _clsid) Register_Class<_class>(_clsid);
+
+ REGISTER_CLASS(WaveClass, ClassID_WaveClass);
+ REGISTER_CLASS(TerrainTypeClass, ClassID_TerrainTypeClass);
+ REGISTER_CLASS(TerrainClass, ClassID_TerrainClass);
+ REGISTER_CLASS(SuperWeaponTypeClass, ClassID_SuperWeaponTypeClass);
+ REGISTER_CLASS(SuperClass, ClassID_SuperWeaponClass);
+ REGISTER_CLASS(Tactical, ClassID_TacticalMapClass);
+ REGISTER_CLASS(CellClass, ClassID_CellClass);
+ REGISTER_CLASS(EMPulseClass, ClassID_EMPulseClass);
+ REGISTER_CLASS(LightSourceClass, ClassID_LightSource);
+ REGISTER_CLASS(SideClass, ClassID_SideClass);
+ REGISTER_CLASS(TiberiumClass, ClassID_TiberiumClass);
+ REGISTER_CLASS(TubeClass, ClassID_TubeClass);
+ REGISTER_CLASS(CampaignClass, ClassID_CampaignClass);
+ REGISTER_CLASS(BuildingLightClass, ClassID_BuildingLightClass);
+ REGISTER_CLASS(WaypointPathClass, ClassID_WaypointPath);
+ REGISTER_CLASS(TEventClass, ClassID_EventClass);
+ REGISTER_CLASS(VoxelAnimTypeClass, ClassID_VoxelAnimTypeClass);
+ REGISTER_CLASS(VoxelAnimClass, ClassID_VoxelAnimClass);
+ REGISTER_CLASS(TActionClass, ClassID_ActionClass);
+ REGISTER_CLASS(TriggerClass, ClassID_TriggerClass);
+ REGISTER_CLASS(TriggerTypeClass, ClassID_TriggerTypeClass);
+ REGISTER_CLASS(ScriptClass, ClassID_ScriptClass);
+ REGISTER_CLASS(ScriptTypeClass, ClassID_ScriptTypeClass);
+ REGISTER_CLASS(TagClass, ClassID_TagClass);
+ REGISTER_CLASS(TagTypeClass, ClassID_TagTypeClass);
+ REGISTER_CLASS(TeamClass, ClassID_TeamClass);
+ REGISTER_CLASS(TeamTypeClass, ClassID_TeamTypeClass);
+ REGISTER_CLASS(TaskForceClass, ClassID_TaskForceClass);
+ REGISTER_CLASS(UnitTypeClass, ClassID_UnitTypeClass);
+ REGISTER_CLASS(BuildingTypeClass, ClassID_BuildingTypeClass);
+ REGISTER_CLASS(AircraftTypeClass, ClassID_AircraftTypeClass);
+ REGISTER_CLASS(InfantryTypeClass, ClassID_InfantryTypeClass);
+ REGISTER_CLASS(BulletTypeClass, ClassID_BulletTypeClass);
+ REGISTER_CLASS(IsometricTileTypeClass, ClassID_IsometricTileTypeClass);
+ REGISTER_CLASS(OverlayTypeClass, ClassID_OverlayTypeClass);
+ REGISTER_CLASS(SmudgeTypeClass, ClassID_SmudgeTypeClass);
+ REGISTER_CLASS(UnitClass, ClassID_UnitClass);
+ REGISTER_CLASS(BuildingClass, ClassID_BuildingClass);
+ REGISTER_CLASS(AircraftClass, ClassID_AircraftClass);
+ REGISTER_CLASS(InfantryClass, ClassID_InfantryClass);
+ REGISTER_CLASS(AnimClass, ClassID_AnimClass);
+ REGISTER_CLASS(AnimTypeClass, ClassID_AnimTypeClass);
+ REGISTER_CLASS(HouseTypeClass, ClassID_HouseTypeClass);
+ REGISTER_CLASS(HouseClass, ClassID_HouseClass);
+ REGISTER_CLASS(DriveLocomotionClass, ClassID_DriveLocomotion);
+ REGISTER_CLASS(JumpjetLocomotionClass, ClassID_JumpjetLocomotion);
+ REGISTER_CLASS(HoverLocomotionClass, ClassID_HoverLocomotion);
+ REGISTER_CLASS(TunnelLocomotionClass, ClassID_TunnelLocomotion);
+ REGISTER_CLASS(WalkLocomotionClass, ClassID_WalkLocomotion);
+ REGISTER_CLASS(DropPodLocomotionClass, ClassID_BallisticLocomotion);
+ REGISTER_CLASS(FlyLocomotionClass, ClassID_FlyerLocomotion);
+ REGISTER_CLASS(TeleportLocomotionClass, ClassID_TeleportLocomotion);
+ REGISTER_CLASS(MechLocomotionClass, ClassID_MechLocomotion);
+ REGISTER_CLASS(LevitateLocomotionClass, ClassID_LevitateLocomotion);
+ REGISTER_CLASS(BulletClass, ClassID_BulletClass);
+ REGISTER_CLASS(FactoryClass, ClassID_FactoryClass);
+ REGISTER_CLASS(WarheadTypeClass, ClassID_WarheadTypeClass);
+ REGISTER_CLASS(WeaponTypeClass, ClassID_WeaponTypeClass);
+ REGISTER_CLASS(ParticleClass, ClassID_ParticleClass);
+ REGISTER_CLASS(ParticleTypeClass, ClassID_ParticleTypeClass);
+ REGISTER_CLASS(ParticleSystemClass, ClassID_ParticleSystemClass);
+ REGISTER_CLASS(ParticleSystemTypeClass, ClassID_ParticleSystemTypeClass);
+ REGISTER_CLASS(AITriggerTypeClass, ClassID_AITriggerTypeClass);
+ REGISTER_CLASS(NeuronClass, ClassID_NeuronClass);
+ REGISTER_CLASS(FoggedObjectClass, ClassID_FoggedObjectClass);
+ REGISTER_CLASS(AlphaShapeClass, ClassID_AlphaShapeClass);
}
///
@@ -525,11 +461,7 @@ int CALLBACK WinMain ( HINSTANCE instance , HINSTANCE , char * , int command_sho
return(EXIT_SUCCESS);
}
- OleInitialize(NULL);
-
- if (RegisterClasses()) {
- exit(EXIT_FAILURE);
- }
+ RegisterClasses();
/*
** Get the full path to the .EXE
@@ -603,7 +535,6 @@ int CALLBACK WinMain ( HINSTANCE instance , HINSTANCE , char * , int command_sho
wsprintf (buffer, Fetch_String(TXT_CRITICALLY_LOW), (INIT_FREE_DISK_SPACE) / (1024 * 1024));
int reply = MessageBox(NULL, buffer, Fetch_String(TXT_SHORT_TITLE), MB_ICONQUESTION|MB_YESNO);
if (reply == IDNO) {
- OleUninitialize();
return(EXIT_FAILURE);
}
}
@@ -734,7 +665,6 @@ int CALLBACK WinMain ( HINSTANCE instance , HINSTANCE , char * , int command_sho
Debug_Console_Hold();
}
- OleUninitialize();
return(error_code);
}
@@ -1055,10 +985,7 @@ void __cdecl Prog_End(void)
Scen = NULL;
}
- for (i = 0; i < RegisteredClasses.Count(); i++) {
- CoRevokeClassObject((DWORD)RegisteredClasses[i]);
- }
- RegisteredClasses.Clear();
+ Unregister_Classes();
if (LanguageResources) {
FreeLibrary(LanguageResources);
@@ -1106,7 +1033,6 @@ void Emergency_Exit(void)
}
}
- OleUninitialize();
if (MouseCursor) {
MouseCursor->Release_Mouse();
diff --git a/code/sun.h b/code/sun.h
index ac6a07811..f0f06b943 100644
--- a/code/sun.h
+++ b/code/sun.h
@@ -13,9 +13,7 @@
#pragma once
-#ifdef INCLUDE_COM
-#include "isun.h"
-#endif
+#include "classids.h"
#include
/// Everything from here on is the content of defines.h.
diff --git a/code/super.cpp b/code/super.cpp
index c1339fa81..e9b9117ed 100644
--- a/code/super.cpp
+++ b/code/super.cpp
@@ -40,7 +40,6 @@
* SuperClass::Suspend -- Suspend the charging of the super weapon. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "super.h"
@@ -801,15 +800,9 @@ bool SuperClass::Is_Charging(void) const
}
-///
-/// Fetches the persistent class identifier for the super weapon.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE SuperClass::GetClassID(CLSID * retval)
+ClassID SuperClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_SuperWeaponClass;
- return(S_OK);
+ return(ClassID_SuperWeaponClass);
}
diff --git a/code/super.h b/code/super.h
index d5dd0954e..2de481e3c 100644
--- a/code/super.h
+++ b/code/super.h
@@ -48,7 +48,7 @@ class SuperClass : public AbstractClass
SuperClass(SuperWeaponTypeClass * type, HouseClass * owner);
virtual ~SuperClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/suprtype.cpp b/code/suprtype.cpp
index 6c5834955..67a37738d 100644
--- a/code/suprtype.cpp
+++ b/code/suprtype.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "suprtype.h"
@@ -97,17 +96,9 @@ SuperWeaponTypeClass::~SuperWeaponTypeClass(void)
}
-///
-/// Fetches the class identifier of this object.
-/// The save game system uses this to know which class to construct when the object is
-/// read back in.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE SuperWeaponTypeClass::GetClassID(CLSID * retval)
+ClassID SuperWeaponTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_SuperWeaponTypeClass;
- return(S_OK);
+ return(ClassID_SuperWeaponTypeClass);
}
diff --git a/code/suprtype.h b/code/suprtype.h
index 8542bcbc9..66ff6b4e0 100644
--- a/code/suprtype.h
+++ b/code/suprtype.h
@@ -33,7 +33,7 @@ class SuperWeaponTypeClass : public AbstractTypeClass
SuperWeaponTypeClass(char const * ininame = NULL);
virtual ~SuperWeaponTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/swizzle.cpp b/code/swizzle.cpp
index 48b2ac401..421128df5 100644
--- a/code/swizzle.cpp
+++ b/code/swizzle.cpp
@@ -229,6 +229,19 @@ void SwizzleManagerClass::Resolve(void)
}
+///
+/// Takes back everything registered since the mark.
+/// The slots those requests name were left null by Swizzle and nothing has filled them,
+/// since Resolve does not run until the load has succeeded, so dropping the requests is
+/// all it takes to let the objects holding them be destroyed.
+///
+void SwizzleManagerClass::Abandon(MarkType const & mark)
+{
+ RequestTable.resize(mark.Requests);
+ PointerTable.resize(mark.Pointers);
+}
+
+
///
/// Throws away every pending request and announcement.
/// The load code calls this routine before it starts reading, so that whatever a load that
diff --git a/code/swizzle.h b/code/swizzle.h
index f49054119..6c536fb8b 100644
--- a/code/swizzle.h
+++ b/code/swizzle.h
@@ -78,6 +78,17 @@ class SwizzleManagerClass
void Resolve(void);
void Discard(void);
+ /*
+ * The tables' extent at some point of a load, so that a record which fails after
+ * it can take back what it registered before its object is destroyed.
+ */
+ struct MarkType {
+ std::size_t Requests;
+ std::size_t Pointers;
+ };
+ MarkType Mark(void) const {return(MarkType{RequestTable.size(), PointerTable.size()});}
+ void Abandon(MarkType const & mark);
+
private:
/*
* These are the pointers read back from the save file that still hold a swizzle ID
diff --git a/code/tactical.cpp b/code/tactical.cpp
index bca9e834e..1935c14eb 100644
--- a/code/tactical.cpp
+++ b/code/tactical.cpp
@@ -11,7 +11,6 @@
* disclaimers apply; see LICENSE.md.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "tactical.h"
@@ -3724,17 +3723,9 @@ bool Tactical::Draw_3D_Line(Coord const & coord1, Coord const & coord2, int colo
}
-///
-/// Fetches the class identifier of the tactical map.
-/// This routine is used by the persistence system to recognize the object when it is read
-/// back out of a save game.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE Tactical::GetClassID(CLSID * retval)
+ClassID Tactical::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TacticalMapClass;
- return(S_OK);
+ return(ClassID_TacticalMapClass);
}
diff --git a/code/tactical.h b/code/tactical.h
index 9c5a73732..91ba6de7d 100644
--- a/code/tactical.h
+++ b/code/tactical.h
@@ -103,7 +103,7 @@ class Tactical : public AbstractClass
Tactical(void);
virtual ~Tactical(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual RTTIType Fetch_RTTI(void) const override {return(RTTI_TACTICALMAP);}
diff --git a/code/taction.cpp b/code/taction.cpp
index 97d2ff0c6..55623b3e8 100644
--- a/code/taction.cpp
+++ b/code/taction.cpp
@@ -40,7 +40,6 @@
* ActionChoiceClass::Draw_It -- Display the action choice as part of a list box. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "taction.h"
@@ -2961,18 +2960,9 @@ NeedType Action_Needs(TActionType action)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the persistence machinery to recognize what kind of object it
-/// is about to load back.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TActionClass::GetClassID(CLSID * retval)
+ClassID TActionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_ActionClass;
- return(S_OK);
+ return(ClassID_ActionClass);
}
diff --git a/code/taction.h b/code/taction.h
index 1dd6a0595..c87a3f454 100644
--- a/code/taction.h
+++ b/code/taction.h
@@ -140,7 +140,7 @@ class TActionClass : public AbstractClass
TActionClass(void);
virtual ~TActionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/tag.cpp b/code/tag.cpp
index 518b7d56d..83dd8384a 100644
--- a/code/tag.cpp
+++ b/code/tag.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "tag.h"
@@ -424,16 +423,9 @@ void TagClass::Detach(AbstractClass const * target, bool all)
}
-///
-/// Fetches the class identifier that this tag persists under.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TagClass::GetClassID(CLSID * retval)
+ClassID TagClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TagClass;
- return(S_OK);
+ return(ClassID_TagClass);
}
diff --git a/code/tag.h b/code/tag.h
index fe24ffe53..8e04d6ab9 100644
--- a/code/tag.h
+++ b/code/tag.h
@@ -29,7 +29,7 @@ class TagClass : public AbstractClass
TagClass(TagTypeClass * type=NULL);
virtual ~TagClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/tagtype.cpp b/code/tagtype.cpp
index 95afd121a..26bef7fbf 100644
--- a/code/tagtype.cpp
+++ b/code/tagtype.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "tagtype.h"
@@ -376,18 +375,9 @@ TagTypeClass * TagTypeClass::Find_Or_Make(char const * name)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the save and load system so that a tag type can be
-/// recognized when it is read back out of a stream.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TagTypeClass::GetClassID(CLSID * retval)
+ClassID TagTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TagTypeClass;
- return(S_OK);
+ return(ClassID_TagTypeClass);
}
diff --git a/code/tagtype.h b/code/tagtype.h
index 1d99464e3..59c60e29a 100644
--- a/code/tagtype.h
+++ b/code/tagtype.h
@@ -33,7 +33,7 @@ class TagTypeClass : public AbstractTypeClass
TagTypeClass(char const * name = NULL);
virtual ~TagTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
static TagTypeClass * From_Name(char const * name);
diff --git a/code/taskforc.cpp b/code/taskforc.cpp
index 5e32142cf..6f8a85060 100644
--- a/code/taskforc.cpp
+++ b/code/taskforc.cpp
@@ -11,7 +11,6 @@
* disclaimers apply; see LICENSE.md.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "taskforc.h"
@@ -319,17 +318,9 @@ void TaskForceClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the save game code so that an object of the right kind can
-/// be created when the game is loaded back in.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TaskForceClass::GetClassID(CLSID * retval)
+ClassID TaskForceClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TaskForceClass;
- return(S_OK);
+ return(ClassID_TaskForceClass);
}
diff --git a/code/taskforc.h b/code/taskforc.h
index dc0d30b99..d9ab3d0cd 100644
--- a/code/taskforc.h
+++ b/code/taskforc.h
@@ -25,7 +25,7 @@ class TaskForceClass : public AbstractTypeClass
TaskForceClass(char const *name=NULL);
virtual ~TaskForceClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
static TaskForceClass * Find_Or_Make(char const * name);
diff --git a/code/team.cpp b/code/team.cpp
index 270cecb7a..4c360d150 100644
--- a/code/team.cpp
+++ b/code/team.cpp
@@ -70,7 +70,6 @@
* _Is_It_Playing -- Determines if unit is active and an initiated team member. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "team.h"
@@ -2296,18 +2295,9 @@ void TeamClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence contract and is what allows the save game loader
-/// to recognize a team when it reads one back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TeamClass::GetClassID(CLSID * retval)
+ClassID TeamClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TeamClass;
- return(S_OK);
+ return(ClassID_TeamClass);
}
diff --git a/code/team.h b/code/team.h
index bc465e36f..c110225ea 100644
--- a/code/team.h
+++ b/code/team.h
@@ -265,7 +265,7 @@ class TeamClass : public AbstractClass
TeamClass(TeamTypeClass const * team=0, HouseClass * owner=0, void * = NULL);
virtual ~TeamClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/teamtype.cpp b/code/teamtype.cpp
index c406abec7..a4809b702 100644
--- a/code/teamtype.cpp
+++ b/code/teamtype.cpp
@@ -54,7 +54,6 @@
* TeamTypeClass::~TeamTypeClass -- class destructor *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "teamtype.h"
@@ -890,18 +889,9 @@ void TeamTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// This is part of the persistence contract that the save and load code leans on to
-/// recognize what it is reading back.
-///
-/// Returns with S_OK and the class identifier filled in, or E_POINTER if no
-/// destination was supplied.
-HRESULT STDMETHODCALLTYPE TeamTypeClass::GetClassID(CLSID * retval)
+ClassID TeamTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TeamTypeClass;
- return(S_OK);
+ return(ClassID_TeamTypeClass);
}
diff --git a/code/teamtype.h b/code/teamtype.h
index 850e04144..d7c369fd1 100644
--- a/code/teamtype.h
+++ b/code/teamtype.h
@@ -68,7 +68,7 @@ class TeamTypeClass : public AbstractTypeClass
TeamTypeClass(char const * name = NULL);
virtual ~TeamTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
static TeamTypeClass * Find_Or_Make(char const * ininame = NULL);
diff --git a/code/techno.cpp b/code/techno.cpp
index 20c2c3ffe..b1c553ad1 100644
--- a/code/techno.cpp
+++ b/code/techno.cpp
@@ -4064,7 +4064,7 @@ BulletClass * TechnoClass::Fire_At(AbstractClass * target, int which)
if (valid_arc) {
if (!bullet->Unlimbo(turret_coord, velocity)) {
- bullet->Release();
+ delete bullet;
bullet = NULL;
} else {
@@ -4187,7 +4187,7 @@ BulletClass * TechnoClass::Fire_At(AbstractClass * target, int which)
}
}
} else {
- bullet->Release();
+ delete bullet;
bullet = NULL;
}
}
diff --git a/code/techtype.cpp b/code/techtype.cpp
index a273870cf..03b73af01 100644
--- a/code/techtype.cpp
+++ b/code/techtype.cpp
@@ -24,10 +24,10 @@
#include "bullet.h"
#include "bullettype.h"
#include "cell.h"
+#include "classids.h"
#include "combat.h"
#include "findmake.h"
#include "globals.h"
-#include "ilocos.h"
#include "infatype.h"
#include "mixfile.h"
#include "psystype.h"
@@ -129,7 +129,7 @@ TechnoTypeClass::TechnoTypeClass(char const * ininame, SpeedType speed) :
CloakingSpeed(7),
DebrisTypes(),
DebrisMaximums(),
- Locomotor(CLSID_TeleportLocomotion),
+ Locomotor(ClassID_TeleportLocomotion),
VoxelCenterY(0),
VoxelCenterX(0),
Weight(1),
@@ -565,7 +565,7 @@ bool TechnoTypeClass::Read_INI(CCINIClass const & ini)
}
PitchSpeed = ini.Get_Float(Name(), "PitchSpeed", PitchSpeed);
- Locomotor = ini.Get_CLSID(IniName, "Locomotor", Locomotor);
+ Locomotor = ini.Get_ClassID(IniName, "Locomotor", Locomotor);
CloakingSpeed = ini.Get_Int(Name(), "CloakingSpeed", CloakingSpeed);
ThreatAvoidanceCoefficient = ini.Get_Float(Name(), "ThreatAvoidanceCoefficient", ThreatAvoidanceCoefficient);
SlowdownDistance = ini.Get_Int(Name(), "SlowdownDistance", SlowdownDistance);
diff --git a/code/techtype.h b/code/techtype.h
index aeb48195c..5743e3900 100644
--- a/code/techtype.h
+++ b/code/techtype.h
@@ -14,6 +14,7 @@
#pragma once
#include "_weapon.h"
+#include "classids.h"
#include "objtype.h"
#include "typelist.h"
@@ -122,7 +123,7 @@ class TechnoTypeClass : public ObjectTypeClass
* about. It is what decides whether the object drives, walks, hovers, flies or
* tunnels, and an instance of it is created for every object as it is unlimboed.
*/
- CLSID Locomotor;
+ ClassID Locomotor;
/*
* These are the half extents of this object's voxel model, measured off the artwork
diff --git a/code/teleport.cpp b/code/teleport.cpp
index ab912d72e..5442f1cc6 100644
--- a/code/teleport.cpp
+++ b/code/teleport.cpp
@@ -36,7 +36,7 @@ TeleportLocomotionClass::TeleportLocomotionClass(void) :
/// The object counts as moving from the moment a destination is handed to this
/// locomotor until the jump has actually been made.
///
-boolean STDMETHODCALLTYPE TeleportLocomotionClass::Is_Moving(void)
+bool TeleportLocomotionClass::Is_Moving(void)
{
if (DestinationCoord != COORD_NONE) {
return(true);
@@ -50,7 +50,7 @@ boolean STDMETHODCALLTYPE TeleportLocomotionClass::Is_Moving(void)
/// This is the plain opposite of Is_Moving. An object with a teleport ordered counts as
/// being on the move even though it has not gone anywhere yet.
///
-boolean TeleportLocomotionClass::Is_Stationary(void)
+bool TeleportLocomotionClass::Is_Stationary(void)
{
if (Is_Moving() == false) {
return(true);
@@ -64,7 +64,7 @@ boolean TeleportLocomotionClass::Is_Stationary(void)
///
/// Returns with the pending teleport destination, or with the object's current
/// position if no teleport has been ordered.
-Coord STDMETHODCALLTYPE TeleportLocomotionClass::Destination(void)
+Coord TeleportLocomotionClass::Destination(void)
{
if (Is_Moving()) {
return(DestinationCoord);
@@ -78,7 +78,7 @@ Coord STDMETHODCALLTYPE TeleportLocomotionClass::Destination(void)
/// The jump is not made here. It happens the next time this locomotor is processed.
///
/// The coordinate to teleport the object to.
-void STDMETHODCALLTYPE TeleportLocomotionClass::Move_To(Coord to)
+void TeleportLocomotionClass::Move_To(Coord to)
{
DestinationCoord = to;
}
@@ -89,7 +89,7 @@ void STDMETHODCALLTYPE TeleportLocomotionClass::Move_To(Coord to)
/// The pending destination is forgotten, so the object stays where it is rather than
/// making the jump.
///
-void STDMETHODCALLTYPE TeleportLocomotionClass::Stop_Moving(void)
+void TeleportLocomotionClass::Stop_Moving(void)
{
DestinationCoord = COORD_NONE;
}
@@ -102,7 +102,7 @@ void STDMETHODCALLTYPE TeleportLocomotionClass::Stop_Moving(void)
/// it now stands. The whole journey is over by the time this routine returns.
///
/// bool; Is there more movement still to process? A teleport never leaves any.
-boolean STDMETHODCALLTYPE TeleportLocomotionClass::Process(void)
+bool TeleportLocomotionClass::Process(void)
{
if (Is_Moving()) {
LinkedTo->Mark(MARK_UP);
@@ -112,22 +112,13 @@ boolean STDMETHODCALLTYPE TeleportLocomotionClass::Process(void)
LinkedTo->Per_Cell_Process(PCP_END);
LinkedTo->Look();
}
- return(VARIANT_FALSE);
+ return(false);
}
-///
-/// Fetches the class identifier of this locomotor.
-/// This routine is used by the persistence system to record which locomotor was
-/// written, so that the right one can be created when the save game is loaded.
-///
-/// Pointer to the class identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TeleportLocomotionClass::GetClassID(CLSID * retval)
+ClassID TeleportLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TeleportLocomotion;
- return(S_OK);
+ return(ClassID_TeleportLocomotion);
}
@@ -149,7 +140,7 @@ void TeleportLocomotionClass::Serialize(SaveStreamClass & stream)
/// the way to its destination, so it never rises out of the ground layer.
///
/// Returns with the layer the object should be rendered in.
-LayerType STDMETHODCALLTYPE TeleportLocomotionClass::In_Which_Layer(void)
+LayerType TeleportLocomotionClass::In_Which_Layer(void)
{
return(LAYER_GROUND);
}
diff --git a/code/teleport.h b/code/teleport.h
index 4efad8853..3e41d3396 100644
--- a/code/teleport.h
+++ b/code/teleport.h
@@ -19,18 +19,18 @@ class TeleportLocomotionClass : public LocomotionClass
public:
TeleportLocomotionClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual bool Process(void) override;
+ virtual LayerType In_Which_Layer(void) override;
- virtual boolean Is_Stationary(void);
+ virtual bool Is_Stationary(void);
private:
/*
diff --git a/code/terrain.cpp b/code/terrain.cpp
index 78039fecb..ee4cb3fff 100644
--- a/code/terrain.cpp
+++ b/code/terrain.cpp
@@ -52,7 +52,6 @@
* TerrainClass::~TerrainClass -- Default destructor for terrain class objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "terrain.h"
@@ -914,8 +913,8 @@ bool TerrainClass::Render(Rect & cliprect, bool forced, bool extras_only) const
/// under the identity it was constructed with is dropped before the members arrive.
///
/// The stream to read the object from.
-/// Returns with S_OK if the object was read successfully.
-HRESULT STDMETHODCALLTYPE TerrainClass::Load(IStream * stream)
+/// bool; Was the record read whole?
+bool TerrainClass::Load(SaveStreamClass & stream)
{
TargetTracker.Remove_Index(Fetch_ID());
@@ -1090,16 +1089,7 @@ RTTIType TerrainClass::Fetch_RTTI(void) const
}
-///
-/// Fetches the class identifier for this object.
-/// This routine is part of the IPersistStream implementation. The save system records
-/// the identifier so that it knows what to recreate when the game is loaded back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TerrainClass::GetClassID(CLSID * retval)
+ClassID TerrainClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TerrainClass;
- return(S_OK);
+ return(ClassID_TerrainClass);
}
diff --git a/code/terrain.h b/code/terrain.h
index 537160275..83fefb448 100644
--- a/code/terrain.h
+++ b/code/terrain.h
@@ -59,8 +59,8 @@ class TerrainClass : public ObjectClass, public StageClass
TerrainClass(TerrainTypeClass const * type, Cell const & cell);
virtual ~TerrainClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Load(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/terrtype.cpp b/code/terrtype.cpp
index 0e86c765b..82932b0ff 100644
--- a/code/terrtype.cpp
+++ b/code/terrtype.cpp
@@ -44,7 +44,6 @@
* TerrainTypeClass::operator new -- Allocates a terrain type object from special pool. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "terrtype.h"
@@ -430,18 +429,9 @@ void TerrainTypeClass::Serialize(SaveStreamClass & stream)
}
-///
-/// Fetches the class identifier of this object.
-/// The save system uses this identifier to know what kind of object to create when the
-/// save file is loaded back in.
-///
-/// Pointer to the buffer to fill in with the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE TerrainTypeClass::GetClassID(CLSID * retval)
+ClassID TerrainTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TerrainTypeClass;
- return(S_OK);
+ return(ClassID_TerrainTypeClass);
}
diff --git a/code/terrtype.h b/code/terrtype.h
index 9bf8ec3c0..37b1fdee9 100644
--- a/code/terrtype.h
+++ b/code/terrtype.h
@@ -111,7 +111,7 @@ class TerrainTypeClass : public ObjectTypeClass
TerrainTypeClass(char const * ininame = NULL);
virtual ~TerrainTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/tevent.cpp b/code/tevent.cpp
index 167c46731..2b724d56a 100644
--- a/code/tevent.cpp
+++ b/code/tevent.cpp
@@ -39,7 +39,6 @@
* TEventClass::operator () -- Action operator to see if event is satisfied. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "tevent.h"
@@ -839,18 +838,9 @@ void TEventClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the persistence machinery to recognize what kind of object it
-/// is about to load back.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TEventClass::GetClassID(CLSID * retval)
+ClassID TEventClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_EventClass;
- return(S_OK);
+ return(ClassID_EventClass);
}
diff --git a/code/tevent.h b/code/tevent.h
index 6dab13515..c7dc9e1bf 100644
--- a/code/tevent.h
+++ b/code/tevent.h
@@ -104,7 +104,7 @@ class TEventClass : public AbstractClass
TEventClass(void);
virtual ~TEventClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/tiberium.cpp b/code/tiberium.cpp
index 4908a18a4..59de5b97f 100644
--- a/code/tiberium.cpp
+++ b/code/tiberium.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "tiberium.h"
@@ -193,17 +192,9 @@ void TiberiumClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of the tiberium class.
-/// This routine tells the save game loader which kind of object to create when this
-/// tiberium type is read back in.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TiberiumClass::GetClassID(CLSID * retval)
+ClassID TiberiumClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TiberiumClass;
- return(S_OK);
+ return(ClassID_TiberiumClass);
}
@@ -212,10 +203,10 @@ HRESULT STDMETHODCALLTYPE TiberiumClass::GetClassID(CLSID * retval)
/// The spread and growth pools are dropped before the members arrive, since the counts
/// they track are about to be replaced with the saved ones.
///
-/// Returns with S_OK if the tiberium type was loaded.
+/// bool; Was the record read whole?
/// The spread and growth systems are not saved, so they come back empty. They
/// must be rebuilt once the game has finished loading.
-HRESULT STDMETHODCALLTYPE TiberiumClass::Load(IStream * stream)
+bool TiberiumClass::Load(SaveStreamClass & stream)
{
Clear_Spread();
Clear_Growth();
diff --git a/code/tiberium.h b/code/tiberium.h
index 9a71bf265..c7f7e8888 100644
--- a/code/tiberium.h
+++ b/code/tiberium.h
@@ -37,8 +37,8 @@ class TiberiumClass : public AbstractTypeClass
TiberiumClass(char const * ininame = NULL);
virtual ~TiberiumClass() override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Load(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/tracker.cpp b/code/tracker.cpp
index 1658bb586..146974e1d 100644
--- a/code/tracker.cpp
+++ b/code/tracker.cpp
@@ -224,15 +224,13 @@ void Process_Deferred_Deletion(void)
break;
}
}
- if (obj->Release()) {
- if (typeid(BuildingClass) == typeid(*obj)
- || typeid(UnitClass) == typeid(*obj)
- || typeid(InfantryClass) == typeid(*obj)
- || typeid(AircraftClass) == typeid(*obj)) {
- ((ObjectClass *)obj)->IsActive = true;
- }
- delete obj;
+ if (typeid(BuildingClass) == typeid(*obj)
+ || typeid(UnitClass) == typeid(*obj)
+ || typeid(InfantryClass) == typeid(*obj)
+ || typeid(AircraftClass) == typeid(*obj)) {
+ ((ObjectClass *)obj)->IsActive = true;
}
+ delete obj;
} else {
++index;
}
diff --git a/code/trigger.cpp b/code/trigger.cpp
index bc1c9720d..2bf8a078a 100644
--- a/code/trigger.cpp
+++ b/code/trigger.cpp
@@ -42,7 +42,6 @@
* TriggerClass::~TriggerClass -- Destructor for trigger objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "trigger.h"
@@ -500,18 +499,9 @@ void TriggerClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence support. The save game system uses the
-/// identifier to work out what kind of object to build when the stream is read back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TriggerClass::GetClassID(CLSID * retval)
+ClassID TriggerClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TriggerClass;
- return(S_OK);
+ return(ClassID_TriggerClass);
}
diff --git a/code/trigger.h b/code/trigger.h
index 771341c7d..35bf3233d 100644
--- a/code/trigger.h
+++ b/code/trigger.h
@@ -65,7 +65,7 @@ class TriggerClass : public AbstractClass
TriggerClass(TriggerTypeClass * trigtype=NULL);
virtual ~TriggerClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/trigtype.cpp b/code/trigtype.cpp
index 9014fe2ae..849f89755 100644
--- a/code/trigtype.cpp
+++ b/code/trigtype.cpp
@@ -46,7 +46,6 @@
* TriggerTypeClass::~TriggerTypeClass -- Deleting a trigger type deletes associated triggers*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "trigtype.h"
@@ -811,18 +810,9 @@ void TriggerTypeClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// The save game system uses this identifier to know which kind of object to build
-/// when the stream is read back in.
-///
-/// Pointer to the location to store the class identifier.
-/// Returns with S_OK, or E_POINTER if no storage location was supplied.
-HRESULT STDMETHODCALLTYPE TriggerTypeClass::GetClassID(CLSID * retval)
+ClassID TriggerTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TriggerTypeClass;
- return(S_OK);
+ return(ClassID_TriggerTypeClass);
}
diff --git a/code/trigtype.h b/code/trigtype.h
index f8ba9e5a2..77c95d070 100644
--- a/code/trigtype.h
+++ b/code/trigtype.h
@@ -55,7 +55,7 @@ class TriggerTypeClass : public AbstractTypeClass
static TriggerTypeClass * Find_Or_Make(char const * ininame = NULL);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
/*
** File I/O routines
diff --git a/code/tube.cpp b/code/tube.cpp
index 0d67042bc..e0f17408a 100644
--- a/code/tube.cpp
+++ b/code/tube.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "tube.h"
@@ -267,16 +266,7 @@ RTTIType TubeClass::Fetch_RTTI(void) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the save game system so that it knows what kind of object to
-/// construct when the stream is read back in.
-///
-/// Pointer to the place to store the class identifier.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TubeClass::GetClassID(CLSID * retval)
+ClassID TubeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TubeClass;
- return(S_OK);
+ return(ClassID_TubeClass);
}
diff --git a/code/tube.h b/code/tube.h
index 71e193c44..f511cd589 100644
--- a/code/tube.h
+++ b/code/tube.h
@@ -22,7 +22,7 @@ class TubeClass : public AbstractClass
{
typedef AbstractClass BASECLASS;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/tunnel.cpp b/code/tunnel.cpp
index aa3fa9ff1..e4727006e 100644
--- a/code/tunnel.cpp
+++ b/code/tunnel.cpp
@@ -53,7 +53,7 @@ TunnelLocomotionClass::TunnelLocomotionClass(void) :
/// Reports whether the unit is anywhere in the dig cycle (State != STATE_IDLE).
///
/// True while dig-moving.
-boolean STDMETHODCALLTYPE TunnelLocomotionClass::Is_Moving(void)
+bool TunnelLocomotionClass::Is_Moving(void)
{
if (State != STATE_IDLE) {
return(true);
@@ -67,7 +67,7 @@ boolean STDMETHODCALLTYPE TunnelLocomotionClass::Is_Moving(void)
/// (not STATE_IDLE and not STATE_TURNING).
///
/// True while actively moving.
-boolean STDMETHODCALLTYPE TunnelLocomotionClass::Is_Moving_Now(void)
+bool TunnelLocomotionClass::Is_Moving_Now(void)
{
if (Is_Moving() && State != STATE_TURNING) {
return(true);
@@ -80,7 +80,7 @@ boolean STDMETHODCALLTYPE TunnelLocomotionClass::Is_Moving_Now(void)
/// Returns the burrow destination while moving, or the current position when idle.
///
/// The destination coordinate.
-Coord STDMETHODCALLTYPE TunnelLocomotionClass::Destination(void)
+Coord TunnelLocomotionClass::Destination(void)
{
if (Is_Moving()) {
return(DestinationCoord);
@@ -96,7 +96,7 @@ Coord STDMETHODCALLTYPE TunnelLocomotionClass::Destination(void)
/// ignores the order altogether.
///
/// The location to travel to.
-void STDMETHODCALLTYPE TunnelLocomotionClass::Move_To(Coord to)
+void TunnelLocomotionClass::Move_To(Coord to)
{
if (LinkedTo->StunDuration <= 0) {
Coord coord = to;
@@ -121,7 +121,7 @@ void STDMETHODCALLTYPE TunnelLocomotionClass::Move_To(Coord to)
/// already traveling underground must make for the nearest ground it can surface on. If
/// there is no such ground to be had, it stays buried for good.
///
-void STDMETHODCALLTYPE TunnelLocomotionClass::Stop_Moving(void)
+void TunnelLocomotionClass::Stop_Moving(void)
{
switch (State) {
case STATE_ABORTING:
@@ -180,7 +180,7 @@ void STDMETHODCALLTYPE TunnelLocomotionClass::Stop_Moving(void)
/// the ground and underground layers.
///
/// bool; Is the unit still working through its dig cycle?
-boolean STDMETHODCALLTYPE TunnelLocomotionClass::Process(void)
+bool TunnelLocomotionClass::Process(void)
{
if (Is_Moving()) {
int agl = LinkedTo->HeightAGL;
@@ -256,7 +256,7 @@ boolean STDMETHODCALLTYPE TunnelLocomotionClass::Process(void)
///
/// Should the buried unit be hidden outright rather than rippled?
/// Returns with the visual treatment to draw the unit with.
-VisualType STDMETHODCALLTYPE TunnelLocomotionClass::Visual_Character(boolean flag)
+VisualType TunnelLocomotionClass::Visual_Character(bool flag)
{
if (State == STATE_TUNNELING) {
if (flag) {
@@ -457,7 +457,7 @@ void TunnelLocomotionClass::Process_Emerging(void)
///
/// The shape cache key to fold this pose into. May be NULL.
/// Returns with the matrix to draw the unit with.
-Matrix3D STDMETHODCALLTYPE TunnelLocomotionClass::Draw_Matrix(int * key)
+Matrix3D TunnelLocomotionClass::Draw_Matrix(int * key)
{
if (State == STATE_IDLE) {
int ramp = Map[(Coord const &)(LinkedTo->PositionCoord)].Ramp;
@@ -528,7 +528,7 @@ Matrix3D STDMETHODCALLTYPE TunnelLocomotionClass::Draw_Matrix(int * key)
/// derived from the terrain-height delta and the rotation progress.
///
/// The Z pixel adjustment.
-int STDMETHODCALLTYPE TunnelLocomotionClass::Z_Adjust(void)
+int TunnelLocomotionClass::Z_Adjust(void)
{
static int tunnel_Z_Adjust[] = {45, 45};
@@ -583,7 +583,7 @@ int STDMETHODCALLTYPE TunnelLocomotionClass::Z_Adjust(void)
/// be shaded down its length rather than across the flat, as the base locomotor would.
///
/// Returns with the Z gradient to draw the unit with.
-ZGradientType STDMETHODCALLTYPE TunnelLocomotionClass::Z_Gradient(void)
+ZGradientType TunnelLocomotionClass::Z_Gradient(void)
{
if (State == STATE_DESCENDING || State == STATE_DIGGING_IN || State == STATE_ABORTING || State == STATE_EMERGING || State == STATE_ASCENDING) {
return(ZGRAD_90DEG);
@@ -597,7 +597,7 @@ ZGradientType STDMETHODCALLTYPE TunnelLocomotionClass::Z_Gradient(void)
/// dig-in, emerging, aborting), false once it is pitched down or underground.
///
/// True if it casts a shadow.
-boolean STDMETHODCALLTYPE TunnelLocomotionClass::Is_To_Have_Shadow(void)
+bool TunnelLocomotionClass::Is_To_Have_Shadow(void)
{
if (State == STATE_IDLE || State == STATE_TURNING || State == STATE_ABORTING || State == STATE_DIGGING_IN || State == STATE_EMERGING) {
return(true);
@@ -612,7 +612,7 @@ boolean STDMETHODCALLTYPE TunnelLocomotionClass::Is_To_Have_Shadow(void)
///
/// Cell to test.
/// MOVE_OK or MOVE_NO.
-MoveType STDMETHODCALLTYPE TunnelLocomotionClass::Can_Enter_Cell(Cell cell)
+MoveType TunnelLocomotionClass::Can_Enter_Cell(Cell cell)
{
if (!Debug_Map && !Map[cell].Can_Burrow_Here()) {
return(MOVE_NO);
@@ -625,25 +625,16 @@ MoveType STDMETHODCALLTYPE TunnelLocomotionClass::Can_Enter_Cell(Cell cell)
/// Sets the unit's desired facing (used while turning to face the dig destination).
///
/// Desired facing.
-void STDMETHODCALLTYPE TunnelLocomotionClass::Do_Turn(DirType coord)
+void TunnelLocomotionClass::Do_Turn(DirType coord)
{
DirType dir = coord;
LinkedTo->PrimaryFacing.Set_Desired(dir);
}
-///
-/// Fetches the class identifier for this locomotor.
-/// This routine is part of the COM persistence support. The save system records the
-/// identifier so that the right locomotor can be created again when the game is loaded.
-///
-/// The location to store the class identifier in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE TunnelLocomotionClass::GetClassID(CLSID * retval)
+ClassID TunnelLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_TunnelLocomotion;
- return(S_OK);
+ return(ClassID_TunnelLocomotion);
}
@@ -666,7 +657,7 @@ void TunnelLocomotionClass::Serialize(SaveStreamClass & stream)
/// Returns the render layer: underground while travelling (STATE_TUNNELING), ground otherwise.
///
/// The render layer.
-LayerType STDMETHODCALLTYPE TunnelLocomotionClass::In_Which_Layer(void)
+LayerType TunnelLocomotionClass::In_Which_Layer(void)
{
if (State != STATE_TUNNELING) {
return(LAYER_GROUND);
@@ -681,7 +672,7 @@ LayerType STDMETHODCALLTYPE TunnelLocomotionClass::In_Which_Layer(void)
/// subterranean unit has no shot while it is lining up, digging, or under the ground.
///
/// Returns with the fire error, or FIRE_OK if the unit is free to shoot.
-FireErrorType STDMETHODCALLTYPE TunnelLocomotionClass::Can_Fire(void)
+FireErrorType TunnelLocomotionClass::Can_Fire(void)
{
FireErrorType fire = BASECLASS::Can_Fire();
@@ -697,7 +688,7 @@ FireErrorType STDMETHODCALLTYPE TunnelLocomotionClass::Can_Fire(void)
/// Reports whether the unit is in the act of surfacing (ascending or emerging).
///
/// True while surfacing.
-boolean STDMETHODCALLTYPE TunnelLocomotionClass::Is_Surfacing(void)
+bool TunnelLocomotionClass::Is_Surfacing(void)
{
return(State == STATE_ASCENDING || State == STATE_EMERGING);
}
diff --git a/code/tunnel.h b/code/tunnel.h
index 2aaf0ad0a..03ae0794f 100644
--- a/code/tunnel.h
+++ b/code/tunnel.h
@@ -29,26 +29,26 @@ class TunnelLocomotionClass : public LocomotionClass
*/
TunnelLocomotionClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual VisualType STDMETHODCALLTYPE Visual_Character(boolean flag) override;
- virtual Matrix3D STDMETHODCALLTYPE Draw_Matrix(int *key) override;
- virtual int STDMETHODCALLTYPE Z_Adjust(void) override;
- virtual ZGradientType STDMETHODCALLTYPE Z_Gradient(void) override;
- virtual boolean STDMETHODCALLTYPE Is_To_Have_Shadow(void) override;
- virtual MoveType STDMETHODCALLTYPE Can_Enter_Cell(Cell cell) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType coord) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual FireErrorType STDMETHODCALLTYPE Can_Fire(void) override;
- virtual boolean STDMETHODCALLTYPE Is_Surfacing(void) override;
+ virtual bool Is_Moving(void) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual Coord Destination(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual bool Process(void) override;
+ virtual VisualType Visual_Character(bool flag) override;
+ virtual Matrix3D Draw_Matrix(int *key) override;
+ virtual int Z_Adjust(void) override;
+ virtual ZGradientType Z_Gradient(void) override;
+ virtual bool Is_To_Have_Shadow(void) override;
+ virtual MoveType Can_Enter_Cell(Cell cell) override;
+ virtual void Do_Turn(DirType coord) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual FireErrorType Can_Fire(void) override;
+ virtual bool Is_Surfacing(void) override;
void Process_Turning(void);
void Process_Digging_In(void);
diff --git a/code/typelist.h b/code/typelist.h
index f1572a0a1..4dcafb564 100644
--- a/code/typelist.h
+++ b/code/typelist.h
@@ -19,7 +19,6 @@
#include "win.h"
#include
-#include
template
class TypeList : public DynamicVectorClass
diff --git a/code/unit.cpp b/code/unit.cpp
index 9274d3060..9673d37de 100644
--- a/code/unit.cpp
+++ b/code/unit.cpp
@@ -95,7 +95,6 @@
* UnitClass::~UnitClass -- Destructor for unit objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "unit.h"
@@ -121,13 +120,13 @@
#include "bullettype.h"
#include "ccrand.h"
#include "cell.h"
+#include "classids.h"
#include "combat.h"
#include "conquer.h"
#include "draw.h"
#include "fog.h"
#include "house.h"
#include "houstype.h"
-#include "ilocos.h"
#include "incdec.h"
#include "infantry.h"
#include "infatype.h"
@@ -223,7 +222,7 @@ UnitClass::UnitClass(UnitTypeClass const * type, HouseClass * house) :
SecondaryFacing.Set(PrimaryFacing.Current());
if (Class != NULL) {
- Locomotion = ILocomotionPtr(Class->Locomotor, NULL, CLSCTX_ALL);
+ Locomotion = Create_Locomotor(Class->Locomotor);
Locomotion->Link_To_Object(this);
}
@@ -2107,10 +2106,8 @@ void UnitClass::Per_Cell_Process(PCPType why)
Cell center = Center_Coord();
Cell whom_center = whom->Center_Coord();
if (Center_Coord().As_Cell() == whom->Center_Coord().As_Cell() && whom->RTTI == RTTI_BUILDING) {
- IPersistPtr persist(Locomotion);
- CLSID clsid;
- persist->GetClassID(&clsid);
- if (clsid == CLSID_HoverLocomotion && static_cast(whom)->Class->IsCanUnitRepair && NavCom == NULL) {
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
+ if (clsid == ClassID_HoverLocomotion && static_cast(whom)->Class->IsCanUnitRepair && NavCom == nullptr) {
NavCom = whom;
}
if (whom == NavCom) {
@@ -5251,10 +5248,8 @@ void UnitClass::Assign_Destination(AbstractClass * target, bool immediate)
* re-target the nearest reachable cell when driving rather than burrowing.
*/
if (target != NULL && Class->IsSubterranean && Locomotion->Is_Moving()) {
- IPersistPtr persist(Locomotion);
- CLSID clsid;
- persist->GetClassID(&clsid);
- if (clsid == CLSID_DriveLocomotion) {
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
+ if (clsid == ClassID_DriveLocomotion) {
NavQueue.Add_Head(target);
RouteQueue.Clear();
CellClass * tcell = Get_Target_Cell_Ptr();
@@ -5345,10 +5340,8 @@ void UnitClass::Assign_Destination(AbstractClass * target, bool immediate)
* (Mirrors BuildingClass weapons-factory exit, building.cpp:6236-6251.)
*/
if (target != NULL && !Locomotion->Is_Moving()) {
- IPersistPtr persist(Locomotion);
- CLSID clsid;
- persist->GetClassID(&clsid);
- if (clsid == CLSID_TunnelLocomotion && Get_Height_AGL() == 0) {
+ ClassID const clsid = Locomotion_Class_ID(Locomotion.get());
+ if (clsid == ClassID_TunnelLocomotion && Get_Height_AGL() == 0) {
Coord tc = target->Center_Coord();
int gl = Map.Get_Height_GL(tc);
if (tc.Z < gl) tc.Z = gl;
@@ -5369,16 +5362,16 @@ void UnitClass::Assign_Destination(AbstractClass * target, bool immediate)
}
if (doswap) {
- IPiggybackPtr piggy(Locomotion);
+ IPiggyback * piggy = Piggyback_Of(Locomotion.get());
if (piggy != NULL && piggy->Is_Piggybacking()) {
- piggy->End_Piggyback(&Locomotion);
+ Locomotion = piggy->End_Piggyback();
}
- ILocomotionPtr walk(CLSID_DriveLocomotion);
+ std::unique_ptr walk = Create_Locomotor(ClassID_DriveLocomotion);
walk->Link_To_Object(this);
- piggy = IPiggybackPtr(walk);
+ piggy = Piggyback_Of(walk.get());
if (piggy != NULL) {
piggy->Begin_Piggyback(Locomotion);
- Locomotion = walk;
+ Locomotion = std::move(walk);
Locomotion->Force_New_Slope(Map[Get_Coord()].Ramp);
}
}
@@ -6044,8 +6037,8 @@ bool UnitClass::Ready_To_Commence(void)
/// again once that identity has arrived.
///
/// The stream to read this unit from.
-/// Returns with S_OK if the unit was read successfully.
-HRESULT STDMETHODCALLTYPE UnitClass::Load(IStream *stream)
+/// bool; Was the record read whole?
+bool UnitClass::Load(SaveStreamClass & stream)
{
TargetTracker.Remove_Index(Fetch_ID());
return(BASECLASS::Load(stream));
@@ -6658,16 +6651,9 @@ bool UnitClass::Is_Immobilized(void) const
}
-///
-/// Fetches the class identifier used by the save game persistence system.
-///
-/// Pointer to the buffer to fill in with the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE UnitClass::GetClassID(CLSID * retval)
+ClassID UnitClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_UnitClass;
- return(S_OK);
+ return(ClassID_UnitClass);
}
diff --git a/code/unit.h b/code/unit.h
index 98c0ca05e..bf2d13086 100644
--- a/code/unit.h
+++ b/code/unit.h
@@ -136,8 +136,8 @@ class UnitClass : public FootClass
UnitClass(UnitTypeClass const * type = NULL, HouseClass * house = NULL);
virtual ~UnitClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
- virtual HRESULT STDMETHODCALLTYPE Load(IStream * stream) override;
+ virtual ClassID Class_ID(void) const override;
+ virtual bool Load(SaveStreamClass & stream) override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/unittype.cpp b/code/unittype.cpp
index 00723fb19..ecbe34b95 100644
--- a/code/unittype.cpp
+++ b/code/unittype.cpp
@@ -45,7 +45,6 @@
* UnitTypeClass::operator new -- Allocates an object from the unit type class heap. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "unittype.h"
@@ -495,15 +494,9 @@ int UnitTypeClass::Repair_Step(void) const
}
-///
-/// Fetches the persistent class identifier for the unit type.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE UnitTypeClass::GetClassID(CLSID * retval)
+ClassID UnitTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_UnitTypeClass;
- return(S_OK);
+ return(ClassID_UnitTypeClass);
}
diff --git a/code/unittype.h b/code/unittype.h
index b5b5f63d9..acfea5043 100644
--- a/code/unittype.h
+++ b/code/unittype.h
@@ -313,7 +313,7 @@ class UnitTypeClass : public TechnoTypeClass
UnitTypeClass(char const * ininame = NULL);
virtual ~UnitTypeClass() override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/vanim.cpp b/code/vanim.cpp
index ea0b2f8d0..afa32b025 100644
--- a/code/vanim.cpp
+++ b/code/vanim.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "vanim.h"
@@ -546,18 +545,9 @@ void VoxelAnimClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier used to persist this object.
-/// The save system writes this identifier ahead of the object data so that the loader
-/// knows what kind of object to reconstruct.
-///
-/// Pointer to the buffer that will receive the class identifier.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE VoxelAnimClass::GetClassID(CLSID * retval)
+ClassID VoxelAnimClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_VoxelAnimClass;
- return(S_OK);
+ return(ClassID_VoxelAnimClass);
}
diff --git a/code/vanim.h b/code/vanim.h
index d7e9f9e4e..8d8af415e 100644
--- a/code/vanim.h
+++ b/code/vanim.h
@@ -33,7 +33,7 @@ class VoxelAnimClass : public ObjectClass, public BounceClass
VoxelAnimClass(VoxelAnimTypeClass const * type, Coord const & coord, HouseClass * house = NULL);
virtual ~VoxelAnimClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/vanimtype.cpp b/code/vanimtype.cpp
index a2f6646b3..0af776362 100644
--- a/code/vanimtype.cpp
+++ b/code/vanimtype.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "vanimtype.h"
@@ -247,18 +246,9 @@ void VoxelAnimTypeClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the persistence system to record what kind of object was
-/// written, so that the right class can be created when the save game is loaded.
-///
-/// Pointer to the class identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE VoxelAnimTypeClass::GetClassID(CLSID * retval)
+ClassID VoxelAnimTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_VoxelAnimTypeClass;
- return(S_OK);
+ return(ClassID_VoxelAnimTypeClass);
}
diff --git a/code/vanimtype.h b/code/vanimtype.h
index aa01e9e9a..9abc1adfe 100644
--- a/code/vanimtype.h
+++ b/code/vanimtype.h
@@ -32,7 +32,7 @@ class VoxelAnimTypeClass : public ObjectTypeClass
VoxelAnimTypeClass(char const * ininame = NULL);
~VoxelAnimTypeClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/vector.h b/code/vector.h
index f608fa4c0..7466a1fae 100644
--- a/code/vector.h
+++ b/code/vector.h
@@ -123,8 +123,7 @@ class VectorClass
stream.Serialize(count);
if (stream.Is_Loading()) {
- if (count < 0) {
- stream.Fail();
+ if (!stream.Fits(count, (std::is_arithmetic_v || std::is_enum_v) ? sizeof(T) : 1)) {
return;
}
Clear();
@@ -518,8 +517,7 @@ class DynamicVectorClass : public VectorClass
stream.Serialize(count);
if (stream.Is_Loading()) {
- if (count < 0) {
- stream.Fail();
+ if (!stream.Fits(count, (std::is_arithmetic_v || std::is_enum_v) ? sizeof(T) : 1)) {
return;
}
Clear();
diff --git a/code/vein.cpp b/code/vein.cpp
index 89de17ba7..5db7f3fd4 100644
--- a/code/vein.cpp
+++ b/code/vein.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "vein.h"
@@ -860,19 +859,21 @@ void VeinholeMonsterClass::Remove_Dead(void)
/// growth records and handed to the swizzler and the target tracker.
///
/// bool; Were all the monsters read successfully?
-bool VeinholeMonsterClass::Load_All(IStream * stream)
+bool VeinholeMonsterClass::Load_All(SaveStreamClass & stream)
{
Reset();
int cell_count = Map_Cell_Count();
int monster_count;
- if (FAILED(stream->Read(&monster_count, sizeof(monster_count), NULL))) {
+ stream.Serialize(monster_count);
+ if (stream.Was_Error()) {
return(false);
}
GlobalGrowthState = new bool[cell_count];
- if (FAILED(stream->Read(GlobalGrowthState, cell_count, NULL))) {
+ stream.Serialize_Bytes(GlobalGrowthState, (int)(cell_count));
+ if (stream.Was_Error()) {
return(false);
}
@@ -885,20 +886,21 @@ bool VeinholeMonsterClass::Load_All(IStream * stream)
VeinholeMonsterClass * monster = new VeinholeMonsterClass();
SwizzleIDType id;
- if (FAILED(stream->Read(&id, sizeof(id), NULL))) {
+ stream.Serialize(id);
+ if (stream.Was_Error()) {
return(false);
}
Swizzler.Here_I_Am(id, monster);
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_LOAD);
- savestream.Set_Context(typeid(*monster).name(), id);
- monster->Serialize(savestream);
- if (FAILED(savestream.Result())) {
+ stream.Set_Context(typeid(*monster).name(), id);
+ monster->Serialize(stream);
+ if (stream.Was_Error()) {
return(false);
}
- if (FAILED(stream->Read(monster->GrowthState, cell_count, NULL))) {
+ stream.Serialize_Bytes(monster->GrowthState, (int)(cell_count));
+ if (stream.Was_Error()) {
return(false);
}
@@ -939,31 +941,34 @@ void VeinholeMonsterClass::Serialize(SaveStreamClass & stream)
/// with its vein growth records so that growth can pick up where it left off.
///
/// bool; Were all the monsters written successfully?
-bool VeinholeMonsterClass::Save_All(IStream * stream)
+bool VeinholeMonsterClass::Save_All(SaveStreamClass & stream)
{
int monster_count = VeinholeMonsters.Count();
- if (FAILED(stream->Write(&monster_count, sizeof(monster_count), NULL))) {
+ stream.Serialize(monster_count);
+ if (stream.Was_Error()) {
return(false);
}
int cell_count = Map_Cell_Count();
- if (FAILED(stream->Write(GlobalGrowthState, cell_count, NULL))) {
+ stream.Serialize_Bytes(GlobalGrowthState, (int)(cell_count));
+ if (stream.Was_Error()) {
return(false);
}
for (int i = 0; i < monster_count; i++) {
SwizzleIDType id = Swizzler.ID_Of(VeinholeMonsters[i]);
- if (FAILED(stream->Write(&id, sizeof(id), NULL))) {
+ stream.Serialize(id);
+ if (stream.Was_Error()) {
return(false);
}
- SaveStreamClass savestream(stream, SaveStreamClass::MODE_SAVE);
- VeinholeMonsters[i]->Serialize(savestream);
- if (FAILED(savestream.Result())) {
+ VeinholeMonsters[i]->Serialize(stream);
+ if (stream.Was_Error()) {
return(false);
}
- if (FAILED(stream->Write(VeinholeMonsters[i]->GrowthState, cell_count, NULL))) {
+ stream.Serialize_Bytes(VeinholeMonsters[i]->GrowthState, (int)(cell_count));
+ if (stream.Was_Error()) {
return(false);
}
@@ -1041,15 +1046,7 @@ void VeinholeMonsterClass::Reduce_Veins_At(CellClass * cellptr)
}
-///
-/// Fetches the class identifier used by the save game system.
-/// This routine is called by the persistence layer so that it knows which class to
-/// recreate when the saved game is read back in.
-///
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE VeinholeMonsterClass::GetClassID(CLSID * retval)
+ClassID VeinholeMonsterClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_VeinholeMonsterClass;
- return(S_OK);
+ return(ClassID_VeinholeMonsterClass);
}
diff --git a/code/vein.h b/code/vein.h
index 4b53f0acc..dba7355a7 100644
--- a/code/vein.h
+++ b/code/vein.h
@@ -34,7 +34,7 @@ class VeinholeMonsterClass : public ObjectClass
VeinholeMonsterClass(Cell const & cell);
~VeinholeMonsterClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
/*---------------------------------------------------------------------
** Member function prototypes.
@@ -61,8 +61,8 @@ class VeinholeMonsterClass : public ObjectClass
void Clear_Growth(void);
void Destroy_Monster(void);
static void Remove_Dead(void);
- static bool Load_All(IStream * stream);
- static bool Save_All(IStream * stream);
+ static bool Load_All(SaveStreamClass & stream);
+ static bool Save_All(SaveStreamClass & stream);
void Reduce_Veins_At(CellClass * cellptr);
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/walk.cpp b/code/walk.cpp
index d3996e10b..887e4e58a 100644
--- a/code/walk.cpp
+++ b/code/walk.cpp
@@ -27,6 +27,7 @@
#include "inline.h"
#include "overtype.h"
#include "rules.h"
+#include "saveload.h"
#include "savestream.h"
#include "tactical.h"
#include "tube.h"
@@ -35,7 +36,6 @@
#include "layer.hh"
-
///
/// Constructs a walking locomotor.
/// This is the locomotor used by infantry, who travel on foot between the sub-cell
@@ -64,7 +64,7 @@ WalkLocomotionClass::~WalkLocomotionClass(void)
/// Is the infantry traveling somewhere?
///
/// bool; Does the infantry have somewhere it is trying to get to?
-boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Moving(void)
+bool WalkLocomotionClass::Is_Moving(void)
{
return(IsMoving);
}
@@ -76,7 +76,7 @@ boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Moving(void)
/// merely under orders to travel but is standing still.
///
/// bool; Is the infantry moving right now?
-boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Moving_Now(void)
+bool WalkLocomotionClass::Is_Moving_Now(void)
{
if (Is_Moving() && LinkedTo->Speed > 0 && HeadToCoord != COORD_NONE) {
return(true);
@@ -90,7 +90,7 @@ boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Moving_Now(void)
///
/// Returns with the coordinate being traveled to, or COORD_NONE if the infantry has
/// nowhere it needs to be.
-Coord STDMETHODCALLTYPE WalkLocomotionClass::Destination(void)
+Coord WalkLocomotionClass::Destination(void)
{
if (Is_Moving()) {
return(DestinationCoord);
@@ -104,7 +104,7 @@ Coord STDMETHODCALLTYPE WalkLocomotionClass::Destination(void)
///
/// Returns with the immediate destination, or the current position if the infantry
/// is not part way between spots.
-Coord STDMETHODCALLTYPE WalkLocomotionClass::Head_To_Coord(void)
+Coord WalkLocomotionClass::Head_To_Coord(void)
{
if (HeadToCoord != COORD_NONE) {
return(HeadToCoord);
@@ -119,7 +119,7 @@ Coord STDMETHODCALLTYPE WalkLocomotionClass::Head_To_Coord(void)
/// infantry along its path.
///
/// bool; Is the infantry still traveling somewhere?
-boolean STDMETHODCALLTYPE WalkLocomotionClass::Process(void)
+bool WalkLocomotionClass::Process(void)
{
IsProcessingMovement = true;
Movement_AI(true);
@@ -135,7 +135,7 @@ boolean STDMETHODCALLTYPE WalkLocomotionClass::Process(void)
/// than beneath it.
///
/// The coordinate to travel to, or COORD_NONE to clear the destination.
-void STDMETHODCALLTYPE WalkLocomotionClass::Move_To(Coord to)
+void WalkLocomotionClass::Move_To(Coord to)
{
if (LinkedTo->StunDuration <= 0) {
DestinationCoord = to;
@@ -158,7 +158,7 @@ void STDMETHODCALLTYPE WalkLocomotionClass::Move_To(Coord to)
/// The step already under way is allowed to finish; it is the ultimate destination
/// that is forgotten.
///
-void STDMETHODCALLTYPE WalkLocomotionClass::Stop_Moving(void)
+void WalkLocomotionClass::Stop_Moving(void)
{
DestinationCoord = COORD_NONE;
if (HeadToCoord == COORD_NONE) {
@@ -172,7 +172,7 @@ void STDMETHODCALLTYPE WalkLocomotionClass::Stop_Moving(void)
/// Infantry snap around instantly, so there is no rotation to play out over time.
///
/// The direction the infantry should face.
-void STDMETHODCALLTYPE WalkLocomotionClass::Do_Turn(DirType dir)
+void WalkLocomotionClass::Do_Turn(DirType dir)
{
LinkedTo->PrimaryFacing.Set(dir);
}
@@ -184,7 +184,7 @@ void STDMETHODCALLTYPE WalkLocomotionClass::Do_Turn(DirType dir)
/// redirected without waiting for the current step to finish.
///
/// The coordinate to step to, or COORD_NONE to abandon the step.
-void STDMETHODCALLTYPE WalkLocomotionClass::Force_Immediate_Destination(Coord coord)
+void WalkLocomotionClass::Force_Immediate_Destination(Coord coord)
{
HeadToCoord = coord;
if (HeadToCoord == COORD_NONE && DestinationCoord == COORD_NONE) {
@@ -608,25 +608,16 @@ bool WalkLocomotionClass::Mark_Head_To(Coord const & coord)
}
-///
-/// Fetches the class ID of this locomotor.
-/// The persistence system uses this to recreate the correct locomotor when a saved
-/// game is loaded.
-///
-/// Pointer to the class ID to fill in.
-/// Returns with S_OK if the class ID was fetched, otherwise E_POINTER.
-HRESULT STDMETHODCALLTYPE WalkLocomotionClass::GetClassID(CLSID * retval)
+ClassID WalkLocomotionClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_WalkLocomotion;
- return(S_OK);
+ return(ClassID_WalkLocomotion);
}
///
/// Lists the members this walk locomotor carries.
/// The locomotor this one was stacked on top of is a separate persistent object rather
-/// than a member, so it still travels framed by OLE and is recreated as the class it was
+/// than a member, so it travels as a record of its own and is recreated as the class it was
/// saved as.
///
/// The stream carrying the members.
@@ -645,10 +636,9 @@ void WalkLocomotionClass::Serialize(SaveStreamClass & stream)
if (haspiggy) {
if (stream.Is_Saving()) {
- IPersistStreamPtr persist(Piggybacker);
- OleSaveToStream(persist, stream.Get_Stream());
+ Save_Object(stream, Piggybacker.get());
} else {
- OleLoadFromStream(stream.Get_Stream(), IID_ILocomotion, (LPVOID *)&Piggybacker);
+ Piggybacker = Load_Locomotor(stream);
}
}
}
@@ -658,56 +648,26 @@ void WalkLocomotionClass::Serialize(SaveStreamClass & stream)
/// Fetches the display layer that walking objects belong in.
///
/// Returns with the layer that objects using this locomotor render into.
-LayerType STDMETHODCALLTYPE WalkLocomotionClass::In_Which_Layer(void)
+LayerType WalkLocomotionClass::In_Which_Layer(void)
{
return(LAYER_GROUND);
}
-///
-/// Fetches an interface pointer from this locomotor.
-/// This routine extends the base locomotor with the piggyback interface.
-///
-/// The interface identifier being asked for.
-/// Pointer to the interface pointer to fill in.
-/// Returns with S_OK if the interface was supplied, otherwise E_NOINTERFACE.
-HRESULT STDMETHODCALLTYPE WalkLocomotionClass::QueryInterface(REFIID riid, LPVOID * ppvObject)
-{
- HRESULT result = BASECLASS::QueryInterface(riid, ppvObject);
-
- if (result == E_NOINTERFACE) {
- if (riid == IID_IPiggyback) {
- *ppvObject = (IPiggyback*)this;
- }
- if (*ppvObject == NULL) {
- result = E_NOINTERFACE;
- } else {
- AddRef();
- result = S_OK;
- }
- }
- return(result);
-}
-
-
///
/// Attaches a piggybacking locomotor to this one.
/// This routine is used when some temporary means of travel, such as being carried
/// along, must take over from ordinary walking.
///
-/// The locomotor that will ride along on this one.
-/// Returns with S_OK if the locomotor was attached, or E_FAIL if one is already
-/// piggybacking.
-HRESULT STDMETHODCALLTYPE WalkLocomotionClass::Begin_Piggyback(ILocomotion * pointer)
+/// The locomotor that is to take over the unit.
+/// bool; Was the locomotor taken on? One already carrying a locomotor refuses.
+bool WalkLocomotionClass::Begin_Piggyback(std::unique_ptr & carried)
{
- if (pointer == NULL) {
- return(E_POINTER);
- }
- if (Piggybacker == NULL) {
- Piggybacker = pointer;
- return(S_OK);
+ if (carried == nullptr || Piggybacker != nullptr) {
+ return(false);
}
- return(E_FAIL);
+ Piggybacker = std::move(carried);
+ return(true);
}
@@ -715,20 +675,10 @@ HRESULT STDMETHODCALLTYPE WalkLocomotionClass::Begin_Piggyback(ILocomotion * poi
/// Ends the piggyback session and hands back the locomotor that was riding along.
/// Ownership of the piggybacking locomotor passes to the caller.
///
-/// Pointer to the locomotor pointer to fill in.
-/// Returns with S_OK if a piggybacking locomotor was handed back, or S_FALSE if
-/// there was none.
-HRESULT STDMETHODCALLTYPE WalkLocomotionClass::End_Piggyback(ILocomotion ** pointer)
+/// Returns with the locomotor that was riding, or nothing when none was.
+std::unique_ptr WalkLocomotionClass::End_Piggyback(void)
{
- if (pointer == NULL) {
- return(E_POINTER);
- }
- if (Piggybacker != NULL) {
- *pointer = Piggybacker;
- Piggybacker.Detach();
- return(S_OK);
- }
- return(S_FALSE);
+ return(std::move(Piggybacker));
}
@@ -738,7 +688,7 @@ HRESULT STDMETHODCALLTYPE WalkLocomotionClass::End_Piggyback(ILocomotion ** poin
/// not resumed part way through a step.
///
/// bool; Is it safe to end the piggyback?
-boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Ok_To_End(void)
+bool WalkLocomotionClass::Is_Ok_To_End(void)
{
if (!Is_Moving() && Piggybacker != NULL && !IsProcessingMovement) {
return(true);
@@ -747,42 +697,13 @@ boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Ok_To_End(void)
}
-///
-/// Fetches the class ID of whichever locomotor is in charge.
-/// This routine reports the piggybacking locomotor's identity when one has taken
-/// over, otherwise it identifies this walking locomotor.
-///
-/// Pointer to the class ID to fill in.
-/// Returns with S_OK if the class ID was fetched, otherwise an error code.
-HRESULT STDMETHODCALLTYPE WalkLocomotionClass::Piggyback_CLSID(GUID * classid)
-{
- if (classid == NULL) {
- return(E_POINTER);
- }
-
- if (Piggybacker != NULL) {
- IPersistPtr ptr(Piggybacker);
- if (ptr == NULL) {
- return(E_FAIL);
- }
- return(ptr->GetClassID(classid));
- }
-
- IPersistPtr ptr(this);
- if (ptr == NULL) {
- return(E_FAIL);
- }
- return(ptr->GetClassID(classid));
-}
-
-
///
/// Releases the sub-cell spot that this infantry has reserved.
/// This routine is called when the infantry is being lifted off the map so that the
/// spot it had claimed becomes available to others again.
///
/// The occupancy marking operation being performed.
-void STDMETHODCALLTYPE WalkLocomotionClass::Mark_All_Occupation_Bits(int mark)
+void WalkLocomotionClass::Mark_All_Occupation_Bits(int mark)
{
if (mark == MARK_UP) {
LinkedTo->Clear_Occupy_Bit(Head_To_Coord());
@@ -797,7 +718,7 @@ void STDMETHODCALLTYPE WalkLocomotionClass::Mark_All_Occupation_Bits(int mark)
///
/// The coordinate to test the immediate destination against.
/// bool; Is the infantry walking to that spot?
-boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Moving_Here(Coord to)
+bool WalkLocomotionClass::Is_Moving_Here(Coord to)
{
Coord headto = Head_To_Coord();
if (headto.As_Cell() == Coord(to).As_Cell() && abs(headto.Z - to.Z) <= LEVEL_LEPTON_H) {
@@ -813,7 +734,7 @@ boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Moving_Here(Coord to)
/// merely holds orders to travel but has yet to take a step.
///
/// bool; Is the infantry really moving at this moment?
-boolean STDMETHODCALLTYPE WalkLocomotionClass::Is_Really_Moving_Now(void)
+bool WalkLocomotionClass::Is_Really_Moving_Now(void)
{
return(IsReallyMoving);
}
diff --git a/code/walk.h b/code/walk.h
index 9ea0db2b3..267a54d66 100644
--- a/code/walk.h
+++ b/code/walk.h
@@ -16,6 +16,8 @@
#include "ipiggy.h"
#include "loco.h"
+#include
+
class WalkLocomotionClass : public LocomotionClass, public IPiggyback
{
@@ -29,34 +31,30 @@ class WalkLocomotionClass : public LocomotionClass, public IPiggyback
WalkLocomotionClass(void);
virtual ~WalkLocomotionClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID * ppvObject) override;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) override {return(BASECLASS::AddRef());}
- virtual ULONG STDMETHODCALLTYPE Release(void) override {return(BASECLASS::Release());}
-
- virtual HRESULT STDMETHODCALLTYPE Begin_Piggyback(ILocomotion * pointer) override;
- virtual HRESULT STDMETHODCALLTYPE End_Piggyback(ILocomotion ** pointer) override;
- virtual boolean STDMETHODCALLTYPE Is_Ok_To_End(void) override;
- virtual HRESULT STDMETHODCALLTYPE Piggyback_CLSID(GUID * classid) override;
- virtual boolean STDMETHODCALLTYPE Is_Piggybacking(void) override {return(Piggybacker != NULL);}
-
- virtual boolean STDMETHODCALLTYPE Is_Moving(void) override;
- virtual Coord STDMETHODCALLTYPE Destination(void) override;
- virtual Coord STDMETHODCALLTYPE Head_To_Coord(void) override;
- virtual boolean STDMETHODCALLTYPE Process(void) override;
- virtual void STDMETHODCALLTYPE Move_To(Coord to) override;
- virtual void STDMETHODCALLTYPE Stop_Moving(void) override;
- virtual void STDMETHODCALLTYPE Do_Turn(DirType dir) override;
- virtual LayerType STDMETHODCALLTYPE In_Which_Layer(void) override;
- virtual void STDMETHODCALLTYPE Force_Immediate_Destination(Coord coord) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Now(void) override;
- virtual void STDMETHODCALLTYPE Mark_All_Occupation_Bits(int mark) override;
- virtual boolean STDMETHODCALLTYPE Is_Moving_Here(Coord to) override;
- virtual boolean STDMETHODCALLTYPE Is_Really_Moving_Now(void) override;
- virtual void STDMETHODCALLTYPE Stop_Movement_Animation(void) override {IsReallyMoving = false;};
+
+ virtual bool Begin_Piggyback(std::unique_ptr & carried) override;
+ virtual std::unique_ptr End_Piggyback(void) override;
+ virtual bool Is_Ok_To_End(void) override;
+ virtual bool Is_Piggybacking(void) override {return(Piggybacker != nullptr);}
+
+ virtual bool Is_Moving(void) override;
+ virtual Coord Destination(void) override;
+ virtual Coord Head_To_Coord(void) override;
+ virtual bool Process(void) override;
+ virtual void Move_To(Coord to) override;
+ virtual void Stop_Moving(void) override;
+ virtual void Do_Turn(DirType dir) override;
+ virtual LayerType In_Which_Layer(void) override;
+ virtual void Force_Immediate_Destination(Coord coord) override;
+ virtual bool Is_Moving_Now(void) override;
+ virtual void Mark_All_Occupation_Bits(int mark) override;
+ virtual bool Is_Moving_Here(Coord to) override;
+ virtual bool Is_Really_Moving_Now(void) override;
+ virtual void Stop_Movement_Animation(void) override {IsReallyMoving = false;};
void Movement_AI(bool first_pass);
bool Mark_Head_To(Coord const & coord);
@@ -100,5 +98,5 @@ class WalkLocomotionClass : public LocomotionClass, public IPiggyback
* temporarily -- a jump jet coming down to cover the last few cells on foot, say --
* and the suspended locomotor is handed back when the walk is finished.
*/
- ILocomotionPtr Piggybacker;
+ std::unique_ptr Piggybacker;
};
diff --git a/code/warhead.cpp b/code/warhead.cpp
index 11cd19645..39f8e8e02 100644
--- a/code/warhead.cpp
+++ b/code/warhead.cpp
@@ -35,7 +35,6 @@
* WarheadTypeClass::operator new -- Allocate a warhead object from the special heap. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "warhead.h"
@@ -248,18 +247,9 @@ void WarheadTypeClass::Compute_CRC(CRCEngine &crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence interface. The save code stores the identifier
-/// so that the object can be recognized when the game is loaded back in.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE WarheadTypeClass::GetClassID(CLSID * retval)
+ClassID WarheadTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_WarheadTypeClass;
- return(S_OK);
+ return(ClassID_WarheadTypeClass);
}
diff --git a/code/warhead.h b/code/warhead.h
index 1449ecd30..02e1a2a64 100644
--- a/code/warhead.h
+++ b/code/warhead.h
@@ -52,7 +52,7 @@ class WarheadTypeClass : public AbstractTypeClass
WarheadTypeClass(char const * ininame = NULL);
virtual ~WarheadTypeClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/wave.cpp b/code/wave.cpp
index d2ae55f06..a9484d2ef 100644
--- a/code/wave.cpp
+++ b/code/wave.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "wave.h"
@@ -466,18 +465,9 @@ void WaveClass::Post_Load(void)
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is part of the persistence interface. The save game loader uses the
-/// identifier to recreate the object as the right kind of class.
-///
-/// Pointer to the buffer to store the class identifier in.
-/// Returns with S_OK, or E_POINTER if no buffer was supplied.
-HRESULT STDMETHODCALLTYPE WaveClass::GetClassID(CLSID * retval)
+ClassID WaveClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_WaveClass;
- return(S_OK);
+ return(ClassID_WaveClass);
}
diff --git a/code/wave.h b/code/wave.h
index 8444e4dcc..d1d291d5c 100644
--- a/code/wave.h
+++ b/code/wave.h
@@ -25,7 +25,7 @@ class WaveClass : public ObjectClass
WaveClass(void);
virtual ~WaveClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
virtual void Post_Load(void) override;
diff --git a/code/waypoint.cpp b/code/waypoint.cpp
index a71d3c6f5..59d2f4fb3 100644
--- a/code/waypoint.cpp
+++ b/code/waypoint.cpp
@@ -7,7 +7,6 @@
* See LICENSE.md for applicable additional terms and warranty disclaimers.
******************************************************************************/
-#define INCLUDE_COM
#include "always.h"
#include "waypoint.h"
@@ -281,18 +280,9 @@ void WaypointPathClass::Compute_CRC(CRCEngine & crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the persistence machinery, which records the identifier so that
-/// it knows what kind of object to create when the game is loaded back.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE WaypointPathClass::GetClassID(CLSID * retval)
+ClassID WaypointPathClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_WaypointPath;
- return(S_OK);
+ return(ClassID_WaypointPath);
}
diff --git a/code/waypoint.h b/code/waypoint.h
index ff7f11d10..5fdac3fb5 100644
--- a/code/waypoint.h
+++ b/code/waypoint.h
@@ -49,7 +49,7 @@ class WaypointPathClass : public AbstractClass
WaypointPathClass(int index);
virtual ~WaypointPathClass(void) override;
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
virtual void Serialize(SaveStreamClass & stream) override;
diff --git a/code/weapon.cpp b/code/weapon.cpp
index 697c9e14d..736582178 100644
--- a/code/weapon.cpp
+++ b/code/weapon.cpp
@@ -39,7 +39,6 @@
* WeaponTypeClass::Allowed_Threats -- Determine what threats this weapon can address. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#define INCLUDE_COM
#include "always.h"
#include "weapon.h"
@@ -363,18 +362,9 @@ void WeaponTypeClass::Compute_CRC(CRCEngine &crc) const
}
-///
-/// Fetches the class identifier of this object.
-/// This routine is used by the persistence machinery to recognize what kind of object it
-/// is about to load back.
-///
-/// Pointer to the identifier to fill in.
-/// Returns with S_OK, or E_POINTER if no destination was supplied.
-HRESULT STDMETHODCALLTYPE WeaponTypeClass::GetClassID(CLSID * retval)
+ClassID WeaponTypeClass::Class_ID(void) const
{
- if (retval == NULL) return(E_POINTER);
- *retval = CLSID_WeaponTypeClass;
- return(S_OK);
+ return(ClassID_WeaponTypeClass);
}
diff --git a/code/weapon.h b/code/weapon.h
index 22df6f3ed..3e1e01f76 100644
--- a/code/weapon.h
+++ b/code/weapon.h
@@ -60,7 +60,7 @@ class WeaponTypeClass : public AbstractTypeClass
WeaponTypeClass(char const * ininame = NULL);
~WeaponTypeClass(void);
- virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID * retval) override;
+ virtual ClassID Class_ID(void) const override;
static WeaponType From_Name(char const * name);
diff --git a/docs/README.md b/docs/README.md
index d983f12aa..963715287 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -11,6 +11,8 @@ The developer guides are split by subject:
- [Project direction](DIRECTION.md) — long-term architecture.
- [UI system design](UI_DESIGN.md) — proposed RmlUi and ImGui integration,
screen-level interchangeable views, and the migration from OwnerDraw.
+- [The saved game format](SAVE-FORMAT.md) — the layout of a `.SAV` file: its
+ header, listing fields, compressed content, and object records.
See [CONTRIBUTING.md](../CONTRIBUTING.md) for contribution and review rules.
Player and modder documentation is under [manual/](../manual/README.md). When a
diff --git a/docs/SAVE-FORMAT.md b/docs/SAVE-FORMAT.md
new file mode 100644
index 000000000..987883a50
--- /dev/null
+++ b/docs/SAVE-FORMAT.md
@@ -0,0 +1,178 @@
+# The saved game format
+
+A saved game is one `.SAV` file written by `code/savefile.cpp` and read back by
+it. This document owns the layout. Where the files live, how they are named,
+and when they are written is on the manual's
+[save games page](../manual/content/formats/save-games.md).
+
+Every integer is little-endian. Offsets are from the start of the file.
+
+## Header
+
+| Offset | Size | Field |
+| --- | --- | --- |
+| 0 | 4 | Signature, the bytes `OTSV` |
+| 4 | 2 | Format version, currently 1 |
+| 6 | 2 | Flags; bit 0 set when the content is LZO-compressed |
+| 8 | 4 | Length of the field table |
+| 12 | 4 | Offset of the content |
+| 16 | 4 | Stored length of the content |
+| 20 | 4 | Uncompressed length of the content |
+| 24 | 4 | CRC-32 of the stored content |
+| 28 | 4 | CRC-32 of the first 28 bytes of the header, continued over the field table |
+
+The header is 32 bytes, the field table follows it directly, and the content
+follows the table directly. The content offset is recorded rather than assumed
+so a later format version can put something between the two; this version
+refuses a file whose offset says otherwise. A field table is refused above
+1 MiB, since a listing is a dozen short fields.
+
+Both checksums are the CRC-32 of IEEE 802.3, polynomial `0xEDB88320`
+reflected, initial value and final complement of all ones, as PNG and gzip
+use it. The header checksum continues over the field table so a listing can
+verify what it shows without reading the content.
+
+## Field table
+
+The fields are what the load dialog lists a save by. Each is:
+
+| Size | Field |
+| --- | --- |
+| 2 | Identifier |
+| 2 | Kind: 1 string, 2 integer, 3 file time |
+| 4 | Length of the value |
+| | The value: string bytes without a terminator, a 4-byte integer, or an 8-byte `FILETIME` |
+
+The identifiers are the `PIDSI_` values in `code/savever.h`, the same ones the
+compound-document property set carried before this format. A field holds at
+most 64 KiB. A reader takes the
+first field that matches both identifier and kind and ignores the rest, so a
+field it does not know costs nothing. A string longer than the buffer it is
+read into is cut on a character boundary, so a shortened description stays
+UTF-8. `SaveVersionInfo` in `code/savever.cpp` is the only writer and reader.
+
+## Content
+
+The content is the game state: the bytes `Put_All` in `code/saveload.cpp`
+writes through `SaveStreamClass`, compressed as one block with LZO1X-1 when
+that makes it smaller, and stored as it is otherwise. The reader checks the
+stored length and checksum before decompressing, and refuses a block that does
+not expand to exactly the recorded length. An uncompressed length above 256 MiB
+is refused before anything is allocated for it.
+
+The block is decompressed through `lzo1x_decompress_safe`, which stops at the
+end of the output buffer, so a block forged to expand past the recorded length
+is refused rather than written past it. The records after the header are still
+read into live objects, so treat a save file from an untrusted source as
+untrusted input.
+
+### Object records
+
+The state is a sequence of values and object records in the order `Put_All`
+names them. An object record is:
+
+| Size | Field |
+| --- | --- |
+| 16 | The class identifier of the object |
+| 4 | Length of the record body |
+| | The body: the swizzle identity, then the members the class's `Serialize` names |
+
+The class identifier is the `ClassID` the object's `Class_ID` reports, the
+same one registered in `code/startup.cpp` and, for a locomotor, named by the
+`Locomotor=` key. Its sixteen bytes are those of the COM class identifier
+the class once registered, kept because the `Locomotor=` values in rules
+files carry them. The reader creates the object through that
+registration, hands it the stream, checks that it consumed exactly the
+recorded length, and only then lets it finish restoring itself, so a refused
+record never reaches the map or a side table. A record that comes up short
+or long fails the load with the object's type and offset in the debug log,
+which is what a member added to one build and not the other looks like. A
+record read where a locomotor belongs fails the load the same way when its
+class is not one. A vector of objects is a 4-byte count followed by that
+many records, all of the heap's own class; a record naming any other class
+fails the load, since nothing else belongs in that heap. A locomotor nested
+inside a unit's record is a record of its own. A count that the bytes
+remaining in the content could not hold fails the load before anything is
+allocated for it.
+
+An object whose record fails is destroyed before the load fails. The pointer
+slots it had registered are cleared first, since they still hold identities
+rather than addresses, and the slots the records before it registered are
+cleared the same way. Those earlier objects stay in their heaps, and the ones
+that had finished loading have already taken their place in the map or a side
+table. A failed load therefore leaves a partly built game that the caller has
+to clear, not one it can carry on from.
+
+A character buffer travels as its text: a length and that many characters, and
+a load clears the rest of the buffer. How much room a build keeps for a string
+is its own business, so the file carries neither the capacity nor whatever the
+memory held past the terminator. The text is at most one character shorter than
+the buffer, so a loaded buffer is always terminated; a length that would fill it
+outright fails the load, since the engine reads these buffers as C strings.
+
+The body is what each class's `Serialize` produces, member by member, in host
+byte order. It is not described here; the classes are the description.
+
+The swizzle identity and every pointer member travel as four bytes. The save
+numbers the objects it meets rather than writing the address one sat at, so
+the body depends neither on the pointer width of the build that wrote it nor
+on where the objects were in memory.
+
+## Versions
+
+Two numbers gate a save. The format version in the header says how to parse
+the file, and a reader refuses a version above its own. The header flags are
+gated the same way: a reader refuses a file with a flag bit it does not know,
+so a later version can mark content it stores differently without moving the
+format version. The internal version in the field table,
+`PIDSI_INTERNAL_VER`, is `ExpectedGameVersion`, the packed project version,
+and a save whose value differs from the running build's is not offered to the
+player. The format version moves only when the layout in this document
+changes; the internal version moves with every release.
+
+## What the reader refuses
+
+`SaveFileClass::Read` and `Read_Fields` answer one of:
+
+| Result | When |
+| --- | --- |
+| `RESULT_MISSING` | No file under that name |
+| `RESULT_NOT_A_SAVE` | The first bytes are not the signature |
+| `RESULT_UNSUPPORTED_VERSION` | A format version above the reader's, or a header flag it does not know |
+| `RESULT_CORRUPT` | A length, checksum or compressed block that does not add up, including a truncated file, a forged block, a field table above 1 MiB, a content offset that does not follow the table, or a content length above 256 MiB |
+| `RESULT_NO_MEMORY` | A file within those limits that the process cannot hold |
+
+`Read` judges the header before it reads or allocates anything else, so a file
+of any size costs the reader no more than the limits above allow, and
+`Read_Fields` reads the header and the table only, so listing a folder never
+allocates for a file's content.
+
+`Load_Game` reads and checks the whole file before it tears down the running
+game, so a refused file costs nothing.
+
+A save written before this format is an OLE compound document, which begins
+with a signature of its own, so the reader answers `RESULT_NOT_A_SAVE` and the
+load dialog leaves the file out of its list. Nothing converts those files.
+
+## Writing
+
+`SaveFileClass::Write` builds the whole image in memory, writes it to the
+target name with `.tmp` appended, flushes and closes it, and then moves it over
+the target with `MoveFileExA` and `MOVEFILE_REPLACE_EXISTING`. A save
+interrupted at any point leaves the previous file untouched under its name,
+and at most a `.tmp` beside it, which the next successful save replaces.
+The reader's limits bind the writer too: content above 256 MiB, a field above
+64 KiB or a table above 1 MiB is refused with `RESULT_TOO_LARGE` before
+anything is written, so a save this build writes is one it reads, and the
+file on disk is left as it was.
+
+## Checks
+
+`tests/save` builds `code/savefile.cpp` against the vendored LZO library and
+covers the round trip, the fields-only read, replacement of an existing file
+and of a stale `.tmp`, and each refusal above, including a later version, an
+unknown flag, a file cut at every boundary, a byte flipped in the header, the
+table and the content, a field table above its limit, a gap before the
+content, a block that ends before or expands past its declared length, and a
+write above each limit that leaves the earlier save in place. It reads no game
+data.
diff --git a/manual/content/formats/save-games.md b/manual/content/formats/save-games.md
index 0e375f94e..8d60d0bb7 100644
--- a/manual/content/formats/save-games.md
+++ b/manual/content/formats/save-games.md
@@ -1,7 +1,7 @@
---
format_id: save-games
title: Save games
-summary: Stores versioned OpenTS game state in `.SAV` compound-document files.
+summary: Stores versioned OpenTS game state in `.SAV` files of the engine's own format.
kind: binary
extensions:
- .SAV
@@ -18,6 +18,7 @@ source_files:
- code/mainloop.cpp
- code/mpload.cpp
- code/netdlg.cpp
+ - code/savefile.cpp
- code/saveload.cpp
- code/savemgr.cpp
- code/savestream.cpp
@@ -30,7 +31,7 @@ source_files:
- code/voc.cpp
---
-The save dialog creates `.SAV` files. Each file is an OLE compound document: the listing details live in the document's own property set, and the game state goes into a single `CONTENTS` stream that is compressed as it is written.
+The save dialog creates `.SAV` files. Each file begins with a fixed header and a table of the details the load dialog lists a save by, followed by the game state as one compressed block. The listing is read from the header and table alone, and a file that is truncated, damaged, or written by a later format version is refused before anything is loaded. A save is written under a temporary name and moved into place once complete, so an interrupted save leaves the previous file intact.
## Where the files are
@@ -60,7 +61,7 @@ Timed saves in a game against other machines run only when a launch file set the
The [`QuickSave`](/commands/quicksave/) command writes a campaign to `QUICKSAVE.SAV` and a skirmish to `QUICKSAVE_SKIRMISH.SAV`, replacing the previous file of that kind, so a skirmish never writes over a campaign. The save is written at the frame boundary after the key was pressed, once the frame has retired its dead objects, behind the saving box a menu save shows; the message list then reports `Game saved.` or that the game could not be saved. Each file is described as `Quick Save` and the scenario's description, and the load dialog lists it like any other save. A quick save starts the automatic-save interval over like any completed save.
-[`QuickLoad`](/commands/quickload/) restores the file for the kind of game being played. It first reads the file's property set and refuses, with a line in the message list, when the file is missing or carries another version's stamp; otherwise the load runs when the frame ends, in the place the options menu runs, and the mission clock resumes in the restored game. A load that fails partway through the restore shows the same error box as the load dialog and leaves the player in the options menu.
+[`QuickLoad`](/commands/quickload/) restores the file for the kind of game being played. It first reads the file's listing fields and refuses, with a line in the message list, when the file is missing or carries another version's stamp; otherwise the load runs when the frame ends, in the place the options menu runs, and the mission clock resumes in the restored game. A load that fails partway through the restore shows the same error box as the load dialog and leaves the player in the options menu.
Both commands are refused in a game against other machines, during playback, while a scripted sequence has locked input, and once the game is being won or lost. Both arrive unbound.
@@ -74,19 +75,21 @@ In a game against other machines the master can load one of the match's saved ga
## What the file holds
-The property set carries the description shown in the list, the player's name and house, the campaign and scenario numbers, the game type, three timestamps, the name of the program that wrote the save, and two version stamps — the save format's own version and the build version of the game that wrote it.
+The field table carries the description shown in the list, the player's house, the campaign and scenario numbers, the game type, three timestamps, the name of the program that wrote the save, and the build version of the game that wrote it. The header carries the format's own version.
-The `CONTENTS` stream is a fixed sequence of records — the scenario, the environment, the rules, the map, the loose global values, and every list of type definitions and runtime objects — written and read back in the same order. Among the loose values are the looping sounds left at waypoints by [Play Sound Effect At](/mapping/actions/taction-play-sound-at/) and the sounds attached to objects, each as the sound and its place or object; the playing sound itself is not saved and starts again on the first sound tick after the load. Each list stores its own length ahead of its members, and each member writes out the members its class declares, in the order that class lists them. What a save holds is therefore a field-by-field record of each object rather than a copy of the bytes it occupied in memory. Type definitions travel with the save, so a save carries the rules types it was made with rather than looking them up again on load. Artwork does not travel with it: once a restored type's members have been read, its shape and voxel pointers are released and fetched from the archives again, so a save loaded against a changed set of files gets the current artwork. One piece does not come back. A UnitType drawn from shapes is given a [voxel turret](/formats/vxl-hva/) when the rules are read, by a routine no restore calls; the restore takes the ordinary voxel path instead, which releases that turret along with the body model it could not find. Its voxel barrel is fetched back, and the barrel is what the shape path draws.
+The game state is a fixed sequence of records — the scenario, the environment, the rules, the map, the loose global values, and every list of type definitions and runtime objects — written and read back in the same order. Among the loose values are the looping sounds left at waypoints by [Play Sound Effect At](/mapping/actions/taction-play-sound-at/) and the sounds attached to objects, each as the sound and its place or object; the playing sound itself is not saved and starts again on the first sound tick after the load. Each list stores its own length ahead of its members, and each member writes out the members its class declares, in the order that class lists them. What a save holds is therefore a field-by-field record of each object rather than a copy of the bytes it occupied in memory. Type definitions travel with the save, so a save carries the rules types it was made with rather than looking them up again on load. Artwork does not travel with it: once a restored type's members have been read, its shape and voxel pointers are released and fetched from the archives again, so a save loaded against a changed set of files gets the current artwork. One piece does not come back. A UnitType drawn from shapes is given a [voxel turret](/formats/vxl-hva/) when the rules are read, by a routine no restore calls; the restore takes the ordinary voxel path instead, which releases that turret along with the body model it could not find. Its voxel barrel is fetched back, and the barrel is what the shape path draws.
The scenario record also holds the scenario file itself, name and bytes, where the deployment's [`CarryScenarioFile`](/formats/opents-ini/#what-a-save-carries) asks for it; the record is written either way, empty when nothing is carried. A [restart or replay](/systems/campaign-progression/#losing-and-restarting) after a load reads that copy, not the file on disk, which a client resuming the save may have replaced. A random map holds no file.
## What is checked
The project-version stamp decides whether a file is offered at all, and only
-the running version's stamp is accepted. The load dialog reads the property set
-of every `.SAV` in the saved-games folder and skips every file stamped by
-anything else, including the Tiberian Sun release and another OpenTS
-release-cycle version. A save that reaches the engine without passing through
+the running version's stamp is accepted. The load dialog reads the header and
+field table of every `.SAV` in the saved-games folder and skips every file
+stamped by anything else, including another OpenTS release-cycle version. A
+file in the compound-document layout that earlier OpenTS releases and Tiberian
+Sun wrote is not a saved game to this reader and is skipped as well; there is
+no conversion. A save that reaches the engine without passing through
the dialog, as a network save or one resumed from a
[launch file](/formats/spawn-ini/) does, is checked the same way and refused.
Development snapshots within one cycle share the stamp, and their save layouts
@@ -95,4 +98,4 @@ leading `*`.
Beyond that stamp and the add-on the scenario declares, nothing about a save is measured against the game it is being loaded into. A save made under one set of rules and loaded under another is not detected, and the type definitions stored in the file are simply restored over the ones the rules built.
-Reading `CONTENTS` clears the scenario before it starts and gives up at the first record it cannot restore. A load that stops there fails, rather than carrying on into a scenario that was cleared and never refilled.
+The file is read and checked in full, checksums included, before the running game is touched, so a truncated or damaged file is refused at no cost. Restoring the game state then clears the scenario before it starts and gives up at the first record it cannot restore. A load that stops there fails, rather than carrying on into a scenario that was cleared and never refilled.
diff --git a/manual/content/internals/class-hierarchy.md b/manual/content/internals/class-hierarchy.md
index 0e3097469..97897fc73 100644
--- a/manual/content/internals/class-hierarchy.md
+++ b/manual/content/internals/class-hierarchy.md
@@ -22,7 +22,7 @@ source_files:
`AbstractClass` is the common base for persistent engine entities. Map objects and INI-backed type definitions are separate branches of that hierarchy. A runtime instance stores state for one object in the current match; a type definition stores data shared by every instance with the same INI identifier.
-This page covers simulation objects and their definitions. UI controls, file classes, and locomotion COM objects use other hierarchies.
+This page covers simulation objects and their definitions. UI controls, file classes, and locomotors use other hierarchies.
## Terms
diff --git a/manual/content/internals/locomotion.md b/manual/content/internals/locomotion.md
index 2ac0434cb..d9e912943 100644
--- a/manual/content/internals/locomotion.md
+++ b/manual/content/internals/locomotion.md
@@ -14,13 +14,13 @@ source_files:
- code/droppod.cpp
---
-`FootClass::Locomotion` is the current `ILocomotion` COM interface for one mobile runtime instance. The locomotor is a separate object linked to the `FootClass`; it is not a behavioral base class of `FootClass`.
+`FootClass::Locomotion` owns the current `ILocomotion` locomotor for one mobile runtime instance. The locomotor is a separate object linked to the `FootClass`; it is not a behavioral base class of `FootClass`.
## Object locomotion
-`TechnoTypeClass::Locomotor` stores the CLSID used to create a type's ordinary locomotor. Concrete `FootClass` constructors create that COM object, call `Link_To_Object`, and assign it to `FootClass::Locomotion`.
+`TechnoTypeClass::Locomotor` stores the class identifier used to create a type's ordinary locomotor. Concrete `FootClass` constructors create that locomotor, call `Link_To_Object`, and assign it to `FootClass::Locomotion`.
-Movement, destination, layer, occupation, and locomotor-specific drawing queries go through the current interface. Code must therefore inspect the runtime `Locomotion` pointer when temporary locomotion is possible; the type's `Locomotor` CLSID describes the ordinary implementation, not necessarily the one currently in control.
+Movement, destination, layer, occupation, and locomotor-specific drawing queries go through the current interface. Code must therefore inspect the runtime `Locomotion` pointer when temporary locomotion is possible; the type's `Locomotor` identifier describes the ordinary implementation, not necessarily the one currently in control.
## Piggybacking
@@ -28,16 +28,16 @@ Movement, destination, layer, occupation, and locomotor-specific drawing queries
| Operation | State transition |
| --- | --- |
-| `Begin_Piggyback(previous)` | Stores `previous` inside the new locomotor. A null pointer returns `E_POINTER`; an already occupied slot returns `E_FAIL`. |
+| `Begin_Piggyback(previous)` | Stores `previous` inside the new locomotor and takes ownership of it. Answers `false` and leaves `previous` with the caller when there is nothing to store or the slot is already occupied. |
| Replace `FootClass::Locomotion` | Makes the new locomotor the object's active movement interface. The new locomotor must already be linked to the same object. |
-| `End_Piggyback(&FootClass::Locomotion)` | Writes the stored locomotor back into the object member and releases the piggyback slot. No stored locomotor returns `S_FALSE`; a null output pointer returns `E_POINTER`. |
+| `End_Piggyback()` | Hands the stored locomotor back to the caller and empties the piggyback slot. Answers nothing when no locomotor was stored. |
-`FootClass::Link_DropPod` applies this sequence with the ballistic locomotor: it retains the passenger's current locomotor through `Begin_Piggyback`, then installs the ballistic interface. Drop-pod touchdown passes the address of `FootClass::Locomotion` to `End_Piggyback` before attempting ground placement.
+`FootClass::Link_DropPod` applies this sequence with the ballistic locomotor: it retains the passenger's current locomotor through `Begin_Piggyback`, then installs the ballistic interface. Drop-pod touchdown assigns the locomotor `End_Piggyback` returns back to `FootClass::Locomotion`, when the pod carried one, before attempting ground placement.
Callers that perform opportunistic restoration first consult `Is_Ok_To_End`. The drop-pod touchdown path calls `End_Piggyback` directly at ground contact because its descent state already establishes the transition.
## Persistence identity
-`FootClass::Serialize` writes the active locomotor through `IPersistStream` when saving and restores it through `OleLoadFromStream` when loading. A piggyback-capable locomotor writes whether it carries another locomotor and serializes that nested COM object when present. A save made during a temporary movement state therefore retains both the active locomotor and the one to restore.
+`FootClass::Serialize` writes the active locomotor as a record of its own, headed by its class identifier, and recreates it from that identifier when loading. A piggyback-capable locomotor writes whether it carries another locomotor and serializes that nested locomotor when present. A save made during a temporary movement state therefore retains both the active locomotor and the one to restore.
-`GetClassID` identifies the active locomotor implementation. `Piggyback_CLSID` returns the carried locomotor's `GetClassID` while piggybacking and the active locomotor's ID otherwise. These identities are distinct while a temporary locomotor is in control.
+`Class_ID` identifies the active locomotor implementation, and the carried locomotor keeps its own. These identities are distinct while a temporary locomotor is in control.
diff --git a/manual/data/ini-keys.yaml b/manual/data/ini-keys.yaml
index 2f4d3e79a..7d90356fb 100644
--- a/manual/data/ini-keys.yaml
+++ b/manual/data/ini-keys.yaml
@@ -13123,7 +13123,7 @@ Locomotor:
section:
kind: identifier
source: object-type
- value_type: Locomotor CLSID
+ value_type: Locomotor class identifier
status: generated
_provenance:
default_candidate: the Teleport locomotor
diff --git a/manual/site/scripts/check-render.mjs b/manual/site/scripts/check-render.mjs
index 94a46df6e..e6d7b1f77 100644
--- a/manual/site/scripts/check-render.mjs
+++ b/manual/site/scripts/check-render.mjs
@@ -57,7 +57,7 @@ const cases = [
['mapping/missions/tmission-loop/index.html', ['Jump to line', 'one-based']],
['internals/class-hierarchy/index.html', ['Object and type system', 'Primary runtime hierarchy', 'Type-definition hierarchy', 'AbstractTypeClass', 'code/abstype.h']],
['internals/radio/index.html', ['Radio contact protocol', 'Contact state', 'Messages and responses', 'Compute_CRC', 'code/radio.cpp']],
- ['internals/locomotion/index.html', ['Locomotion and piggybacking', 'FootClass::Locomotion', 'IPiggyback', 'Piggyback_CLSID']],
+ ['internals/locomotion/index.html', ['Locomotion and piggybacking', 'FootClass::Locomotion', 'IPiggyback', 'Class_ID']],
['reference/enums/mission/index.html', ['data-enum-table', 'MISSION_HUNT', 'Stored value', 'Used by', 'TMISSION_DO']],
['systems/drop-pods/index.html', ['ots-page-subtitle', 'Entry paths', 'Approach and descent', 'Touchdown']],
['systems/base-adjacency/index.html', ['ots-page-subtitle', 'Base placement and adjacency', 'Placement decision order', 'Adjacent']],
diff --git a/manual/site/tests/documentation-source-contract.test.mjs b/manual/site/tests/documentation-source-contract.test.mjs
index 817e341ba..b9b9d3713 100644
--- a/manual/site/tests/documentation-source-contract.test.mjs
+++ b/manual/site/tests/documentation-source-contract.test.mjs
@@ -36,7 +36,7 @@ test('Drop pod approach selection keeps its ordered candidates and unconditional
const droppod = source('code/droppod.cpp');
const moveTo = functionBody(
droppod,
- 'void STDMETHODCALLTYPE DropPodLocomotionClass::Move_To(Coord to)',
+ 'void DropPodLocomotionClass::Move_To(Coord to)',
);
assert.match(
@@ -74,7 +74,7 @@ test('Drop pod directions retain their hard-coded airborne and landing-art mappi
const drawingCode = functionBody(
droppod,
- 'int STDMETHODCALLTYPE DropPodLocomotionClass::Drawing_Code(void)',
+ 'int DropPodLocomotionClass::Drawing_Code(void)',
);
assert.match(drawingCode, /Direction\s*%\s*2/);
assertOrdered(infantry, [
@@ -84,13 +84,13 @@ test('Drop pod directions retain their hard-coded airborne and landing-art mappi
const process = functionBody(
droppod,
- 'boolean STDMETHODCALLTYPE DropPodLocomotionClass::Process(void)',
+ 'bool DropPodLocomotionClass::Process(void)',
);
assert.match(process, /Rule->DropPod\[Direction\s*%\s*Rule->DropPod\.Count\(\)\]/);
const moveTo = functionBody(
droppod,
- 'void STDMETHODCALLTYPE DropPodLocomotionClass::Move_To(Coord to)',
+ 'void DropPodLocomotionClass::Move_To(Coord to)',
);
assertOrdered(moveTo, [
'dropcoord.Z += Rule->DropPodHeight;',
@@ -102,13 +102,13 @@ test('Drop pod directions retain their hard-coded airborne and landing-art mappi
test('Blocked Drop pod touchdown retains its exact damage, animation, and deletion payload', () => {
const process = functionBody(
source('code/droppod.cpp'),
- 'boolean STDMETHODCALLTYPE DropPodLocomotionClass::Process(void)',
+ 'bool DropPodLocomotionClass::Process(void)',
);
assertOrdered(process, [
'FootClass * linked = LinkedTo;',
'coord = linked->PositionCoord;',
'linked->Limbo();',
- 'End_Piggyback(&LinkedTo->Locomotion);',
+ 'LinkedTo->Locomotion = std::move(carried);',
'if (!linked->Unlimbo(coord, DIR_N)) {',
'Explosion_Damage(coord, 100, LinkedTo, Rule->C4Warhead);',
'Combat_Anim(100, Rule->C4Warhead, LAND_CLEAR, coord)',
diff --git a/manual/tools/extract_engine.py b/manual/tools/extract_engine.py
index c4f3cf656..f999f560c 100644
--- a/manual/tools/extract_engine.py
+++ b/manual/tools/extract_engine.py
@@ -93,7 +93,7 @@ def _yaml():
"SpeedType": "SpeedType",
"MPHType": "speed",
"Side": "Side",
- "CLSID": "Locomotor CLSID",
+ "ClassID": "Locomotor class identifier",
"Owners": "list of HouseTypes",
"Scheme_Index": "colour scheme",
"BuildingType_List": "list of BuildingTypes",
@@ -975,7 +975,7 @@ def resolve_default(rec, all_defaults, tree):
"DIR_N": "0",
"CALL_WAIT_CUSTOM": "3",
"MAX_PLAYERS": "8",
- "CLSID_TeleportLocomotion": "the Teleport locomotor",
+ "ClassID_TeleportLocomotion": "the Teleport locomotor",
"CELL_LEPTON_W": "256",
"4*CELL_LEPTON_W": "1024",
"9*CELL_LEPTON_W": "2304",
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4b335c072..b2c0e9a8b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -97,6 +97,6 @@ add_subdirectory(deploymentconfig)
add_subdirectory(tutorial)
add_subdirectory(utf8)
add_subdirectory(shapefacing)
-add_subdirectory(cstream)
add_subdirectory(zbufring)
add_subdirectory(priorityqueue)
+add_subdirectory(save)
diff --git a/tests/cstream/CMakeLists.txt b/tests/cstream/CMakeLists.txt
deleted file mode 100644
index c996e004c..000000000
--- a/tests/cstream/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-opents_add_test(CStreamContract
- NAME cstream
- SOURCES cstreamcontract.cpp
- ENGINE
- cstream.cpp
- isun_i.c
- DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX
- LIBRARIES lzo
- FLOAT
-)
diff --git a/tests/cstream/cstreamcontract.cpp b/tests/cstream/cstreamcontract.cpp
deleted file mode 100644
index c08ad63a0..000000000
--- a/tests/cstream/cstreamcontract.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*******************************************************************************
- * O P E N T S
- *******************************************************************************
- * SPDX-License-Identifier: GPL-3.0-or-later
- * Copyright 2026 OpenTS contributors
- *
- * See LICENSE.md for applicable additional terms and warranty disclaimers.
- ******************************************************************************/
-
-#include "cstream.h"
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-ULONG COMRefCount = 0;
-
-namespace {
-
-int Failures = 0;
-
-
-void Report(char const * name, bool ok)
-{
- std::printf("%-64s %s\n", name, ok ? "ok" : "FAILED");
- if (!ok) Failures++;
-}
-
-
-bool Create_Storage(IStreamPtr & storage)
-{
- IStream * stream = nullptr;
- if (FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream))) {
- return(false);
- }
- storage.Attach(stream, false);
- return(true);
-}
-
-
-bool Rewind(IStream * storage)
-{
- LARGE_INTEGER const start = {};
- return(SUCCEEDED(storage->Seek(start, STREAM_SEEK_SET, nullptr)));
-}
-
-
-std::vector Make_Source(ULONG size)
-{
- std::vector source(size);
- std::uint32_t seed = 123456789;
- for (unsigned char & value : source) {
- seed ^= seed << 13;
- seed ^= seed >> 17;
- seed ^= seed << 5;
- value = static_cast(seed & 15);
- }
- return(source);
-}
-
-
-void Test_Roundtrip(bool fragmented, ULONG tail)
-{
- std::vector const source = Make_Source(CStreamClass::BUFFER_SIZE + tail);
- IStreamPtr storage;
- bool ok = Create_Storage(storage);
- if (ok) {
- CStreamClass writer;
- ok = SUCCEEDED(writer.Link_Stream(storage));
- for (ULONG offset = 0; ok && offset < source.size();) {
- ULONG const count = std::min(static_cast(source.size()) - offset, fragmented ? 997UL : static_cast(source.size()));
- ULONG written = 0;
- ok = SUCCEEDED(writer.Write(source.data() + offset, count, &written)) && written == count;
- offset += count;
- }
- ok = SUCCEEDED(writer.Unlink_Stream(nullptr)) && ok;
- }
-
- std::array header = {};
- if (ok) {
- ULONG read = 0;
- ok = Rewind(storage) && SUCCEEDED(storage->Read(header.data(), sizeof(header), &read)) && read == sizeof(header);
- ok = ok && header[0] > CStreamClass::BUFFER_SIZE && header[0] <= CStreamClass::STREAM_BUFFER_SIZE;
- std::printf("First compressed block: %lu bytes\n", header[0]);
- }
-
- if (ok) {
- ok = Rewind(storage);
- CStreamClass reader;
- ok = SUCCEEDED(reader.Link_Stream(storage)) && ok;
- std::vector restored(source.size());
- for (ULONG offset = 0; ok && offset < restored.size();) {
- ULONG const count = std::min(static_cast(restored.size()) - offset, fragmented ? 613UL : static_cast(restored.size()));
- ULONG read = 0;
- ok = SUCCEEDED(reader.Read(restored.data() + offset, count, &read)) && read == count;
- offset += count;
- }
- ok = ok && restored == source;
-
- unsigned char extra = 0xA5;
- ULONG read = 123;
- ok = FAILED(reader.Read(&extra, sizeof(extra), &read)) && read == 0 && extra == 0xA5 && ok;
- }
-
- Report(fragmented ? "Fragmented writes and reads with a partial final block" : "Full expanded block and exact end of stream", ok);
-}
-
-
-void Test_Read_Bound(void)
-{
- IStreamPtr storage;
- bool ok = Create_Storage(storage);
- if (ok) {
- std::array const header = {CStreamClass::STREAM_BUFFER_SIZE + 1, CStreamClass::BUFFER_SIZE};
- unsigned char const payload = 0;
- ULONG written = 0;
- ok = SUCCEEDED(storage->Write(header.data(), sizeof(header), &written)) && written == sizeof(header);
- ok = SUCCEEDED(storage->Write(&payload, sizeof(payload), &written)) && written == sizeof(payload) && ok;
- ok = Rewind(storage) && ok;
-
- CStreamClass reader;
- ok = SUCCEEDED(reader.Link_Stream(storage)) && ok;
- unsigned char result = 0xA5;
- ULONG read = 123;
- ok = FAILED(reader.Read(&result, sizeof(result), &read)) && read == 0 && result == 0xA5 && ok;
-
- LARGE_INTEGER const offset = {};
- ULARGE_INTEGER position = {};
- ok = SUCCEEDED(storage->Seek(offset, STREAM_SEEK_CUR, &position)) && position.QuadPart == sizeof(header) && ok;
- }
- Report("Oversized compressed header rejected before reading payload", ok);
-}
-
-}
-
-
-int main(void)
-{
- Report("LZO initialization", lzo_init() == LZO_E_OK);
- Test_Roundtrip(false, 0);
- Test_Roundtrip(true, 137);
- Test_Read_Bound();
- return(Failures == 0 ? 0 : 1);
-}
diff --git a/tests/save/CMakeLists.txt b/tests/save/CMakeLists.txt
new file mode 100644
index 000000000..fd69fde2f
--- /dev/null
+++ b/tests/save/CMakeLists.txt
@@ -0,0 +1,14 @@
+# The harness drives the file a saved game is kept in: savefile.cpp's writer and reader over
+# the field table, the compressed content, and every refusal the reader makes. It links the
+# same vendored LZO library the engine does, and compiles crc.cpp for the checksum the
+# header and the content carry.
+
+opents_add_test(SaveTest
+ NAME save
+ SOURCES savetest.cpp
+ ENGINE
+ crc.cpp
+ savefile.cpp
+ DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX
+ LIBRARIES lzo
+)
diff --git a/tests/save/savetest.cpp b/tests/save/savetest.cpp
new file mode 100644
index 000000000..24bf41d0e
--- /dev/null
+++ b/tests/save/savetest.cpp
@@ -0,0 +1,485 @@
+// Exercises the file a saved game is kept in: the field table the load dialog lists from,
+// the compressed content block, and every way the reader refuses a file that is not a
+// whole, intact save of a version it knows.
+//
+// Every file it touches it creates itself, in a scratch directory named by the first
+// argument or the working directory, so it reads no game data and leaves nothing behind.
+
+#include "savefile.h"
+
+#include
+
+#include
+#include
+#include
+#include
+
+static int Failures = 0;
+static int Checks = 0;
+static std::string Scratch;
+
+
+static void Check(char const * name, bool condition)
+{
+ Checks++;
+ if (condition) return;
+ Failures++;
+ printf("FAIL %s\n", name);
+}
+
+
+static void Check_Result(char const * name, SaveFileClass::ResultType actual, SaveFileClass::ResultType expected)
+{
+ Checks++;
+ if (actual == expected) return;
+ Failures++;
+ printf("FAIL %s: got \"%s\", expected \"%s\"\n", name,
+ SaveFileClass::Result_Text(actual), SaveFileClass::Result_Text(expected));
+}
+
+
+static std::string Scratch_Path(char const * name)
+{
+ return(Scratch + "\\" + name);
+}
+
+
+static std::vector Noise(std::size_t length, unsigned int seed)
+{
+ std::vector data(length);
+ unsigned int state = seed * 2654435761u + 1u;
+ for (std::size_t index = 0; index < length; index++) {
+ state = state * 1103515245u + 12345u;
+ data[index] = (unsigned char)((state >> 16) & 0xFF);
+ }
+ return(data);
+}
+
+
+static std::vector Prose(std::size_t length)
+{
+ static char const text[] = "The quick brown fox jumps over the lazy dog. ";
+ std::vector data;
+ while (data.size() < length) {
+ data.push_back((unsigned char)text[data.size() % (sizeof(text) - 1)]);
+ }
+ return(data);
+}
+
+
+static std::vector Read_Whole_File(char const * path)
+{
+ std::vector data;
+ HANDLE const file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE) return(data);
+ DWORD const size = GetFileSize(file, nullptr);
+ if (size != INVALID_FILE_SIZE && size > 0) {
+ data.resize(size);
+ DWORD got = 0;
+ if (!ReadFile(file, data.data(), size, &got, nullptr) || got != size) data.clear();
+ }
+ CloseHandle(file);
+ return(data);
+}
+
+
+static bool Write_Whole_File(char const * path, std::vector const & data)
+{
+ HANDLE const file = CreateFileA(path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE) return(false);
+ DWORD written = 0;
+ bool ok = true;
+ if (!data.empty()) {
+ ok = WriteFile(file, data.data(), (DWORD)data.size(), &written, nullptr) && written == data.size();
+ }
+ CloseHandle(file);
+ return(ok);
+}
+
+
+static bool File_Exists(char const * path)
+{
+ HANDLE const file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE) return(false);
+ CloseHandle(file);
+ return(true);
+}
+
+
+enum {
+ FIELD_TITLE = 2,
+ FIELD_HOUSE = 3,
+ FIELD_VERSION = 16,
+ FIELD_WHEN = 13,
+ FIELD_MISSING = 77,
+};
+
+
+static void Fill(SaveFileClass & save, std::vector const & content)
+{
+ FILETIME when;
+ when.dwLowDateTime = 0x12345678u;
+ when.dwHighDateTime = 0x01D2C3B4u;
+
+ save.Set_String(FIELD_TITLE, "GDI 04: Eviction Notice");
+ save.Set_String(FIELD_HOUSE, "GDI");
+ save.Set_Int(FIELD_VERSION, 0x00010203);
+ save.Set_Time(FIELD_WHEN, when);
+ save.Content = content;
+}
+
+
+static void Check_Fields(char const * prefix, SaveFileClass const & save)
+{
+ char text[64];
+ int value = 0;
+ FILETIME when = {};
+
+ Check((std::string(prefix) + ": title present").c_str(), save.Get_String(FIELD_TITLE, text, sizeof(text)));
+ Check((std::string(prefix) + ": title text").c_str(), strcmp(text, "GDI 04: Eviction Notice") == 0);
+ Check((std::string(prefix) + ": house present").c_str(), save.Get_String(FIELD_HOUSE, text, sizeof(text)));
+ Check((std::string(prefix) + ": house text").c_str(), strcmp(text, "GDI") == 0);
+ Check((std::string(prefix) + ": version present").c_str(), save.Get_Int(FIELD_VERSION, &value));
+ Check((std::string(prefix) + ": version value").c_str(), value == 0x00010203);
+ Check((std::string(prefix) + ": time present").c_str(), save.Get_Time(FIELD_WHEN, &when));
+ Check((std::string(prefix) + ": time value").c_str(),
+ when.dwLowDateTime == 0x12345678u && when.dwHighDateTime == 0x01D2C3B4u);
+ Check((std::string(prefix) + ": a missing field is absent").c_str(), !save.Get_String(FIELD_MISSING, text, sizeof(text)));
+ Check((std::string(prefix) + ": a field is not found under another kind").c_str(), !save.Get_Int(FIELD_TITLE, &value));
+
+ Check((std::string(prefix) + ": a short buffer is clipped").c_str(),
+ save.Get_String(FIELD_TITLE, text, 4) && strcmp(text, "GDI") == 0);
+}
+
+
+static void Test_Round_Trip(void)
+{
+ std::string const path = Scratch_Path("ROUNDTRIP.SAV");
+ std::vector const content = Prose(300000);
+
+ SaveFileClass written;
+ Fill(written, content);
+ Check_Result("round trip: write", written.Write(path.c_str()), SaveFileClass::RESULT_OK);
+ Check("round trip: no temporary file is left behind", !File_Exists((path + ".tmp").c_str()));
+
+ std::vector const image = Read_Whole_File(path.c_str());
+ Check("round trip: the prose was compressed", !image.empty() && image.size() < content.size() / 4);
+
+ SaveFileClass read;
+ Check_Result("round trip: read", read.Read(path.c_str()), SaveFileClass::RESULT_OK);
+ Check_Fields("round trip", read);
+ Check("round trip: content reads back whole", read.Content == content);
+
+ SaveFileClass listed;
+ Check_Result("round trip: fields alone", listed.Read_Fields(path.c_str()), SaveFileClass::RESULT_OK);
+ Check_Fields("fields alone", listed);
+ Check("fields alone: no content is read", listed.Content.empty());
+}
+
+
+static void Test_Cuts(void)
+{
+ SaveFileClass save;
+ save.Set_String(FIELD_TITLE, "ab\xC3\xA9" "cd");
+
+ char text[8];
+ Check("cuts: a cut never splits a character", save.Get_String(FIELD_TITLE, text, 4) && strcmp(text, "ab") == 0);
+ Check("cuts: a cut after a character keeps it whole", save.Get_String(FIELD_TITLE, text, 5) && strcmp(text, "ab\xC3\xA9") == 0);
+ Check("cuts: a buffer that fits keeps everything", save.Get_String(FIELD_TITLE, text, 8) && strcmp(text, "ab\xC3\xA9" "cd") == 0);
+}
+
+
+static void Test_Incompressible(void)
+{
+ std::string const path = Scratch_Path("NOISE.SAV");
+ std::vector const content = Noise(70000, 7);
+
+ SaveFileClass written;
+ Fill(written, content);
+ Check_Result("noise: write", written.Write(path.c_str()), SaveFileClass::RESULT_OK);
+
+ std::vector const image = Read_Whole_File(path.c_str());
+ Check("noise: stored as it is when compression does not pay", image.size() >= content.size() + SaveFileClass::HEADER_SIZE);
+
+ SaveFileClass read;
+ Check_Result("noise: read", read.Read(path.c_str()), SaveFileClass::RESULT_OK);
+ Check("noise: content reads back whole", read.Content == content);
+}
+
+
+static void Test_Empty(void)
+{
+ std::string const path = Scratch_Path("EMPTY.SAV");
+
+ SaveFileClass written;
+ Check_Result("empty: write", written.Write(path.c_str()), SaveFileClass::RESULT_OK);
+
+ std::vector const image = Read_Whole_File(path.c_str());
+ Check("empty: a header alone", image.size() == SaveFileClass::HEADER_SIZE);
+
+ SaveFileClass read;
+ Check_Result("empty: read", read.Read(path.c_str()), SaveFileClass::RESULT_OK);
+ Check("empty: no content", read.Content.empty());
+ char text[8];
+ Check("empty: no fields", !read.Get_String(FIELD_TITLE, text, sizeof(text)));
+}
+
+
+static void Test_Overwrite(void)
+{
+ std::string const path = Scratch_Path("REPLACE.SAV");
+
+ SaveFileClass first;
+ Fill(first, Prose(5000));
+ first.Set_String(FIELD_TITLE, "the earlier save");
+ Check_Result("replace: first write", first.Write(path.c_str()), SaveFileClass::RESULT_OK);
+
+ Check("replace: a stale temporary is planted", Write_Whole_File((path + ".tmp").c_str(), Noise(100, 3)));
+
+ SaveFileClass second;
+ Fill(second, Noise(20000, 11));
+ second.Set_String(FIELD_TITLE, "the later save");
+ Check_Result("replace: second write", second.Write(path.c_str()), SaveFileClass::RESULT_OK);
+ Check("replace: the stale temporary is gone", !File_Exists((path + ".tmp").c_str()));
+
+ SaveFileClass read;
+ Check_Result("replace: read", read.Read(path.c_str()), SaveFileClass::RESULT_OK);
+ char text[64];
+ Check("replace: the later save is the one on disk",
+ read.Get_String(FIELD_TITLE, text, sizeof(text)) && strcmp(text, "the later save") == 0);
+ Check("replace: the later content is the one on disk", read.Content == second.Content);
+
+ SaveFileClass rewritten;
+ rewritten.Set_String(FIELD_TITLE, "overwritten field");
+ rewritten.Set_String(FIELD_TITLE, "final field");
+ Check_Result("replace: field rewrite", rewritten.Write(path.c_str()), SaveFileClass::RESULT_OK);
+ Check_Result("replace: field rewrite read", read.Read_Fields(path.c_str()), SaveFileClass::RESULT_OK);
+ Check("replace: a field set twice keeps the last value",
+ read.Get_String(FIELD_TITLE, text, sizeof(text)) && strcmp(text, "final field") == 0);
+}
+
+
+static void Put_U32(std::vector & image, std::size_t at, unsigned int value)
+{
+ image[at] = (unsigned char)(value & 0xFF);
+ image[at + 1] = (unsigned char)((value >> 8) & 0xFF);
+ image[at + 2] = (unsigned char)((value >> 16) & 0xFF);
+ image[at + 3] = (unsigned char)((value >> 24) & 0xFF);
+}
+
+
+static void Test_Limits(void)
+{
+ std::string const path = Scratch_Path("LIMITS.SAV");
+
+ SaveFileClass kept;
+ Fill(kept, Prose(3000));
+ kept.Set_String(FIELD_TITLE, "the save that stays");
+ Check_Result("limits: the save that stays", kept.Write(path.c_str()), SaveFileClass::RESULT_OK);
+
+ SaveFileClass wide;
+ Fill(wide, Prose(3000));
+ wide.Set_String(FIELD_TITLE, std::string(0x10001, 'x').c_str());
+ Check_Result("limits: a field beyond its limit is refused", wide.Write(path.c_str()), SaveFileClass::RESULT_TOO_LARGE);
+
+ SaveFileClass many;
+ Fill(many, Prose(3000));
+ for (int id = 100; id < 117; id++) {
+ many.Set_String(id, std::string(0x10000, 'y').c_str());
+ }
+ Check_Result("limits: a table beyond its limit is refused", many.Write(path.c_str()), SaveFileClass::RESULT_TOO_LARGE);
+
+ SaveFileClass huge;
+ huge.Content.resize(0x10000001);
+ Check_Result("limits: content beyond its limit is refused", huge.Write(path.c_str()), SaveFileClass::RESULT_TOO_LARGE);
+
+ Check("limits: no temporary is left behind", !File_Exists((path + ".tmp").c_str()));
+ SaveFileClass read;
+ Check_Result("limits: the earlier save still reads", read.Read(path.c_str()), SaveFileClass::RESULT_OK);
+ char text[64];
+ Check("limits: the earlier save is the one on disk",
+ read.Get_String(FIELD_TITLE, text, sizeof(text)) && strcmp(text, "the save that stays") == 0);
+}
+
+
+// Recomputes the header checksum after a test has changed a header byte on purpose.
+static void Reseal_Header(std::vector & image, unsigned int table)
+{
+ unsigned int crc = SaveFileClass::Checksum(image.data(), SaveFileClass::HEADER_SIZE - 4);
+ crc = SaveFileClass::Checksum(image.data() + SaveFileClass::HEADER_SIZE, table, crc);
+ Put_U32(image, 28, crc);
+}
+
+
+// Rebuilds a save image around a field table of the test's own making, with the content
+// kept and every checksum made good.
+static std::vector Forge_Table(std::vector const & image, unsigned int table,
+ std::vector const & newtable)
+{
+ std::vector forged(image.begin(), image.begin() + SaveFileClass::HEADER_SIZE);
+ forged.insert(forged.end(), newtable.begin(), newtable.end());
+ forged.insert(forged.end(), image.begin() + SaveFileClass::HEADER_SIZE + table, image.end());
+ Put_U32(forged, 8, (unsigned int)newtable.size());
+ Put_U32(forged, 12, SaveFileClass::HEADER_SIZE + (unsigned int)newtable.size());
+ Reseal_Header(forged, (unsigned int)newtable.size());
+ return(forged);
+}
+
+
+// Replaces the content of a save image with a compressed block of the test's own making,
+// declared as expanding to the length given, with every checksum made good.
+static std::vector Forge_Content(std::vector const & image, unsigned int table,
+ std::vector const & stored, unsigned int expands_to)
+{
+ std::vector forged(image.begin(), image.begin() + SaveFileClass::HEADER_SIZE + table);
+ forged.insert(forged.end(), stored.begin(), stored.end());
+ forged[6] |= 0x01;
+ Put_U32(forged, 12, SaveFileClass::HEADER_SIZE + table);
+ Put_U32(forged, 16, (unsigned int)stored.size());
+ Put_U32(forged, 20, expands_to);
+ Put_U32(forged, 24, SaveFileClass::Checksum(stored.data(), (unsigned int)stored.size()));
+ Reseal_Header(forged, table);
+ return(forged);
+}
+
+
+static void Test_Refusals(void)
+{
+ SaveFileClass read;
+
+ std::string const missing = Scratch_Path("MISSING.SAV");
+ Check_Result("refuse: a missing file", read.Read(missing.c_str()), SaveFileClass::RESULT_MISSING);
+ Check_Result("refuse: a missing file's fields", read.Read_Fields(missing.c_str()), SaveFileClass::RESULT_MISSING);
+
+ std::string const plain = Scratch_Path("PLAIN.SAV");
+ std::vector hello = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' };
+ Write_Whole_File(plain.c_str(), hello);
+ Check_Result("refuse: a file that is not a save", read.Read(plain.c_str()), SaveFileClass::RESULT_NOT_A_SAVE);
+ Check_Result("refuse: its fields", read.Read_Fields(plain.c_str()), SaveFileClass::RESULT_NOT_A_SAVE);
+
+ std::string const good = Scratch_Path("GOOD.SAV");
+ SaveFileClass written;
+ Fill(written, Prose(40000));
+ Check_Result("refuse: the reference save", written.Write(good.c_str()), SaveFileClass::RESULT_OK);
+ std::vector const image = Read_Whole_File(good.c_str());
+ Check("refuse: the reference save is readable", !image.empty());
+
+ std::string const damaged = Scratch_Path("DAMAGED.SAV");
+
+ unsigned int const table = (unsigned int)image[8] | ((unsigned int)image[9] << 8)
+ | ((unsigned int)image[10] << 16) | ((unsigned int)image[11] << 24);
+
+ std::vector