/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WORLDSTATEVERSION_NOCLASSCONTAINER 9 #define WORLDSTATEVERSION_MULTITEXTURING 8 #define WORLDSTATEVERSION_SHADOWSPERMIP 7 #define WORLDSTATEVERSION_CURRENT WORLDSTATEVERSION_NOCLASSCONTAINER CWorld *_pwoCurrentLoading = NULL; // world that is currently loading BOOL _bReadEntitiesByID = FALSE; extern BOOL _bPortalSectorLinksPreLoaded; extern BOOL _bEntitySectorLinksPreLoaded; extern BOOL _bFileReplacingApplied; /* * Save entire world (both brushes current state). */ void CWorld::Save_t(const CTFileName &fnmWorld) // throw char * { // create the file CTFileStream strmFile; strmFile.Create_t(fnmWorld); // save engine build _pNetwork->WriteVersion_t(strmFile); // write the world to the file Write_t(&strmFile); } /* * Load entire world (both brushes and current state). */ void CWorld::Load_t(const CTFileName &fnmWorld) // throw char * { // remember the file wo_fnmFileName = fnmWorld; // open the file CTFileStream strmFile; strmFile.Open_t(fnmWorld); // check engine build allowing reinit BOOL bNeedsReinit; _pNetwork->CheckVersion_t(strmFile, TRUE, bNeedsReinit); // read the world from the file Read_t(&strmFile); // close the file strmFile.Close(); // if reinit is needed if (bNeedsReinit) { // reinitialize SetProgressDescription(TRANS("converting from old version")); CallProgressHook_t(0.0f); ReinitializeEntities(); CallProgressHook_t(1.0f); // reinitialize SetProgressDescription(TRANS("saving converted file")); CallProgressHook_t(0.0f); Save_t(fnmWorld); CallProgressHook_t(1.0f); } } /* * Write entire world (both brushes and current state). */ void CWorld::Write_t(CTStream *postrm) // throw char * { // need high FPU precision CSetFPUPrecision FPUPrecision(FPT_53BIT); // delete all predictor entities before saving UnmarkForPrediction(); DeletePredictors(); // lock all arrays and containers LockAll(); postrm->WriteID_t("WRLD"); // 'world' // write the world brushes to the file WriteBrushes_t(postrm); // write current world state to the file WriteState_t(postrm); postrm->WriteID_t("WEND"); // 'world end' // unlock all arrays and containers UnlockAll(); } /* * Read entire world (both brushes and current state). */ void CWorld::Read_t(CTStream *pistrm) // throw char * { _pfWorldEditingProfile.IncrementAveragingCounter(); _bFileReplacingApplied = FALSE; // need high FPU precision CSetFPUPrecision FPUPrecision(FPT_53BIT); // clear eventual old data in the world Clear(); // lock all arrays and containers LockAll(); pistrm->ExpectID_t("WRLD"); // 'world' // read the world brushes from the file ReadBrushes_t(pistrm); // read current world state from the file ReadState_t(pistrm); pistrm->ExpectID_t("WEND"); // 'world end' // unlock all arrays and containers UnlockAll(); if( _bFileReplacingApplied) WarningMessage("Some of files needed to load world have been replaced while loading"); } /* * Load just world brushes from a file with entire world information. */ void CWorld::LoadBrushes_t(const CTFileName &fnmWorld) // throw char * { // remember the file wo_fnmFileName = fnmWorld; // open the file CTFileStream strmFile; strmFile.Open_t(fnmWorld); // check engine build disallowing reinit BOOL bNeedsReinit; _pNetwork->CheckVersion_t(strmFile, FALSE, bNeedsReinit); ASSERT(!bNeedsReinit); strmFile.ExpectID_t("WRLD"); // 'world' // read the world brushes from the file ReadBrushes_t(&strmFile); } /* * Read world brushes from stream. */ void CWorld::ReadBrushes_t( CTStream *istrm)// throw char * { _pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_READBRUSHES); // must be in 53bit mode when managing brushes CSetFPUPrecision FPUPrecision(FPT_53BIT); ReadInfo_t(istrm, FALSE); SetProgressDescription(TRANS("loading world textures")); CallProgressHook_t(0.0f); // read the brushes from the file _pwoCurrentLoading = this; istrm->DictionaryReadBegin_t(); istrm->DictionaryPreload_t(); CallProgressHook_t(1.0f); SetProgressDescription(TRANS("loading brushes")); CallProgressHook_t(0.0f); wo_baBrushes.Read_t(istrm); CallProgressHook_t(1.0f); // if there are some terrains in world if(istrm->PeekID_t()==CChunkID("TRAR")) { // 'terrain archive' SetProgressDescription(TRANS("loading terrains")); CallProgressHook_t(0.0f); wo_taTerrains.Read_t(istrm); CallProgressHook_t(1.0f); } istrm->DictionaryReadEnd_t(); _pwoCurrentLoading = NULL; _pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_READBRUSHES); } /* * Write world brushes to stream. */ void CWorld::WriteBrushes_t( CTStream *ostrm) // throw char * { WriteInfo_t(ostrm); // write the brushes to the file ostrm->DictionaryWriteBegin_t(CTString(""), 0); wo_baBrushes.Write_t(ostrm); wo_taTerrains.Write_t(ostrm); ostrm->DictionaryWriteEnd_t(); } /* * Read current world state from stream. */ void CWorld::ReadState_t( CTStream *istr) // throw char * { _pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_READSTATE); // must be in 24bit mode when managing entities CSetFPUPrecision FPUPrecision(FPT_24BIT); CTmpPrecachingNow tpn; _bReadEntitiesByID = FALSE; SetProgressDescription(TRANS("loading models")); CallProgressHook_t(0.0f); wo_slStateDictionaryOffset = istr->DictionaryReadBegin_t(); istr->DictionaryPreload_t(); CallProgressHook_t(1.0f); istr->ExpectID_t("WSTA"); // world state // read the version number INDEX iSavedVersion; (*istr)>>iSavedVersion; // if the version number is the newest if(iSavedVersion==WORLDSTATEVERSION_CURRENT) { // read current version ReadState_new_t(istr); // if the version number is not the newest } else { // if the version can be converted if(iSavedVersion==WORLDSTATEVERSION_CURRENT-1) { // show warning // WarningMessage( // "World state version was %d (old).\n" // "Auto-converting to version %d.", // iSavedVersion, WORLDSTATEVERSION_CURRENT); // read previous version ReadState_old_t(istr); // if the version can be converted } else if(iSavedVersion==WORLDSTATEVERSION_CURRENT-2) { // show warning WarningMessage( TRANS("World state version was %d (very old).\n" "Auto-converting to version %d."), iSavedVersion, WORLDSTATEVERSION_CURRENT); // read previous version ReadState_veryold_t(istr); } else { // report error ThrowF_t( TRANS("World state version is %d (unsupported).\n" "Current supported version is %d."), iSavedVersion, WORLDSTATEVERSION_CURRENT); } } istr->DictionaryReadEnd_t(); SetProgressDescription(TRANS("precaching")); CallProgressHook_t(0.0f); // precache data needed by entities if( gam_iPrecachePolicy==PRECACHE_SMART) { PrecacheEntities_t(); } CallProgressHook_t(1.0f); _pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_READSTATE); } /* * Read current world state from stream -- preprevious version. */ void CWorld::ReadState_veryold_t( CTStream *istr) // throw char * { // read the world description (*istr)>>wo_strDescription; // read the world background color (*istr)>>wo_colBackground; INDEX ienBackgroundViewer = -1; // if background viewer entity is saved here if (istr->PeekID_t() == CChunkID("BGVW")) { // read background viewer entity index istr->ExpectID_t("BGVW"); // background viewer (*istr)>>ienBackgroundViewer; } else { BOOL bUseBackgroundTexture; (*istr)>>(SLONG &)bUseBackgroundTexture; // read the world background texture name CTString strBackgroundTexture; (*istr)>>strBackgroundTexture; // saved as string to bypass dependency catcher // skip the 6 dummy texture names used for dependencies CTFileName fnmDummy; (*istr)>>fnmDummy>>fnmDummy>>fnmDummy >>fnmDummy>>fnmDummy>>fnmDummy; } // if backdrop image data is saved here if (istr->PeekID_t() == CChunkID("BRDP")) { // read backdrop image data istr->ExpectID_t("BRDP"); // backdrop (*istr)>>wo_strBackdropUp; (*istr)>>wo_strBackdropFt; (*istr)>>wo_strBackdropRt; (*istr)>>wo_fUpW>>wo_fUpL>>wo_fUpCX>>wo_fUpCZ; (*istr)>>wo_fFtW>>wo_fFtH>>wo_fFtCX>>wo_fFtCY; (*istr)>>wo_fRtL>>wo_fRtH>>wo_fRtCZ>>wo_fRtCY; } // if backdrop object name is saved here if (istr->PeekID_t() == CChunkID("BDRO")) { // read backdrop object name istr->ExpectID_t("BDRO"); // backdrop object (*istr)>>wo_strBackdropObject; } // if viewer position should be loaded if (istr->PeekID_t() == CChunkID("VWPS")) { istr->ExpectID_t("VWPS"); // viewer position (*istr)>>wo_plFocus; (*istr)>>wo_fTargetDistance; } istr->ExpectID_t("SHAN"); // shadow animations // for all anim objects {for(INDEX iao=0; iao<256; iao++) { // skip animation object CAnimObject ao; ao.Read_t(istr); }} istr->ExpectID_t("ECLs"); // entity classes // read number of entity classes INDEX ctEntityClasses; (*istr)>>ctEntityClasses; CStaticArray cecClasses; cecClasses.New(ctEntityClasses); // for each entity class {for(INDEX iEntityClass=0; iEntityClass>cecClasses[iEntityClass]; }} /* NOTE: Entities must be loaded in two passes, since all entities must be created * before any entity pointer properties can be loaded. */ istr->ExpectID_t("ENTs"); // entities // read number of entities INDEX ctEntities; (*istr)>>ctEntities; // for each entity {for(INDEX iEntity=0; iEntity>iEntityClass>>plPlacement; // create an entity of that class /* CEntity *penNew = */ CreateEntity_t(plPlacement, cecClasses[iEntityClass]); }} // for each entity {for(INDEX iEntity=0; iEntity1); // must be referenced by someone else too en.RemReference(); wo_cenEntities.Remove(&en); } }} // after all entities have been read and brushes are connected to entities, // calculate bounding boxes of all brushes wo_baBrushes.CalculateBoundingBoxes(); // after all bounding boxes and BSP trees are created, // create links between portals and sectors on their other side wo_baBrushes.LinkPortalsAndSectors(); wo_bPortalLinksUpToDate = TRUE; // create links between sectors and non-zoning entities in sectors LinkEntitiesToSectors(); } /* * Read current world state from stream -- previous version. */ void CWorld::ReadState_old_t( CTStream *istr) // throw char * { // read the world description (*istr)>>wo_strDescription; // read the world background color (*istr)>>wo_colBackground; INDEX ienBackgroundViewer = -1; // read background viewer entity index istr->ExpectID_t("BGVW"); // background viewer (*istr)>>ienBackgroundViewer; // read backdrop image data istr->ExpectID_t("BRDP"); // backdrop (*istr)>>wo_strBackdropUp; (*istr)>>wo_strBackdropFt; (*istr)>>wo_strBackdropRt; (*istr)>>wo_fUpW>>wo_fUpL>>wo_fUpCX>>wo_fUpCZ; (*istr)>>wo_fFtW>>wo_fFtH>>wo_fFtCX>>wo_fFtCY; (*istr)>>wo_fRtL>>wo_fRtH>>wo_fRtCZ>>wo_fRtCY; // read backdrop object name istr->ExpectID_t("BDRO"); // backdrop object (*istr)>>wo_strBackdropObject; istr->ExpectID_t("VWPS"); // viewer position (*istr)>>wo_plFocus; (*istr)>>wo_fTargetDistance; // if thumbnail saving position should be loaded if (istr->PeekID_t() == CChunkID("TBPS")) { istr->ExpectID_t("TBPS"); // thumbnail position (*istr)>>wo_plThumbnailFocus; (*istr)>>wo_fThumbnailTargetDistance; } istr->ExpectID_t("SHAN"); // shadow animations // for all anim objects {for(INDEX iao=0; iao<256; iao++) { // skip animation object CAnimObject ao; ao.Read_t(istr); }} istr->ExpectID_t("ECLs"); // entity classes // read number of entity classes INDEX ctEntityClasses; (*istr)>>ctEntityClasses; CStaticArray cecClasses; cecClasses.New(ctEntityClasses); // for each entity class {for(INDEX iEntityClass=0; iEntityClass>cecClasses[iEntityClass]; }} /* NOTE: Entities must be loaded in two passes, since all entities must be created * before any entity pointer properties can be loaded. */ istr->ExpectID_t("ENTs"); // entities // read number of entities INDEX ctEntities; (*istr)>>ctEntities; // for each entity {for(INDEX iEntity=0; iEntity>iEntityClass>>plPlacement; // create an entity of that class /* CEntity *penNew = */ CreateEntity_t(plPlacement, cecClasses[iEntityClass]); }} // for each entity {for(INDEX iEntity=0; iEntity1); // must be referenced by someone else too en.RemReference(); wo_cenEntities.Remove(&en); } }} // after all entities have been read and brushes are connected to entities, // calculate bounding boxes of all brushes wo_baBrushes.CalculateBoundingBoxes(); // after all bounding boxes and BSP trees are created, // create links between portals and sectors on their other side if needed if (!_bPortalSectorLinksPreLoaded) { wo_baBrushes.LinkPortalsAndSectors(); } wo_bPortalLinksUpToDate = TRUE; _bPortalSectorLinksPreLoaded = FALSE; // create links between sectors and non-zoning entities in sectors LinkEntitiesToSectors(); } /* * Read current world state from stream -- current version. */ void CWorld::ReadState_new_t( CTStream *istr) // throw char * { // read the world info ReadInfo_t(istr, TRUE); // read the world background color (*istr)>>wo_colBackground; // read entity ID counter if (istr->PeekID_t()==CChunkID("NFID")) { istr->ExpectID_t("NFID"); (*istr)>>wo_ulNextEntityID; } else { wo_ulNextEntityID = 1; } INDEX ienBackgroundViewer = -1; // read background viewer entity index istr->ExpectID_t("BGVW"); // background viewer (*istr)>>ienBackgroundViewer; // read backdrop image data istr->ExpectID_t("BRDP"); // backdrop (*istr)>>wo_strBackdropUp; (*istr)>>wo_strBackdropFt; (*istr)>>wo_strBackdropRt; (*istr)>>wo_fUpW>>wo_fUpL>>wo_fUpCX>>wo_fUpCZ; (*istr)>>wo_fFtW>>wo_fFtH>>wo_fFtCX>>wo_fFtCY; (*istr)>>wo_fRtL>>wo_fRtH>>wo_fRtCZ>>wo_fRtCY; // read backdrop object name istr->ExpectID_t("BDRO"); // backdrop object (*istr)>>wo_strBackdropObject; istr->ExpectID_t("VWPS"); // viewer position (*istr)>>wo_plFocus; (*istr)>>wo_fTargetDistance; // if thumbnail saving position should be loaded if (istr->PeekID_t() == CChunkID("TBPS")) { istr->ExpectID_t("TBPS"); // thumbnail position (*istr)>>wo_plThumbnailFocus; (*istr)>>wo_fThumbnailTargetDistance; } /* NOTE: Entities must be loaded in two passes, since all entities must be created * before any entity pointer properties can be loaded. */ if (istr->PeekID_t()== CChunkID("ENTs")) { istr->ExpectID_t("ENTs"); // entities } else { istr->ExpectID_t("ENs2"); // entities v2 _bReadEntitiesByID = TRUE; } // read number of entities INDEX ctEntities; (*istr)>>ctEntities; SetProgressDescription(TRANS("creating entities")); CallProgressHook_t(0.0f); // for each entity {for(INDEX iEntity=0; iEntity>ulID; } // read entity class and entity placement CTFileName fnmClass; CPlacement3D plPlacement; (*istr)>>fnmClass>>plPlacement; // create an entity of that class CEntity *penNew = CreateEntity_t(plPlacement, fnmClass); // adjust id if needed if (_bReadEntitiesByID) { wo_ulNextEntityID--; penNew->en_ulID = ulID; } CallProgressHook_t(FLOAT(iEntity)/ctEntities); }} CallProgressHook_t(1.0f); SetProgressDescription(TRANS("loading entities")); CallProgressHook_t(0.0f); // for each entity {for(INDEX iEntity=0; iEntity1); // must be referenced by someone else too en.RemReference(); wo_cenEntities.Remove(&en); } }} wo_cenEntities.Lock(); // if version with entity order if (istr->PeekID_t()==CChunkID("ENOR")) { // entity order istr->ExpectID_t(CChunkID("ENOR")); // entity order INDEX ctEntities; *istr>>ctEntities; wo_cenEntities.Clear(); // for each non-deleted entity for(INDEX i=0; i>ulID; wo_cenEntities.Add(EntityFromID(ulID)); } } // some shadow layers might not have light sources, remove such to prevent crashes wo_baBrushes.RemoveDummyLayers(); SetProgressDescription(TRANS("preparing world")); CallProgressHook_t(0.0f); // after all entities have been read and brushes are connected to entities, // calculate bounding boxes of all brushes wo_baBrushes.CalculateBoundingBoxes(); CallProgressHook_t(0.3f); // after all bounding boxes and BSP trees are created, // create links between portals and sectors on their other side if needed if (!_bPortalSectorLinksPreLoaded) { wo_baBrushes.LinkPortalsAndSectors(); } wo_bPortalLinksUpToDate = TRUE; // create links between sectors and non-zoning entities in sectors wo_baBrushes.ReadEntitySectorLinks_t(*istr); CallProgressHook_t(0.6f); LinkEntitiesToSectors(); CallProgressHook_t(1.0f); _bPortalSectorLinksPreLoaded = FALSE; _bEntitySectorLinksPreLoaded = FALSE; } /* * Write current world state to stream. */ void CWorld::WriteState_t( CTStream *ostr, BOOL bImportDictionary /* = FALSE */) // throw char * { // must be in 24bit mode when managing entities CSetFPUPrecision FPUPrecision(FPT_24BIT); // all predictors must be deleted ASSERT( wo_cenPredicted.Count()==0 && wo_cenPredictor.Count()==0 && wo_cenWillBePredicted.Count()==0); if (bImportDictionary) { ostr->DictionaryWriteBegin_t(wo_fnmFileName, wo_slStateDictionaryOffset); } else { ostr->DictionaryWriteBegin_t(CTString(""), 0); } (*ostr).WriteID_t("WSTA"); // world state // write the world version (*ostr)<WriteID_t("NFID"); (*ostr)<WriteID_t("BGVW"); // background viewer CEntity *penBackgroundViewer = GetBackgroundViewer(); if (penBackgroundViewer==NULL) { (*ostr)<en_ulID; } // write backdrop image data ostr->WriteID_t("BRDP"); // backdrop (*ostr)<WriteID_t("BDRO"); // backdrop object (*ostr)<WriteID_t("VWPS"); // viewer placement (*ostr)<WriteID_t("TBPS"); // thumbnail placement (*ostr)<WriteID_t("ENs2"); // entities // write number of entities (*ostr)<GetName()<GetPos_t(); // serialize entity into stream iten->Write_t(ostr); // save the size of data in start chunk, after chunkid and entity id SLONG slOffsetAfter = ostr->GetPos_t(); ostr->SetPos_t(slOffset+2*sizeof(SLONG)); *ostr<SetPos_t(slOffsetAfter); }} ostr->WriteID_t(CChunkID("ENOR")); // entity order *ostr<en_ulID; }} wo_baBrushes.WriteEntitySectorLinks_t(*ostr); ostr->DictionaryWriteEnd_t(); } // read/write world information (description, name, flags...) void CWorld::ReadInfo_t(CTStream *strm, BOOL bMaybeDescription) // throw char * { // if version with world info if (strm->PeekID_t()==CChunkID("WLIF")) { // world info strm->ExpectID_t(CChunkID("WLIF")); // world info // skip eventual translation chunk if (strm->PeekID_t()==CChunkID("DTRS")) { strm->ExpectID_t("DTRS"); } // read the name (*strm)>>wo_strName; // read the flags (*strm)>>wo_ulSpawnFlags; // read the world description (*strm)>>wo_strDescription; // if version with description only } else if (bMaybeDescription) { // read the world description (*strm)>>wo_strDescription; } } void CWorld::WriteInfo_t(CTStream *strm) // throw char * { strm->WriteID_t(CChunkID("WLIF")); // world info // write the name strm->WriteID_t(CChunkID("DTRS")); (*strm)<