- some preparations for converting the player serialization code.

- converted sound and canvas texture serialization.
- refactored file_zip, so that it can be used to load loose zip files and extract their compressed data directly.
- added handling to FSerializer to generate and consume compressed Zip file entries.

If all goes well this will allow saving savegames as Zips when the rework is done, which will make analyzing them a lot easier.
This commit is contained in:
Christoph Oelckers 2016-09-20 23:13:12 +02:00
parent 970c168b13
commit 3a1191281f
12 changed files with 720 additions and 530 deletions

View File

@ -65,287 +65,8 @@
#include "serializer.h"
// just the stuff that already got converted to FSerializer so that it can be seen as 'done' when searching.
#define COMMON_STUFF
#include "zzz_old.cpp"
void CopyPlayer (player_t *dst, player_t *src, const char *name);
static void ReadOnePlayer (FArchive &arc, bool skipload);
static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload);
static void SpawnExtraPlayers ();
//
// P_ArchivePlayers
//
void P_SerializePlayers (FArchive &arc, bool skipload)
{
BYTE numPlayers, numPlayersNow;
int i;
// Count the number of players present right now.
for (numPlayersNow = 0, i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
{
++numPlayersNow;
}
}
if (arc.IsStoring())
{
// Record the number of players in this save.
arc << numPlayersNow;
// Record each player's name, followed by their data.
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
{
arc.WriteString (players[i].userinfo.GetName());
players[i].Serialize (arc);
}
}
}
else
{
arc << numPlayers;
// If there is only one player in the game, they go to the
// first player present, no matter what their name.
if (numPlayers == 1)
{
ReadOnePlayer (arc, skipload);
}
else
{
ReadMultiplePlayers (arc, numPlayers, numPlayersNow, skipload);
}
if (!skipload && numPlayersNow > numPlayers)
{
SpawnExtraPlayers ();
}
// Redo pitch limits, since the spawned player has them at 0.
players[consoleplayer].SendPitchLimits();
}
}
static void ReadOnePlayer (FArchive &arc, bool skipload)
{
int i;
char *name = NULL;
bool didIt = false;
arc << name;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
{
if (!didIt)
{
didIt = true;
player_t playerTemp;
playerTemp.Serialize (arc);
if (!skipload)
{
CopyPlayer (&players[i], &playerTemp, name);
}
}
else
{
if (players[i].mo != NULL)
{
players[i].mo->Destroy();
players[i].mo = NULL;
}
}
}
}
delete[] name;
}
static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload)
{
// For two or more players, read each player into a temporary array.
int i, j;
char **nametemp = new char *[numPlayers];
player_t *playertemp = new player_t[numPlayers];
BYTE *tempPlayerUsed = new BYTE[numPlayers];
BYTE playerUsed[MAXPLAYERS];
for (i = 0; i < numPlayers; ++i)
{
nametemp[i] = NULL;
arc << nametemp[i];
playertemp[i].Serialize (arc);
tempPlayerUsed[i] = 0;
}
for (i = 0; i < MAXPLAYERS; ++i)
{
playerUsed[i] = playeringame[i] ? 0 : 2;
}
if (!skipload)
{
// Now try to match players from the savegame with players present
// based on their names. If two players in the savegame have the
// same name, then they are assigned to players in the current game
// on a first-come, first-served basis.
for (i = 0; i < numPlayers; ++i)
{
for (j = 0; j < MAXPLAYERS; ++j)
{
if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0)
{ // Found a match, so copy our temp player to the real player
Printf ("Found player %d (%s) at %d\n", i, nametemp[i], j);
CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
playerUsed[j] = 1;
tempPlayerUsed[i] = 1;
break;
}
}
}
// Any players that didn't have matching names are assigned to existing
// players on a first-come, first-served basis.
for (i = 0; i < numPlayers; ++i)
{
if (tempPlayerUsed[i] == 0)
{
for (j = 0; j < MAXPLAYERS; ++j)
{
if (playerUsed[j] == 0)
{
Printf ("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName());
CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
playerUsed[j] = 1;
tempPlayerUsed[i] = 1;
break;
}
}
}
}
// Make sure any extra players don't have actors spawned yet. Happens if the players
// present now got the same slots as they had in the save, but there are not as many
// as there were in the save.
for (j = 0; j < MAXPLAYERS; ++j)
{
if (playerUsed[j] == 0)
{
if (players[j].mo != NULL)
{
players[j].mo->Destroy();
players[j].mo = NULL;
}
}
}
// Remove any temp players that were not used. Happens if there are fewer players
// than there were in the save, and they got shuffled.
for (i = 0; i < numPlayers; ++i)
{
if (tempPlayerUsed[i] == 0)
{
playertemp[i].mo->Destroy();
playertemp[i].mo = NULL;
}
}
}
delete[] tempPlayerUsed;
delete[] playertemp;
for (i = 0; i < numPlayers; ++i)
{
delete[] nametemp[i];
}
delete[] nametemp;
}
void CopyPlayer (player_t *dst, player_t *src, const char *name)
{
// The userinfo needs to be saved for real players, but it
// needs to come from the save for bots.
userinfo_t uibackup;
userinfo_t uibackup2;
uibackup.TransferFrom(dst->userinfo);
uibackup2.TransferFrom(src->userinfo);
int chasecam = dst->cheats & CF_CHASECAM; // Remember the chasecam setting
bool attackdown = dst->attackdown;
bool usedown = dst->usedown;
*dst = *src; // To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above.
dst->cheats |= chasecam;
if (dst->Bot != nullptr)
{
botinfo_t *thebot = bglobal.botinfo;
while (thebot && stricmp (name, thebot->name))
{
thebot = thebot->next;
}
if (thebot)
{
thebot->inuse = BOTINUSE_Yes;
}
bglobal.botnum++;
dst->userinfo.TransferFrom(uibackup2);
}
else
{
dst->userinfo.TransferFrom(uibackup);
}
// Validate the skin
dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass));
// Make sure the player pawn points to the proper player struct.
if (dst->mo != nullptr)
{
dst->mo->player = dst;
}
// Same for the psprites.
DPSprite *pspr = dst->psprites;
while (pspr)
{
pspr->Owner = dst;
pspr = pspr->Next;
}
// Don't let the psprites be destroyed when src is destroyed.
src->psprites = nullptr;
// These 2 variables may not be overwritten.
dst->attackdown = attackdown;
dst->usedown = usedown;
}
static void SpawnExtraPlayers ()
{
// If there are more players now than there were in the savegame,
// be sure to spawn the extra players.
int i;
if (deathmatch)
{
return;
}
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo == NULL)
{
players[i].playerstate = PST_ENTER;
P_SpawnPlayer(&playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
}
}
//
// Thinkers
@ -807,6 +528,351 @@ FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z, zone_t *def
return Serialize(arc, key, z.Environment, nullptr);
}
//==========================================================================
//
// ArchiveSounds
//
//==========================================================================
void P_SerializeSounds(FSerializer &arc)
{
S_SerializeSounds(arc);
DSeqNode::SerializeSequences (arc);
char *name = NULL;
BYTE order;
if (arc.isWriting())
{
order = S_GetMusic(&name);
}
arc("musicname", name)
("musicorder", order);
if (arc.isReading())
{
if (!S_ChangeMusic(name, order))
if (level.cdtrack == 0 || !S_ChangeCDMusic(level.cdtrack, level.cdid))
S_ChangeMusic(level.Music, level.musicorder);
}
delete[] name;
}
//==========================================================================
//
//
//
//==========================================================================
void CopyPlayer(player_t *dst, player_t *src, const char *name);
static void ReadOnePlayer(FSerializer &arc, bool skipload);
static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload);
static void SpawnExtraPlayers();
//==========================================================================
//
// P_ArchivePlayers
//
//==========================================================================
void P_SerializePlayers(FSerializer &arc, bool skipload)
{
BYTE numPlayers, numPlayersNow;
int i;
// Count the number of players present right now.
for (numPlayersNow = 0, i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
{
++numPlayersNow;
}
}
#if 0
if (arc.isWriting())
{
// Record the number of players in this save.
arc << numPlayersNow;
// Record each player's name, followed by their data.
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
{
arc.WriteString(players[i].userinfo.GetName());
players[i].Serialize(arc);
}
}
}
else
{
arc << numPlayers;
// If there is only one player in the game, they go to the
// first player present, no matter what their name.
if (numPlayers == 1)
{
ReadOnePlayer(arc, skipload);
}
else
{
ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload);
}
if (!skipload && numPlayersNow > numPlayers)
{
SpawnExtraPlayers();
}
// Redo pitch limits, since the spawned player has them at 0.
players[consoleplayer].SendPitchLimits();
}
#endif
}
//==========================================================================
//
//
//
//==========================================================================
static void ReadOnePlayer(FSerializer &arc, bool skipload)
{
#if 0
int i;
char *name = NULL;
bool didIt = false;
arc << name;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
{
if (!didIt)
{
didIt = true;
player_t playerTemp;
playerTemp.Serialize(arc);
if (!skipload)
{
CopyPlayer(&players[i], &playerTemp, name);
}
}
else
{
if (players[i].mo != NULL)
{
players[i].mo->Destroy();
players[i].mo = NULL;
}
}
}
}
delete[] name;
#endif
}
//==========================================================================
//
//
//
//==========================================================================
static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload)
{
#if 0
// For two or more players, read each player into a temporary array.
int i, j;
char **nametemp = new char *[numPlayers];
player_t *playertemp = new player_t[numPlayers];
BYTE *tempPlayerUsed = new BYTE[numPlayers];
BYTE playerUsed[MAXPLAYERS];
for (i = 0; i < numPlayers; ++i)
{
nametemp[i] = NULL;
arc << nametemp[i];
playertemp[i].Serialize(arc);
tempPlayerUsed[i] = 0;
}
for (i = 0; i < MAXPLAYERS; ++i)
{
playerUsed[i] = playeringame[i] ? 0 : 2;
}
if (!skipload)
{
// Now try to match players from the savegame with players present
// based on their names. If two players in the savegame have the
// same name, then they are assigned to players in the current game
// on a first-come, first-served basis.
for (i = 0; i < numPlayers; ++i)
{
for (j = 0; j < MAXPLAYERS; ++j)
{
if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0)
{ // Found a match, so copy our temp player to the real player
Printf("Found player %d (%s) at %d\n", i, nametemp[i], j);
CopyPlayer(&players[j], &playertemp[i], nametemp[i]);
playerUsed[j] = 1;
tempPlayerUsed[i] = 1;
break;
}
}
}
// Any players that didn't have matching names are assigned to existing
// players on a first-come, first-served basis.
for (i = 0; i < numPlayers; ++i)
{
if (tempPlayerUsed[i] == 0)
{
for (j = 0; j < MAXPLAYERS; ++j)
{
if (playerUsed[j] == 0)
{
Printf("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName());
CopyPlayer(&players[j], &playertemp[i], nametemp[i]);
playerUsed[j] = 1;
tempPlayerUsed[i] = 1;
break;
}
}
}
}
// Make sure any extra players don't have actors spawned yet. Happens if the players
// present now got the same slots as they had in the save, but there are not as many
// as there were in the save.
for (j = 0; j < MAXPLAYERS; ++j)
{
if (playerUsed[j] == 0)
{
if (players[j].mo != NULL)
{
players[j].mo->Destroy();
players[j].mo = NULL;
}
}
}
// Remove any temp players that were not used. Happens if there are fewer players
// than there were in the save, and they got shuffled.
for (i = 0; i < numPlayers; ++i)
{
if (tempPlayerUsed[i] == 0)
{
playertemp[i].mo->Destroy();
playertemp[i].mo = NULL;
}
}
}
delete[] tempPlayerUsed;
delete[] playertemp;
for (i = 0; i < numPlayers; ++i)
{
delete[] nametemp[i];
}
delete[] nametemp;
#endif
}
//==========================================================================
//
//
//
//==========================================================================
void CopyPlayer(player_t *dst, player_t *src, const char *name)
{
// The userinfo needs to be saved for real players, but it
// needs to come from the save for bots.
userinfo_t uibackup;
userinfo_t uibackup2;
uibackup.TransferFrom(dst->userinfo);
uibackup2.TransferFrom(src->userinfo);
int chasecam = dst->cheats & CF_CHASECAM; // Remember the chasecam setting
bool attackdown = dst->attackdown;
bool usedown = dst->usedown;
*dst = *src; // To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above.
dst->cheats |= chasecam;
if (dst->Bot != nullptr)
{
botinfo_t *thebot = bglobal.botinfo;
while (thebot && stricmp(name, thebot->name))
{
thebot = thebot->next;
}
if (thebot)
{
thebot->inuse = BOTINUSE_Yes;
}
bglobal.botnum++;
dst->userinfo.TransferFrom(uibackup2);
}
else
{
dst->userinfo.TransferFrom(uibackup);
}
// Validate the skin
dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass));
// Make sure the player pawn points to the proper player struct.
if (dst->mo != nullptr)
{
dst->mo->player = dst;
}
// Same for the psprites.
DPSprite *pspr = dst->psprites;
while (pspr)
{
pspr->Owner = dst;
pspr = pspr->Next;
}
// Don't let the psprites be destroyed when src is destroyed.
src->psprites = nullptr;
// These 2 variables may not be overwritten.
dst->attackdown = attackdown;
dst->usedown = usedown;
}
//==========================================================================
//
//
//
//==========================================================================
static void SpawnExtraPlayers()
{
// If there are more players now than there were in the savegame,
// be sure to spawn the extra players.
int i;
if (deathmatch)
{
return;
}
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo == NULL)
{
players[i].playerstate = PST_ENTER;
P_SpawnPlayer(&playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
}
}
//============================================================================
//
//
@ -838,7 +904,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload)
}
}
//Renderer->StartSerialize(arc);
Renderer->StartSerialize(arc);
if (arc.isReading())
{
P_DestroyThinkers(hubload);
@ -894,53 +960,10 @@ void G_SerializeLevel(FSerializer &arc, bool hubload)
StatusBar->SerializeMessages(arc);
AM_SerializeMarkers(arc);
FRemapTable::StaticSerializeTranslations(arc);
}
//==========================================================================
//
// ArchiveSounds
//
//==========================================================================
void P_SerializeSounds (FArchive &arc)
{
S_SerializeSounds (arc);
//DSeqNode::SerializeSequences (arc);
char *name = NULL;
BYTE order;
if (arc.IsStoring ())
{
order = S_GetMusic (&name);
}
arc << name << order;
if (arc.IsLoading ())
{
if (!S_ChangeMusic (name, order))
if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid))
S_ChangeMusic (level.Music, level.musicorder);
}
delete[] name;
}
//==========================================================================
//
//
//==========================================================================
void G_SerializeLevel(FArchive &arc, bool hubLoad)
{
#if 0
// This must be saved, too, of course!
FCanvasTextureInfo::Serialize(arc);
P_SerializePlayers(arc, hubLoad);
//P_SerializePlayers(arc, hubLoad);
P_SerializeSounds(arc);
if (arc.IsLoading())
if (arc.isReading())
{
for (int i = 0; i < numsectors; i++)
{
@ -955,6 +978,16 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad)
}
}
Renderer->EndSerialize(arc);
#endif
}
//==========================================================================
//
//
//==========================================================================
void G_SerializeLevel(FArchive &arc, bool hubLoad)
{
}

View File

@ -40,9 +40,7 @@ struct PNGHandle;
// Persistent storage/archiving.
// These are the load / save game routines.
// Also see farchive.(h|cpp)
void P_SerializePlayers (FArchive &arc, bool fakeload);
void P_DestroyThinkers(bool hubLoad);
void P_SerializeSounds (FArchive &arc);
void P_ReadACSDefereds (PNGHandle *png);
void P_WriteACSDefereds (FILE *file);

View File

@ -272,14 +272,13 @@ class ASkyViewpoint;
struct secplane_t
{
friend FArchive &operator<< (FArchive &arc, secplane_t &plane);
// the plane is defined as a*x + b*y + c*z + d = 0
// ic is 1/c, for faster Z calculations
//private: // restore when JSON serializer is done.
DVector3 normal;
double D, negiC; // negative iC because that also saves a negation in all methods using this.
public:
friend FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplane_t *def);
void set(double aa, double bb, double cc, double dd)
{
@ -437,9 +436,6 @@ public:
};
FArchive &operator<< (FArchive &arc, secplane_t &plane);
#include "p_3dfloors.h"
// Ceiling/floor flags
enum
@ -544,8 +540,6 @@ struct extsector_t
TArray<lightlist_t> lightlist; // 3D light list
TArray<sector_t*> attached; // 3D floors attached to this sector
} XFloor;
void Serialize(FArchive &arc);
};
struct FTransform
@ -1033,9 +1027,6 @@ public:
extsector_t * e; // This stores data that requires construction/destruction. Such data must not be copied by R_FakeFlat.
};
FArchive &operator<< (FArchive &arc, sector_t::splane &p);
struct ReverbContainer;
struct zone_t
{
@ -1198,8 +1189,6 @@ struct side_t
vertex_t *V2() const;
};
FArchive &operator<< (FArchive &arc, side_t::part &p);
struct line_t
{
vertex_t *v1, *v2; // vertices, from v1 to v2

View File

@ -5,7 +5,7 @@
struct FRenderer;
extern FRenderer *Renderer;
class FArchive;
class FSerializer;
class FTexture;
class AActor;
class player_t;
@ -46,8 +46,8 @@ struct FRenderer
virtual void StateChanged(AActor *actor) {}
// notify the renderer that serialization of the curent level is about to start/end
virtual void StartSerialize(FArchive &arc) {}
virtual void EndSerialize(FArchive &arc) {}
virtual void StartSerialize(FSerializer &arc) {}
virtual void EndSerialize(FSerializer &arc) {}
virtual int GetMaxViewPitch(bool down) = 0; // return value is in plain degrees

View File

@ -53,7 +53,7 @@
#include "v_font.h"
#include "r_renderer.h"
#include "r_data/colormaps.h"
#include "farchive.h"
#include "serializer.h"
#include "r_utility.h"
#include "d_player.h"
#include "p_local.h"
@ -1041,37 +1041,49 @@ void FCanvasTextureInfo::EmptyList ()
//
//==========================================================================
void FCanvasTextureInfo::Serialize(FArchive &arc)
void FCanvasTextureInfo::Serialize(FSerializer &arc)
{
#if 0
if (arc.IsStoring ())
if (arc.isWriting())
{
FCanvasTextureInfo *probe;
for (probe = List; probe != NULL; probe = probe->Next)
if (List != nullptr)
{
if (probe->Texture != NULL && probe->Viewpoint != NULL)
if (arc.BeginArray("canvastextures"))
{
arc << probe->Viewpoint << probe->FOV << probe->PicNum;
FCanvasTextureInfo *probe;
for (probe = List; probe != nullptr; probe = probe->Next)
{
if (probe->Texture != nullptr && probe->Viewpoint != nullptr)
{
if (arc.BeginObject(nullptr))
{
arc("viewpoint", probe->Viewpoint)
("fov", probe->FOV)
("texture", probe->PicNum)
.EndObject();
}
}
}
}
}
AActor *nullactor = NULL;
arc << nullactor;
}
else
{
AActor *viewpoint;
int fov;
FTextureID picnum;
EmptyList ();
while (arc << viewpoint, viewpoint != NULL)
if (arc.BeginArray("canvastextures"))
{
arc << fov << picnum;
Add (viewpoint, picnum, fov);
AActor *viewpoint;
int fov;
FTextureID picnum;
while (arc.BeginObject(nullptr))
{
arc("viewpoint", viewpoint)
("fov", fov)
("texture", picnum)
.EndObject();
Add(viewpoint, picnum, fov);
}
}
}
#endif
}
//==========================================================================

View File

@ -3,6 +3,8 @@
#include "r_state.h"
#include "vectors.h"
class FSerializer;
//
// Stuff from r_main.h that's needed outside the rendering code.
@ -113,7 +115,7 @@ struct FCanvasTextureInfo
static void Add (AActor *viewpoint, FTextureID picnum, int fov);
static void UpdateAll ();
static void EmptyList ();
static void Serialize(FArchive &arc);
static void Serialize(FSerializer &arc);
static void Mark();
private:

View File

@ -33,7 +33,7 @@
**
*/
#include "resourcefile.h"
#include "file_zip.h"
#include "cmdlib.h"
#include "templates.h"
#include "v_text.h"
@ -44,6 +44,69 @@
#define BUFREADCOMMENT (0x400)
//==========================================================================
//
// Decompression subroutine
//
//==========================================================================
static bool UncompressZipLump(char *Cache, FileReader *Reader, int Method, int LumpSize, int CompressedSize, int GPFlags)
{
switch (Method)
{
case METHOD_STORED:
{
Reader->Read(Cache, LumpSize);
break;
}
case METHOD_DEFLATE:
{
FileReaderZ frz(*Reader, true);
frz.Read(Cache, LumpSize);
break;
}
case METHOD_BZIP2:
{
FileReaderBZ2 frz(*Reader);
frz.Read(Cache, LumpSize);
break;
}
case METHOD_LZMA:
{
FileReaderLZMA frz(*Reader, LumpSize, true);
frz.Read(Cache, LumpSize);
break;
}
case METHOD_IMPLODE:
{
FZipExploder exploder;
exploder.Explode((unsigned char *)Cache, LumpSize, Reader, CompressedSize, GPFlags);
break;
}
case METHOD_SHRINK:
{
ShrinkLoop((unsigned char *)Cache, LumpSize, Reader, CompressedSize);
break;
}
default:
assert(0);
return false;
}
return true;
}
bool FCompressedBuffer::Decompress(char *destbuffer)
{
MemoryReader mr(mBuffer, mCompressedSize);
return UncompressZipLump(destbuffer, &mr, mMethod, mSize, mCompressedSize, mZipFlags);
}
//-----------------------------------------------------------------------
//
// Finds the central directory end record in the end of the file.
@ -96,56 +159,6 @@ static DWORD Zip_FindCentralDir(FileReader * fin)
return uPosFound;
}
enum
{
LUMPFZIP_NEEDFILESTART = 128
};
//==========================================================================
//
// Zip Lump
//
//==========================================================================
struct FZipLump : public FResourceLump
{
WORD GPFlags;
BYTE Method;
int CompressedSize;
int Position;
virtual FileReader *GetReader();
virtual int FillCache();
private:
void SetLumpAddress();
virtual int GetFileOffset()
{
if (Method != METHOD_STORED) return -1;
if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress(); return Position;
}
};
//==========================================================================
//
// Zip file
//
//==========================================================================
class FZipFile : public FResourceFile
{
FZipLump *Lumps;
public:
FZipFile(const char * filename, FileReader *file);
virtual ~FZipFile();
bool Open(bool quiet);
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
};
//==========================================================================
//
// Zip file
@ -285,6 +298,24 @@ FZipFile::~FZipFile()
if (Lumps != NULL) delete [] Lumps;
}
//==========================================================================
//
//
//
//==========================================================================
FCompressedBuffer FZipFile::GetRawLump(int lumpnum)
{
if ((unsigned)lumpnum >= NumLumps)
{
return{ 0,0,0,0,nullptr };
}
FZipLump *lmp = &Lumps[lumpnum];
FCompressedBuffer cbuf = { (unsigned)lmp->LumpSize, (unsigned)lmp->CompressedSize, lmp->Method, lmp->GPFlags, new char[lmp->CompressedSize] };
Reader->Seek(lmp->Position, SEEK_SET);
Reader->Read(cbuf.mBuffer, lmp->CompressedSize);
}
//==========================================================================
//
// SetLumpAddress
@ -348,56 +379,22 @@ int FZipLump::FillCache()
Owner->Reader->Seek(Position, SEEK_SET);
Cache = new char[LumpSize];
switch (Method)
{
case METHOD_STORED:
{
Owner->Reader->Read(Cache, LumpSize);
break;
}
case METHOD_DEFLATE:
{
FileReaderZ frz(*Owner->Reader, true);
frz.Read(Cache, LumpSize);
break;
}
case METHOD_BZIP2:
{
FileReaderBZ2 frz(*Owner->Reader);
frz.Read(Cache, LumpSize);
break;
}
case METHOD_LZMA:
{
FileReaderLZMA frz(*Owner->Reader, LumpSize, true);
frz.Read(Cache, LumpSize);
break;
}
case METHOD_IMPLODE:
{
FZipExploder exploder;
exploder.Explode((unsigned char *)Cache, LumpSize, Owner->Reader, CompressedSize, GPFlags);
break;
}
case METHOD_SHRINK:
{
ShrinkLoop((unsigned char *)Cache, LumpSize, Owner->Reader, CompressedSize);
break;
}
default:
assert(0);
return 0;
}
UncompressZipLump(Cache, Owner->Reader, Method, LumpSize, CompressedSize, GPFlags);
RefCount = 1;
return 1;
}
//==========================================================================
//
//
//
//==========================================================================
int FZipLump::GetFileOffset()
{
if (Method != METHOD_STORED) return -1;
if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress(); return Position;
}
//==========================================================================
//

View File

@ -0,0 +1,64 @@
#ifndef __FILE_ZIP_H
#define __FILE_ZIP_H
#include "resourcefile.h"
// This holds a compresed Zip entry with all needed info to decompress it.
struct FCompressedBuffer
{
unsigned mSize;
unsigned mCompressedSize;
int mMethod;
int mZipFlags;
char *mBuffer;
bool Decompress(char *destbuffer);
};
enum
{
LUMPFZIP_NEEDFILESTART = 128
};
//==========================================================================
//
// Zip Lump
//
//==========================================================================
struct FZipLump : public FResourceLump
{
WORD GPFlags;
BYTE Method;
int CompressedSize;
int Position;
virtual FileReader *GetReader();
virtual int FillCache();
private:
void SetLumpAddress();
virtual int GetFileOffset();
};
//==========================================================================
//
// Zip file
//
//==========================================================================
class FZipFile : public FResourceFile
{
FZipLump *Lumps;
public:
FZipFile(const char * filename, FileReader *file);
virtual ~FZipFile();
bool Open(bool quiet);
virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; }
FCompressedBuffer GetRawLump(int lumpnum);
};
#endif

View File

@ -50,8 +50,9 @@
#include "timidity/timidity.h"
#include "g_level.h"
#include "po_man.h"
#include "farchive.h"
#include "serializer.h"
#include "d_player.h"
#include "r_state.h"
// MACROS ------------------------------------------------------------------
@ -2208,58 +2209,41 @@ void S_StopChannel(FSoundChan *chan)
//==========================================================================
//
// (FArchive &) << (FSoundID &)
//
//
//==========================================================================
FArchive &operator<<(FArchive &arc, FSoundID &sid)
static FSerializer &Serialize(FSerializer &arc, const char *key, FSoundChan &chan, FSoundChan *def)
{
if (arc.IsStoring())
if (arc.BeginObject(key))
{
arc.WriteName((const char *)sid);
}
else
{
sid = arc.ReadName();
}
return arc;
}
arc("sourcetype", chan.SourceType)
("soundid", chan.SoundID)
("orgid", chan.OrgID)
("volume", chan.Volume)
("distancescale", chan.DistanceScale)
("pitch", chan.Pitch)
("chanflags", chan.ChanFlags)
("entchannel", chan.EntChannel)
("priority", chan.Priority)
("nearlimit", chan.NearLimit)
("starttime", chan.StartTime.AsOne)
("rolloftype", chan.Rolloff.RolloffType)
("rolloffmin", chan.Rolloff.MinDistance)
("rolloffmax", chan.Rolloff.MaxDistance)
("limitrange", chan.LimitRange);
//==========================================================================
//
// (FArchive &) << (FSoundChan &)
//
//==========================================================================
static FArchive &operator<<(FArchive &arc, FSoundChan &chan)
{
arc << chan.SourceType;
#if 0
switch (chan.SourceType)
{
case SOURCE_None: break;
case SOURCE_Actor: arc << chan.Actor; break;
case SOURCE_Sector: arc << chan.Sector; break;
case SOURCE_Polyobj: /*arc << chan.Poly;*/ break;
case SOURCE_Unattached: arc << chan.Point[0] << chan.Point[1] << chan.Point[2]; break;
default: I_Error("Unknown sound source type %d\n", chan.SourceType); break;
switch (chan.SourceType)
{
case SOURCE_None: break;
case SOURCE_Actor: arc("actor", chan.Actor); break;
case SOURCE_Sector: arc("sector", chan.Sector); break;
case SOURCE_Polyobj: arc("poly", chan.Poly); break;
case SOURCE_Unattached: arc.Array("point", chan.Point, 3); break;
default: I_Error("Unknown sound source type %d\n", chan.SourceType); break;
}
arc.EndObject();
}
#endif
arc << chan.SoundID
<< chan.OrgID
<< chan.Volume
<< chan.DistanceScale
<< chan.Pitch
<< chan.ChanFlags
<< chan.EntChannel
<< chan.Priority
<< chan.NearLimit
<< chan.StartTime
<< chan.Rolloff.RolloffType
<< chan.Rolloff.MinDistance
<< chan.Rolloff.MaxDistance
<< chan.LimitRange;
return arc;
}
@ -2269,13 +2253,13 @@ static FArchive &operator<<(FArchive &arc, FSoundChan &chan)
//
//==========================================================================
void S_SerializeSounds(FArchive &arc)
void S_SerializeSounds(FSerializer &arc)
{
FSoundChan *chan;
GSnd->Sync(true);
if (arc.IsStoring())
if (arc.isWriting())
{
TArray<FSoundChan *> chans;
@ -2292,16 +2276,17 @@ void S_SerializeSounds(FArchive &arc)
chans.Push(chan);
}
}
arc.WriteCount(chans.Size());
for (unsigned int i = chans.Size(); i-- != 0; )
if (chans.Size() > 0 && arc.BeginArray("sounds"))
{
// Replace start time with sample position.
QWORD start = chans[i]->StartTime.AsOne;
chans[i]->StartTime.AsOne = GSnd ? GSnd->GetPosition(chans[i]) : 0;
arc << *chans[i];
chans[i]->StartTime.AsOne = start;
for (unsigned int i = chans.Size(); i-- != 0; )
{
// Replace start time with sample position.
QWORD start = chans[i]->StartTime.AsOne;
chans[i]->StartTime.AsOne = GSnd ? GSnd->GetPosition(chans[i]) : 0;
arc(nullptr, *chans[i]);
chans[i]->StartTime.AsOne = start;
}
arc.EndArray();
}
}
else
@ -2309,13 +2294,17 @@ void S_SerializeSounds(FArchive &arc)
unsigned int count;
S_StopAllChannels();
count = arc.ReadCount();
for (unsigned int i = 0; i < count; ++i)
if (arc.BeginArray("sounds"))
{
chan = (FSoundChan*)S_GetChannel(NULL);
arc << *chan;
// Sounds always start out evicted when restored from a save.
chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME;
count = arc.ArraySize();
for (unsigned int i = 0; i < count; ++i)
{
chan = (FSoundChan*)S_GetChannel(NULL);
arc(nullptr, *chan);
// Sounds always start out evicted when restored from a save.
chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME;
}
arc.EndArray();
}
// The two tic delay is to make sure any screenwipes have finished.
// This needs to be two because the game is run for one tic before
@ -2326,7 +2315,6 @@ void S_SerializeSounds(FArchive &arc)
// sounds might be heard briefly before pausing for the wipe.
RestartEvictionsAt = level.time + 2;
}
//DSeqNode::SerializeSequences(arc);
GSnd->Sync(false);
GSnd->UpdateSounds();
}

View File

@ -321,7 +321,7 @@ bool S_ChangeSoundVolume(AActor *actor, int channel, float volume);
void S_RelinkSound (AActor *from, AActor *to);
// Stores/retrieves playing channel information in an archive.
void S_SerializeSounds(FArchive &arc);
void S_SerializeSounds(FSerializer &arc);
// Start music using <music_name>
bool S_StartMusic (const char *music_name);

View File

@ -30,6 +30,7 @@
#include "g_shared/a_sharedglobal.h"
#include "po_man.h"
#include "v_font.h"
#include "w_zip.h"
char nulspace[1024 * 1024 * 4];
@ -133,7 +134,7 @@ struct FReader
return &it->value;
}
}
else if (obj.mObject->IsArray() && obj.mIndex < obj.mObject->Size())
else if (obj.mObject->IsArray() && (unsigned)obj.mIndex < obj.mObject->Size())
{
return &(*obj.mObject)[obj.mIndex++];
}
@ -175,8 +176,33 @@ bool FSerializer::OpenReader(const char *buffer, size_t length)
//
//==========================================================================
void FSerializer::Close()
bool FSerializer::OpenReader(FCompressedBuffer *input)
{
if (input->mSize <= 0 || input->mBuffer == nullptr) return false;
if (w != nullptr || r != nullptr) return false;
if (input->mMethod == METHOD_STORED)
{
r = new FReader((char*)input->mBuffer, input->mSize);
return true;
}
else
{
char *unpacked = new char[input->mSize];
input->Decompress(unpacked);
r = new FReader(unpacked, input->mSize);
return true;
}
}
//==========================================================================
//
//
//
//==========================================================================
void FSerializer::Close()
{
if (w != nullptr)
{
delete w;
@ -622,8 +648,15 @@ void FSerializer::WriteObjects()
}
}
//==========================================================================
//
//
//
//==========================================================================
const char *FSerializer::GetOutput(unsigned *len)
{
if (isReading()) return nullptr;
if (len != nullptr)
{
*len = (unsigned)w->mOutString.GetSize();
@ -637,6 +670,63 @@ const char *FSerializer::GetOutput(unsigned *len)
//
//==========================================================================
FCompressedBuffer FSerializer::GetCompressedOutput()
{
if (isReading()) return{ 0,0,0,0,nullptr };
FCompressedBuffer buff;
buff.mSize = (unsigned)w->mOutString.GetSize();
buff.mZipFlags = 0;
uint8_t *compressbuf = new uint8_t[buff.mSize+1];
z_stream stream;
int err;
stream.next_in = (Bytef *)w->mOutString.GetString();
stream.avail_in = buff.mSize;
stream.next_out = (Bytef*)compressbuf;
stream.avail_out = buff.mSize;
stream.zalloc = (alloc_func)0;
stream.zfree = (free_func)0;
stream.opaque = (voidpf)0;
// create output in zip-compatible form as required by FCompressedBuffer
err = deflateInit2(&stream, 8, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
if (err != Z_OK)
{
goto error;
}
err = deflate(&stream, Z_FINISH);
if (err != Z_STREAM_END)
{
deflateEnd(&stream);
goto error;
}
buff.mCompressedSize = stream.total_out;
err = deflateEnd(&stream);
if (err == Z_OK)
{
buff.mBuffer = new char[buff.mCompressedSize];
buff.mMethod = METHOD_DEFLATE;
memcpy(buff.mBuffer, compressbuf, buff.mCompressedSize);
delete[] compressbuf;
}
error:
memcpy(compressbuf, w->mOutString.GetString(), buff.mSize + 1);
buff.mCompressedSize = buff.mSize;
buff.mMethod = METHOD_STORED;
return buff;
}
//==========================================================================
//
//
//
//==========================================================================
FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval)
{
if (arc.isWriting())
@ -916,6 +1006,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&
return SerializePointer(arc, key, value, defval, polyobjs);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval)
{
return SerializePointer<const FPolyObj>(arc, key, value, defval, polyobjs);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval)
{
return SerializePointer(arc, key, value, defval, sides);
@ -926,6 +1021,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&
return SerializePointer(arc, key, value, defval, sectors);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval)
{
return SerializePointer<const sector_t>(arc, key, value, defval, sectors);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval)
{
return SerializePointer(arc, key, value, defval, players);

View File

@ -5,6 +5,7 @@
#include <type_traits>
#include "tarray.h"
#include "r_defs.h"
#include "resourcefiles/file_zip.h"
struct FWriter;
struct FReader;
@ -38,7 +39,6 @@ struct NumericValue
};
class FSerializer
{
@ -57,6 +57,7 @@ public:
}
bool OpenWriter();
bool OpenReader(const char *buffer, size_t length);
bool OpenReader(FCompressedBuffer *input);
void Close();
bool BeginObject(const char *name, bool randomaccess = false);
void EndObject();
@ -66,6 +67,7 @@ public:
unsigned GetSize(const char *group);
const char *GetKey();
const char *GetOutput(unsigned *len = nullptr);
FCompressedBuffer GetCompressedOutput();
FSerializer &Args(const char *key, int *args, int *defargs, int special);
FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr);
FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def);
@ -203,6 +205,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, TArray<T, TT> &value,
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval);
@ -216,6 +220,9 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDoorAnimat
template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr, char **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval);
FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def, bool *retcode);
template<> inline FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def)
{
@ -250,7 +257,7 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, PalEntry &pe, P
inline FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style, FRenderStyle *def)
{
return Serialize(arc, key, style.AsDWORD, def ? &def->AsDWORD : nullptr);
return arc.Array(key, &style.BlendOp, def ? &def->BlendOp : nullptr, 4);
}
template<class T, class TT>