void STAT_Serialize(FSerializer &arc) { FString startlevel; int i = LevelData.Size(); if (arc.BeginObject("statistics")) { if (arc.isReading()) { arc("startlevel", startlevel); StartEpisode = NULL; for (unsigned int j = 0; j < AllEpisodes.Size(); j++) { if (!AllEpisodes[j].mEpisodeMap.CompareNoCase(startlevel)) { StartEpisode = &AllEpisodes[j]; break; } } LevelData.Resize(i); } else { if (StartEpisode != NULL) startlevel = StartEpisode->mEpisodeMap; arc("startlevel", startlevel); } arc("levels", LevelData); arc.EndObject(); } }
void P_WriteACSDefereds (FSerializer &arc) { bool found = false; // only write this stuff if needed for (auto &wi : wadlevelinfos) { if (wi.deferred.Size() > 0) { found = true; break; } } if (found && arc.BeginObject("deferred")) { for (auto &wi : wadlevelinfos) { if (wi.deferred.Size() > 0) { if (wi.deferred.Size() > 0) { arc(wi.MapName, wi.deferred); } } } arc.EndObject(); } }
static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *addr) { if (type != nullptr) { RecurseWriteFields(type->ParentClass, ar, addr); // Don't write this part if it has no non-transient variables for (unsigned i = 0; i < type->Fields.Size(); ++i) { if (!(type->Fields[i]->Flags & (VARF_Transient|VARF_Meta))) { // Tag this section with the class it came from in case // a more-derived class has variables that shadow a less- // derived class. Whether or not that is a language feature // that will actually be allowed remains to be seen. FString key; key.Format("class:%s", type->TypeName.GetChars()); if (ar.BeginObject(key.GetChars())) { type->VMType->Symbols.WriteFields(ar, addr); ar.EndObject(); } break; } } } }
void FRandom::StaticWriteRNGState (FSerializer &arc) { FRandom *rng; arc("rngseed", rngseed); if (arc.BeginArray("rngs")) { for (rng = FRandom::RNGList; rng != NULL; rng = rng->Next) { // Only write those RNGs that have names if (rng->NameCRC != 0) { if (arc.BeginObject(nullptr)) { arc("crc", rng->NameCRC) ("index", rng->idx) .Array("u", rng->sfmt.u, SFMT::N32) .EndObject(); } } } arc.EndArray(); } }
void FRandom::StaticReadRNGState(FSerializer &arc) { FRandom *rng; arc("rngseed", rngseed); // Call StaticClearRandom in order to ensure that SFMT is initialized FRandom::StaticClearRandom (); if (arc.BeginArray("rngs")) { int count = arc.ArraySize(); for (int i = 0; i < count; i++) { if (arc.BeginObject(nullptr)) { uint32_t crc; arc("crc", crc); for (rng = FRandom::RNGList; rng != NULL; rng = rng->Next) { if (rng->NameCRC == crc) { arc("index", rng->idx) .Array("u", rng->sfmt.u, SFMT::N32); break; } } arc.EndObject(); } } arc.EndArray(); } }
void G_UnSnapshotLevel (bool hubLoad) { if (level.info->Snapshot.mBuffer == nullptr) return; if (level.info->isValid()) { FSerializer arc; if (!arc.OpenReader(&level.info->Snapshot)) { I_Error("Failed to load savegame"); return; } G_SerializeLevel (arc, hubLoad); level.FromSnapshot = true; TThinkerIterator<APlayerPawn> it; APlayerPawn *pawn, *next; next = it.Next(); while ((pawn = next) != 0) { next = it.Next(); if (pawn->player == NULL || pawn->player->mo == NULL || !playeringame[pawn->player - players]) { int i; // If this isn't the unmorphed original copy of a player, destroy it, because it's extra. for (i = 0; i < MAXPLAYERS; ++i) { if (playeringame[i] && players[i].morphTics && players[i].mo->tracer == pawn) { break; } } if (i == MAXPLAYERS) { pawn->Destroy (); } } } } // No reason to keep the snapshot around once the level's been entered. level.info->Snapshot.Clean(); if (hubLoad) { // Unlock ACS global strings that were locked when the snapshot was made. FBehavior::StaticUnlockLevelVarStrings(); } }
static void ReadOnePlayer(FSerializer &arc, bool skipload) { int i; const char *name = NULL; bool didIt = false; if (arc.BeginObject(nullptr)) { arc.StringPtr("playername", name); for (i = 0; i < MAXPLAYERS; ++i) { if (playeringame[i]) { if (!didIt) { didIt = true; player_t playerTemp; playerTemp.Serialize(arc); if (!skipload) { // This temp player has undefined pitch limits, so set them to something // that should leave the pitch stored in the savegame intact when // rendering. The real pitch limits will be set by P_SerializePlayers() // via a net command, but that won't be processed in time for a screen // wipe, so we need something here. playerTemp.MaxPitch = playerTemp.MinPitch = playerTemp.mo->Angles.Pitch; CopyPlayer(&players[i], &playerTemp, name); } else { // we need the player actor, so that G_FinishTravel can destroy it later. players[i].mo = playerTemp.mo; } } else { if (players[i].mo != NULL) { players[i].mo->Destroy(); players[i].mo = NULL; } } } } arc.EndObject(); } }
void G_SnapshotLevel () { level.info->Snapshot.Clean(); if (level.info->isValid()) { FSerializer arc; if (arc.OpenWriter(save_formatted)) { SaveVersion = SAVEVER; G_SerializeLevel(arc, false); level.info->Snapshot = arc.GetCompressedOutput(); } } }
void FRemapTable::Serialize(FSerializer &arc) { int n = NumEntries; arc("numentries", NumEntries); if (arc.isReading()) { if (n != NumEntries) { Free(); Alloc(NumEntries); } } arc.Array("remap", Remap, NumEntries); arc.Array("palette", Palette, NumEntries); }
bool PClass::ReadAllFields(FSerializer &ar, void *addr) const { bool readsomething = false; bool foundsomething = false; const char *key; key = ar.GetKey(); if (strcmp(key, "classtype")) { // this does not represent a DObject Printf(TEXTCOLOR_RED "trying to read user variables but got a non-object (first key is '%s')", key); ar.mErrors++; return false; } while ((key = ar.GetKey())) { if (strncmp(key, "class:", 6)) { // We have read all user variable blocks. break; } foundsomething = true; PClass *type = PClass::FindClass(key + 6); if (type != nullptr) { // Only read it if the type is related to this one. if (IsDescendantOf(type)) { if (ar.BeginObject(nullptr)) { readsomething |= type->VMType->Symbols.ReadFields(ar, addr, type->TypeName.GetChars()); ar.EndObject(); } } else { DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n", type->TypeName.GetChars(), TypeName.GetChars()); } } else { DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n", key+6, TypeName.GetChars()); } } return readsomething || !foundsomething; }
void FGLInterface::EndSerialize(FSerializer &arc) { if (arc.isReading()) { gl_RecreateAllAttachedLights(); gl_InitPortals(); } }
void FGLInterface::StartSerialize(FSerializer &arc) { if (arc.BeginObject("glinfo")) { arc("fogdensity", fogdensity) ("outsidefogdensity", outsidefogdensity) ("skyfog", skyfog) .EndObject(); } }
void DElevator::Serialize(FSerializer &arc) { Super::Serialize (arc); arc.Enum("type", m_Type) ("direction", m_Direction) ("floordestdist", m_FloorDestDist) ("ceilingdestdist", m_CeilingDestDist) ("speed", m_Speed) ("interp_floor", m_Interp_Floor) ("interp_ceiling", m_Interp_Ceiling); }
void P_SerializeSounds(FSerializer &arc) { S_SerializeSounds(arc); DSeqNode::SerializeSequences (arc); const char *name = NULL; BYTE order; if (arc.isWriting()) { order = S_GetMusic(&name); } arc.StringPtr("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); } }
void G_ReadVisited(FSerializer &arc) { if (arc.BeginArray("visited")) { for (int s = arc.ArraySize(); s > 0; s--) { FString str; arc(nullptr, str); auto i = FindLevelInfo(str); if (i != nullptr) i->flags |= LEVEL_VISITED; } arc.EndArray(); } arc.Array("randomclasses", SinglePlayerClass, MAXPLAYERS); if (arc.BeginObject("playerclasses")) { for (int i = 0; i < MAXPLAYERS; ++i) { FString key; key.Format("%d", i); arc(key, players[i].cls); } arc.EndObject(); } }
void G_WriteVisited(FSerializer &arc) { if (arc.BeginArray("visited")) { // Write out which levels have been visited for (auto & wi : wadlevelinfos) { if (wi.flags & LEVEL_VISITED) { arc.AddString(nullptr, wi.MapName); } } arc.EndArray(); } // Store player classes to be used when spawning a random class if (multiplayer) { arc.Array("randomclasses", SinglePlayerClass, MAXPLAYERS); } if (arc.BeginObject("playerclasses")) { for (int i = 0; i < MAXPLAYERS; ++i) { if (playeringame[i]) { FString key; key.Format("%d", i); arc(key, players[i].cls); } } arc.EndObject(); } }
void DPillar::Serialize(FSerializer &arc) { Super::Serialize (arc); arc.Enum("type", m_Type) ("floorspeed", m_FloorSpeed) ("ceilingspeed", m_CeilingSpeed) ("floortarget", m_FloorTarget) ("ceilingtarget", m_CeilingTarget) ("crush", m_Crush) ("hexencrush", m_Hexencrush) ("interp_floor", m_Interp_Floor) ("interp_ceiling", m_Interp_Ceiling); }
void P_ReadACSDefereds (FSerializer &arc) { FString MapName; P_RemoveDefereds (); if (arc.BeginObject("deferred")) { const char *key; while ((key = arc.GetKey())) { level_info_t *i = FindLevelInfo(key); if (i == NULL) { I_Error("Unknown map '%s' in savegame", key); } arc(nullptr, i->deferred); } arc.EndObject(); } }
void DDoor::Serialize(FSerializer &arc) { Super::Serialize (arc); arc.Enum("type", m_Type) ("topdist", m_TopDist) ("botspot", m_BotSpot) ("botdist", m_BotDist) ("oldfloordist", m_OldFloorDist) ("speed", m_Speed) ("direction", m_Direction) ("topwait", m_TopWait) ("topcountdown", m_TopCountdown) ("lighttag", m_LightTag); }
void DPlat::Serialize(FSerializer &arc) { Super::Serialize (arc); arc.Enum("type", m_Type) ("speed", m_Speed) ("low", m_Low) ("high", m_High) ("wait", m_Wait) ("count", m_Count) .Enum("status", m_Status) .Enum("oldstatus", m_OldStatus) ("crush", m_Crush) ("tag", m_Tag); }
void DCeiling::Serialize(FSerializer &arc) { Super::Serialize (arc); arc.Enum("type", m_Type) ("bottomheight", m_BottomHeight) ("topheight", m_TopHeight) ("speed", m_Speed) ("speed1", m_Speed1) ("speed2", m_Speed2) ("crush", m_Crush) ("silent", m_Silent) ("direction", m_Direction) ("texture", m_Texture) ("newspecial", m_NewSpecial) ("tag", m_Tag) ("olddirecton", m_OldDirection) .Enum("crushmode", m_CrushMode); }
void DFloor::Serialize(FSerializer &arc) { Super::Serialize (arc); arc.Enum("type", m_Type) ("crush", m_Crush) ("direction", m_Direction) ("newspecial", m_NewSpecial) ("texture", m_Texture) ("floordestdist", m_FloorDestDist) ("speed", m_Speed) ("resetcount", m_ResetCount) ("orgdist", m_OrgDist) ("delay", m_Delay) ("pausetime", m_PauseTime) ("steptime", m_StepTime) ("persteptime", m_PerStepTime) ("crushmode", m_Hexencrush); }
void FRemapTable::StaticSerializeTranslations(FSerializer &arc) { if (arc.BeginArray("translations")) { // Does this level have custom translations? FRemapTable *trans; int w; if (arc.isWriting()) { for (unsigned int i = 0; i < translationtables[TRANSLATION_LevelScripted].Size(); ++i) { trans = translationtables[TRANSLATION_LevelScripted][i]; if (trans != NULL && !trans->IsIdentity()) { if (arc.BeginObject(nullptr)) { arc("index", i); trans->Serialize(arc); arc.EndObject(); } } } } else { while (arc.BeginObject(nullptr)) { arc("index", w); trans = translationtables[TRANSLATION_LevelScripted].GetVal(w); if (trans == NULL) { trans = new FRemapTable; translationtables[TRANSLATION_LevelScripted].SetVal(w, trans); } trans->Serialize(arc); arc.EndObject(); } } arc.EndArray(); } }
void G_SerializeLevel(FSerializer &arc, bool hubload) { int i = level.totaltime; if (arc.isWriting()) { arc.Array("checksum", level.md5, 16); } else { // prevent bad things from happening by doing a check on the size of level arrays and the map's entire checksum. // The old code happily tried to load savegames with any mismatch here, often causing meaningless errors // deep down in the deserializer or just a crash if the few insufficient safeguards were not triggered. BYTE chk[16] = { 0 }; arc.Array("checksum", chk, 16); if (arc.GetSize("linedefs") != (unsigned)numlines || arc.GetSize("sidedefs") != (unsigned)numsides || arc.GetSize("sectors") != (unsigned)numsectors || arc.GetSize("polyobjs") != (unsigned)po_NumPolyobjs || memcmp(chk, level.md5, 16)) { I_Error("Savegame is from a different level"); } } arc("saveversion", SaveVersion); Renderer->StartSerialize(arc); if (arc.isReading()) { DThinker::DestroyAllThinkers(); interpolator.ClearInterpolations(); arc.ReadObjects(hubload); } arc("level.flags", level.flags) ("level.flags2", level.flags2) ("level.fadeto", level.fadeto) ("level.found_secrets", level.found_secrets) ("level.found_items", level.found_items) ("level.killed_monsters", level.killed_monsters) ("level.total_secrets", level.total_secrets) ("level.total_items", level.total_items) ("level.total_monsters", level.total_monsters) ("level.gravity", level.gravity) ("level.aircontrol", level.aircontrol) ("level.teamdamage", level.teamdamage) ("level.maptime", level.maptime) ("level.totaltime", i) ("level.skytexture1", level.skytexture1) ("level.skytexture2", level.skytexture2); // Hub transitions must keep the current total time if (!hubload) level.totaltime = i; if (arc.isReading()) { sky1texture = level.skytexture1; sky2texture = level.skytexture2; R_InitSkyMap(); G_AirControlChanged(); } // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON FBehavior::StaticSerializeModuleStates(arc); // The order here is important: First world state, then portal state, then thinkers, and last polyobjects. arc.Array("linedefs", lines, &loadlines[0], numlines); arc.Array("sidedefs", sides, &loadsides[0], numsides); arc.Array("sectors", sectors, &loadsectors[0], numsectors); arc("zones", Zones); arc("lineportals", linePortals); arc("sectorportals", sectorPortals); if (arc.isReading()) P_CollectLinkedPortals(); DThinker::SerializeThinkers(arc, !hubload); arc.Array("polyobjs", polyobjs, po_NumPolyobjs); arc("subsectors", subsectors); StatusBar->SerializeMessages(arc); AM_SerializeMarkers(arc); FRemapTable::StaticSerializeTranslations(arc); FCanvasTextureInfo::Serialize(arc); P_SerializePlayers(arc, hubload); P_SerializeSounds(arc); if (arc.isReading()) { for (int i = 0; i < numsectors; i++) { P_Recalculate3DFloors(§ors[i]); } for (int i = 0; i < MAXPLAYERS; ++i) { if (playeringame[i] && players[i].mo != NULL) { players[i].mo->SetupWeaponSlots(); } } } Renderer->EndSerialize(arc); }
static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload) { // For two or more players, read each player into a temporary array. int i, j; const char **nametemp = new const 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; if (arc.BeginObject(nullptr)) { arc.StringPtr("playername", nametemp[i]); playertemp[i].Serialize(arc); arc.EndObject(); } 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; } } } else { for (i = 0; i < MAXPLAYERS; ++i) { players[i].mo = playertemp[i].mo; } } delete[] tempPlayerUsed; delete[] playertemp; delete[] nametemp; }
void P_SerializePlayers(FSerializer &arc, bool skipload) { int 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.isWriting()) { // Record the number of players in this save. arc("numplayers", numPlayersNow); if (arc.BeginArray("players")) { // Record each player's name, followed by their data. for (i = 0; i < MAXPLAYERS; ++i) { if (playeringame[i]) { if (arc.BeginObject(nullptr)) { const char *n = players[i].userinfo.GetName(); arc.StringPtr("playername", n); players[i].Serialize(arc); arc.EndObject(); } } } arc.EndArray(); } } else { arc("numplayers", numPlayers); if (arc.BeginArray("players")) { // 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); } arc.EndArray(); } if (!skipload && numPlayersNow > numPlayers) { SpawnExtraPlayers(); } // Redo pitch limits, since the spawned player has them at 0. players[consoleplayer].SendPitchLimits(); } }