/* Emacs style mode select -*- C++ -*- *----------------------------------------------------------------------------- * * * PrBoom: a Doom port merged with LxDoom and LSDLDoom * based on BOOM, a modified and improved DOOM engine * Copyright (C) 1999 by * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman * Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze * Copyright 2005, 2006 by * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * DESCRIPTION: * Archiving: SaveGame I/O. * *-----------------------------------------------------------------------------*/ #include "doomstat.h" #include "r_main.h" #include "p_maputl.h" #include "p_spec.h" #include "p_tick.h" #include "p_saveg.h" #include "m_random.h" #include "am_map.h" #include "p_enemy.h" #include "lprintf.h" byte *save_p; // Pads save_p to a 4-byte boundary // so that the load/save works on SGI&Gecko. #define PADSAVEP() do { save_p += (4 - ((int) save_p & 3)) & 3; } while (0) // // P_ArchivePlayers // void P_ArchivePlayers (void) { int i; CheckSaveGame(sizeof(player_t) * MAXPLAYERS); // killough for (i=0 ; ipsprites[j].state) dest->psprites[j].state = (state_t *)(dest->psprites[j].state-states); } } // // P_UnArchivePlayers // void P_UnArchivePlayers (void) { int i; for (i=0 ; ifloorheight + sizeof sec->ceilingheight) * numsectors + sizeof(short)*3*numlines + 4; for (i=0; itextureoffset + sizeof si->rowoffset; if (lines[i].sidenum[1] != NO_INDEX) size += sizeof(short)*3 + sizeof si->textureoffset + sizeof si->rowoffset; } CheckSaveGame(size); // killough PADSAVEP(); // killough 3/22/98 put = (short *)save_p; // do sectors for (i=0, sec = sectors ; ifloorheight, sizeof sec->floorheight); put = (void *)((char *) put + sizeof sec->floorheight); memcpy(put, &sec->ceilingheight, sizeof sec->ceilingheight); put = (void *)((char *) put + sizeof sec->ceilingheight); *put++ = sec->floorpic; *put++ = sec->ceilingpic; *put++ = sec->lightlevel; *put++ = sec->special; // needed? yes -- transfer types *put++ = sec->tag; // needed? need them -- killough } // do lines for (i=0, li = lines ; iflags; *put++ = li->special; *put++ = li->tag; for (j=0; j<2; j++) if (li->sidenum[j] != NO_INDEX) { si = &sides[li->sidenum[j]]; // killough 10/98: save full sidedef offsets, // preserving fractional scroll offsets memcpy(put, &si->textureoffset, sizeof si->textureoffset); put = (void *)((char *) put + sizeof si->textureoffset); memcpy(put, &si->rowoffset, sizeof si->rowoffset); put = (void *)((char *) put + sizeof si->rowoffset); *put++ = si->toptexture; *put++ = si->bottomtexture; *put++ = si->midtexture; } } save_p = (byte *) put; } // // P_UnArchiveWorld // void P_UnArchiveWorld (void) { int i; sector_t *sec; line_t *li; const short *get; PADSAVEP(); // killough 3/22/98 get = (short *) save_p; // do sectors for (i=0, sec = sectors ; ifloorheight, get, sizeof sec->floorheight); get = (void *)((char *) get + sizeof sec->floorheight); memcpy(&sec->ceilingheight, get, sizeof sec->ceilingheight); get = (void *)((char *) get + sizeof sec->ceilingheight); sec->floorpic = *get++; sec->ceilingpic = *get++; sec->lightlevel = *get++; sec->special = *get++; sec->tag = *get++; sec->ceilingdata = 0; //jff 2/22/98 now three thinker fields, not two sec->floordata = 0; sec->lightingdata = 0; sec->soundtarget = 0; } // do lines for (i=0, li = lines ; iflags = *get++; li->special = *get++; li->tag = *get++; for (j=0 ; j<2 ; j++) if (li->sidenum[j] != NO_INDEX) { side_t *si = &sides[li->sidenum[j]]; // killough 10/98: load full sidedef offsets, including fractions memcpy(&si->textureoffset, get, sizeof si->textureoffset); get = (void *)((char *) get + sizeof si->textureoffset); memcpy(&si->rowoffset, get, sizeof si->rowoffset); get = (void *)((char *) get + sizeof si->rowoffset); si->toptexture = *get++; si->bottomtexture = *get++; si->midtexture = *get++; } } save_p = (byte *) get; } // // Thinkers // typedef enum { tc_end, tc_mobj } thinkerclass_t; // phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the // thinker indices could be used by the code that saves sector info. static int number_of_thinkers; void P_ThinkerToIndex(void) { thinker_t *th; // killough 2/14/98: // count the number of thinkers, and mark each one with its index, using // the prev field as a placeholder, since it can be restored later. number_of_thinkers = 0; for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (th->function == P_MobjThinker) th->prev = (thinker_t *) ++number_of_thinkers; } // phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the // thinker indices could be used by the code that saves sector info. void P_IndexToThinker(void) { // killough 2/14/98: restore prev pointers thinker_t *th; thinker_t *prev = &thinkercap; for (th = thinkercap.next ; th != &thinkercap ; prev=th, th=th->next) th->prev = prev; } // // P_ArchiveThinkers // // 2/14/98 killough: substantially modified to fix savegame bugs void P_ArchiveThinkers (void) { thinker_t *th; CheckSaveGame(sizeof brain); // killough 3/26/98: Save boss brain state memcpy(save_p, &brain, sizeof brain); save_p += sizeof brain; /* check that enough room is available in savegame buffer * - killough 2/14/98 * cph - use number_of_thinkers saved by P_ThinkerToIndex above * size per object is sizeof(mobj_t) - 2*sizeof(void*) - 4*sizeof(fixed_t) plus * padded type (4) plus 5*sizeof(void*), i.e. sizeof(mobj_t) + 4 + * 3*sizeof(void*) * cph - +1 for the tc_end */ CheckSaveGame(number_of_thinkers*(sizeof(mobj_t)-3*sizeof(fixed_t)+4+3*sizeof(void*)) +1); // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (th->function == P_MobjThinker) { mobj_t *mobj; *save_p++ = tc_mobj; PADSAVEP(); mobj = (mobj_t *)save_p; /* cph 2006/07/30 - * The end of mobj_t changed from * boolean invisible; * mobj_t* lastenemy; * mobj_t* above_monster; * mobj_t* below_monster; * void* touching_sectorlist; * to * mobj_t* lastenemy; * void* touching_sectorlist; * fixed_t PrevX, PrevY, PrevZ, padding; * at prboom 2.4.4. There is code here to preserve the savegame format. * * touching_sectorlist is reconstructed anyway, so we now leave off the * last 2 words of mobj_t, write 5 words of 0 and then write lastenemy * into the second of these. */ memcpy (mobj, th, sizeof(*mobj) - 2*sizeof(void*)); save_p += sizeof(*mobj) - 2*sizeof(void*) - 4*sizeof(fixed_t); memset (save_p, 0, 5*sizeof(void*)); mobj->state = (state_t *)(mobj->state - states); // killough 2/14/98: convert pointers into indices. // Fixes many savegame problems, by properly saving // target and tracer fields. Note: we store NULL if // the thinker pointed to by these fields is not a // mobj thinker. if (mobj->target) mobj->target = mobj->target->thinker.function == P_MobjThinker ? (mobj_t *) mobj->target->thinker.prev : NULL; if (mobj->tracer) mobj->tracer = mobj->tracer->thinker.function == P_MobjThinker ? (mobj_t *) mobj->tracer->thinker.prev : NULL; // killough 2/14/98: new field: save last known enemy. Prevents // monsters from going to sleep after killing monsters and not // seeing player anymore. if (((mobj_t*)th)->lastenemy && ((mobj_t*)th)->lastenemy->thinker.function == P_MobjThinker) { memcpy (save_p + sizeof(void*), &(((mobj_t*)th)->lastenemy->thinker.prev), sizeof(void*)); } // killough 2/14/98: end changes save_p += 5*sizeof(void*); if (mobj->player) mobj->player = (player_t *)((mobj->player-players) + 1); } // add a terminating marker *save_p++ = tc_end; // killough 9/14/98: save soundtargets { int i; CheckSaveGame(numsectors * sizeof(mobj_t *)); // killough 9/14/98 for (i = 0; i < numsectors; i++) { mobj_t *target = sectors[i].soundtarget; // Fix crash on reload when a soundtarget points to a removed corpse // (prboom bug #1590350) if (target && target->thinker.function == P_MobjThinker) target = (mobj_t *) target->thinker.prev; else target = NULL; memcpy(save_p, &target, sizeof target); save_p += sizeof target; } } } /* * killough 11/98 * * Same as P_SetTarget() in p_tick.c, except that the target is nullified * first, so that no old target's reference count is decreased (when loading * savegames, old targets are indices, not really pointers to targets). */ static void P_SetNewTarget(mobj_t **mop, mobj_t *targ) { *mop = NULL; P_SetTarget(mop, targ); } // // P_UnArchiveThinkers // // 2/14/98 killough: substantially modified to fix savegame bugs // // savegame file stores ints in the corresponding * field; this function // safely casts them back to int. static int P_GetMobj(mobj_t* mi, size_t s) { size_t i = (size_t)mi; if (i >= s) I_Error("Corrupt savegame"); return i; } void P_UnArchiveThinkers (void) { thinker_t *th; mobj_t **mobj_p; // killough 2/14/98: Translation table size_t size; // killough 2/14/98: size of or index into table totallive = 0; // killough 3/26/98: Load boss brain state memcpy(&brain, save_p, sizeof brain); save_p += sizeof brain; // remove all the current thinkers for (th = thinkercap.next; th != &thinkercap; ) { thinker_t *next = th->next; if (th->function == P_MobjThinker) P_RemoveMobj ((mobj_t *) th); else Z_Free (th); th = next; } P_InitThinkers (); // killough 2/14/98: count number of thinkers by skipping through them { byte *sp = save_p; // save pointer and skip header for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 { // skip all entries, adding up count PADSAVEP(); /* cph 2006/07/30 - see comment below for change in layout of mobj_t */ save_p += sizeof(mobj_t)+3*sizeof(void*)-4*sizeof(fixed_t); } if (*--save_p != tc_end) I_Error ("P_UnArchiveThinkers: Unknown tclass %i in savegame", *save_p); // first table entry special: 0 maps to NULL *(mobj_p = malloc(size * sizeof *mobj_p)) = 0; // table of pointers save_p = sp; // restore save pointer } // read in saved thinkers for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 { mobj_t *mobj = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); // killough 2/14/98 -- insert pointers to thinkers into table, in order: mobj_p[size] = mobj; PADSAVEP(); /* cph 2006/07/30 - * The end of mobj_t changed from * boolean invisible; * mobj_t* lastenemy; * mobj_t* above_monster; * mobj_t* below_monster; * void* touching_sectorlist; * to * mobj_t* lastenemy; * void* touching_sectorlist; * fixed_t PrevX, PrevY, PrevZ; * at prboom 2.4.4. There is code here to preserve the savegame format. * * touching_sectorlist is reconstructed anyway, so we now read in all * but the last 5 words from the savegame (filling all but the last 2 * fields of our current mobj_t. We then pull lastenemy from the 2nd of * the 5 leftover words, and skip the others. */ memcpy (mobj, save_p, sizeof(mobj_t)-2*sizeof(void*)-4*sizeof(fixed_t)); save_p += sizeof(mobj_t)-sizeof(void*)-4*sizeof(fixed_t); memcpy (&(mobj->lastenemy), save_p, sizeof(void*)); save_p += 4*sizeof(void*); mobj->state = states + (int) mobj->state; if (mobj->player) (mobj->player = &players[(int) mobj->player - 1]) -> mo = mobj; P_SetThingPosition (mobj); mobj->info = &mobjinfo[mobj->type]; // killough 2/28/98: // Fix for falling down into a wall after savegame loaded: // mobj->floorz = mobj->subsector->sector->floorheight; // mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->thinker.function = P_MobjThinker; P_AddThinker (&mobj->thinker); if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL | MF_CORPSE))) totallive++; } // killough 2/14/98: adjust target and tracer fields, plus // lastenemy field, to correctly point to mobj thinkers. // NULL entries automatically handled by first table entry. // // killough 11/98: use P_SetNewTarget() to set fields for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { P_SetNewTarget(&((mobj_t *) th)->target, mobj_p[P_GetMobj(((mobj_t *)th)->target,size)]); P_SetNewTarget(&((mobj_t *) th)->tracer, mobj_p[P_GetMobj(((mobj_t *)th)->tracer,size)]); P_SetNewTarget(&((mobj_t *) th)->lastenemy, mobj_p[P_GetMobj(((mobj_t *)th)->lastenemy,size)]); } { // killough 9/14/98: restore soundtargets int i; for (i = 0; i < numsectors; i++) { mobj_t *target; memcpy(&target, save_p, sizeof target); save_p += sizeof target; // Must verify soundtarget. See P_ArchiveThinkers. P_SetNewTarget(§ors[i].soundtarget, mobj_p[P_GetMobj(target,size)]); } } free(mobj_p); // free translation table // killough 3/26/98: Spawn icon landings: if (gamemode == commercial) P_SpawnBrainTargets(); } // // P_ArchiveSpecials // enum { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, tc_elevator, //jff 2/22/98 new elevator type thinker tc_scroll, // killough 3/7/98: new scroll effect thinker tc_pusher, // phares 3/22/98: new push/pull effect thinker tc_flicker, // killough 10/4/98 tc_endspecials } specials_e; // // Things to handle: // // T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list // T_VerticalDoor, (vldoor_t: sector_t * swizzle), // T_MoveFloor, (floormove_t: sector_t * swizzle), // T_LightFlash, (lightflash_t: sector_t * swizzle), // T_StrobeFlash, (strobe_t: sector_t *), // T_Glow, (glow_t: sector_t *), // T_PlatRaise, (plat_t: sector_t *), - active list // T_MoveElevator, (plat_t: sector_t *), - active list // jff 2/22/98 // T_Scroll // killough 3/7/98 // T_Pusher // phares 3/22/98 // T_FireFlicker // killough 10/4/98 // void P_ArchiveSpecials (void) { thinker_t *th; size_t size = 0; // killough // save off the current thinkers (memory size calculation -- killough) for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (!th->function) { platlist_t *pl; ceilinglist_t *cl; //jff 2/22/98 need this for ceilings too now for (pl=activeplats; pl; pl=pl->next) if (pl->plat == (plat_t *) th) // killough 2/14/98 { size += 4+sizeof(plat_t); goto end; } for (cl=activeceilings; cl; cl=cl->next) // search for activeceiling if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 { size += 4+sizeof(ceiling_t); goto end; } end:; } else size += th->function==T_MoveCeiling ? 4+sizeof(ceiling_t) : th->function==T_VerticalDoor ? 4+sizeof(vldoor_t) : th->function==T_MoveFloor ? 4+sizeof(floormove_t): th->function==T_PlatRaise ? 4+sizeof(plat_t) : th->function==T_LightFlash ? 4+sizeof(lightflash_t): th->function==T_StrobeFlash ? 4+sizeof(strobe_t) : th->function==T_Glow ? 4+sizeof(glow_t) : th->function==T_MoveElevator ? 4+sizeof(elevator_t): th->function==T_Scroll ? 4+sizeof(scroll_t) : th->function==T_Pusher ? 4+sizeof(pusher_t) : th->function==T_FireFlicker? 4+sizeof(fireflicker_t) : 0; CheckSaveGame(size + 1); // killough; cph: +1 for the tc_endspecials // save off the current thinkers for (th=thinkercap.next; th!=&thinkercap; th=th->next) { if (!th->function) { platlist_t *pl; ceilinglist_t *cl; //jff 2/22/98 add iter variable for ceilings // killough 2/8/98: fix plat original height bug. // Since acv==NULL, this could be a plat in stasis. // so check the active plats list, and save this // plat (jff: or ceiling) even if it is in stasis. for (pl=activeplats; pl; pl=pl->next) if (pl->plat == (plat_t *) th) // killough 2/14/98 goto plat; for (cl=activeceilings; cl; cl=cl->next) if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 goto ceiling; continue; } if (th->function == T_MoveCeiling) { ceiling_t *ceiling; ceiling: // killough 2/14/98 *save_p++ = tc_ceiling; PADSAVEP(); ceiling = (ceiling_t *)save_p; memcpy (ceiling, th, sizeof(*ceiling)); save_p += sizeof(*ceiling); ceiling->sector = (sector_t *)(ceiling->sector - sectors); continue; } if (th->function == T_VerticalDoor) { vldoor_t *door; *save_p++ = tc_door; PADSAVEP(); door = (vldoor_t *) save_p; memcpy (door, th, sizeof *door); save_p += sizeof(*door); door->sector = (sector_t *)(door->sector - sectors); //jff 1/31/98 archive line remembered by door as well door->line = (line_t *) (door->line ? door->line-lines : -1); continue; } if (th->function == T_MoveFloor) { floormove_t *floor; *save_p++ = tc_floor; PADSAVEP(); floor = (floormove_t *)save_p; memcpy (floor, th, sizeof(*floor)); save_p += sizeof(*floor); floor->sector = (sector_t *)(floor->sector - sectors); continue; } if (th->function == T_PlatRaise) { plat_t *plat; plat: // killough 2/14/98: added fix for original plat height above *save_p++ = tc_plat; PADSAVEP(); plat = (plat_t *)save_p; memcpy (plat, th, sizeof(*plat)); save_p += sizeof(*plat); plat->sector = (sector_t *)(plat->sector - sectors); continue; } if (th->function == T_LightFlash) { lightflash_t *flash; *save_p++ = tc_flash; PADSAVEP(); flash = (lightflash_t *)save_p; memcpy (flash, th, sizeof(*flash)); save_p += sizeof(*flash); flash->sector = (sector_t *)(flash->sector - sectors); continue; } if (th->function == T_StrobeFlash) { strobe_t *strobe; *save_p++ = tc_strobe; PADSAVEP(); strobe = (strobe_t *)save_p; memcpy (strobe, th, sizeof(*strobe)); save_p += sizeof(*strobe); strobe->sector = (sector_t *)(strobe->sector - sectors); continue; } if (th->function == T_Glow) { glow_t *glow; *save_p++ = tc_glow; PADSAVEP(); glow = (glow_t *)save_p; memcpy (glow, th, sizeof(*glow)); save_p += sizeof(*glow); glow->sector = (sector_t *)(glow->sector - sectors); continue; } // killough 10/4/98: save flickers if (th->function == T_FireFlicker) { fireflicker_t *flicker; *save_p++ = tc_flicker; PADSAVEP(); flicker = (fireflicker_t *)save_p; memcpy (flicker, th, sizeof(*flicker)); save_p += sizeof(*flicker); flicker->sector = (sector_t *)(flicker->sector - sectors); continue; } //jff 2/22/98 new case for elevators if (th->function == T_MoveElevator) { elevator_t *elevator; //jff 2/22/98 *save_p++ = tc_elevator; PADSAVEP(); elevator = (elevator_t *)save_p; memcpy (elevator, th, sizeof(*elevator)); save_p += sizeof(*elevator); elevator->sector = (sector_t *)(elevator->sector - sectors); continue; } // killough 3/7/98: Scroll effect thinkers if (th->function == T_Scroll) { *save_p++ = tc_scroll; memcpy (save_p, th, sizeof(scroll_t)); save_p += sizeof(scroll_t); continue; } // phares 3/22/98: Push/Pull effect thinkers if (th->function == T_Pusher) { *save_p++ = tc_pusher; memcpy (save_p, th, sizeof(pusher_t)); save_p += sizeof(pusher_t); continue; } } // add a terminating marker *save_p++ = tc_endspecials; } // // P_UnArchiveSpecials // void P_UnArchiveSpecials (void) { byte tclass; // read in saved thinkers while ((tclass = *save_p++) != tc_endspecials) // killough 2/14/98 switch (tclass) { case tc_ceiling: PADSAVEP(); { ceiling_t *ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); memcpy (ceiling, save_p, sizeof(*ceiling)); save_p += sizeof(*ceiling); ceiling->sector = §ors[(int)ceiling->sector]; ceiling->sector->ceilingdata = ceiling; //jff 2/22/98 if (ceiling->thinker.function) ceiling->thinker.function = T_MoveCeiling; P_AddThinker (&ceiling->thinker); P_AddActiveCeiling(ceiling); break; } case tc_door: PADSAVEP(); { vldoor_t *door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); memcpy (door, save_p, sizeof(*door)); save_p += sizeof(*door); door->sector = §ors[(int)door->sector]; //jff 1/31/98 unarchive line remembered by door as well door->line = (int)door->line!=-1? &lines[(int)door->line] : NULL; door->sector->ceilingdata = door; //jff 2/22/98 door->thinker.function = T_VerticalDoor; P_AddThinker (&door->thinker); break; } case tc_floor: PADSAVEP(); { floormove_t *floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); memcpy (floor, save_p, sizeof(*floor)); save_p += sizeof(*floor); floor->sector = §ors[(int)floor->sector]; floor->sector->floordata = floor; //jff 2/22/98 floor->thinker.function = T_MoveFloor; P_AddThinker (&floor->thinker); break; } case tc_plat: PADSAVEP(); { plat_t *plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); memcpy (plat, save_p, sizeof(*plat)); save_p += sizeof(*plat); plat->sector = §ors[(int)plat->sector]; plat->sector->floordata = plat; //jff 2/22/98 if (plat->thinker.function) plat->thinker.function = T_PlatRaise; P_AddThinker (&plat->thinker); P_AddActivePlat(plat); break; } case tc_flash: PADSAVEP(); { lightflash_t *flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); memcpy (flash, save_p, sizeof(*flash)); save_p += sizeof(*flash); flash->sector = §ors[(int)flash->sector]; flash->thinker.function = T_LightFlash; P_AddThinker (&flash->thinker); break; } case tc_strobe: PADSAVEP(); { strobe_t *strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); memcpy (strobe, save_p, sizeof(*strobe)); save_p += sizeof(*strobe); strobe->sector = §ors[(int)strobe->sector]; strobe->thinker.function = T_StrobeFlash; P_AddThinker (&strobe->thinker); break; } case tc_glow: PADSAVEP(); { glow_t *glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); memcpy (glow, save_p, sizeof(*glow)); save_p += sizeof(*glow); glow->sector = §ors[(int)glow->sector]; glow->thinker.function = T_Glow; P_AddThinker (&glow->thinker); break; } case tc_flicker: // killough 10/4/98 PADSAVEP(); { fireflicker_t *flicker = Z_Malloc (sizeof(*flicker), PU_LEVEL, NULL); memcpy (flicker, save_p, sizeof(*flicker)); save_p += sizeof(*flicker); flicker->sector = §ors[(int)flicker->sector]; flicker->thinker.function = T_FireFlicker; P_AddThinker (&flicker->thinker); break; } //jff 2/22/98 new case for elevators case tc_elevator: PADSAVEP(); { elevator_t *elevator = Z_Malloc (sizeof(*elevator), PU_LEVEL, NULL); memcpy (elevator, save_p, sizeof(*elevator)); save_p += sizeof(*elevator); elevator->sector = §ors[(int)elevator->sector]; elevator->sector->floordata = elevator; //jff 2/22/98 elevator->sector->ceilingdata = elevator; //jff 2/22/98 elevator->thinker.function = T_MoveElevator; P_AddThinker (&elevator->thinker); break; } case tc_scroll: // killough 3/7/98: scroll effect thinkers { scroll_t *scroll = Z_Malloc (sizeof(scroll_t), PU_LEVEL, NULL); memcpy (scroll, save_p, sizeof(scroll_t)); save_p += sizeof(scroll_t); scroll->thinker.function = T_Scroll; P_AddThinker(&scroll->thinker); break; } case tc_pusher: // phares 3/22/98: new Push/Pull effect thinkers { pusher_t *pusher = Z_Malloc (sizeof(pusher_t), PU_LEVEL, NULL); memcpy (pusher, save_p, sizeof(pusher_t)); save_p += sizeof(pusher_t); pusher->thinker.function = T_Pusher; pusher->source = P_GetPushThing(pusher->affectee); P_AddThinker(&pusher->thinker); break; } default: I_Error("P_UnarchiveSpecials: Unknown tclass %i in savegame", tclass); } } // killough 2/16/98: save/restore random number generator state information void P_ArchiveRNG(void) { CheckSaveGame(sizeof rng); memcpy(save_p, &rng, sizeof rng); save_p += sizeof rng; } void P_UnArchiveRNG(void) { memcpy(&rng, save_p, sizeof rng); save_p += sizeof rng; } // killough 2/22/98: Save/restore automap state // killough 2/22/98: Save/restore automap state void P_ArchiveMap(void) { int zero = 0, one = 1; CheckSaveGame(2 * sizeof zero + sizeof markpointnum + markpointnum * sizeof *markpoints + sizeof automapmode + sizeof one); memcpy(save_p, &automapmode, sizeof automapmode); save_p += sizeof automapmode; memcpy(save_p, &one, sizeof one); // CPhipps - used to be viewactive, now save_p += sizeof one; // that's worked out locally by D_Display memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be followplayer save_p += sizeof zero; // that is now part of automapmode memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be automap_grid, ditto save_p += sizeof zero; memcpy(save_p, &markpointnum, sizeof markpointnum); save_p += sizeof markpointnum; if (markpointnum) { memcpy(save_p, markpoints, sizeof *markpoints * markpointnum); save_p += markpointnum * sizeof *markpoints; } } void P_UnArchiveMap(void) { int unused; memcpy(&automapmode, save_p, sizeof automapmode); save_p += sizeof automapmode; memcpy(&unused, save_p, sizeof unused); save_p += sizeof unused; memcpy(&unused, save_p, sizeof unused); save_p += sizeof unused; memcpy(&unused, save_p, sizeof unused); save_p += sizeof unused; if (automapmode & am_active) AM_Start(); memcpy(&markpointnum, save_p, sizeof markpointnum); save_p += sizeof markpointnum; if (markpointnum) { while (markpointnum >= markpointnum_max) markpoints = realloc(markpoints, sizeof *markpoints * (markpointnum_max = markpointnum_max ? markpointnum_max*2 : 16)); memcpy(markpoints, save_p, markpointnum * sizeof *markpoints); save_p += markpointnum * sizeof *markpoints; } }