2011-04-06 20:11:08 +04:00
# include "loadcell.hpp"
2011-09-10 11:22:32 +02:00
# include <string>
2021-07-21 16:00:25 +00:00
# include <limits>
2013-01-19 23:33:18 +01:00
# include <list>
2014-01-14 12:25:35 +01:00
2021-07-21 16:00:25 +00:00
# include <components/debug/debuglog.hpp>
2022-08-03 00:00:54 +02:00
# include <components/misc/strings/algorithm.hpp>
2014-01-14 12:25:35 +01:00
2012-09-23 22:41:41 +04:00
# include "esmreader.hpp"
# include "esmwriter.hpp"
2014-01-14 12:25:35 +01:00
# include "cellid.hpp"
2012-09-17 11:37:50 +04:00
2022-04-12 00:18:39 +02:00
namespace ESM
{
2013-12-17 21:19:05 +01:00
namespace
2011-04-06 20:11:08 +04:00
{
2013-12-17 21:19:05 +01:00
///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum
2022-04-12 00:18:39 +02:00
void adjustRefNum ( RefNum & refNum , const ESMReader & reader )
2013-12-17 21:19:05 +01:00
{
2015-05-27 10:19:26 +10:00
unsigned int local = ( refNum . mIndex & 0xff000000 ) > > 24 ;
2011-04-06 20:11:08 +04:00
2015-05-27 10:19:26 +10:00
// If we have an index value that does not make sense, assume that it was an addition
// by the present plugin (but a faulty one)
2020-04-19 16:31:54 +02:00
if ( local & & local < = reader . getParentFileIndices ( ) . size ( ) )
2013-12-17 21:19:05 +01:00
{
// If the most significant 8 bits are used, then this reference already exists.
// In this case, do not spawn a new reference, but overwrite the old one.
refNum . mIndex & = 0x00ffffff ; // delete old plugin ID
2020-04-19 16:31:54 +02:00
refNum . mContentFile = reader . getParentFileIndices ( ) [ local - 1 ] ;
2013-12-17 21:19:05 +01:00
}
else
{
// This is an addition by the present plugin. Set the corresponding plugin index.
refNum . mContentFile = reader . getIndex ( ) ;
}
}
2013-02-09 13:00:57 +01:00
}
2022-04-12 00:18:39 +02:00
}
2013-02-09 13:00:57 +01:00
2013-12-17 21:19:05 +01:00
namespace ESM
2013-02-09 13:00:57 +01:00
{
2013-12-17 21:19:05 +01:00
// Some overloaded compare operators.
2014-05-25 14:13:07 +02:00
bool operator = = ( const MovedCellRef & ref , const RefNum & refNum )
2013-12-17 21:19:05 +01:00
{
return ref . mRefNum = = refNum ;
}
2014-05-25 14:13:07 +02:00
bool operator = = ( const CellRef & ref , const RefNum & refNum )
2013-12-17 21:19:05 +01:00
{
return ref . mRefNum = = refNum ;
}
2013-02-09 13:00:57 +01:00
2015-07-20 17:23:14 +03:00
void Cell : : load ( ESMReader & esm , bool & isDeleted , bool saveContext )
2015-07-16 22:31:59 +03:00
{
2015-07-20 17:23:14 +03:00
loadNameAndData ( esm , isDeleted ) ;
2015-07-16 22:31:59 +03:00
loadCell ( esm , saveContext ) ;
}
2011-04-06 20:11:08 +04:00
2015-07-20 17:23:14 +03:00
void Cell : : loadNameAndData ( ESMReader & esm , bool & isDeleted )
2015-07-16 22:31:59 +03:00
{
2015-07-20 17:23:14 +03:00
isDeleted = false ;
2015-07-07 20:41:39 +03:00
2015-07-28 15:04:22 +03:00
blank ( ) ;
2015-07-20 17:23:14 +03:00
bool hasData = false ;
bool isLoaded = false ;
while ( ! isLoaded & & esm . hasMoreSubs ( ) )
2015-07-16 22:31:59 +03:00
{
2015-07-20 17:23:14 +03:00
esm . getSubName ( ) ;
2021-10-17 02:52:22 +02:00
switch ( esm . retSubName ( ) . toInt ( ) )
2015-07-20 17:23:14 +03:00
{
2022-04-12 00:18:39 +02:00
case SREC_NAME :
2015-07-20 17:23:14 +03:00
mName = esm . getHString ( ) ;
break ;
2022-04-12 00:18:39 +02:00
case fourCC ( " DATA " ) :
2022-04-10 13:35:00 +02:00
esm . getHTSized < 12 > ( mData ) ;
2015-07-20 17:23:14 +03:00
hasData = true ;
break ;
2022-04-12 00:18:39 +02:00
case SREC_DELE :
2015-07-20 17:23:14 +03:00
esm . skipHSub ( ) ;
isDeleted = true ;
break ;
default :
esm . cacheSubName ( ) ;
isLoaded = true ;
break ;
}
2015-07-16 22:31:59 +03:00
}
2015-07-20 17:23:14 +03:00
if ( ! hasData )
esm . fail ( " Missing DATA subrecord " ) ;
2015-11-26 23:46:41 +01:00
mCellId . mPaged = ! ( mData . mFlags & Interior ) ;
if ( mCellId . mPaged )
{
2022-04-12 00:18:39 +02:00
mCellId . mWorldspace = CellId : : sDefaultWorldspace ;
2015-11-26 23:46:41 +01:00
mCellId . mIndex . mX = mData . mX ;
mCellId . mIndex . mY = mData . mY ;
}
else
{
mCellId . mWorldspace = Misc : : StringUtils : : lowerCase ( mName ) ;
mCellId . mIndex . mX = 0 ;
mCellId . mIndex . mY = 0 ;
}
2015-07-16 22:31:59 +03:00
}
2011-04-06 20:11:08 +04:00
2015-07-16 22:31:59 +03:00
void Cell : : loadCell ( ESMReader & esm , bool saveContext )
2011-04-06 20:11:08 +04:00
{
2021-07-21 16:00:25 +00:00
bool overriding = ! mName . empty ( ) ;
2015-07-20 17:23:14 +03:00
bool isLoaded = false ;
2020-02-02 15:12:53 +03:00
mHasAmbi = false ;
2015-07-20 17:23:14 +03:00
while ( ! isLoaded & & esm . hasMoreSubs ( ) )
2012-03-30 10:12:28 +02:00
{
2015-07-20 17:23:14 +03:00
esm . getSubName ( ) ;
2021-10-17 02:52:22 +02:00
switch ( esm . retSubName ( ) . toInt ( ) )
2015-07-16 22:31:59 +03:00
{
2022-04-12 00:18:39 +02:00
case fourCC ( " INTV " ) :
2015-07-20 17:23:14 +03:00
int waterl ;
esm . getHT ( waterl ) ;
mWater = static_cast < float > ( waterl ) ;
mWaterInt = true ;
break ;
2022-04-12 00:18:39 +02:00
case fourCC ( " WHGT " ) :
2021-07-21 16:00:25 +00:00
float waterLevel ;
esm . getHT ( waterLevel ) ;
2015-07-28 15:04:22 +03:00
mWaterInt = false ;
2021-07-21 16:00:25 +00:00
if ( ! std : : isfinite ( waterLevel ) )
{
if ( ! overriding )
mWater = std : : numeric_limits < float > : : max ( ) ;
2022-06-19 13:28:33 +02:00
Log ( Debug : : Warning ) < < " Warning: Encountered invalid water level in cell " < < mName < < " defined in " < < esm . getContext ( ) . filename ; //TODO(Project579): This will probably break in windows with unicode paths
2021-07-21 16:00:25 +00:00
}
else
mWater = waterLevel ;
2015-07-20 17:23:14 +03:00
break ;
2022-04-12 00:18:39 +02:00
case fourCC ( " AMBI " ) :
2015-07-20 17:23:14 +03:00
esm . getHT ( mAmbi ) ;
2020-02-02 15:12:53 +03:00
mHasAmbi = true ;
2015-07-20 17:23:14 +03:00
break ;
2022-04-12 00:18:39 +02:00
case fourCC ( " RGNN " ) :
2015-07-20 17:23:14 +03:00
mRegion = esm . getHString ( ) ;
break ;
2022-04-12 00:18:39 +02:00
case fourCC ( " NAM5 " ) :
2015-07-20 17:23:14 +03:00
esm . getHT ( mMapColor ) ;
break ;
2022-04-12 00:18:39 +02:00
case fourCC ( " NAM0 " ) :
2015-07-20 17:23:14 +03:00
esm . getHT ( mRefNumCounter ) ;
break ;
default :
esm . cacheSubName ( ) ;
isLoaded = true ;
break ;
2015-07-16 22:31:59 +03:00
}
2014-01-18 21:11:12 +01:00
}
2011-04-06 20:11:08 +04:00
2021-06-29 19:20:01 +10:00
if ( saveContext )
2015-07-20 17:23:14 +03:00
{
2015-07-16 22:31:59 +03:00
mContextList . push_back ( esm . getContext ( ) ) ;
esm . skipRecord ( ) ;
}
2011-04-06 20:11:08 +04:00
}
2012-09-30 23:34:53 +04:00
2015-07-16 22:31:59 +03:00
void Cell : : postLoad ( ESMReader & esm )
{
// Save position of the cell references and move on
2013-03-05 20:25:20 +04:00
mContextList . push_back ( esm . getContext ( ) ) ;
esm . skipRecord ( ) ;
}
2015-07-20 17:23:14 +03:00
void Cell : : save ( ESMWriter & esm , bool isDeleted ) const
2015-07-07 20:41:39 +03:00
{
2019-12-03 18:46:42 +03:00
esm . writeHNCString ( " NAME " , mName ) ;
2015-07-28 15:04:22 +03:00
esm . writeHNT ( " DATA " , mData , 12 ) ;
2015-07-07 20:41:39 +03:00
2015-07-20 17:23:14 +03:00
if ( isDeleted )
2015-07-16 22:31:59 +03:00
{
2021-07-06 14:57:58 +10:00
esm . writeHNString ( " DELE " , " " , 3 ) ;
2015-07-28 15:04:22 +03:00
return ;
2012-09-20 20:33:30 +04:00
}
2012-04-06 21:14:52 +02:00
2015-07-16 22:31:59 +03:00
if ( mData . mFlags & Interior )
{
if ( mWaterInt ) {
int water =
( mWater > = 0 ) ? ( int ) ( mWater + 0.5 ) : ( int ) ( mWater - 0.5 ) ;
esm . writeHNT ( " INTV " , water ) ;
} else {
esm . writeHNT ( " WHGT " , mWater ) ;
}
if ( mData . mFlags & QuasiEx )
esm . writeHNOCString ( " RGNN " , mRegion ) ;
else
2020-02-02 15:12:53 +03:00
{
// Try to avoid saving ambient lighting information when it's unnecessary.
// This is to fix black lighting in resaved cell records that lack this information.
if ( mHasAmbi )
esm . writeHNT ( " AMBI " , mAmbi , 16 ) ;
}
2015-07-16 22:31:59 +03:00
}
2012-04-06 21:04:30 +02:00
else
2015-07-16 22:31:59 +03:00
{
esm . writeHNOCString ( " RGNN " , mRegion ) ;
if ( mMapColor ! = 0 )
esm . writeHNT ( " NAM5 " , mMapColor ) ;
}
2021-06-29 19:20:01 +10:00
}
2011-04-06 20:11:08 +04:00
2021-06-29 19:20:01 +10:00
void Cell : : saveTempMarker ( ESMWriter & esm , int tempCount ) const
{
if ( tempCount ! = 0 )
esm . writeHNT ( " NAM0 " , tempCount ) ;
2011-09-10 11:22:32 +02:00
}
2015-07-16 22:31:59 +03:00
void Cell : : restore ( ESMReader & esm , int iCtx ) const
2011-09-10 11:22:32 +02:00
{
2015-07-16 22:31:59 +03:00
esm . restoreContext ( mContextList . at ( iCtx ) ) ;
2011-09-10 11:22:32 +02:00
}
2015-07-16 22:31:59 +03:00
std : : string Cell : : getDescription ( ) const
2015-01-24 15:01:38 +01:00
{
2015-07-16 22:31:59 +03:00
if ( mData . mFlags & Interior )
return mName ;
2020-05-09 20:17:49 +03:00
std : : string cellGrid = " ( " + std : : to_string ( mData . mX ) + " , " + std : : to_string ( mData . mY ) + " ) " ;
if ( ! mName . empty ( ) )
return mName + ' ' + cellGrid ;
// FIXME: should use sDefaultCellname GMST instead, but it's not available in this scope
std : : string region = ! mRegion . empty ( ) ? mRegion : " Wilderness " ;
return region + ' ' + cellGrid ;
2014-11-30 04:00:06 +11:00
}
2014-11-29 20:39:25 +11:00
2021-07-12 17:30:39 +02:00
bool Cell : : getNextRef ( ESMReader & esm , CellRef & ref , bool & isDeleted )
2015-07-16 22:31:59 +03:00
{
2015-07-20 17:23:14 +03:00
isDeleted = false ;
2015-07-16 22:31:59 +03:00
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
if ( ! esm . hasMoreSubs ( ) )
return false ;
2013-02-27 01:37:40 +04:00
2021-07-12 18:20:51 +02:00
// MVRF are FRMR are present in pairs. MVRF indicates that following FRMR describes moved CellRef.
// This function has to skip all moved CellRefs therefore read all such pairs to ignored values.
while ( esm . isNextSub ( " MVRF " ) )
2015-07-16 22:31:59 +03:00
{
2021-07-12 18:20:51 +02:00
MovedCellRef movedCellRef ;
esm . getHT ( movedCellRef . mRefNum . mIndex ) ;
esm . getHNOT ( movedCellRef . mTarget , " CNDT " ) ;
CellRef skippedCellRef ;
if ( ! esm . peekNextSub ( " FRMR " ) )
return false ;
bool skippedDeleted ;
skippedCellRef . load ( esm , skippedDeleted ) ;
2015-07-16 22:31:59 +03:00
}
2011-04-06 20:11:08 +04:00
2015-07-20 17:23:14 +03:00
if ( esm . peekNextSub ( " FRMR " ) )
{
2021-07-06 14:21:17 +10:00
ref . load ( esm , isDeleted ) ;
2021-06-29 19:20:01 +10:00
// TODO: should count the number of temp refs and validate the number
2013-02-27 01:37:40 +04:00
2015-07-20 17:23:14 +03:00
// Identify references belonging to a parent file and adapt the ID accordingly.
adjustRefNum ( ref . mRefNum , esm ) ;
return true ;
}
return false ;
2015-07-16 22:31:59 +03:00
}
2013-01-19 23:33:18 +01:00
2022-04-08 17:08:18 +02:00
bool Cell : : getNextRef ( ESMReader & esm , CellRef & cellRef , bool & deleted , MovedCellRef & movedCellRef , bool & moved ,
GetNextRefMode mode )
2021-07-12 17:30:39 +02:00
{
deleted = false ;
moved = false ;
if ( ! esm . hasMoreSubs ( ) )
return false ;
if ( esm . isNextSub ( " MVRF " ) )
{
moved = true ;
getNextMVRF ( esm , movedCellRef ) ;
}
if ( ! esm . peekNextSub ( " FRMR " ) )
return false ;
2022-04-08 17:08:18 +02:00
if ( ( ! moved & & mode = = GetNextRefMode : : LoadOnlyMoved )
| | ( moved & & mode = = GetNextRefMode : : LoadOnlyNotMoved ) )
{
skipLoadCellRef ( esm ) ;
return true ;
}
2021-07-12 17:30:39 +02:00
cellRef . load ( esm , deleted ) ;
adjustRefNum ( cellRef . mRefNum , esm ) ;
return true ;
}
2015-07-16 22:31:59 +03:00
bool Cell : : getNextMVRF ( ESMReader & esm , MovedCellRef & mref )
{
esm . getHT ( mref . mRefNum . mIndex ) ;
esm . getHNOT ( mref . mTarget , " CNDT " ) ;
adjustRefNum ( mref . mRefNum , esm ) ;
return true ;
}
2013-01-19 23:33:18 +01:00
2013-04-14 17:04:55 +02:00
void Cell : : blank ( )
{
mName . clear ( ) ;
mRegion . clear ( ) ;
mWater = 0 ;
mWaterInt = false ;
mMapColor = 0 ;
2021-08-03 23:08:57 +00:00
mRefNumCounter = 0 ;
2013-04-14 17:04:55 +02:00
mData . mFlags = 0 ;
mData . mX = 0 ;
mData . mY = 0 ;
2020-02-02 15:12:53 +03:00
mHasAmbi = true ;
2013-04-14 17:04:55 +02:00
mAmbi . mAmbient = 0 ;
mAmbi . mSunlight = 0 ;
mAmbi . mFog = 0 ;
mAmbi . mFogDensity = 0 ;
}
2014-01-18 21:11:12 +01:00
2015-11-26 23:46:41 +01:00
const CellId & Cell : : getCellId ( ) const
2014-01-14 12:25:35 +01:00
{
2015-11-26 23:46:41 +01:00
return mCellId ;
2014-01-14 12:25:35 +01:00
}
2011-04-06 20:11:08 +04:00
}