[openjk] 02/130: Add wrappers for writing saved games

Simon McVittie smcv at debian.org
Fri Oct 28 11:09:10 UTC 2016


This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to branch debian/master
in repository openjk.

commit 7d1b1e704fa5415b24f9bea8181c60d2a2f4d4a5
Author: bibendovsky <bibendovsky at NOSTROMO>
Date:   Mon Jun 20 17:06:28 2016 +0300

    Add wrappers for writing saved games
---
 code/cgame/FxScheduler.cpp           |   4 +-
 code/cgame/cg_main.cpp               |   4 +-
 code/game/G_Timer.cpp                |   6 +-
 code/game/Q3_Interface.cpp           |  20 +-
 code/game/Q3_Interface.h             |   2 +-
 code/game/g_main.cpp                 |   4 +-
 code/game/g_objectives.cpp           |   2 +-
 code/game/g_roff.cpp                 |   6 +-
 code/game/g_savegame.cpp             |  14 +-
 code/icarus/IcarusImplementation.cpp |   6 +-
 code/icarus/IcarusInterface.h        |   2 +-
 code/qcommon/cm_load.cpp             |   2 +-
 code/rd-vanilla/G2_misc.cpp          |  39 +-
 code/server/sv_savegame.cpp          |  38 +-
 codeJK2/cgame/cg_main.cpp            |   4 +-
 codeJK2/game/G_Timer.cpp             |   8 +-
 codeJK2/game/Q3_Interface.cpp        |   2 +-
 codeJK2/game/Q3_Registers.cpp        |  20 +-
 codeJK2/game/g_main.cpp              |   4 +-
 codeJK2/game/g_objectives.cpp        |   2 +-
 codeJK2/game/g_roff.cpp              |   6 +-
 codeJK2/game/g_savegame.cpp          |  77 +++-
 codeJK2/icarus/Instance.cpp          |  16 +-
 codeJK2/icarus/Sequence.cpp          |  26 +-
 codeJK2/icarus/Sequencer.cpp         |  18 +-
 codeJK2/icarus/TaskManager.cpp       |  42 +-
 codeJK2/icarus/interface.h           |   2 +-
 codemp/icarus/Q3_Interface.cpp       |   2 +-
 codemp/icarus/interface.h            |   2 +-
 shared/qcommon/ojk_sg_wrappers.h     | 840 ++++++++++++++++++++++++++++++++++-
 30 files changed, 1055 insertions(+), 165 deletions(-)

diff --git a/code/cgame/FxScheduler.cpp b/code/cgame/FxScheduler.cpp
index 020f29c..ae8800b 100644
--- a/code/cgame/FxScheduler.cpp
+++ b/code/cgame/FxScheduler.cpp
@@ -125,7 +125,7 @@ void CFxScheduler::LoadSave_Write()
 {
 	// bsave the data we need...
 	//
-	gi.AppendToSaveGame(INT_ID('F','X','L','E'), mLoopedEffectArray, sizeof(mLoopedEffectArray));
+	::sg_write_no_cast(::gi, INT_ID('F','X','L','E'), mLoopedEffectArray);
 	//
 	// then cope with the fact that the mID field in each struct of the array we've just saved will not
 	//	necessarily point at the same thing when reloading, so save out the actual fx filename strings they
@@ -157,7 +157,7 @@ void CFxScheduler::LoadSave_Write()
 
 		// write out this string...
 		//
-		gi.AppendToSaveGame(INT_ID('F','X','F','N'), sFX_Filename, sizeof(sFX_Filename));
+		::sg_write_no_cast(::gi, INT_ID('F','X','F','N'), sFX_Filename);
 	}
 }
 
diff --git a/code/cgame/cg_main.cpp b/code/cgame/cg_main.cpp
index 732c9c7..b9646d5 100644
--- a/code/cgame/cg_main.cpp
+++ b/code/cgame/cg_main.cpp
@@ -1905,8 +1905,8 @@ static void CG_GameStateReceived( void ) {
 
 void CG_WriteTheEvilCGHackStuff(void)
 {
-	gi.AppendToSaveGame(INT_ID('F','P','S','L'), &cg.forcepowerSelect, sizeof(cg.forcepowerSelect));
-	gi.AppendToSaveGame(INT_ID('I','V','S','L'), &cg.inventorySelect,  sizeof(cg.inventorySelect));
+	::sg_write<int32_t>(::gi, INT_ID('F','P','S','L'), ::cg.forcepowerSelect);
+	::sg_write<int32_t>(::gi, INT_ID('I','V','S','L'), ::cg.inventorySelect);
 
 }
 void CG_ReadTheEvilCGHackStuff(void)
diff --git a/code/game/G_Timer.cpp b/code/game/G_Timer.cpp
index 6591847..0835dde 100644
--- a/code/game/G_Timer.cpp
+++ b/code/game/G_Timer.cpp
@@ -171,7 +171,7 @@ void TIMER_Save( void )
 		}
 
 		//Write out the timer information
-		gi.AppendToSaveGame(INT_ID('T','I','M','E'), (void *)&numTimers, sizeof(numTimers));
+		::sg_write<uint8_t>(::gi, INT_ID('T','I','M','E'), numTimers);
 
 		gtimer_t *p = g_timers[j];
 		assert ((numTimers && p) || (!numTimers && !p));
@@ -185,10 +185,10 @@ void TIMER_Save( void )
 			assert( length < 1024 );//This will cause problems when loading the timer if longer
 
 			//Write out the id string
-			gi.AppendToSaveGame(INT_ID('T','M','I','D'), (void *) timerID, length);
+			::sg_write_no_cast(::gi, INT_ID('T','M','I','D'), timerID, length);
 
 			//Write out the timer data
-			gi.AppendToSaveGame(INT_ID('T','D','T','A'), (void *) &time, sizeof( time ) );
+			::sg_write<int32_t>(::gi, INT_ID('T','D','T','A'), time);
 			p = p->next;
 		}
 	}
diff --git a/code/game/Q3_Interface.cpp b/code/game/Q3_Interface.cpp
index 51b3e79..bb4e71d 100644
--- a/code/game/Q3_Interface.cpp
+++ b/code/game/Q3_Interface.cpp
@@ -7206,7 +7206,7 @@ VariableSaveFloats
 void CQuake3GameInterface::VariableSaveFloats( varFloat_m &fmap )
 {
 	int numFloats = fmap.size();
-	gi.AppendToSaveGame( INT_ID('F','V','A','R'), &numFloats, sizeof( numFloats ) );
+	::sg_write<int32_t>(::gi, INT_ID('F','V','A','R'), numFloats);
 
 	varFloat_m::iterator	vfi;
 	STL_ITERATE( vfi, fmap )
@@ -7215,11 +7215,11 @@ void CQuake3GameInterface::VariableSaveFloats( varFloat_m &fmap )
 		int	idSize = strlen( ((*vfi).first).c_str() );
 
 		//Save out the real data
-		gi.AppendToSaveGame( INT_ID('F','I','D','L'), &idSize, sizeof( idSize ) );
-		gi.AppendToSaveGame( INT_ID('F','I','D','S'), (void *) ((*vfi).first).c_str(), idSize );
+		::sg_write<int32_t>(::gi, INT_ID('F','I','D','L'), idSize);
+		::sg_write_no_cast(::gi, INT_ID('F','I','D','S'), ((*vfi).first).c_str(), idSize);
 
 		//Save out the float value
-		gi.AppendToSaveGame( INT_ID('F','V','A','L'), &((*vfi).second), sizeof( float ) );
+		::sg_write_no_cast(::gi, INT_ID('F','V','A','L'), (*vfi).second);
 	}
 }
 
@@ -7232,7 +7232,7 @@ VariableSaveStrings
 void CQuake3GameInterface::VariableSaveStrings( varString_m &smap )
 {
 	int numStrings = smap.size();
-	gi.AppendToSaveGame( INT_ID('S','V','A','R'), &numStrings, sizeof( numStrings ) );
+	::sg_write<int32_t>(::gi, INT_ID('S','V','A','R'), numStrings);
 
 	varString_m::iterator	vsi;
 	STL_ITERATE( vsi, smap )
@@ -7241,14 +7241,14 @@ void CQuake3GameInterface::VariableSaveStrings( varString_m &smap )
 		int	idSize = strlen( ((*vsi).first).c_str() );
 
 		//Save out the real data
-		gi.AppendToSaveGame( INT_ID('S','I','D','L'), &idSize, sizeof( idSize ) );
-		gi.AppendToSaveGame( INT_ID('S','I','D','S'), (void *) ((*vsi).first).c_str(), idSize );
+		::sg_write<int32_t>(::gi, INT_ID('S','I','D','L'), idSize);
+		::sg_write_no_cast(::gi, INT_ID('S','I','D','S'), ((*vsi).first).c_str(), idSize);
 
 		//Save out the string value
 		idSize = strlen( ((*vsi).second).c_str() );
 
-		gi.AppendToSaveGame( INT_ID('S','V','S','Z'), &idSize, sizeof( idSize ) );
-		gi.AppendToSaveGame( INT_ID('S','V','A','L'), (void *) ((*vsi).second).c_str(), idSize );
+		::sg_write<int32_t>(::gi, INT_ID('S','V','S','Z'), idSize);
+		::sg_write_no_cast(::gi, INT_ID('S','V','A','L'), ((*vsi).second).c_str(), idSize);
 	}
 }
 
@@ -11100,7 +11100,7 @@ void	CQuake3GameInterface::FreeVariable( const char *name )
 }
 
 //Save / Load functions
-int		CQuake3GameInterface::WriteSaveData( unsigned int chid, void *data, int length )
+int		CQuake3GameInterface::WriteSaveData( unsigned int chid, const void *data, int length )
 {
 	return gi.AppendToSaveGame( chid, data, length );
 }
diff --git a/code/game/Q3_Interface.h b/code/game/Q3_Interface.h
index df15fda..56bcb05 100644
--- a/code/game/Q3_Interface.h
+++ b/code/game/Q3_Interface.h
@@ -697,7 +697,7 @@ public:
 	void	FreeVariable( const char *name );
 
 	//Save / Load functions
-	int		WriteSaveData( unsigned int chid, void *data, int length );
+	int		WriteSaveData( unsigned int chid, const void *data, int length );
 	int		ReadSaveData( unsigned int chid, void *address, int length, void **addressptr = NULL );
 	int		LinkGame( int entID, int icarusID );
 
diff --git a/code/game/g_main.cpp b/code/game/g_main.cpp
index 6ddf5c4..8b4154f 100644
--- a/code/game/g_main.cpp
+++ b/code/game/g_main.cpp
@@ -105,7 +105,7 @@ qboolean PInUse(unsigned int entNum)
 
 void WriteInUseBits(void)
 {
-	gi.AppendToSaveGame(INT_ID('I','N','U','S'), &g_entityInUseBits, sizeof(g_entityInUseBits) );
+	::sg_write<uint32_t>(::gi, INT_ID('I','N','U','S'), ::g_entityInUseBits);
 }
 
 void ReadInUseBits(void)
@@ -2108,7 +2108,7 @@ extern qboolean player_locked;
 
 void G_LoadSave_WriteMiscData(void)
 {
-	gi.AppendToSaveGame(INT_ID('L','C','K','D'), &player_locked, sizeof(player_locked));
+	::sg_write<int32_t>(::gi, INT_ID('L','C','K','D'), player_locked);
 }
 
 
diff --git a/code/game/g_objectives.cpp b/code/game/g_objectives.cpp
index 57d13fd..9805cbc 100644
--- a/code/game/g_objectives.cpp
+++ b/code/game/g_objectives.cpp
@@ -59,7 +59,7 @@ OBJ_SaveMissionObjectives
 */
 void OBJ_SaveMissionObjectives( gclient_t *client )
 {
-	gi.AppendToSaveGame(INT_ID('O','B','J','T'), client->sess.mission_objectives, sizeof(client->sess.mission_objectives));
+	::sg_write_no_cast(::gi, INT_ID('O','B','J','T'), client->sess.mission_objectives);
 }
 
 
diff --git a/code/game/g_roff.cpp b/code/game/g_roff.cpp
index 01c671c..588dce0 100644
--- a/code/game/g_roff.cpp
+++ b/code/game/g_roff.cpp
@@ -650,15 +650,15 @@ void G_SaveCachedRoffs()
 	int i, len;
 
 	// Write out the number of cached ROFFs
-	gi.AppendToSaveGame( INT_ID('R','O','F','F'), (void *)&num_roffs, sizeof(num_roffs) );
+	::sg_write<int32_t>(::gi, INT_ID('R','O','F','F'), num_roffs);
 
 	// Now dump out the cached ROFF file names in order so they can be loaded on the other end
 	for ( i = 0; i < num_roffs; i++ )
 	{
 		// Dump out the string length to make things a bit easier on the other end...heh heh.
 		len = strlen( roffs[i].fileName ) + 1;
-		gi.AppendToSaveGame( INT_ID('S','L','E','N'), (void *)&len, sizeof(len) );
-		gi.AppendToSaveGame( INT_ID('R','S','T','R'), (void *)roffs[i].fileName, len );
+		::sg_write<int32_t>(::gi, INT_ID('S','L','E','N'), len);
+		::sg_write_no_cast(::gi, INT_ID('R','S','T','R'), roffs[i].fileName, len);
 	}
 }
 
diff --git a/code/game/g_savegame.cpp b/code/game/g_savegame.cpp
index f469f6b..ea612f9 100644
--- a/code/game/g_savegame.cpp
+++ b/code/game/g_savegame.cpp
@@ -517,14 +517,14 @@ static void EnumerateFields(const save_field_t *pFields, const byte *pbData, uns
 
 	// save out raw data...
 	//
-	gi.AppendToSaveGame(ulChid, pbData, iLen);
+	::sg_write_no_cast(::gi, ulChid, pbData, static_cast<int>(iLen));
 
 	// save out any associated strings..
 	//
 	std::list<sstring_t>::iterator it = strList->begin();
 	for (size_t i=0; i<strList->size(); i++, ++it)
 	{
-		gi.AppendToSaveGame(INT_ID('S','T','R','G'), (void *)(*it).c_str(), (*it).length() + 1);
+		::sg_write_no_cast(::gi, INT_ID('S','T','R','G'), (*it).c_str(), static_cast<int>((*it).length() + 1));
 	}
 
 	delete strList;
@@ -834,7 +834,7 @@ static void WriteGEntities(qboolean qbAutosave)
 		}
 	}
 
-	gi.AppendToSaveGame(INT_ID('N','M','E','D'), &iCount, sizeof(iCount));
+	::sg_write<int32_t>(::gi, INT_ID('N','M','E','D'), iCount);
 
 	for (i=0; i<(qbAutosave?1:globals.num_entities); i++)
 	{
@@ -842,7 +842,7 @@ static void WriteGEntities(qboolean qbAutosave)
 
 		if ( ent->inuse)
 		{
-			gi.AppendToSaveGame(INT_ID('E','D','N','M'), (void *)&i, sizeof(i));
+			::sg_write<int32_t>(::gi, INT_ID('E','D','N','M'), i);
 
 			qboolean qbLinked = ent->linked;
 			gi.unlinkentity( ent );
@@ -873,7 +873,7 @@ static void WriteGEntities(qboolean qbAutosave)
 
 			if (tempEnt.parms)
 			{
-				gi.AppendToSaveGame(INT_ID('P','A','R','M'), ent->parms, sizeof(*ent->parms));
+				::sg_write_no_cast(::gi, INT_ID('P','A','R','M'), *ent->parms);
 			}
 
 			if (tempEnt.m_pVehicle)
@@ -902,7 +902,7 @@ static void WriteGEntities(qboolean qbAutosave)
 		//	This saves time debugging, and makes things easier to track.
 		//
 		static int iBlah = 1234;
-		gi.AppendToSaveGame(INT_ID('I','C','O','K'), &iBlah, sizeof(iBlah));
+		::sg_write<int32_t>(::gi, INT_ID('I','C','O','K'), iBlah);
 	}
 	if (!qbAutosave )//really shouldn't need to write these bits at all, just restore them from the ents...
 	{
@@ -1188,7 +1188,7 @@ void WriteLevel(qboolean qbAutosave)
 	// put out an end-marker so that the load code can check everything was read in...
 	//
 	static int iDONE = 1234;
-	gi.AppendToSaveGame(INT_ID('D','O','N','E'), &iDONE, sizeof(iDONE));
+	::sg_write<int32_t>(::gi, INT_ID('D','O','N','E'), iDONE);
 }
 
 void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition)
diff --git a/code/icarus/IcarusImplementation.cpp b/code/icarus/IcarusImplementation.cpp
index b35870c..71b4394 100644
--- a/code/icarus/IcarusImplementation.cpp
+++ b/code/icarus/IcarusImplementation.cpp
@@ -540,7 +540,7 @@ int CIcarus::Save()
 
 	//Save out a ICARUS save block header with the ICARUS version
 	double	version = ICARUS_VERSION;
-	game->WriteSaveData( INT_ID('I','C','A','R'), &version, sizeof( version ) );
+	::sg_write_no_cast(game, INT_ID('I','C','A','R'), version);
 
 	//Save out the signals
 	if ( SaveSignals() == false )
@@ -564,7 +564,7 @@ int CIcarus::Save()
 	}
 
 	// Write out the buffer with all our collected data.
-	game->WriteSaveData( INT_ID('I','S','E','Q'), m_byBuffer, m_ulBufferCurPos );
+	::sg_write_no_cast(game, INT_ID('I','S','E','Q'), m_byBuffer, static_cast<int>(m_ulBufferCurPos));
 
 	// De-allocate the temporary buffer.
 	DestroyBuffer();
@@ -782,7 +782,7 @@ void CIcarus::BufferWrite( void *pSrcData, unsigned long ulNumBytesToWrite )
 	if ( MAX_BUFFER_SIZE - m_ulBufferCurPos < ulNumBytesToWrite )
 	{	// Write out the buffer with all our collected data so far...
 		IGameInterface::GetGame()->DebugPrint( IGameInterface::WL_ERROR, "BufferWrite: Out of buffer space, Flushing." );
-		IGameInterface::GetGame()->WriteSaveData( INT_ID('I','S','E','Q'), m_byBuffer, m_ulBufferCurPos );
+		::sg_write_no_cast(IGameInterface::GetGame(), INT_ID('I','S','E','Q'), m_byBuffer, static_cast<int>(m_ulBufferCurPos));
 		m_ulBufferCurPos = 0;	//reset buffer
 	}
 
diff --git a/code/icarus/IcarusInterface.h b/code/icarus/IcarusInterface.h
index baa6981..9ba27ec 100644
--- a/code/icarus/IcarusInterface.h
+++ b/code/icarus/IcarusInterface.h
@@ -144,7 +144,7 @@ public:
 
 	// Save / Load functions
 
-	virtual int		WriteSaveData( unsigned int chid, void *data, int length ) = 0;
+	virtual int		WriteSaveData( unsigned int chid, const void *data, int length ) = 0;
 	virtual int		ReadSaveData( unsigned int chid, void *address, int length, void **addressptr = NULL )  = 0;
 	virtual int		LinkGame( int gameID, int icarusID ) = 0;
 
diff --git a/code/qcommon/cm_load.cpp b/code/qcommon/cm_load.cpp
index f76f318..a1ff55f 100644
--- a/code/qcommon/cm_load.cpp
+++ b/code/qcommon/cm_load.cpp
@@ -1215,7 +1215,7 @@ int SG_Read(unsigned int chid, void *pvAddress, int iLength, void **ppvAddressPt
 
 void CM_WritePortalState ()
 {
-	SG_Append(INT_ID('P','R','T','S'), (void *)cmg.areaPortals, cmg.numAreas * cmg.numAreas * sizeof( *cmg.areaPortals ));
+	::sg_write<int32_t>(::SG_Append, INT_ID('P','R','T','S'), ::cmg.areaPortals, ::cmg.numAreas * ::cmg.numAreas);
 }
 
 /*
diff --git a/code/rd-vanilla/G2_misc.cpp b/code/rd-vanilla/G2_misc.cpp
index b9cf1ef..548fa18 100644
--- a/code/rd-vanilla/G2_misc.cpp
+++ b/code/rd-vanilla/G2_misc.cpp
@@ -1778,12 +1778,13 @@ void G2_SaveGhoul2Models(CGhoul2Info_v &ghoul2)
 	// is there anything to save?
 	if (!ghoul2.IsValid()||!ghoul2.size())
 	{
-		ri.SG_Append(INT_ID('G','H','L','2'),&pGhoul2Data, 4);	//write out a zero buffer
+        uint32_t empty_value = 0;
+		::sg_write_no_cast(::ri, INT_ID('G','H','L','2'), empty_value);	//write out a zero buffer
 		return;
 	}
 
 	// this one isn't a define since I couldn't work out how to figure it out at compile time
-	const int ghoul2BlockSize = (intptr_t)&ghoul2[0].BSAVE_END_FIELD - (intptr_t)&ghoul2[0].BSAVE_START_FIELD;
+	constexpr auto ghoul2BlockSize = static_cast<int>(sizeof(SgCGhoul2Info));
 
 	// add in count for number of ghoul2 models
 	iGhoul2Size += 4;
@@ -1809,51 +1810,75 @@ void G2_SaveGhoul2Models(CGhoul2Info_v &ghoul2)
 	char *tempBuffer = pGhoul2Data;
 
 	// save out how many ghoul2 models we have
-	*(int *)tempBuffer = ghoul2.size();
+	*(int32_t*)tempBuffer = static_cast<int32_t>(ghoul2.size());
 	tempBuffer +=4;
 
 	for (int i = 0; i<ghoul2.size();i++)
 	{
 		// first save out the ghoul2 details themselves
 //		OutputDebugString(va("G2_SaveGhoul2Models(): ghoul2[%d].mModelindex = %d\n",i,ghoul2[i].mModelindex));
+#if 0
 		memcpy(tempBuffer, &ghoul2[i].mModelindex, ghoul2BlockSize);
+#else
+        ::sg_export(
+            ghoul2[i],
+            *reinterpret_cast<SgCGhoul2Info*>(tempBuffer));
+#endif
 		tempBuffer += ghoul2BlockSize;
 
 		// save out how many surfaces we have
-		*(int*)tempBuffer = ghoul2[i].mSlist.size();
+		*(int32_t*)tempBuffer = static_cast<int32_t>(ghoul2[i].mSlist.size());
 		tempBuffer +=4;
 
 		// now save the all the surface list info
 		for (size_t x=0; x<ghoul2[i].mSlist.size(); x++)
 		{
+#if 0
 			memcpy(tempBuffer, &ghoul2[i].mSlist[x], SURFACE_SAVE_BLOCK_SIZE);
+#else
+            ::sg_export(
+                ghoul2[i].mSlist[x],
+                *reinterpret_cast<SgSurfaceInfo*>(tempBuffer));
+#endif
 			tempBuffer += SURFACE_SAVE_BLOCK_SIZE;
 		}
 
 		// save out how many bones we have
-		*(int*)tempBuffer = ghoul2[i].mBlist.size();
+		*(int32_t*)tempBuffer = static_cast<int32_t>(ghoul2[i].mBlist.size());
 		tempBuffer +=4;
 
 		// now save the all the bone list info
 		for (size_t x = 0; x<ghoul2[i].mBlist.size(); x++)
 		{
+#if 0
 			memcpy(tempBuffer, &ghoul2[i].mBlist[x], BONE_SAVE_BLOCK_SIZE);
+#else
+            ::sg_export(
+                ghoul2[i].mBlist[x],
+                *reinterpret_cast<SgBoneInfo*>(tempBuffer));
+#endif
 			tempBuffer += BONE_SAVE_BLOCK_SIZE;
 		}
 
 		// save out how many bolts we have
-		*(int*)tempBuffer = ghoul2[i].mBltlist.size();
+		*(int32_t*)tempBuffer = static_cast<int32_t>(ghoul2[i].mBltlist.size());
 		tempBuffer +=4;
 
 		// lastly save the all the bolt list info
 		for (size_t x = 0; x<ghoul2[i].mBltlist.size(); x++)
 		{
+#if 0
 			memcpy(tempBuffer, &ghoul2[i].mBltlist[x], BOLT_SAVE_BLOCK_SIZE);
+#else
+            ::sg_export(
+                ghoul2[i].mBltlist[x],
+                *reinterpret_cast<SgBoltInfo*>(tempBuffer));
+#endif
 			tempBuffer += BOLT_SAVE_BLOCK_SIZE;
 		}
 	}
 
-	ri.SG_Append(INT_ID('G','H','L','2'),pGhoul2Data, iGhoul2Size);
+	::sg_write_no_cast(::ri, INT_ID('G','H','L','2'), pGhoul2Data, iGhoul2Size);
 	R_Free(pGhoul2Data);
 }
 
diff --git a/code/server/sv_savegame.cpp b/code/server/sv_savegame.cpp
index 55614a0..99a3a06 100644
--- a/code/server/sv_savegame.cpp
+++ b/code/server/sv_savegame.cpp
@@ -178,7 +178,7 @@ static qboolean SG_Create( const char *psPathlessBaseName )
 	}
 
 	giSaveGameVersion = iSAVEGAME_VERSION;
-	SG_Append(INT_ID('_','V','E','R'), &giSaveGameVersion, sizeof(giSaveGameVersion));
+	::sg_write<int32_t>(::SG_Append, INT_ID('_','V','E','R'), ::giSaveGameVersion);
 
 	return qtrue;
 }
@@ -539,7 +539,7 @@ void SV_SaveGame_f(void)
 //---------------
 static void WriteGame(qboolean autosave)
 {
-	SG_Append(INT_ID('G','A','M','E'), &autosave, sizeof(autosave));
+	::sg_write<int32_t>(::SG_Append, INT_ID('G','A','M','E'), autosave);
 
 	if (autosave)
 	{
@@ -554,25 +554,25 @@ static void WriteGame(qboolean autosave)
 		//
 		memset(s,0,sizeof(s));
 		Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
-		SG_Append(INT_ID('C','V','S','V'), &s, sizeof(s));
+		::sg_write_no_cast(::SG_Append, INT_ID('C','V','S','V'), s);
 
 		// write ammo...
 		//
 		memset(s,0,sizeof(s));
 		Cvar_VariableStringBuffer( "playerammo", s, sizeof(s) );
-		SG_Append(INT_ID('A','M','M','O'), &s, sizeof(s));
+		::sg_write_no_cast(::SG_Append, INT_ID('A','M','M','O'), s);
 
 		// write inventory...
 		//
 		memset(s,0,sizeof(s));
 		Cvar_VariableStringBuffer( "playerinv", s, sizeof(s) );
-		SG_Append(INT_ID('I','V','T','Y'), &s, sizeof(s));
+		::sg_write_no_cast(::SG_Append, INT_ID('I','V','T','Y'), s);
 
 		// the new JK2 stuff - force powers, etc...
 		//
 		memset(s,0,sizeof(s));
 		Cvar_VariableStringBuffer( "playerfplvl", s, sizeof(s) );
-		SG_Append(INT_ID('F','P','L','V'), &s, sizeof(s));
+		::sg_write_no_cast(::SG_Append, INT_ID('F','P','L','V'), s);
 	}
 }
 
@@ -643,7 +643,7 @@ void SG_WriteCvars(void)
 
 	// store count...
 	//
-	SG_Append(INT_ID('C','V','C','N'), &iCount, sizeof(iCount));
+	::sg_write<int32_t>(::SG_Append, INT_ID('C','V','C','N'), iCount);
 
 	// write 'em...
 	//
@@ -657,8 +657,8 @@ void SG_WriteCvars(void)
 		{
 			continue;
 		}
-		SG_Append(INT_ID('C','V','A','R'), var->name,   strlen(var->name) + 1);
-		SG_Append(INT_ID('V','A','L','U'), var->string, strlen(var->string) + 1);
+		::sg_write_no_cast(::SG_Append, INT_ID('C','V','A','R'), var->name, static_cast<int>(strlen(var->name) + 1));
+		::sg_write_no_cast(::SG_Append, INT_ID('V','A','L','U'), var->string, static_cast<int>(strlen(var->string) + 1));
 	}
 }
 
@@ -700,7 +700,7 @@ void SG_WriteServerConfigStrings( void )
 		}
 	}
 
-	SG_Append(INT_ID('C','S','C','N'), &iCount, sizeof(iCount));
+	::sg_write<int32_t>(::SG_Append, INT_ID('C','S','C','N'), iCount);
 
 	// now write 'em...
 	//
@@ -710,8 +710,8 @@ void SG_WriteServerConfigStrings( void )
 		{
 			if (sv.configstrings[i]	&& strlen(sv.configstrings[i]))
 			{
-				SG_Append(INT_ID('C','S','I','N'), &i, sizeof(i));
-				SG_Append(INT_ID('C','S','D','A'), sv.configstrings[i], strlen(sv.configstrings[i])+1);
+				::sg_write<int32_t>(::SG_Append, INT_ID('C','S','I','N'), i);
+				::sg_write_no_cast(::SG_Append, INT_ID('C','S','D','A'), ::sv.configstrings[i], static_cast<int>(strlen(::sv.configstrings[i])+1));
 			}
 		}
 	}
@@ -775,11 +775,11 @@ static void SG_WriteComment(qboolean qbAutosave, const char *psMapName)
 		Q_strncpyz(sComment,saveGameComment, sizeof(sComment));
 	}
 
-	SG_Append(INT_ID('C','O','M','M'), sComment, sizeof(sComment));
+	::sg_write_no_cast(::SG_Append, INT_ID('C','O','M','M'), sComment);
 
 	// Add Date/Time/Map stamp
 	unsigned int timestamp = SG_UnixTimestamp (time (NULL));
-	SG_Append(INT_ID('C','M','T','M'), &timestamp, sizeof (timestamp));
+	::sg_write<uint32_t>(::SG_Append, INT_ID('C','M','T','M'), timestamp);
 
 	Com_DPrintf("Saving: current (%s)\n", sComment);
 }
@@ -975,8 +975,8 @@ static void SG_WriteScreenshot(qboolean qbAutosave, const char *psMapName)
 	iJPGDataSize = re.SaveJPGToBuffer(pJPGData, bufSize, JPEG_IMAGE_QUALITY, SG_SCR_WIDTH, SG_SCR_HEIGHT, pbRawScreenShot, 0 );
 	if ( qbAutosave )
 		delete[] byBlank;
-	SG_Append(INT_ID('S','H','L','N'), &iJPGDataSize, sizeof(iJPGDataSize));
-	SG_Append(INT_ID('S','H','O','T'), pJPGData, iJPGDataSize);
+	::sg_write<uint32_t>(::SG_Append, INT_ID('S','H','L','N'), iJPGDataSize);
+	::sg_write_no_cast(::SG_Append, INT_ID('S','H','O','T'), pJPGData, static_cast<int>(iJPGDataSize));
 	Z_Free(pJPGData);
 	SCR_TempRawImage_CleanUp();
 }
@@ -1056,7 +1056,7 @@ qboolean SG_WriteSavegame(const char *psPathlessBaseName, qboolean qbAutosave)
 #ifdef JK2_MODE
 	SG_WriteScreenshot(qbAutosave, sMapCmd);
 #endif
-	SG_Append(INT_ID('M','P','C','M'), sMapCmd, sizeof(sMapCmd));
+	::sg_write_no_cast(::SG_Append, INT_ID('M','P','C','M'), sMapCmd);
 	SG_WriteCvars();
 
 	WriteGame (qbAutosave);
@@ -1065,8 +1065,8 @@ qboolean SG_WriteSavegame(const char *psPathlessBaseName, qboolean qbAutosave)
 	//
 	if (!qbAutosave)
 	{
-		SG_Append(INT_ID('T','I','M','E'), (void *)&sv.time, sizeof(sv.time));
-		SG_Append(INT_ID('T','I','M','R'), (void *)&sv.timeResidual, sizeof(sv.timeResidual));
+		::sg_write<int32_t>(::SG_Append, INT_ID('T','I','M','E'), ::sv.time);
+		::sg_write<int32_t>(::SG_Append, INT_ID('T','I','M','R'), ::sv.timeResidual);
 		CM_WritePortalState();
 		SG_WriteServerConfigStrings();
 	}
diff --git a/codeJK2/cgame/cg_main.cpp b/codeJK2/cgame/cg_main.cpp
index 15f1ebc..50f1840 100644
--- a/codeJK2/cgame/cg_main.cpp
+++ b/codeJK2/cgame/cg_main.cpp
@@ -1638,8 +1638,8 @@ Ghoul2 Insert End
 
 void CG_WriteTheEvilCGHackStuff(void)
 {
-	gi.AppendToSaveGame(INT_ID('F','P','S','L'), &cg.forcepowerSelect, sizeof(cg.forcepowerSelect));
-	gi.AppendToSaveGame(INT_ID('I','V','S','L'), &cg.inventorySelect,  sizeof(cg.inventorySelect));
+	::sg_write<int32_t>(::gi, INT_ID('F','P','S','L'), ::cg.forcepowerSelect);
+	::sg_write<int32_t>(::gi, INT_ID('I','V','S','L'), ::cg.inventorySelect);
 
 }
 void CG_ReadTheEvilCGHackStuff(void)
diff --git a/codeJK2/game/G_Timer.cpp b/codeJK2/game/G_Timer.cpp
index ef7f2d4..ab1ed3c 100644
--- a/codeJK2/game/G_Timer.cpp
+++ b/codeJK2/game/G_Timer.cpp
@@ -177,7 +177,7 @@ void TIMER_Save( void )
 		}
 
 		//Write out the timer information
-		gi.AppendToSaveGame(INT_ID('T','I','M','E'), (void *)&numTimers, sizeof(numTimers));
+		::sg_write<int32_t>(::gi, INT_ID('T','I','M','E'), numTimers);
 	
 		gtimer_t *p = g_timers[j];
 		assert ((numTimers && p) || (!numTimers && !p));
@@ -191,11 +191,11 @@ void TIMER_Save( void )
 			assert( length < 1024 );//This will cause problems when loading the timer if longer
 
 			//Write out the string size and data
-			gi.AppendToSaveGame(INT_ID('T','S','L','N'), (void*)&length, sizeof(length));
-			gi.AppendToSaveGame(INT_ID('T','S','N','M'), (void*)timerID, length);
+			::sg_write<int32_t>(::gi, INT_ID('T','S','L','N'), length);
+			::sg_write_no_cast(::gi, INT_ID('T','S','N','M'), timerID, length);
 
 			//Write out the timer data
-			gi.AppendToSaveGame(INT_ID('T','D','T','A'), (void *) &time, sizeof( time ) );
+			::sg_write<int32_t>(::gi, INT_ID('T','D','T','A'), time);
 			p = p->next;
 		}
 	}
diff --git a/codeJK2/game/Q3_Interface.cpp b/codeJK2/game/Q3_Interface.cpp
index 95415ee..bd34699 100644
--- a/codeJK2/game/Q3_Interface.cpp
+++ b/codeJK2/game/Q3_Interface.cpp
@@ -9262,7 +9262,7 @@ void Interface_Init( interface_export_t *pe )
 	pe->I_FreeVariable			=	Q3_FreeVariable;
 
 	//Save / Load functions
-	pe->I_WriteSaveData			=	(int(*)(unsigned int, void *, int))gi.AppendToSaveGame;
+	pe->I_WriteSaveData			=	gi.AppendToSaveGame;
 	pe->I_ReadSaveData			=	gi.ReadFromSaveGame;
 	pe->I_LinkEntity			=	ICARUS_LinkEntity;
 
diff --git a/codeJK2/game/Q3_Registers.cpp b/codeJK2/game/Q3_Registers.cpp
index 3108138..2264971 100644
--- a/codeJK2/game/Q3_Registers.cpp
+++ b/codeJK2/game/Q3_Registers.cpp
@@ -288,7 +288,7 @@ Q3_VariableSaveFloats
 void Q3_VariableSaveFloats( varFloat_m &fmap )
 {
 	int numFloats = fmap.size();
-	gi.AppendToSaveGame( INT_ID('F','V','A','R'), &numFloats, sizeof( numFloats ) );
+	::sg_write<int32_t>(::gi, INT_ID('F','V','A','R'), numFloats);
 
 	varFloat_m::iterator	vfi;
 	STL_ITERATE( vfi, fmap )
@@ -297,11 +297,11 @@ void Q3_VariableSaveFloats( varFloat_m &fmap )
 		int	idSize = strlen( ((*vfi).first).c_str() );
 		
 		//Save out the real data
-		gi.AppendToSaveGame( INT_ID('F','I','D','L'), &idSize, sizeof( idSize ) );
-		gi.AppendToSaveGame( INT_ID('F','I','D','S'), (void *) ((*vfi).first).c_str(), idSize );
+		::sg_write<int32_t>(::gi, INT_ID('F','I','D','L'), idSize);
+		::sg_write_no_cast(::gi, INT_ID('F','I','D','S'), ((*vfi).first).c_str(), idSize);
 
 		//Save out the float value
-		gi.AppendToSaveGame( INT_ID('F','V','A','L'), &((*vfi).second), sizeof( float ) );
+		::sg_write_no_cast(::gi, INT_ID('F','V','A','L'), (*vfi).second);
 	}
 }
 
@@ -314,7 +314,7 @@ Q3_VariableSaveStrings
 void Q3_VariableSaveStrings( varString_m &smap )
 {
 	int numStrings = smap.size();
-	gi.AppendToSaveGame( INT_ID('S','V','A','R'), &numStrings, sizeof( numStrings ) );
+	::sg_write<int32_t>(::gi, INT_ID('S','V','A','R'), numStrings);
 
 	varString_m::iterator	vsi;
 	STL_ITERATE( vsi, smap )
@@ -323,14 +323,14 @@ void Q3_VariableSaveStrings( varString_m &smap )
 		int	idSize = strlen( ((*vsi).first).c_str() );
 		
 		//Save out the real data
-		gi.AppendToSaveGame( INT_ID('S','I','D','L'), &idSize, sizeof( idSize ) );
-		gi.AppendToSaveGame( INT_ID('S','I','D','S'), (void *) ((*vsi).first).c_str(), idSize );
+		::sg_write<int32_t>(::gi, INT_ID('S','I','D','L'), idSize);
+		::sg_write_no_cast(::gi, INT_ID('S','I','D','S'), ((*vsi).first).c_str(), idSize);
 
 		//Save out the string value
 		idSize = strlen( ((*vsi).second).c_str() );
 
-		gi.AppendToSaveGame( INT_ID('S','V','S','Z'), &idSize, sizeof( idSize ) );
-		gi.AppendToSaveGame( INT_ID('S','V','A','L'), (void *) ((*vsi).second).c_str(), idSize );
+		::sg_write<int32_t>(::gi, INT_ID('S','V','S','Z'), idSize);
+		::sg_write_no_cast(::gi, INT_ID('S','V','A','L'), ((*vsi).second).c_str(), idSize);
 	}
 }
 
@@ -372,7 +372,7 @@ void Q3_VariableLoadFloats( varFloat_m &fmap )
 
 		float	val;
 
-		::sg_read<float>(::gi, INT_ID('F','V','A','L'), val);
+		::sg_read_no_cast(::gi, INT_ID('F','V','A','L'), val);
 
 		Q3_DeclareVariable( TK_FLOAT, (const char *) &tempBuffer );
 		Q3_SetFloatVariable( (const char *) &tempBuffer, val );
diff --git a/codeJK2/game/g_main.cpp b/codeJK2/game/g_main.cpp
index 01e728b..17c1a17 100644
--- a/codeJK2/game/g_main.cpp
+++ b/codeJK2/game/g_main.cpp
@@ -89,7 +89,7 @@ qboolean PInUse2(gentity_t *ent)
 
 void WriteInUseBits(void)
 {
-	gi.AppendToSaveGame(INT_ID('I','N','U','S'), &g_entityInUseBits, sizeof(g_entityInUseBits) );
+	::sg_write<uint32_t>(::gi, INT_ID('I','N','U','S'), ::g_entityInUseBits);
 }
 
 void ReadInUseBits(void)
@@ -1477,7 +1477,7 @@ extern qboolean player_locked;
 
 void G_LoadSave_WriteMiscData(void)
 {
-	gi.AppendToSaveGame(INT_ID('L','C','K','D'), &player_locked, sizeof(player_locked));
+	::sg_write<int32_t>(::gi, INT_ID('L','C','K','D'), player_locked);
 }
 
 
diff --git a/codeJK2/game/g_objectives.cpp b/codeJK2/game/g_objectives.cpp
index b78ca13..e66a339 100644
--- a/codeJK2/game/g_objectives.cpp
+++ b/codeJK2/game/g_objectives.cpp
@@ -61,7 +61,7 @@ OBJ_SaveMissionObjectives
 */
 void OBJ_SaveMissionObjectives( gclient_t *client )
 {
-	gi.AppendToSaveGame(INT_ID('O','B','J','T'), client->sess.mission_objectives, sizeof(client->sess.mission_objectives));
+	::sg_write_no_cast(::gi, INT_ID('O','B','J','T'), client->sess.mission_objectives);
 }
 
 
diff --git a/codeJK2/game/g_roff.cpp b/codeJK2/game/g_roff.cpp
index e0d3d10..eb7ea81 100644
--- a/codeJK2/game/g_roff.cpp
+++ b/codeJK2/game/g_roff.cpp
@@ -625,15 +625,15 @@ void G_SaveCachedRoffs()
 	int i, len;
 
 	// Write out the number of cached ROFFs
-	gi.AppendToSaveGame( INT_ID('R','O','F','F'), (void *)&num_roffs, sizeof(num_roffs) );
+	::sg_write<int32_t>(::gi, INT_ID('R','O','F','F'), num_roffs);
 
 	// Now dump out the cached ROFF file names in order so they can be loaded on the other end
 	for ( i = 0; i < num_roffs; i++ )
 	{
 		// Dump out the string length to make things a bit easier on the other end...heh heh.
 		len = strlen( roffs[i].fileName ) + 1;
-		gi.AppendToSaveGame( INT_ID('S','L','E','N'), (void *)&len, sizeof(len) );
-		gi.AppendToSaveGame( INT_ID('R','S','T','R'), (void *)(roffs[i].fileName), len );
+		::sg_write<int32_t>(::gi, INT_ID('S','L','E','N'), len);
+		::sg_write_no_cast(::gi, INT_ID('R','S','T','R'), roffs[i].fileName, len);
 	}
 }
 
diff --git a/codeJK2/game/g_savegame.cpp b/codeJK2/game/g_savegame.cpp
index cfd84ce..bc3028c 100644
--- a/codeJK2/game/g_savegame.cpp
+++ b/codeJK2/game/g_savegame.cpp
@@ -338,7 +338,7 @@ void EnumerateField(const field_t *pField, byte *pbBase)
 	switch (pField->eFieldType)
 	{
 	case F_STRING:
-		*(int *)pv = GetStringNum(*(char **)pv);
+		*(intptr_t *)pv = GetStringNum(*(char **)pv);
 		break;
 
 	case F_GENTITY:
@@ -346,7 +346,7 @@ void EnumerateField(const field_t *pField, byte *pbBase)
 		break;
 
 	case F_GROUP:
-		*(int *)pv = GetGroupNumber(*(AIGroupInfo_t **)pv);
+		*(intptr_t *)pv = GetGroupNumber(*(AIGroupInfo_t **)pv);
 		break;
 
 	case F_GCLIENT:
@@ -373,7 +373,7 @@ void EnumerateField(const field_t *pField, byte *pbBase)
 		break;
 
 	case F_ITEM:
-		*(int *)pv = GetGItemNum(*(gitem_t **)pv);
+		*(intptr_t *)pv = GetGItemNum(*(gitem_t **)pv);
 		break;
 
 	case F_BEHAVIORSET:
@@ -382,7 +382,7 @@ void EnumerateField(const field_t *pField, byte *pbBase)
 			for (int i=0; i<NUM_BSETS; i++)
 			{
 				pv = &p[i];	// since you can't ++ a void ptr
-				*(int *)pv = GetStringNum(*(char **)pv);
+				*(intptr_t *)pv = GetStringNum(*(char **)pv);
 			}
 		}
 		break;
@@ -441,6 +441,7 @@ void EnumerateField(const field_t *pField, byte *pbBase)
 	}
 }
 
+#if 0
 static void EnumerateFields(const field_t *pFields, byte *pbData, unsigned int ulChid, size_t iLen)
 {
 	strList = new std::list<sstring_t>;
@@ -458,19 +459,63 @@ static void EnumerateFields(const field_t *pFields, byte *pbData, unsigned int u
 
 	// save out raw data...
 	//
-	gi.AppendToSaveGame(ulChid, pbData, iLen);
+	::sg_write_no_cast(::gi, ulChid, pbData, static_cast<int>(iLen));
 
 	// save out any associated strings..
 	//
 	for (std::list<sstring_t>::iterator it = strList->begin(); it != strList->end(); ++it)
 	{
-		gi.AppendToSaveGame(INT_ID('S','T','R','G'), (void*)it->c_str(), it->length()+1);
+		::sg_write_no_cast(::gi, INT_ID('S','T','R','G'), it->c_str(), static_cast<int>(it->length()+1));
 	}
 
 	delete strList;
 	strList = NULL;
 }
+#else
+template<typename T>
+static void EnumerateFields(const field_t *pFields, T* src_instance, unsigned int ulChid, size_t iLen)
+{
+	strList = new std::list<sstring_t>;
+
+    auto pbData = reinterpret_cast<byte*>(src_instance);
+
+	// enumerate all the fields...
+	//
+	if (pFields)
+	{
+		for (const field_t *pField = pFields; pField->psName; pField++)
+		{
+			assert(pField->iOffset < iLen);
+			EnumerateField(pField, pbData);
+		}
+	}
+
+	// save out raw data...
+	//
+    using SgType = typename T::SgType;
+
+    constexpr auto dst_size = static_cast<int>(sizeof(SgType));
+
+    auto& dst_buffer = ::sg_get_buffer(
+        dst_size);
+
+    auto dst_instance = reinterpret_cast<SgType*>(dst_buffer.data());
 
+    ::sg_export(*src_instance, *dst_instance);
+
+	::sg_write_no_cast(::gi, ulChid, dst_buffer.data(), dst_size);
+
+	// save out any associated strings..
+	//
+	for (std::list<sstring_t>::iterator it = strList->begin(); it != strList->end(); ++it)
+	{
+		::sg_write_no_cast(::gi, INT_ID('S','T','R','G'), it->c_str(), static_cast<int>(it->length()+1));
+	}
+
+	delete strList;
+	strList = NULL;
+}
+#endif
 
 static void EvaluateField(const field_t *pField, byte *pbBase, byte *pbOriginalRefData/* may be NULL*/)
 {
@@ -630,7 +675,7 @@ static void WriteLevelLocals ()
 	level_locals_t *temp = (level_locals_t *)gi.Malloc(sizeof(level_locals_t), TAG_TEMP_WORKSPACE, qfalse);
 	*temp = level;	// copy out all data into a temp space
 
-	EnumerateFields(savefields_LevelLocals, (byte *)temp, INT_ID('L','V','L','C'), LLOFS(LEVEL_LOCALS_T_SAVESTOP));
+	EnumerateFields(savefields_LevelLocals, temp, INT_ID('L','V','L','C'), LLOFS(LEVEL_LOCALS_T_SAVESTOP));
 	gi.Free(temp);
 }
 
@@ -671,7 +716,7 @@ static void WriteGEntities(qboolean qbAutosave)
 		}
 	}
 
-	gi.AppendToSaveGame(INT_ID('N','M','E','D'), &iCount, sizeof(iCount));
+	::sg_write<int32_t>(::gi, INT_ID('N','M','E','D'), iCount);
 
 	for (i=0; i<(qbAutosave?1:globals.num_entities); i++)
 	{
@@ -679,7 +724,7 @@ static void WriteGEntities(qboolean qbAutosave)
 
 		if ( ent->inuse)
 		{
-			gi.AppendToSaveGame(INT_ID('E','D','N','M'), (void *)&i, sizeof(i));
+			::sg_write<int32_t>(::gi, INT_ID('E','D','N','M'), i);
 
 			qboolean qbLinked = ent->linked;
 			gi.unlinkentity( ent );
@@ -691,7 +736,7 @@ static void WriteGEntities(qboolean qbAutosave)
 				gi.linkentity( ent );
 			}
 
-			EnumerateFields(savefields_gEntity, (byte *)&tempEnt, INT_ID('G','E','N','T'), sizeof(tempEnt));
+			EnumerateFields(savefields_gEntity, &tempEnt, INT_ID('G','E','N','T'), sizeof(tempEnt));
 
 			// now for any fiddly bits that would be rather awkward to build into the enumerator...
 			//
@@ -699,18 +744,18 @@ static void WriteGEntities(qboolean qbAutosave)
 			{
 				gNPC_t npc = *ent->NPC;	// NOT *tempEnt.NPC; !! :-)
 
-				EnumerateFields(savefields_gNPC, (byte *)&npc, INT_ID('G','N','P','C'), sizeof(npc));
+				EnumerateFields(savefields_gNPC, &npc, INT_ID('G','N','P','C'), sizeof(npc));
 			}
 
 			if (tempEnt.client == (gclient_t *)-2)	// I know, I know...
 			{
 				gclient_t client = *ent->client;	// NOT *tempEnt.client!!
-				EnumerateFields(savefields_gClient, (byte *)&client, INT_ID('G','C','L','I'), sizeof(client));
+				EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I'), sizeof(client));
 			}
 
 			if (tempEnt.parms)
 			{
-				gi.AppendToSaveGame(INT_ID('P','A','R','M'), ent->parms, sizeof(*ent->parms));
+				::sg_write_no_cast(::gi, INT_ID('P','A','R','M'), *ent->parms);
 			}
 
 			// the scary ghoul2 saver stuff...  (fingers crossed)
@@ -733,7 +778,7 @@ static void WriteGEntities(qboolean qbAutosave)
 		//	This saves time debugging, and makes things easier to track.
 		//
 		static int iBlah = 1234;
-		gi.AppendToSaveGame(INT_ID('I','C','O','K'), &iBlah, sizeof(iBlah));
+		::sg_write<int32_t>(::gi, INT_ID('I','C','O','K'), iBlah);
 	}
 	if (!qbAutosave )//really shouldn't need to write these bits at all, just restore them from the ents...
 	{
@@ -956,7 +1001,7 @@ void WriteLevel(qboolean qbAutosave)
 		//
 		assert(level.maxclients == 1);	// I'll need to know if this changes, otherwise I'll need to change the way ReadGame works
 		gclient_t client = level.clients[0];
-		EnumerateFields(savefields_gClient, (byte *)&client, INT_ID('G','C','L','I'), sizeof(client));
+		EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I'), sizeof(client));
 		WriteLevelLocals();	// level_locals_t level
 	}
 
@@ -975,7 +1020,7 @@ void WriteLevel(qboolean qbAutosave)
 	// put out an end-marker so that the load code can check everything was read in...
 	//
 	static int iDONE = 1234;
-	gi.AppendToSaveGame(INT_ID('D','O','N','E'), &iDONE, sizeof(iDONE));
+	::sg_write<int32_t>(::gi, INT_ID('D','O','N','E'), iDONE);
 }
 
 void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition)
diff --git a/codeJK2/icarus/Instance.cpp b/codeJK2/icarus/Instance.cpp
index 0eff61d..299f774 100644
--- a/codeJK2/icarus/Instance.cpp
+++ b/codeJK2/icarus/Instance.cpp
@@ -302,7 +302,7 @@ int ICARUS_Instance::SaveSequenceIDTable( void )
 {
 	//Save out the number of sequences to follow
 	int		numSequences = m_sequences.size();
-	m_interface->I_WriteSaveData( INT_ID('#','S','E','Q'), &numSequences, sizeof( numSequences ) );
+	::sg_write<int32_t>(m_interface, INT_ID('#','S','E','Q'), numSequences);
 
 	//Sequences are saved first, by ID and information
 	sequence_l::iterator	sqi;
@@ -319,7 +319,7 @@ int ICARUS_Instance::SaveSequenceIDTable( void )
 		idTable[itr++] = (*sqi)->GetID();
 	}
 
-	m_interface->I_WriteSaveData( INT_ID('S','Q','T','B'), idTable, sizeof( int ) * numSequences );
+	::sg_write<int32_t>(m_interface, INT_ID('S','Q','T','B'), idTable, numSequences);
 
 	delete[] idTable;
 
@@ -357,7 +357,7 @@ int ICARUS_Instance::SaveSequencers( void )
 {
 	//Save out the number of sequences to follow
 	int		numSequencers = m_sequencers.size();
-	m_interface->I_WriteSaveData( INT_ID('#','S','Q','R'), &numSequencers, sizeof( numSequencers ) );
+	::sg_write<int32_t>(m_interface, INT_ID('#','S','Q','R'), numSequencers);
 
 	//The sequencers are then saved
 	sequencer_l::iterator	si;
@@ -379,7 +379,7 @@ int ICARUS_Instance::SaveSignals( void )
 {
 	int	numSignals = m_signals.size();
 
-	m_interface->I_WriteSaveData( INT_ID('I','S','I','G'), &numSignals, sizeof( numSignals ) );
+	::sg_write<int32_t>(m_interface, INT_ID('I','S','I','G'), numSignals);
 
 	signal_m::iterator	si;
 	STL_ITERATE( si, m_signals )
@@ -393,10 +393,10 @@ int ICARUS_Instance::SaveSignals( void )
 		int length = strlen( name ) + 1;
 
 		//Save out the string size
-		m_interface->I_WriteSaveData( INT_ID('S','I','G','#'), &length, sizeof ( length ) );
+		::sg_write<int32_t>(m_interface, INT_ID('S','I','G','#'), length);
 
 		//Write out the string
-		m_interface->I_WriteSaveData( INT_ID('S','I','G','N'), (void *) name, length );
+		::sg_write_no_cast(m_interface, INT_ID('S','I','G','N'), name, length );
 	}
 
 	return true;
@@ -412,7 +412,7 @@ int ICARUS_Instance::Save( void )
 {	
 	//Save out a ICARUS save block header with the ICARUS version
 	double	version = ICARUS_VERSION;
-	m_interface->I_WriteSaveData( INT_ID('I','C','A','R'), &version, sizeof( version ) );
+	::sg_write_no_cast(m_interface, INT_ID('I','C','A','R'), version);
 
 	//Save out the signals
 	if ( SaveSignals() == false )
@@ -426,7 +426,7 @@ int ICARUS_Instance::Save( void )
 	if ( SaveSequencers() == false )
 		return false;
 
-	m_interface->I_WriteSaveData( INT_ID('I','E','N','D'), &version, sizeof( version ) );
+	::sg_write_no_cast(m_interface, INT_ID('I','E','N','D'), version);
 
 	return true;
 }
diff --git a/codeJK2/icarus/Sequence.cpp b/codeJK2/icarus/Sequence.cpp
index c91712a..012ff25 100644
--- a/codeJK2/icarus/Sequence.cpp
+++ b/codeJK2/icarus/Sequence.cpp
@@ -344,15 +344,15 @@ int CSequence::SaveCommand( CBlock *block )
 	
 	//Save out the block ID
 	bID = block->GetBlockID();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','L','I','D'), &bID, sizeof ( bID ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','L','I','D'), bID);
 
 	//Save out the block's flags
 	flags = block->GetFlags();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','F','L','G'), &flags, sizeof ( flags ) );
+	::sg_write<uint8_t>(m_owner->GetInterface(), INT_ID('B','F','L','G'), flags);
 
 	//Save out the number of members to read
 	numMembers = block->GetNumMembers();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','N','U','M'), &numMembers, sizeof ( numMembers ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','N','U','M'), numMembers);
 
 	for ( int i = 0; i < numMembers; i++ )
 	{
@@ -360,14 +360,14 @@ int CSequence::SaveCommand( CBlock *block )
 
 		//Save the block id
 		bID = bm->GetID();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','M','I','D'), &bID, sizeof ( bID ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','M','I','D'), bID);
 		
 		//Save out the data size
 		size = bm->GetSize();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','S','I','Z'), &size, sizeof( size ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','S','I','Z'), size);
 		
 		//Save out the raw data
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','M','E','M'), bm->GetData(), size );
+		::sg_write_no_cast(m_owner->GetInterface(), INT_ID('B','M','E','M'), bm->GetData(), size);
 	}
 
 	return true;
@@ -387,30 +387,30 @@ int CSequence::Save( void )
 
 	//Save the parent (by GUID)
 	id = ( m_parent != NULL ) ? m_parent->GetID() : -1;
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('S','P','I','D'), &id, sizeof( id ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('S','P','I','D'), id);
 
 	//Save the return (by GUID)
 	id = ( m_return != NULL ) ? m_return->GetID() : -1;
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('S','R','I','D'), &id, sizeof( id ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('S','R','I','D'), id);
 	
 	//Save the number of children
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('S','N','C','H'), &m_numChildren, sizeof( m_numChildren ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('S','N','C','H'), m_numChildren);
 
 	//Save out the children (only by GUID)
 	STL_ITERATE( ci, m_children )
 	{
 		id = (*ci)->GetID();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('S','C','H','D'), &id, sizeof( id ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('S','C','H','D'), id);
 	}
 
 	//Save flags
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('S','F','L','G'), &m_flags, sizeof( m_flags ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('S','F','L','G'), m_flags);
 
 	//Save iterations
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('S','I','T','R'), &m_iterations, sizeof( m_iterations ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('S','I','T','R'), m_iterations);
 
 	//Save the number of commands
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('S','N','M','C'), &m_numCommands, sizeof( m_numCommands ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('S','N','M','C'), m_numCommands);
 
 	//Save the commands
 	STL_ITERATE( bi, m_commands )
diff --git a/codeJK2/icarus/Sequencer.cpp b/codeJK2/icarus/Sequencer.cpp
index 3d5b051..0d80d5f 100644
--- a/codeJK2/icarus/Sequencer.cpp
+++ b/codeJK2/icarus/Sequencer.cpp
@@ -2330,16 +2330,16 @@ int	CSequencer::Save( void )
 	numSequences = m_sequences.size();
 
 	//Save out the owner sequence
-	m_ie->I_WriteSaveData( INT_ID('S','Q','R','E'), &m_ownerID, sizeof( m_ownerID ) );
+	::sg_write<int32_t>(m_ie, INT_ID('S','Q','R','E'), m_ownerID);
 
 	//Write out the number of sequences we need to read
-	m_ie->I_WriteSaveData( INT_ID('S','Q','R','#'), &numSequences, sizeof( numSequences ) );
+	::sg_write<int32_t>(m_ie, INT_ID('S','Q','R','#'), numSequences);
 
 	//Second pass, save out all sequences, in order
 	STL_ITERATE( si, m_sequences )
 	{
 		id = (*si)->GetID();
-		m_ie->I_WriteSaveData( INT_ID('S','Q','R','I'), &id, sizeof( id ) );
+		::sg_write<int32_t>(m_ie, INT_ID('S','Q','R','I'), id);
 	}
 
 	//Save out the taskManager
@@ -2347,29 +2347,29 @@ int	CSequencer::Save( void )
 
 	//Save out the task sequences mapping the name to the GUIDs
 	numTasks = m_taskSequences.size();
-	m_ie->I_WriteSaveData( INT_ID('S','Q','T','#'), &numTasks, sizeof ( numTasks ) );
+	::sg_write<int32_t>(m_ie, INT_ID('S','Q','T','#'), numTasks);
 
 	STL_ITERATE( ti, m_taskSequences )
 	{	
 		//Save the task group's ID
 		id = ((*ti).first)->GetGUID();
-		m_ie->I_WriteSaveData( INT_ID('S','T','I','D'), &id, sizeof( id ) );
+		::sg_write<int32_t>(m_ie, INT_ID('S','T','I','D'), id);
 
 		//Save the sequence's ID
 		id = ((*ti).second)->GetID();
-		m_ie->I_WriteSaveData( INT_ID('S','S','I','D'), &id, sizeof( id ) );
+		::sg_write<int32_t>(m_ie, INT_ID('S','S','I','D'), id);
 	}
 
 	int	curGroupID = ( m_curGroup == NULL ) ? -1 : m_curGroup->GetGUID();
 
-	m_ie->I_WriteSaveData( INT_ID('S','Q','C','T'), &curGroupID, sizeof ( m_numCommands ) );
+	::sg_write<int32_t>(m_ie, INT_ID('S','Q','C','T'), curGroupID);
 
 	//Output the number of commands
-	m_ie->I_WriteSaveData( INT_ID('S','Q','#','C'), &m_numCommands, sizeof ( m_numCommands ) );	//FIXME: This can be reconstructed
+	::sg_write<int32_t>(m_ie, INT_ID('S','Q','#','C'), m_numCommands);	//FIXME: This can be reconstructed
 
 	//Output the ID of the current sequence
 	id = ( m_curSequence != NULL ) ? m_curSequence->GetID() : -1;
-	m_ie->I_WriteSaveData( INT_ID('S','Q','C','S'), &id, sizeof ( id ) );
+	::sg_write<int32_t>(m_ie, INT_ID('S','Q','C','S'), id);
 
 	return true;
 }
diff --git a/codeJK2/icarus/TaskManager.cpp b/codeJK2/icarus/TaskManager.cpp
index 5123b00..f51b492 100644
--- a/codeJK2/icarus/TaskManager.cpp
+++ b/codeJK2/icarus/TaskManager.cpp
@@ -1641,15 +1641,15 @@ int CTaskManager::SaveCommand( CBlock *block )
 	
 	//Save out the block ID
 	bID = block->GetBlockID();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','L','I','D'), &bID, sizeof ( bID ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','L','I','D'), bID);
 
 	//Save out the block's flags
 	flags = block->GetFlags();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','F','L','G'), &flags, sizeof ( flags ) );
+	::sg_write<uint8_t>(m_owner->GetInterface(), INT_ID('B','F','L','G'), flags);
 
 	//Save out the number of members to read
 	numMembers = block->GetNumMembers();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','N','U','M'), &numMembers, sizeof ( numMembers ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','N','U','M'), numMembers);
 
 	for ( int i = 0; i < numMembers; i++ )
 	{
@@ -1657,14 +1657,14 @@ int CTaskManager::SaveCommand( CBlock *block )
 
 		//Save the block id
 		bID = bm->GetID();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','M','I','D'), &bID, sizeof ( bID ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','M','I','D'), bID);
 		
 		//Save out the data size
 		size = bm->GetSize();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','S','I','Z'), &size, sizeof( size ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('B','S','I','Z'), size);
 		
 		//Save out the raw data
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('B','M','E','M'), bm->GetData(), size );
+		::sg_write_no_cast(m_owner->GetInterface(), INT_ID('B','M','E','M'), bm->GetData(), size);
 	}
 
 	return true;
@@ -1687,11 +1687,11 @@ void CTaskManager::Save( void )
 	int			numWritten;
 
 	//Save the taskmanager's GUID
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','M','I','D'), &m_GUID, sizeof( m_GUID ) );	//FIXME: This can be reconstructed
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','M','I','D'), m_GUID);	//FIXME: This can be reconstructed
 
 	//Save out the number of tasks that will follow
 	int iNumTasks = m_tasks.size();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','S','K','#'), &iNumTasks, sizeof(iNumTasks) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','S','K','#'), iNumTasks);
 
 	//Save out all the tasks
 	tasks_l::iterator	ti;
@@ -1700,11 +1700,11 @@ void CTaskManager::Save( void )
 	{
 		//Save the GUID
 		id = (*ti)->GetGUID();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','K','I','D'), &id, sizeof ( id ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','K','I','D'), id);
 
 		//Save the timeStamp (FIXME: Although, this is going to be worthless if time is not consistent...)
 		timeStamp = (*ti)->GetTimeStamp();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','K','T','S'), &timeStamp, sizeof ( timeStamp ) );
+		::sg_write<uint32_t>(m_owner->GetInterface(), INT_ID('T','K','T','S'), timeStamp);
 
 		//Save out the block
 		block = (*ti)->GetBlock();
@@ -1713,14 +1713,14 @@ void CTaskManager::Save( void )
 
 	//Save out the number of task groups
 	int numTaskGroups = m_taskGroups.size();
-	(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','G','#','G'), &numTaskGroups, sizeof( numTaskGroups ) );
+	::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','G','#','G'), numTaskGroups);
 	//Save out the IDs of all the task groups
 	numWritten = 0;
 	taskGroup_v::iterator	tgi;
 	STL_ITERATE( tgi, m_taskGroups )
 	{
 		id = (*tgi)->GetGUID();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','K','G','#'), &id, sizeof( id ) );		
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','K','G','#'), id);		
 		numWritten++;
 	}
 	assert (numWritten == numTaskGroups);
@@ -1731,11 +1731,11 @@ void CTaskManager::Save( void )
 	{
 		//Save out the parent
 		id = ( (*tgi)->GetParent() == NULL ) ? -1 : ((*tgi)->GetParent())->GetGUID();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','K','G','P'), &id, sizeof( id ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','K','G','P'), id);
 
 		//Save out the number of commands
 		numCommands = (*tgi)->m_completedTasks.size();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','G','N','C'), &numCommands, sizeof( numCommands ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','G','N','C'), numCommands);
 
 		//Save out the command map
 		CTaskGroup::taskCallback_m::iterator	tci;
@@ -1744,16 +1744,16 @@ void CTaskManager::Save( void )
 		{
 			//Write out the ID
 			id = (*tci).first;
-			(m_owner->GetInterface())->I_WriteSaveData( INT_ID('G','M','I','D'), &id, sizeof( id ) );
+			::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('G','M','I','D'), id);
 
 			//Write out the state of completion
 			completed = (*tci).second;
-			(m_owner->GetInterface())->I_WriteSaveData( INT_ID('G','M','D','N'), &completed, sizeof( completed ) );
+			::sg_write<uint8_t>(m_owner->GetInterface(), INT_ID('G','M','D','N'), completed);
 		}
 
 		//Save out the number of completed commands
 		id = (*tgi)->m_numCompleted;
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','G','D','N'), &id, sizeof( id ) );	//FIXME: This can be reconstructed
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','G','D','N'), id);	//FIXME: This can be reconstructed
 		numWritten++;
 	}
 	assert (numWritten == numTaskGroups);
@@ -1763,7 +1763,7 @@ void CTaskManager::Save( void )
 	{
 		//Save out the currently active group
 		int	curGroupID = ( m_curGroup == NULL ) ? -1 : m_curGroup->GetGUID();
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','G','C','G'), &curGroupID, sizeof( curGroupID ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','G','C','G'), curGroupID);
 	}
 
 	//Save out the task group name maps
@@ -1779,17 +1779,17 @@ void CTaskManager::Save( void )
 		int length = strlen( name ) + 1;
 
 		//Save out the string size
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','G','N','L'), &length, sizeof ( length ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','G','N','L'), length);
 
 		//Write out the string
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','G','N','S'), (void *) name, length );
+		::sg_write_no_cast(m_owner->GetInterface(), INT_ID('T','G','N','S'), name, length);
 
 		taskGroup = (*tmi).second;
 
 		id = taskGroup->GetGUID();
 
 		//Write out the ID
-		(m_owner->GetInterface())->I_WriteSaveData( INT_ID('T','G','N','I'), &id, sizeof( id ) );
+		::sg_write<int32_t>(m_owner->GetInterface(), INT_ID('T','G','N','I'), id);
 		numWritten++;
 	}
 	assert (numWritten == numTaskGroups);
diff --git a/codeJK2/icarus/interface.h b/codeJK2/icarus/interface.h
index aca6e3f..051c8c9 100644
--- a/codeJK2/icarus/interface.h
+++ b/codeJK2/icarus/interface.h
@@ -81,7 +81,7 @@ typedef struct interface_export_s
 
 	//Save / Load functions
 
-	int				(*I_WriteSaveData)( unsigned int chid, void *data, int length );
+	int				(*I_WriteSaveData)( unsigned int chid, const void *data, int length );
 	int				(*I_ReadSaveData)( unsigned int chid, void *address, int length, void **addressptr/* = NULL */);
 	int				(*I_LinkEntity)( int entID, CSequencer *sequencer, CTaskManager *taskManager );
 
diff --git a/codemp/icarus/Q3_Interface.cpp b/codemp/icarus/Q3_Interface.cpp
index 667bafe..059d65d 100644
--- a/codemp/icarus/Q3_Interface.cpp
+++ b/codemp/icarus/Q3_Interface.cpp
@@ -710,7 +710,7 @@ void CGCam_Anything( void )
 }
 
 //These are useless for MP. Just taking it for now since I don't want to remove all calls to this in ICARUS.
-int AppendToSaveGame(unsigned long chid, void *data, int length)
+int AppendToSaveGame(unsigned long chid, const void *data, int length)
 {
 	return 1;
 }
diff --git a/codemp/icarus/interface.h b/codemp/icarus/interface.h
index 6013e45..4bfbba5 100644
--- a/codemp/icarus/interface.h
+++ b/codemp/icarus/interface.h
@@ -77,7 +77,7 @@ typedef struct interface_export_s
 
 	//Save / Load functions
 
-	int				(*I_WriteSaveData)( unsigned long chid, void *data, int length );
+	int				(*I_WriteSaveData)( unsigned long chid, const void *data, int length );
 	// Below changed by BTO (VV). Visual C++ 7.1 compiler no longer allows default args on function pointers. Ack.
 	int				(*I_ReadSaveData)( unsigned long chid, void *address, int length /* , void **addressptr = NULL */ );
 	int				(*I_LinkEntity)( int entID, CSequencer *sequencer, CTaskManager *taskManager );
diff --git a/shared/qcommon/ojk_sg_wrappers.h b/shared/qcommon/ojk_sg_wrappers.h
index 2babdc3..bb08838 100644
--- a/shared/qcommon/ojk_sg_wrappers.h
+++ b/shared/qcommon/ojk_sg_wrappers.h
@@ -20,9 +20,14 @@
 
 using SgReadFunc = std::function<int (
     unsigned int chunk_id,
-    void *value_storage,
+    void* value_storage,
     int value_size,
-    void **allocated_value)>;
+    void** allocated_value)>;
+
+using SgWriteFunc = std::function<int (
+    unsigned int chunk_id,
+    const void* value_storage,
+    int value_size)>;
 
 
 template<typename T, std::size_t TCount>
@@ -146,6 +151,90 @@ private:
 
 
     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+    // WriteSaveData
+
+    template<typename U>
+    static auto test_write_save_data(int) -> decltype(
+        std::declval<U>().WriteSaveData(
+            static_cast<unsigned int>(0),
+            static_cast<const void*>(nullptr),
+            static_cast<int>(0)
+        ) == 0,
+
+        yes()
+    );
+
+    template<typename>
+    static no test_write_save_data(...);
+
+    // WriteSaveData
+    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+    // I_WriteSaveData
+
+    template<typename U>
+    static auto test_i_write_save_data(int) -> decltype(
+        std::declval<U>().I_WriteSaveData(
+            static_cast<unsigned int>(0),
+            static_cast<const void*>(nullptr),
+            static_cast<int>(0)
+        ) == 0,
+
+        yes()
+    );
+
+    template<typename>
+    static no test_i_write_save_data(...);
+
+    // I_WriteSaveData
+    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+    // AppendToSaveGame
+
+    template<typename U>
+    static auto test_append_to_save_game(int) -> decltype(
+        std::declval<U>().AppendToSaveGame(
+            static_cast<unsigned int>(0),
+            static_cast<const void*>(nullptr),
+            static_cast<int>(0)
+        ) == 0,
+
+        yes()
+    );
+
+    template<typename>
+    static no test_append_to_save_game(...);
+
+    // AppendToSaveGame
+    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+    // SG_Append
+
+    template<typename U>
+    static auto test_sg_append(int) -> decltype(
+        std::declval<U>().SG_Append(
+            static_cast<unsigned int>(0),
+            static_cast<const void*>(nullptr),
+            static_cast<int>(0)
+        ) == 0,
+
+        yes()
+    );
+
+    template<typename>
+    static no test_sg_append(...);
+
+    // SG_Append
+    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
     // has_sg_export
 
     template<typename U>
@@ -222,6 +311,26 @@ public:
         return std::is_same<decltype(test_read_from_save_game<T>(0)), yes>::value;
     }
 
+    static constexpr bool has_write_save_data()
+    {
+        return std::is_same<decltype(test_write_save_data<T>(0)), yes>::value;
+    }
+
+    static constexpr bool has_i_write_save_data()
+    {
+        return std::is_same<decltype(test_i_write_save_data<T>(0)), yes>::value;
+    }
+
+    static constexpr bool has_append_to_save_game()
+    {
+        return std::is_same<decltype(test_append_to_save_game<T>(0)), yes>::value;
+    }
+
+    static constexpr bool has_sg_append()
+    {
+        return std::is_same<decltype(test_sg_append<T>(0)), yes>::value;
+    }
+
     static constexpr bool has_sg_export()
     {
         return std::is_same<decltype(test_sg_export<T>(0)), yes>::value;
@@ -304,6 +413,26 @@ class SgReadFromSaveGameFuncTag
 public:
 }; // SgReadFromSaveGameFuncTag
 
+class SgSgAppendFuncTag
+{
+public:
+}; // SgSgAppendFuncTag
+
+class SgWriteSaveDataFuncTag
+{
+public:
+}; // SgWriteSaveDataFuncTag
+
+class SgIWriteSaveDataFuncTag
+{
+public:
+}; // SgIWriteSaveDataFuncTag
+
+class SgAppendToSaveGameFuncTag
+{
+public:
+}; // SgAppendToSaveGameFuncTag
+
 
 } // detail
 
@@ -311,8 +440,8 @@ public:
 // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 // sg_get_read_func
 //
-// Returns a function wrapper for a function pointer, pointer to an object or
-// a reference to an object.
+// Returns a read function wrapper for a function pointer,
+// pointer to an object or a reference to an object.
 //
 
 namespace detail {
@@ -433,6 +562,142 @@ inline SgReadFunc sg_get_read_func(
 
 
 // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+// sg_get_write_func
+//
+// Returns a write function wrapper for a function pointer,
+// pointer to an object or a reference to an object.
+//
+
+namespace detail {
+
+
+template<typename TFunc>
+inline SgWriteFunc sg_get_write_func(
+    TFunc& func,
+    SgFuncTag)
+{
+    return func;
+}
+
+template<typename TInstance>
+inline SgWriteFunc sg_get_write_func(
+    TInstance* instance,
+    SgPointerTag,
+    SgSgAppendFuncTag)
+{
+    return instance->SG_Append;
+}
+
+template<typename TInstance>
+inline SgWriteFunc sg_get_write_func(
+    TInstance* instance,
+    SgPointerTag,
+    SgWriteSaveDataFuncTag)
+{
+    return std::bind(
+        &TInstance::WriteSaveData,
+        instance,
+        std::placeholders::_1,
+        std::placeholders::_2,
+        std::placeholders::_3);
+}
+
+template<typename TInstance>
+inline SgWriteFunc sg_get_write_func(
+    TInstance* instance,
+    SgPointerTag,
+    SgIWriteSaveDataFuncTag)
+{
+    return instance->I_WriteSaveData;
+}
+
+template<typename TInstance>
+inline SgWriteFunc sg_get_write_func(
+    TInstance* instance,
+    SgPointerTag,
+    SgAppendToSaveGameFuncTag)
+{
+    return instance->AppendToSaveGame;
+}
+
+template<typename TInstance>
+inline SgWriteFunc sg_get_write_func(
+    TInstance* instance,
+    SgPointerTag)
+{
+    using Tag = typename std::conditional<
+        SgTraits<TInstance>::has_sg_append(),
+        SgSgAppendFuncTag,
+        typename std::conditional<
+            SgTraits<TInstance>::has_write_save_data(),
+            SgWriteSaveDataFuncTag,
+            typename std::conditional<
+                SgTraits<TInstance>::has_i_write_save_data(),
+                SgIWriteSaveDataFuncTag,
+                typename std::conditional<
+                    SgTraits<TInstance>::has_append_to_save_game(),
+                    SgAppendToSaveGameFuncTag,
+                    void
+                >::type
+            >::type
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported instance type.");
+
+    return sg_get_write_func(
+        instance,
+        SgPointerTag(),
+        Tag());
+}
+
+template<typename TInstance>
+inline SgWriteFunc sg_get_write_func(
+    TInstance& instance,
+    SgClassTag)
+{
+    return sg_get_write_func(
+        &instance,
+        SgPointerTag());
+}
+
+template<typename TInstance>
+inline SgWriteFunc sg_get_write_func(
+    TInstance& instance)
+{
+    using Tag = typename std::conditional<
+        std::is_function<TInstance>::value,
+        SgFuncTag,
+        typename std::conditional<
+            std::is_class<TInstance>::value,
+            SgClassTag,
+            typename std::conditional<
+                std::is_pointer<TInstance>::value,
+                SgPointerTag,
+                void
+            >::type
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported instance type.");
+
+    return sg_get_write_func(
+        instance,
+        Tag());
+}
+
+
+} // detail
+
+// sg_get_write_func
+// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 // sg_export
 //
 // Exports a value or a fixed-array of values from a
@@ -1058,10 +1323,6 @@ inline int sg_read_no_cast(
         !std::is_same<Tag, void>::value,
         "Unsupported types.");
 
-    if (!dst_values) {
-        throw SgException("Null pointer.");
-    }
-
     return sg_read_no_cast(
         read_func,
         chunk_id,
@@ -1335,7 +1596,7 @@ inline int sg_read(
     SgPointerTag)
 {
     if (!dst_values) {
-        throw SgException("Null destination poiner.");
+        throw SgException("Null pointer.");
     }
 
     const auto src_size = static_cast<int>(dst_count * sizeof(TSrc));
@@ -1402,7 +1663,7 @@ inline int sg_read(
         "Unsupported types.");
 
     if (dst_count < 0) {
-        throw SgException("Negative destination count.");
+        throw SgException("Negative count.");
     }
 
     auto read_func = detail::sg_get_read_func(
@@ -1487,6 +1748,565 @@ inline int sg_read_allocate(
 }
 
 
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+// sg_write_no_cast (fixed size)
+//
+// Writes a value or a fixed-size array of values into a
+// saved game without conversion.
+//
+
+namespace detail {
+
+
+template<typename TSrc>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc& src_value,
+    SgNumericTag)
+{
+    constexpr auto src_size = static_cast<int>(sizeof(TSrc));
+
+    return write_func(
+        chunk_id,
+        const_cast<TSrc*>(&src_value),
+        src_size);
+}
+
+template<typename TSrc>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc& src_value,
+    SgClassTag)
+{
+    using SgType = typename TSrc::SgType;
+
+    constexpr auto dst_size = static_cast<int>(sizeof(SgType));
+
+    auto& dst_buffer = sg_get_buffer(
+        dst_size);
+
+    auto& dst_value = *reinterpret_cast<SgType*>(dst_buffer.data());
+
+    ::sg_export(src_value, dst_value);
+
+    return write_func(
+        chunk_id,
+        &dst_value,
+        dst_size);
+}
+
+template<typename TSrc, std::size_t TCount>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc (&src_values)[TCount],
+    SgArray1dTag,
+    SgNumericTag)
+{
+    constexpr auto src_size = static_cast<int>(TCount * sizeof(TSrc));
+
+    return write_func(
+        chunk_id,
+        const_cast<TSrc*>(&src_values[0]),
+        src_size);
+}
+
+template<typename TSrc, std::size_t TCount>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc (&src_values)[TCount],
+    SgArray1dTag,
+    SgClassTag)
+{
+    using SgType = typename TSrc::SgType;
+
+    constexpr auto dst_size = static_cast<int>(TCount * sizeof(SgType));
+
+    auto& dst_buffer = sg_get_buffer(
+        dst_size);
+
+    auto dst_values = reinterpret_cast<SgType*>(dst_buffer.data());
+
+    for (decltype(TCount) i = 0; i < TCount; ++i) {
+        ::sg_export(src_values[i], dst_values[i]);
+    }
+
+    return write_func(
+        chunk_id,
+        dst_values,
+        dst_size);
+}
+
+template<typename TSrc, std::size_t TCount>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc (&src_values)[TCount],
+    SgArray1dTag)
+{
+    using Tag = typename std::conditional<
+        std::is_same<TSrc, bool>::value,
+        void,
+        typename std::conditional<
+            detail::SgTraits<TSrc>::is_numeric(),
+            detail::SgNumericTag,
+            typename std::conditional<
+                detail::SgTraits<TSrc>::has_sg_export(),
+                detail::SgClassTag,
+                void
+            >::type
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported types.");
+
+    return sg_write_no_cast(
+        write_func,
+        chunk_id,
+        src_values,
+        SgArray1dTag(),
+        Tag()
+    );
+}
+
+
+} // detail
+
+
+template<typename TSrc, typename TInstance>
+inline int sg_write_no_cast(
+    TInstance&& instance,
+    unsigned int chunk_id,
+    const TSrc& src_value)
+{
+    using Tag = typename std::conditional<
+        std::is_same<TSrc, bool>::value,
+        void,
+        typename std::conditional<
+            detail::SgTraits<TSrc>::is_numeric(),
+            detail::SgNumericTag,
+            typename std::conditional<
+                detail::SgTraits<TSrc>::has_sg_export(),
+                detail::SgClassTag,
+                typename std::conditional<
+                    detail::SgTraits<TSrc>::is_array_1d(),
+                    detail::SgArray1dTag,
+                    void
+                >::type
+            >::type
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported types.");
+
+    auto write_func = detail::sg_get_write_func(
+        instance);
+
+    return detail::sg_write_no_cast(
+        write_func,
+        chunk_id,
+        src_value,
+        Tag());
+}
+
+// sg_write_no_cast (fixed size)
+// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+// sg_write_no_cast (dynamic size)
+//
+// Writes a specified number of values into a
+// saved game without conversion.
+//
+
+namespace detail {
+
+
+template<typename TSrc>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc* src_values,
+    int src_count,
+    SgPointerTag,
+    SgVoidTag)
+{
+    return write_func(
+        chunk_id,
+        const_cast<TSrc*>(src_values),
+        src_count);
+}
+
+template<typename TSrc>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc* src_values,
+    int src_count,
+    SgPointerTag,
+    SgNumericTag)
+{
+    const auto src_size = static_cast<int>(src_count * sizeof(TSrc));
+
+    return write_func(
+        chunk_id,
+        const_cast<TSrc*>(src_values),
+        src_size);
+}
+
+template<typename TSrc>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc* src_values,
+    int src_count,
+    SgPointerTag)
+{
+    using Tag = typename std::conditional<
+        std::is_same<TSrc, bool>::value,
+        void,
+        typename std::conditional<
+            SgTraits<TSrc>::is_numeric(),
+            SgNumericTag,
+            typename std::conditional<
+                std::is_same<TSrc, void>::value,
+                SgVoidTag,
+                void
+            >::type
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported types.");
+
+    if (!src_values) {
+        throw SgException("Null pointer.");
+    }
+
+    return sg_write_no_cast(
+        write_func,
+        chunk_id,
+        src_values,
+        src_count,
+        SgPointerTag(),
+        Tag());
+}
+
+template<typename TSrc, std::size_t TCount>
+inline int sg_write_no_cast(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc (&src_values)[TCount],
+    int src_count,
+    SgArray1dTag)
+{
+    if (src_count > TCount) {
+        throw SgException("Write overflow.");
+    }
+
+    return sg_write_no_cast(
+        write_func,
+        chunk_id,
+        &src_values[0],
+        src_count,
+        SgPointerTag());
+}
+
+
+} // detail
+
+
+template<typename TSrc, typename TInstance>
+inline int sg_write_no_cast(
+    TInstance&& instance,
+    unsigned int chunk_id,
+    TSrc&& src_value,
+    int src_count)
+{
+    using TSrcTag = typename std::remove_reference<TSrc>::type;
+
+    using Tag = typename std::conditional<
+        std::is_same<TSrcTag, bool>::value,
+        void,
+        typename std::conditional<
+            std::is_pointer<TSrcTag>::value,
+            detail::SgPointerTag,
+            typename std::conditional<
+                detail::SgTraits<TSrcTag>::is_array_1d(),
+                detail::SgArray1dTag,
+                void
+            >::type
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported types.");
+
+
+    auto write_func = detail::sg_get_write_func(
+        instance);
+
+    return detail::sg_write_no_cast(
+        write_func,
+        chunk_id,
+        src_value,
+        src_count,
+        Tag());
+}
+
+// sg_write_no_cast (dynamic size)
+// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+// sg_write (fixed size)
+//
+// Writes a value or a fixed-array of values into a
+// saved game with conversion.
+//
+
+namespace detail {
+
+
+template<typename TDst, typename TSrc>
+inline int sg_write(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc& src_value,
+    SgNumericTag)
+{
+    auto dst_value = static_cast<TDst>(src_value);
+    constexpr auto dst_size = static_cast<int>(sizeof(TDst));
+
+    return write_func(
+        chunk_id,
+        &dst_value,
+        dst_size);
+}
+
+template<typename TDst, typename TSrc>
+inline int sg_write(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc& src_value,
+    SgPointerTag)
+{
+    auto dst_value = static_cast<TDst>(reinterpret_cast<std::intptr_t>(src_value));
+
+    constexpr auto dst_size = static_cast<int>(sizeof(TDst));
+
+    return write_func(
+        chunk_id,
+        &dst_value,
+        dst_size);
+}
+
+template<typename TDst, typename TSrc, std::size_t TCount>
+inline int sg_write(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc (&src_values)[TCount],
+    SgArray1dTag,
+    SgNumericTag)
+{
+    constexpr auto dst_size = static_cast<int>(TCount * sizeof(TDst));
+
+    auto& dst_buffer = sg_get_buffer(
+        dst_size);
+
+    auto dst_values = reinterpret_cast<TDst*>(dst_buffer.data());
+
+    std::uninitialized_copy_n(
+        src_values,
+        TCount,
+        dst_values);
+
+    return write_func(
+        chunk_id,
+        dst_values,
+        dst_size);
+}
+
+template<typename TDst, typename TSrc, std::size_t TCount>
+inline int sg_write(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc (&src_values)[TCount],
+    SgArray1dTag)
+{
+    using Tag = typename std::conditional<
+        SgTraits<TSrc>::is_numeric() && SgTraits<TDst>::is_numeric(),
+        SgNumericTag,
+        void
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported types.");
+
+    return sg_write<TDst>(
+        write_func,
+        chunk_id,
+        src_values,
+        SgArray1dTag(),
+        Tag());
+}
+
+
+} // detail
+
+
+template<typename TDst, typename TSrc, typename TInstance>
+inline int sg_write(
+    TInstance&& instance,
+    unsigned int chunk_id,
+    const TSrc& src_value)
+{
+    using Tag = typename std::conditional<
+        detail::SgTraits<TSrc>::is_numeric() &&
+            detail::SgTraits<TDst>::is_numeric(),
+        detail::SgNumericTag,
+        typename std::conditional<
+            std::is_pointer<TSrc>::value &&
+                detail::SgTraits<TDst>::is_numeric(),
+            detail::SgPointerTag,
+            typename std::conditional<
+                detail::SgTraits<TSrc>::is_array_1d(),
+                detail::SgArray1dTag,
+                void
+            >::type
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported types.");
+
+    auto write_func = detail::sg_get_write_func(
+        instance);
+
+    return detail::sg_write<TDst>(
+        write_func,
+        chunk_id,
+        src_value,
+        Tag());
+}
+
+// sg_write (fixed size)
+// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+// sg_write (dynamic size)
+//
+// Reads a spesified number of values from a
+// saved game with conversion.
+//
+
+namespace detail {
+
+
+template<typename TDst, typename TSrc>
+inline int sg_write(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc* src_values,
+    int src_count,
+    SgPointerTag)
+{
+    if (!src_values) {
+        throw SgException("Null pointer.");
+    }
+
+    const auto dst_size = static_cast<int>(src_count * sizeof(TDst));
+
+    auto& dst_buffer = sg_get_buffer(
+        dst_size);
+
+    auto dst_values = reinterpret_cast<TDst*>(dst_buffer.data());
+
+    std::uninitialized_copy_n(
+        src_values,
+        src_count,
+        dst_values);
+
+    return write_func(
+        chunk_id,
+        dst_values,
+        dst_size);
+}
+
+template<typename TDst, typename TSrc, std::size_t TCount>
+inline int sg_write(
+    SgWriteFunc& write_func,
+    unsigned int chunk_id,
+    const TSrc (&src_values)[TCount],
+    int src_count,
+    SgArray1dTag)
+{
+    return sg_write<TDst>(
+        write_func,
+        chunk_id,
+        &src_values[0],
+        src_count,
+        SgPointerTag());
+}
+
+
+} // detail
+
+
+template<typename TDst, typename TSrc, typename TInstance>
+inline int sg_write(
+    TInstance&& instance,
+    unsigned int chunk_id,
+    const TSrc& src_value,
+    int src_count)
+{
+    using Tag = typename std::conditional<
+        std::is_pointer<TSrc>::value &&
+            detail::SgTraits<TDst>::is_numeric(),
+        detail::SgPointerTag,
+        typename std::conditional<
+            detail::SgTraits<TSrc>::is_array_1d(),
+            detail::SgArray1dTag,
+            void
+        >::type
+    >::type;
+
+    static_assert(
+        !std::is_same<Tag, void>::value,
+        "Unsupported types.");
+
+    if (src_count < 0) {
+        throw SgException("Negative count.");
+    }
+
+    auto write_func = detail::sg_get_write_func(
+        instance);
+
+    return detail::sg_write<TDst>(
+        write_func,
+        chunk_id,
+        src_value,
+        src_count,
+        Tag());
+}
+
+// sg_write (dynamic size)
+// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+
 #endif // __cplusplus
 
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/openjk.git



More information about the Pkg-games-commits mailing list