/**
 * @file loadsave.cpp
 *
 * Implementation of save game functionality.
 */
#include "all.h"

DEVILUTION_BEGIN_NAMESPACE

bool gbIsHellfireSaveGame;
int giNumberOfLevels;
int giNumberQuests;
int giNumberOfSmithPremiumItems;

namespace {

template <class T>
T SwapLE(T in)
{
	switch (sizeof(T)) {
	case 2:
		return SDL_SwapLE16(in);
	case 4:
		return SDL_SwapLE32(in);
	case 8:
		return SDL_SwapLE64(in);
	default:
		return in;
	}
}

template <class T>
T SwapBE(T in)
{
	switch (sizeof(T)) {
	case 2:
		return SDL_SwapBE16(in);
	case 4:
		return SDL_SwapBE32(in);
	case 8:
		return SDL_SwapBE64(in);
	default:
		return in;
	}
}

class LoadHelper {
	Uint8 *m_buffer;
	Uint32 m_bufferPtr = 0;
	Uint32 m_bufferLen;

	template <class T>
	T next()
	{
		const auto size = sizeof(T);
		if (!isValid(size))
			return 0;

		T value;
		memcpy(&value, &m_buffer[m_bufferPtr], size);
		m_bufferPtr += size;

		return value;
	}

public:
	LoadHelper(const char *szFileName)
	{
		m_buffer = pfile_read(szFileName, &m_bufferLen);
	}

	bool isValid(Uint32 size = 1)
	{
		return m_buffer != nullptr
		    && m_bufferLen >= (m_bufferPtr + size);
	}

	void skip(Uint32 size)
	{
		m_bufferPtr += size;
	}

	void nextBytes(void *bytes, size_t size)
	{
		if (!isValid(size))
			return;

		memcpy(bytes, &m_buffer[m_bufferPtr], size);
		m_bufferPtr += size;
	}

	template <class T>
	T nextLE()
	{
		return SwapLE(next<T>());
	}

	template <class T>
	T nextBE()
	{
		return SwapBE(next<T>());
	}

	bool nextBool8()
	{
		return next<Uint8>() != 0;
	}

	bool nextBool32()
	{
		return next<Uint32>() != 0;
	}

	~LoadHelper()
	{
		mem_free_dbg(m_buffer);
	}
};

class SaveHelper {
	const char *m_szFileName;
	Uint8 *m_buffer;
	Uint32 m_bufferPtr = 0;
	Uint32 m_bufferLen;

public:
	SaveHelper(const char *szFileName, size_t bufferLen)
	{
		m_szFileName = szFileName;
		m_bufferLen = bufferLen;
		m_buffer = DiabloAllocPtr(codec_get_encoded_len(m_bufferLen));
	}

	bool isValid(Uint32 len = 1)
	{
		return m_buffer != nullptr
		    && m_bufferLen >= (m_bufferPtr + len);
	}

	void skip(Uint32 len)
	{
		m_bufferPtr += len;
	}

	void writeBytes(const void *bytes, size_t len)
	{
		if (!isValid(len))
			return;

		memcpy(&m_buffer[m_bufferPtr], bytes, len);
		m_bufferPtr += len;
	}

	template <class T>
	void writeLE(T value)
	{
		value = SwapLE(value);
		writeBytes(&value, sizeof(value));
	}

	template <class T>
	void writeBE(T value)
	{
		value = SwapBE(value);
		writeBytes(&value, sizeof(value));
	}

	void flush()
	{
		if (m_buffer == nullptr)
			return;

		pfile_write_save_file(m_szFileName, m_buffer, m_bufferPtr, codec_get_encoded_len(m_bufferPtr));
		mem_free_dbg(m_buffer);
		m_buffer = nullptr;
	}

	~SaveHelper()
	{
		flush();
	}
};

}

void RemoveInvalidItem(ItemStruct *pItem)
{
	bool isInvalid = !IsItemAvailable(pItem->IDidx) || !IsUniqueAvailable(pItem->_iUid);

	if (!gbIsHellfire) {
		isInvalid = isInvalid || (pItem->_itype == ITYPE_STAFF && GetSpellStaffLevel(pItem->_iSpell) == -1);
		isInvalid = isInvalid || (pItem->_iMiscId == IMISC_BOOK && GetSpellBookLevel(pItem->_iSpell) == -1);
		isInvalid = isInvalid || pItem->_iDamAcFlags != 0;
		isInvalid = isInvalid || pItem->_iPrePower > IDI_LASTDIABLO;
		isInvalid = isInvalid || pItem->_iSufPower > IDI_LASTDIABLO;
	}

	if (isInvalid) {
		pItem->_itype = ITYPE_NONE;
	}
}

static void LoadItemData(LoadHelper *file, ItemStruct *pItem)
{
	pItem->_iSeed = file->nextLE<Sint32>();
	pItem->_iCreateInfo = file->nextLE<Uint16>();
	file->skip(2); // Alignment
	pItem->_itype = (item_type)file->nextLE<Uint32>();
	pItem->_ix = file->nextLE<Sint32>();
	pItem->_iy = file->nextLE<Sint32>();
	pItem->_iAnimFlag = file->nextBool32();
	file->skip(4); // Skip pointer _iAnimData
	pItem->_iAnimLen = file->nextLE<Sint32>();
	pItem->_iAnimFrame = file->nextLE<Sint32>();
	pItem->_iAnimWidth = file->nextLE<Sint32>();
	pItem->_iAnimWidth2 = file->nextLE<Sint32>();
	file->skip(4); // Unused since 1.02
	pItem->_iSelFlag = file->nextLE<Uint8>();
	file->skip(3); // Alignment
	pItem->_iPostDraw = file->nextBool32();
	pItem->_iIdentified = file->nextBool32();
	pItem->_iMagical = file->nextLE<Sint8>();
	file->nextBytes(pItem->_iName, 64);
	file->nextBytes(pItem->_iIName, 64);
	pItem->_iLoc = (item_equip_type)file->nextLE<Sint8>();
	pItem->_iClass = (item_class)file->nextLE<Uint8>();
	file->skip(1); // Alignment
	pItem->_iCurs = file->nextLE<Sint32>();
	pItem->_ivalue = file->nextLE<Sint32>();
	pItem->_iIvalue = file->nextLE<Sint32>();
	pItem->_iMinDam = file->nextLE<Sint32>();
	pItem->_iMaxDam = file->nextLE<Sint32>();
	pItem->_iAC = file->nextLE<Sint32>();
	pItem->_iFlags = file->nextLE<Sint32>();
	pItem->_iMiscId = (item_misc_id)file->nextLE<Sint32>();
	pItem->_iSpell = (spell_id)file->nextLE<Sint32>();
	pItem->_iCharges = file->nextLE<Sint32>();
	pItem->_iMaxCharges = file->nextLE<Sint32>();
	pItem->_iDurability = file->nextLE<Sint32>();
	pItem->_iMaxDur = file->nextLE<Sint32>();
	pItem->_iPLDam = file->nextLE<Sint32>();
	pItem->_iPLToHit = file->nextLE<Sint32>();
	pItem->_iPLAC = file->nextLE<Sint32>();
	pItem->_iPLStr = file->nextLE<Sint32>();
	pItem->_iPLMag = file->nextLE<Sint32>();
	pItem->_iPLDex = file->nextLE<Sint32>();
	pItem->_iPLVit = file->nextLE<Sint32>();
	pItem->_iPLFR = file->nextLE<Sint32>();
	pItem->_iPLLR = file->nextLE<Sint32>();
	pItem->_iPLMR = file->nextLE<Sint32>();
	pItem->_iPLMana = file->nextLE<Sint32>();
	pItem->_iPLHP = file->nextLE<Sint32>();
	pItem->_iPLDamMod = file->nextLE<Sint32>();
	pItem->_iPLGetHit = file->nextLE<Sint32>();
	pItem->_iPLLight = file->nextLE<Sint32>();
	pItem->_iSplLvlAdd = file->nextLE<Sint8>();
	pItem->_iRequest = file->nextLE<Sint8>();
	file->skip(2); // Alignment
	pItem->_iUid = file->nextLE<Sint32>();
	pItem->_iFMinDam = file->nextLE<Sint32>();
	pItem->_iFMaxDam = file->nextLE<Sint32>();
	pItem->_iLMinDam = file->nextLE<Sint32>();
	pItem->_iLMaxDam = file->nextLE<Sint32>();
	pItem->_iPLEnAc = file->nextLE<Sint32>();
	pItem->_iPrePower = (item_effect_type)file->nextLE<Sint8>();
	pItem->_iSufPower = (item_effect_type)file->nextLE<Sint8>();
	file->skip(2); // Alignment
	pItem->_iVAdd1 = file->nextLE<Sint32>();
	pItem->_iVMult1 = file->nextLE<Sint32>();
	pItem->_iVAdd2 = file->nextLE<Sint32>();
	pItem->_iVMult2 = file->nextLE<Sint32>();
	pItem->_iMinStr = file->nextLE<Sint8>();
	pItem->_iMinMag = file->nextLE<Uint8>();
	pItem->_iMinDex = file->nextLE<Sint8>();
	file->skip(1); // Alignment
	pItem->_iStatFlag = file->nextBool32();
	pItem->IDidx = file->nextLE<Sint32>();
	if (!gbIsHellfireSaveGame) {
		pItem->IDidx = RemapItemIdxFromDiablo(pItem->IDidx);
	}
	pItem->dwBuff = file->nextLE<Uint32>();
	if (gbIsHellfireSaveGame)
		pItem->_iDamAcFlags = file->nextLE<Sint32>();
	else
		pItem->_iDamAcFlags = 0;

	RemoveInvalidItem(pItem);
}

static void LoadItems(LoadHelper *file, const int n, ItemStruct *pItem)
{
	for (int i = 0; i < n; i++) {
		LoadItemData(file, &pItem[i]);
	}
}

static void LoadPlayer(LoadHelper *file, int p)
{
	PlayerStruct *pPlayer = &plr[p];

	pPlayer->_pmode = (PLR_MODE)file->nextLE<Sint32>();

	for (int i = 0; i < MAX_PATH_LENGTH; i++) {
		pPlayer->walkpath[i] = file->nextLE<Sint8>();
	}
	pPlayer->plractive = file->nextBool8();
	file->skip(2); // Alignment
	pPlayer->destAction = (action_id)file->nextLE<Sint32>();
	pPlayer->destParam1 = file->nextLE<Sint32>();
	pPlayer->destParam2 = file->nextLE<Sint32>();
	pPlayer->destParam3 = (direction)file->nextLE<Sint32>();
	pPlayer->destParam4 = file->nextLE<Sint32>();
	pPlayer->plrlevel = file->nextLE<Sint32>();
	pPlayer->_px = file->nextLE<Sint32>();
	pPlayer->_py = file->nextLE<Sint32>();
	pPlayer->_pfutx = file->nextLE<Sint32>();
	pPlayer->_pfuty = file->nextLE<Sint32>();
	pPlayer->_ptargx = file->nextLE<Sint32>();
	pPlayer->_ptargy = file->nextLE<Sint32>();
	pPlayer->_pownerx = file->nextLE<Sint32>();
	pPlayer->_pownery = file->nextLE<Sint32>();
	pPlayer->_poldx = file->nextLE<Sint32>();
	pPlayer->_poldy = file->nextLE<Sint32>();
	pPlayer->_pxoff = file->nextLE<Sint32>();
	pPlayer->_pyoff = file->nextLE<Sint32>();
	pPlayer->_pxvel = file->nextLE<Sint32>();
	pPlayer->_pyvel = file->nextLE<Sint32>();
	pPlayer->_pdir = (direction)file->nextLE<Sint32>();
	file->skip(4); // Unused
	pPlayer->_pgfxnum = file->nextLE<Sint32>();
	file->skip(4); // Skip pointer _pAnimData
	pPlayer->_pAnimDelay = file->nextLE<Sint32>();
	pPlayer->_pAnimCnt = file->nextLE<Sint32>();
	pPlayer->_pAnimLen = file->nextLE<Sint32>();
	pPlayer->_pAnimFrame = file->nextLE<Sint32>();
	pPlayer->_pAnimWidth = file->nextLE<Sint32>();
	pPlayer->_pAnimWidth2 = file->nextLE<Sint32>();
	file->skip(4); // Skip _peflag
	pPlayer->_plid = file->nextLE<Sint32>();
	pPlayer->_pvid = file->nextLE<Sint32>();

	pPlayer->_pSpell = (spell_id)file->nextLE<Sint32>();
	pPlayer->_pSplType = (spell_type)file->nextLE<Sint8>();
	pPlayer->_pSplFrom = file->nextLE<Sint8>();
	file->skip(2); // Alignment
	pPlayer->_pTSpell = (spell_id)file->nextLE<Sint32>();
	pPlayer->_pTSplType = (spell_type)file->nextLE<Sint8>();
	file->skip(3); // Alignment
	pPlayer->_pRSpell = (spell_id)file->nextLE<Sint32>();
	pPlayer->_pRSplType = (spell_type)file->nextLE<Sint8>();
	file->skip(3); // Alignment
	pPlayer->_pSBkSpell = (spell_id)file->nextLE<Sint32>();
	pPlayer->_pSBkSplType = (spell_type)file->nextLE<Sint8>();
	for (int i = 0; i < 64; i++)
		pPlayer->_pSplLvl[i] = file->nextLE<Sint8>();
	file->skip(7); // Alignment
	pPlayer->_pMemSpells = file->nextLE<Uint64>();
	pPlayer->_pAblSpells = file->nextLE<Uint64>();
	pPlayer->_pScrlSpells = file->nextLE<Uint64>();
	pPlayer->_pSpellFlags = file->nextLE<Uint8>();
	file->skip(3); // Alignment
	for (int i = 0; i < 4; i++)
		pPlayer->_pSplHotKey[i] = (spell_id)file->nextLE<Sint32>();
	for (int i = 0; i < 4; i++)
		pPlayer->_pSplTHotKey[i] = (spell_type)file->nextLE<Sint8>();

	pPlayer->_pwtype = (player_weapon_type)file->nextLE<Sint32>();
	pPlayer->_pBlockFlag = file->nextBool8();
	pPlayer->_pInvincible = file->nextBool8();
	pPlayer->_pLightRad = file->nextLE<Sint8>();
	pPlayer->_pLvlChanging = file->nextBool8();

	file->nextBytes(pPlayer->_pName, PLR_NAME_LEN);
	pPlayer->_pClass = (plr_class)file->nextLE<Sint8>();
	file->skip(3); // Alignment
	pPlayer->_pStrength = file->nextLE<Sint32>();
	pPlayer->_pBaseStr = file->nextLE<Sint32>();
	pPlayer->_pMagic = file->nextLE<Sint32>();
	pPlayer->_pBaseMag = file->nextLE<Sint32>();
	pPlayer->_pDexterity = file->nextLE<Sint32>();
	pPlayer->_pBaseDex = file->nextLE<Sint32>();
	pPlayer->_pVitality = file->nextLE<Sint32>();
	pPlayer->_pBaseVit = file->nextLE<Sint32>();
	pPlayer->_pStatPts = file->nextLE<Sint32>();
	pPlayer->_pDamageMod = file->nextLE<Sint32>();
	pPlayer->_pBaseToBlk = file->nextLE<Sint32>();
	if (pPlayer->_pBaseToBlk == 0)
		pPlayer->_pBaseToBlk = ToBlkTbl[pPlayer->_pClass];
	pPlayer->_pHPBase = file->nextLE<Sint32>();
	pPlayer->_pMaxHPBase = file->nextLE<Sint32>();
	pPlayer->_pHitPoints = file->nextLE<Sint32>();
	pPlayer->_pMaxHP = file->nextLE<Sint32>();
	pPlayer->_pHPPer = file->nextLE<Sint32>();
	pPlayer->_pManaBase = file->nextLE<Sint32>();
	pPlayer->_pMaxManaBase = file->nextLE<Sint32>();
	pPlayer->_pMana = file->nextLE<Sint32>();
	pPlayer->_pMaxMana = file->nextLE<Sint32>();
	pPlayer->_pManaPer = file->nextLE<Sint32>();
	pPlayer->_pLevel = file->nextLE<Sint8>();
	pPlayer->_pMaxLvl = file->nextLE<Sint8>();
	file->skip(2); // Alignment
	pPlayer->_pExperience = file->nextLE<Sint32>();
	pPlayer->_pMaxExp = file->nextLE<Sint32>();
	pPlayer->_pNextExper = file->nextLE<Sint32>();
	pPlayer->_pArmorClass = file->nextLE<Sint8>();
	pPlayer->_pMagResist = file->nextLE<Sint8>();
	pPlayer->_pFireResist = file->nextLE<Sint8>();
	pPlayer->_pLghtResist = file->nextLE<Sint8>();
	pPlayer->_pGold = file->nextLE<Sint32>();

	pPlayer->_pInfraFlag = file->nextBool32();
	pPlayer->_pVar1 = file->nextLE<Sint32>();
	pPlayer->_pVar2 = file->nextLE<Sint32>();
	pPlayer->_pVar3 = (direction)file->nextLE<Sint32>();
	pPlayer->_pVar4 = file->nextLE<Sint32>();
	pPlayer->_pVar5 = file->nextLE<Sint32>();
	pPlayer->_pVar6 = file->nextLE<Sint32>();
	pPlayer->_pVar7 = file->nextLE<Sint32>();
	pPlayer->_pVar8 = file->nextLE<Sint32>();
	for (int i = 0; i < giNumberOfLevels; i++)
		pPlayer->_pLvlVisited[i] = file->nextBool8();
	for (int i = 0; i < giNumberOfLevels; i++)
		pPlayer->_pSLvlVisited[i] = file->nextBool8();

	file->skip(2); // Alignment

	pPlayer->_pGFXLoad = file->nextLE<Sint32>();
	file->skip(4 * 8); // Skip pointers _pNAnim
	pPlayer->_pNFrames = file->nextLE<Sint32>();
	pPlayer->_pNWidth = file->nextLE<Sint32>();
	file->skip(4 * 8); // Skip pointers _pWAnim
	pPlayer->_pWFrames = file->nextLE<Sint32>();
	pPlayer->_pWWidth = file->nextLE<Sint32>();
	file->skip(4 * 8); // Skip pointers _pAAnim
	pPlayer->_pAFrames = file->nextLE<Sint32>();
	pPlayer->_pAWidth = file->nextLE<Sint32>();
	pPlayer->_pAFNum = file->nextLE<Sint32>();
	file->skip(4 * 8); // Skip pointers _pLAnim
	file->skip(4 * 8); // Skip pointers _pFAnim
	file->skip(4 * 8); // Skip pointers _pTAnim
	pPlayer->_pSFrames = file->nextLE<Sint32>();
	pPlayer->_pSWidth = file->nextLE<Sint32>();
	pPlayer->_pSFNum = file->nextLE<Sint32>();
	file->skip(4 * 8); // Skip pointers _pHAnim
	pPlayer->_pHFrames = file->nextLE<Sint32>();
	pPlayer->_pHWidth = file->nextLE<Sint32>();
	file->skip(4 * 8); // Skip pointers _pDAnim
	pPlayer->_pDFrames = file->nextLE<Sint32>();
	pPlayer->_pDWidth = file->nextLE<Sint32>();
	file->skip(4 * 8); // Skip pointers _pBAnim
	pPlayer->_pBFrames = file->nextLE<Sint32>();
	pPlayer->_pBWidth = file->nextLE<Sint32>();

	LoadItems(file, NUM_INVLOC, pPlayer->InvBody);
	LoadItems(file, NUM_INV_GRID_ELEM, pPlayer->InvList);
	pPlayer->_pNumInv = file->nextLE<Sint32>();
	for (int i = 0; i < NUM_INV_GRID_ELEM; i++)
		pPlayer->InvGrid[i] = file->nextLE<Sint8>();
	LoadItems(file, MAXBELTITEMS, pPlayer->SpdList);
	LoadItemData(file, &pPlayer->HoldItem);

	pPlayer->_pIMinDam = file->nextLE<Sint32>();
	pPlayer->_pIMaxDam = file->nextLE<Sint32>();
	pPlayer->_pIAC = file->nextLE<Sint32>();
	pPlayer->_pIBonusDam = file->nextLE<Sint32>();
	pPlayer->_pIBonusToHit = file->nextLE<Sint32>();
	pPlayer->_pIBonusAC = file->nextLE<Sint32>();
	pPlayer->_pIBonusDamMod = file->nextLE<Sint32>();
	file->skip(4); // Alignment

	pPlayer->_pISpells = file->nextLE<Uint64>();
	pPlayer->_pIFlags = file->nextLE<Sint32>();
	pPlayer->_pIGetHit = file->nextLE<Sint32>();
	pPlayer->_pISplLvlAdd = file->nextLE<Sint8>();
	file->skip(1); // Unused
	file->skip(2); // Alignment
	pPlayer->_pISplDur = file->nextLE<Sint32>();
	pPlayer->_pIEnAc = file->nextLE<Sint32>();
	pPlayer->_pIFMinDam = file->nextLE<Sint32>();
	pPlayer->_pIFMaxDam = file->nextLE<Sint32>();
	pPlayer->_pILMinDam = file->nextLE<Sint32>();
	pPlayer->_pILMaxDam = file->nextLE<Sint32>();
	pPlayer->_pOilType = (item_misc_id)file->nextLE<Sint32>();
	pPlayer->pTownWarps = file->nextLE<Uint8>();
	pPlayer->pDungMsgs = file->nextLE<Uint8>();
	pPlayer->pLvlLoad = file->nextLE<Uint8>();

	if (gbIsHellfireSaveGame) {
		pPlayer->pDungMsgs2 = file->nextLE<Uint8>();
		pPlayer->pBattleNet = false;
	} else {
		pPlayer->pDungMsgs2 = 0;
		pPlayer->pBattleNet = file->nextBool8();
	}
	pPlayer->pManaShield = file->nextBool8();
	if (gbIsHellfireSaveGame) {
		pPlayer->pOriginalCathedral = file->nextBool8();
	} else {
		file->skip(1);
		pPlayer->pOriginalCathedral = true;
	}
	file->skip(2); // Available bytes
	pPlayer->wReflections = file->nextLE<Uint16>();
	file->skip(14); // Available bytes

	pPlayer->pDiabloKillLevel = file->nextLE<Uint32>();
	pPlayer->pDifficulty = file->nextLE<Uint32>();
	pPlayer->pDamAcFlags = file->nextLE<Uint32>();
	file->skip(20); // Available bytes
	CalcPlrItemVals(p, FALSE);

	// Omit pointer _pNData
	// Omit pointer _pWData
	// Omit pointer _pAData
	// Omit pointer _pLData
	// Omit pointer _pFData
	// Omit pointer  _pTData
	// Omit pointer _pHData
	// Omit pointer _pDData
	// Omit pointer _pBData
	// Omit pointer pReserved
}

bool gbSkipSync = false;

static void LoadMonster(LoadHelper *file, int i)
{
	MonsterStruct *pMonster = &monster[i];

	pMonster->_mMTidx = file->nextLE<Sint32>();
	pMonster->_mmode = (MON_MODE)file->nextLE<Sint32>();
	pMonster->_mgoal = file->nextLE<Uint8>();
	file->skip(3); // Alignment
	pMonster->_mgoalvar1 = file->nextLE<Sint32>();
	pMonster->_mgoalvar2 = file->nextLE<Sint32>();
	pMonster->_mgoalvar3 = file->nextLE<Sint32>();
	file->skip(4); // Unused
	pMonster->_pathcount = file->nextLE<Uint8>();
	file->skip(3); // Alignment
	pMonster->_mx = file->nextLE<Sint32>();
	pMonster->_my = file->nextLE<Sint32>();
	pMonster->_mfutx = file->nextLE<Sint32>();
	pMonster->_mfuty = file->nextLE<Sint32>();
	pMonster->_moldx = file->nextLE<Sint32>();
	pMonster->_moldy = file->nextLE<Sint32>();
	pMonster->_mxoff = file->nextLE<Sint32>();
	pMonster->_myoff = file->nextLE<Sint32>();
	pMonster->_mxvel = file->nextLE<Sint32>();
	pMonster->_myvel = file->nextLE<Sint32>();
	pMonster->_mdir = file->nextLE<Sint32>();
	pMonster->_menemy = file->nextLE<Sint32>();
	pMonster->_menemyx = file->nextLE<Uint8>();
	pMonster->_menemyy = file->nextLE<Uint8>();
	file->skip(2); // Unused

	file->skip(4); // Skip pointer _mAnimData
	pMonster->_mAnimDelay = file->nextLE<Sint32>();
	pMonster->_mAnimCnt = file->nextLE<Sint32>();
	pMonster->_mAnimLen = file->nextLE<Sint32>();
	pMonster->_mAnimFrame = file->nextLE<Sint32>();
	file->skip(4); // Skip _meflag
	pMonster->_mDelFlag = file->nextBool32();
	pMonster->_mVar1 = file->nextLE<Sint32>();
	pMonster->_mVar2 = file->nextLE<Sint32>();
	pMonster->_mVar3 = file->nextLE<Sint32>();
	pMonster->_mVar4 = file->nextLE<Sint32>();
	pMonster->_mVar5 = file->nextLE<Sint32>();
	pMonster->_mVar6 = file->nextLE<Sint32>();
	pMonster->_mVar7 = file->nextLE<Sint32>();
	pMonster->_mVar8 = file->nextLE<Sint32>();
	pMonster->_mmaxhp = file->nextLE<Sint32>();
	pMonster->_mhitpoints = file->nextLE<Sint32>();

	pMonster->_mAi = (_mai_id)file->nextLE<Uint8>();
	pMonster->_mint = file->nextLE<Uint8>();
	file->skip(2); // Alignment
	pMonster->_mFlags = file->nextLE<Uint32>();
	pMonster->_msquelch = file->nextLE<Uint8>();
	file->skip(3); // Alignment
	file->skip(4); // Unused
	pMonster->_lastx = file->nextLE<Sint32>();
	pMonster->_lasty = file->nextLE<Sint32>();
	pMonster->_mRndSeed = file->nextLE<Sint32>();
	pMonster->_mAISeed = file->nextLE<Sint32>();
	file->skip(4); // Unused

	pMonster->_uniqtype = file->nextLE<Uint8>();
	pMonster->_uniqtrans = file->nextLE<Uint8>();
	pMonster->_udeadval = file->nextLE<Sint8>();

	pMonster->mWhoHit = file->nextLE<Sint8>();
	pMonster->mLevel = file->nextLE<Sint8>();
	file->skip(1); // Alignment
	pMonster->mExp = file->nextLE<Uint16>();

	file->skip(1); // Skip mHit as it's already initialized
	pMonster->mMinDamage = file->nextLE<Uint8>();
	pMonster->mMaxDamage = file->nextLE<Uint8>();
	file->skip(1); // Skip mHit2 as it's already initialized
	pMonster->mMinDamage2 = file->nextLE<Uint8>();
	pMonster->mMaxDamage2 = file->nextLE<Uint8>();
	pMonster->mArmorClass = file->nextLE<Uint8>();
	file->skip(1); // Alignment
	pMonster->mMagicRes = file->nextLE<Uint16>();
	file->skip(2); // Alignment

	pMonster->mtalkmsg = file->nextLE<Sint32>();
	pMonster->leader = file->nextLE<Uint8>();
	pMonster->leaderflag = file->nextLE<Uint8>();
	pMonster->packsize = file->nextLE<Uint8>();
	pMonster->mlid = file->nextLE<Sint8>();
	if (pMonster->mlid == plr[myplr]._plid)
		pMonster->mlid = NO_LIGHT; // Correct incorect values in old saves

	// Omit pointer mName;
	// Omit pointer MType;
	// Omit pointer MData;

	if (gbSkipSync)
		return;

	SyncMonsterAnim(i);
}

static void LoadMissile(LoadHelper *file, int i)
{
	MissileStruct *pMissile = &missile[i];

	pMissile->_mitype = file->nextLE<Sint32>();
	pMissile->_mix = file->nextLE<Sint32>();
	pMissile->_miy = file->nextLE<Sint32>();
	pMissile->_mixoff = file->nextLE<Sint32>();
	pMissile->_miyoff = file->nextLE<Sint32>();
	pMissile->_mixvel = file->nextLE<Sint32>();
	pMissile->_miyvel = file->nextLE<Sint32>();
	pMissile->_misx = file->nextLE<Sint32>();
	pMissile->_misy = file->nextLE<Sint32>();
	pMissile->_mitxoff = file->nextLE<Sint32>();
	pMissile->_mityoff = file->nextLE<Sint32>();
	pMissile->_mimfnum = file->nextLE<Sint32>();
	pMissile->_mispllvl = file->nextLE<Sint32>();
	pMissile->_miDelFlag = file->nextBool32();
	pMissile->_miAnimType = file->nextLE<Uint8>();
	file->skip(3); // Alignment
	pMissile->_miAnimFlags = file->nextLE<Sint32>();
	file->skip(4); // Skip pointer _miAnimData
	pMissile->_miAnimDelay = file->nextLE<Sint32>();
	pMissile->_miAnimLen = file->nextLE<Sint32>();
	pMissile->_miAnimWidth = file->nextLE<Sint32>();
	pMissile->_miAnimWidth2 = file->nextLE<Sint32>();
	pMissile->_miAnimCnt = file->nextLE<Sint32>();
	pMissile->_miAnimAdd = file->nextLE<Sint32>();
	pMissile->_miAnimFrame = file->nextLE<Sint32>();
	pMissile->_miDrawFlag = file->nextBool32();
	pMissile->_miLightFlag = file->nextBool32();
	pMissile->_miPreFlag = file->nextBool32();
	pMissile->_miUniqTrans = file->nextLE<Uint32>();
	pMissile->_mirange = file->nextLE<Sint32>();
	pMissile->_misource = file->nextLE<Sint32>();
	pMissile->_micaster = file->nextLE<Sint32>();
	pMissile->_midam = file->nextLE<Sint32>();
	pMissile->_miHitFlag = file->nextBool32();
	pMissile->_midist = file->nextLE<Sint32>();
	pMissile->_mlid = file->nextLE<Sint32>();
	pMissile->_mirnd = file->nextLE<Sint32>();
	pMissile->_miVar1 = file->nextLE<Sint32>();
	pMissile->_miVar2 = file->nextLE<Sint32>();
	pMissile->_miVar3 = file->nextLE<Sint32>();
	pMissile->_miVar4 = file->nextLE<Sint32>();
	pMissile->_miVar5 = file->nextLE<Sint32>();
	pMissile->_miVar6 = file->nextLE<Sint32>();
	pMissile->_miVar7 = file->nextLE<Sint32>();
	pMissile->_miVar8 = file->nextLE<Sint32>();
}

static void LoadObject(LoadHelper *file, int i)
{
	ObjectStruct *pObject = &object[i];

	pObject->_otype = file->nextLE<Sint32>();
	pObject->_ox = file->nextLE<Sint32>();
	pObject->_oy = file->nextLE<Sint32>();
	pObject->_oLight = file->nextBool32();
	pObject->_oAnimFlag = file->nextLE<Uint32>();
	file->skip(4); // Skip pointer _oAnimData
	pObject->_oAnimDelay = file->nextLE<Sint32>();
	pObject->_oAnimCnt = file->nextLE<Sint32>();
	pObject->_oAnimLen = file->nextLE<Sint32>();
	pObject->_oAnimFrame = file->nextLE<Sint32>();
	pObject->_oAnimWidth = file->nextLE<Sint32>();
	pObject->_oAnimWidth2 = file->nextLE<Sint32>();
	pObject->_oDelFlag = file->nextBool32();
	pObject->_oBreak = file->nextLE<Sint8>();
	file->skip(3); // Alignment
	pObject->_oSolidFlag = file->nextBool32();
	pObject->_oMissFlag = file->nextBool32();

	pObject->_oSelFlag = file->nextLE<Sint8>();
	file->skip(3); // Alignment
	pObject->_oPreFlag = file->nextBool32();
	pObject->_oTrapFlag = file->nextBool32();
	pObject->_oDoorFlag = file->nextBool32();
	pObject->_olid = file->nextLE<Sint32>();
	pObject->_oRndSeed = file->nextLE<Sint32>();
	pObject->_oVar1 = file->nextLE<Sint32>();
	pObject->_oVar2 = file->nextLE<Sint32>();
	pObject->_oVar3 = file->nextLE<Sint32>();
	pObject->_oVar4 = file->nextLE<Sint32>();
	pObject->_oVar5 = file->nextLE<Sint32>();
	pObject->_oVar6 = file->nextLE<Sint32>();
	pObject->_oVar7 = (_speech_id)file->nextLE<Sint32>();
	pObject->_oVar8 = file->nextLE<Sint32>();
}

static void LoadItem(LoadHelper *file, int i)
{
	LoadItemData(file, &item[i]);
	GetItemFrm(i);
}

static void LoadPremium(LoadHelper *file, int i)
{
	LoadItemData(file, &premiumitem[i]);
}

static void LoadQuest(LoadHelper *file, int i)
{
	QuestStruct *pQuest = &quests[i];

	pQuest->_qlevel = file->nextLE<Uint8>();
	pQuest->_qtype = file->nextLE<Uint8>();
	pQuest->_qactive = file->nextLE<Uint8>();
	pQuest->_qlvltype = (dungeon_type)file->nextLE<Uint8>();
	pQuest->_qtx = file->nextLE<Sint32>();
	pQuest->_qty = file->nextLE<Sint32>();
	pQuest->_qslvl = file->nextLE<Uint8>();
	pQuest->_qidx = file->nextLE<Uint8>();
	if (gbIsHellfireSaveGame) {
		file->skip(2); // Alignment
		pQuest->_qmsg = file->nextLE<Sint32>();
	} else {
		pQuest->_qmsg = file->nextLE<Uint8>();
	}
	pQuest->_qvar1 = file->nextLE<Uint8>();
	pQuest->_qvar2 = file->nextLE<Uint8>();
	file->skip(2); // Alignment
	if (!gbIsHellfireSaveGame)
		file->skip(1); // Alignment
	pQuest->_qlog = file->nextBool32();

	ReturnLvlX = file->nextBE<Sint32>();
	ReturnLvlY = file->nextBE<Sint32>();
	ReturnLvl = file->nextBE<Sint32>();
	ReturnLvlT = (dungeon_type)file->nextBE<Sint32>();
	DoomQuestState = file->nextBE<Sint32>();
}

static void LoadLighting(LoadHelper *file, LightListStruct *pLight)
{
	pLight->_lx = file->nextLE<Sint32>();
	pLight->_ly = file->nextLE<Sint32>();
	pLight->_lradius = file->nextLE<Sint32>();
	pLight->_lid = file->nextLE<Sint32>();
	pLight->_ldel = file->nextBool32();
	pLight->_lunflag = file->nextBool32();
	file->skip(4); // Unused
	pLight->_lunx = file->nextLE<Sint32>();
	pLight->_luny = file->nextLE<Sint32>();
	pLight->_lunr = file->nextLE<Sint32>();
	pLight->_xoff = file->nextLE<Sint32>();
	pLight->_yoff = file->nextLE<Sint32>();
	pLight->_lflags = file->nextBool32();
}

static void LoadPortal(LoadHelper *file, int i)
{
	PortalStruct *pPortal = &portal[i];

	pPortal->open = file->nextBool32();
	pPortal->x = file->nextLE<Sint32>();
	pPortal->y = file->nextLE<Sint32>();
	pPortal->level = file->nextLE<Sint32>();
	pPortal->ltype = (dungeon_type)file->nextLE<Sint32>();
	pPortal->setlvl = file->nextBool32();
}

int RemapItemIdxFromDiablo(int i)
{
	if (i == IDI_SORCERER) {
		return 166;
	}
	if (i >= 156) {
		i += 5; // Hellfire exclusive items
	}
	if (i >= 88) {
		i += 1; // Scroll of Search
	}
	if (i >= 83) {
		i += 4; // Oils
	}

	return i;
}

int RemapItemIdxToDiablo(int i)
{
	if (i == 166) {
		return IDI_SORCERER;
	}
	if ((i >= 83 && i <= 86) || i == 92 || i >= 161) {
		return -1; // Hellfire exclusive items
	}
	if (i >= 93) {
		i -= 1; // Scroll of Search
	}
	if (i >= 87) {
		i -= 4; // Oils
	}

	return i;
}

bool IsHeaderValid(Uint32 magicNumber)
{
	gbIsHellfireSaveGame = false;
	if (magicNumber == LOAD_LE32("SHAR")) {
		return true;
	} else if (magicNumber == LOAD_LE32("SHLF")) {
		gbIsHellfireSaveGame = true;
		return true;
	} else if (!gbIsSpawn && magicNumber == LOAD_LE32("RETL")) {
		return true;
	} else if (!gbIsSpawn && magicNumber == LOAD_LE32("HELF")) {
		gbIsHellfireSaveGame = true;
		return true;
	}

	return false;
}

static void ConvertLevels()
{
	// Backup current level state
	bool _setlevel = setlevel;
	int _setlvlnum = setlvlnum;
	int _currlevel = currlevel;
	dungeon_type _leveltype = leveltype;

	gbSkipSync = true;

	setlevel = false; // Convert regular levels
	for (int i = 0; i < giNumberOfLevels; i++) {
		currlevel = i;
		if (!LevelFileExists())
			continue;

		leveltype = gnLevelTypeTbl[i];

		LoadLevel();
		SaveLevel();
	}

	setlevel = true; // Convert quest levels
	for (int i = 0; i < MAXQUESTS; i++) {
		if (quests[i]._qactive == QUEST_NOTAVAIL) {
			continue;
		}

		leveltype = quests[i]._qlvltype;
		if (leveltype == DTYPE_NONE) {
			continue;
		}

		setlvlnum = quests[i]._qslvl;
		if (!LevelFileExists())
			continue;

		LoadLevel();
		SaveLevel();
	}

	gbSkipSync = false;

	// Restor current level state
	setlevel = _setlevel;
	setlvlnum = _setlvlnum;
	currlevel = _currlevel;
	leveltype = _leveltype;
}

void LoadHotkeys()
{
	LoadHelper file("hotkeys");
	if (!file.isValid())
		return;

	const size_t nHotkeyTypes = sizeof(plr[myplr]._pSplHotKey) / sizeof(plr[myplr]._pSplHotKey[0]);
	const size_t nHotkeySpells = sizeof(plr[myplr]._pSplTHotKey) / sizeof(plr[myplr]._pSplTHotKey[0]);

	for (size_t i = 0; i < nHotkeyTypes; i++) {
		plr[myplr]._pSplHotKey[i] = (spell_id)file.nextLE<Sint32>();
	}
	for (size_t i = 0; i < nHotkeySpells; i++) {
		plr[myplr]._pSplTHotKey[i] = (spell_type)file.nextLE<Sint8>();
	}
	plr[myplr]._pRSpell = (spell_id)file.nextLE<Sint32>();
	plr[myplr]._pRSplType = (spell_type)file.nextLE<Sint8>();
}

void SaveHotkeys()
{
	const size_t nHotkeyTypes = sizeof(plr[myplr]._pSplHotKey) / sizeof(plr[myplr]._pSplHotKey[0]);
	const size_t nHotkeySpells = sizeof(plr[myplr]._pSplTHotKey) / sizeof(plr[myplr]._pSplTHotKey[0]);

	SaveHelper file("hotkeys", (nHotkeyTypes * 4) + nHotkeySpells + 4 + 1);

	for (size_t i = 0; i < nHotkeyTypes; i++) {
		file.writeLE<Sint32>(plr[myplr]._pSplHotKey[i]);
	}
	for (size_t i = 0; i < nHotkeySpells; i++) {
		file.writeLE<Uint8>(plr[myplr]._pSplTHotKey[i]);
	}
	file.writeLE<Sint32>(plr[myplr]._pRSpell);
	file.writeLE<Uint8>(plr[myplr]._pRSplType);
}

static void LoadMatchingItems(LoadHelper *file, const int n, ItemStruct *pItem)
{
	ItemStruct tempItem;

	for (int i = 0; i < n; i++) {
		LoadItemData(file, &tempItem);
		if (pItem[i].isEmpty() || tempItem.isEmpty())
			continue;
		if (pItem[i]._iSeed != tempItem._iSeed)
			continue;
		pItem[i] = tempItem;
	}
}

void LoadHeroItems(PlayerStruct *pPlayer)
{
	LoadHelper file("heroitems");
	if (!file.isValid())
		return;

	gbIsHellfireSaveGame = file.nextBool8();

	LoadMatchingItems(&file, NUM_INVLOC, pPlayer->InvBody);
	LoadMatchingItems(&file, NUM_INV_GRID_ELEM, pPlayer->InvList);
	LoadMatchingItems(&file, MAXBELTITEMS, pPlayer->SpdList);

	gbIsHellfireSaveGame = gbIsHellfire;
}

void RemoveEmptyInventory(int pnum)
{
	for (int i = NUM_INV_GRID_ELEM; i > 0; i--) {
		int idx = plr[pnum].InvGrid[i - 1];
		if (idx > 0 && plr[pnum].InvList[idx - 1].isEmpty()) {
			RemoveInvItem(pnum, idx - 1);
		}
	};
}

void RemoveEmptyLevelItems()
{
	for (int i = numitems; i > 0; i--) {
		int ii = itemactive[i];
		if (item[ii].isEmpty()) {
			dItem[item[ii]._ix][item[ii]._iy] = 0;
			DeleteItem(ii, i);
		}
	}
}

/**
 * @brief Load game state
 * @param firstflag Can be set to false if we are simply reloading the current game
 */
void LoadGame(BOOL firstflag)
{
	FreeGameMem();
	pfile_remove_temp_files();

	LoadHelper file("game");
	if (!file.isValid())
		app_fatal("Unable to open save file archive");

	if (!IsHeaderValid(file.nextLE<Uint32>()))
		app_fatal("Invalid save file");

	if (gbIsHellfireSaveGame) {
		giNumberOfLevels = 25;
		giNumberQuests = 24;
		giNumberOfSmithPremiumItems = 15;
	} else {
		// Todo initialize additional levels and quests if we are running Hellfire
		giNumberOfLevels = 17;
		giNumberQuests = 16;
		giNumberOfSmithPremiumItems = 6;
	}

	setlevel = file.nextBool8();
	setlvlnum = file.nextBE<Uint32>();
	currlevel = file.nextBE<Uint32>();
	leveltype = (dungeon_type)file.nextBE<Uint32>();
	if (!setlevel)
		leveltype = gnLevelTypeTbl[currlevel];
	int _ViewX = file.nextBE<Sint32>();
	int _ViewY = file.nextBE<Sint32>();
	invflag = file.nextBool8();
	chrflag = file.nextBool8();
	int _nummonsters = file.nextBE<Sint32>();
	int _numitems = file.nextBE<Sint32>();
	int _nummissiles = file.nextBE<Sint32>();
	int _nobjects = file.nextBE<Sint32>();

	if (!gbIsHellfire && currlevel > 17)
		app_fatal("Player is on a Hellfire only level");

	for (int i = 0; i < giNumberOfLevels; i++) {
		glSeedTbl[i] = file.nextBE<Uint32>();
		file.skip(4); // Skip loading gnLevelTypeTbl
	}

	LoadPlayer(&file, myplr);

	gnDifficulty = plr[myplr].pDifficulty;
	if (gnDifficulty < DIFF_NORMAL || gnDifficulty > DIFF_HELL)
		gnDifficulty = DIFF_NORMAL;

	for (int i = 0; i < giNumberQuests; i++)
		LoadQuest(&file, i);
	for (int i = 0; i < MAXPORTAL; i++)
		LoadPortal(&file, i);

	if (gbIsHellfireSaveGame != gbIsHellfire) {
		ConvertLevels();
		RemoveEmptyInventory(myplr);
	}

	LoadGameLevel(firstflag, ENTRY_LOAD);
	SyncInitPlr(myplr);
	SyncPlrAnim(myplr);

	ViewX = _ViewX;
	ViewY = _ViewY;
	nummonsters = _nummonsters;
	numitems = _numitems;
	nummissiles = _nummissiles;
	nobjects = _nobjects;

	for (int i = 0; i < MAXMONSTERS; i++)
		monstkills[i] = file.nextBE<Sint32>();

	if (leveltype != DTYPE_TOWN) {
		for (int i = 0; i < MAXMONSTERS; i++)
			monstactive[i] = file.nextBE<Sint32>();
		for (int i = 0; i < nummonsters; i++)
			LoadMonster(&file, monstactive[i]);
		for (int i = 0; i < MAXMISSILES; i++)
			missileactive[i] = file.nextLE<Sint8>();
		for (int i = 0; i < MAXMISSILES; i++)
			missileavail[i] = file.nextLE<Sint8>();
		for (int i = 0; i < nummissiles; i++)
			LoadMissile(&file, missileactive[i]);
		for (int i = 0; i < MAXOBJECTS; i++)
			objectactive[i] = file.nextLE<Sint8>();
		for (int i = 0; i < MAXOBJECTS; i++)
			objectavail[i] = file.nextLE<Sint8>();
		for (int i = 0; i < nobjects; i++)
			LoadObject(&file, objectactive[i]);
		for (int i = 0; i < nobjects; i++)
			SyncObjectAnim(objectactive[i]);

		numlights = file.nextBE<Sint32>();

		for (int i = 0; i < MAXLIGHTS; i++)
			lightactive[i] = file.nextLE<Uint8>();
		for (int i = 0; i < numlights; i++)
			LoadLighting(&file, &LightList[lightactive[i]]);

		visionid = file.nextBE<Sint32>();
		numvision = file.nextBE<Sint32>();

		for (int i = 0; i < numvision; i++)
			LoadLighting(&file, &VisionList[i]);
	}

	for (int i = 0; i < MAXITEMS; i++)
		itemactive[i] = file.nextLE<Sint8>();
	for (int i = 0; i < MAXITEMS; i++)
		itemavail[i] = file.nextLE<Sint8>();
	for (int i = 0; i < numitems; i++)
		LoadItem(&file, itemactive[i]);
	for (int i = 0; i < 128; i++)
		UniqueItemFlag[i] = file.nextBool8();

	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			dLight[i][j] = file.nextLE<Sint8>();
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			dFlags[i][j] = file.nextLE<Sint8>();
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			dPlayer[i][j] = file.nextLE<Sint8>();
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			dItem[i][j] = file.nextLE<Sint8>();
	}

	if (leveltype != DTYPE_TOWN) {
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dMonster[i][j] = file.nextBE<Sint32>();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dDead[i][j] = file.nextLE<Sint8>();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dObject[i][j] = file.nextLE<Sint8>();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dLight[i][j] = file.nextLE<Sint8>();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dPreLight[i][j] = file.nextLE<Sint8>();
		}
		for (int j = 0; j < DMAXY; j++) {
			for (int i = 0; i < DMAXX; i++)
				automapview[i][j] = file.nextBool8();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dMissile[i][j] = file.nextLE<Sint8>();
		}
	}

	numpremium = file.nextBE<Sint32>();
	premiumlevel = file.nextBE<Sint32>();

	for (int i = 0; i < giNumberOfSmithPremiumItems; i++)
		LoadPremium(&file, i);
	if (gbIsHellfire && !gbIsHellfireSaveGame)
		SpawnPremium(myplr);

	automapflag = file.nextBool8();
	AutoMapScale = file.nextBE<Sint32>();
	AutomapZoomReset();
	ResyncQuests();

	if (leveltype != DTYPE_TOWN)
		ProcessLightList();

	RedoPlayerVision();
	ProcessVisionList();
	missiles_process_charge();
	ResetPal();
	SetCursor_(CURSOR_HAND);
	gbProcessPlayers = TRUE;

	if (gbIsHellfireSaveGame != gbIsHellfire) {
		RemoveEmptyLevelItems();
		SaveGame();
	}

	gbIsHellfireSaveGame = gbIsHellfire;
}

static void SaveItem(SaveHelper *file, ItemStruct *pItem)
{
	int idx = pItem->IDidx;
	if (!gbIsHellfire)
		idx = RemapItemIdxToDiablo(idx);
	int iType = pItem->_itype;
	if (idx == -1) {
		idx = 0;
		iType = ITYPE_NONE;
	}

	file->writeLE<Sint32>(pItem->_iSeed);
	file->writeLE<Sint16>(pItem->_iCreateInfo);
	file->skip(2); // Alignment
	file->writeLE<Sint32>(iType);
	file->writeLE<Sint32>(pItem->_ix);
	file->writeLE<Sint32>(pItem->_iy);
	file->writeLE<Uint32>(pItem->_iAnimFlag);
	file->skip(4); // Skip pointer _iAnimData
	file->writeLE<Sint32>(pItem->_iAnimLen);
	file->writeLE<Sint32>(pItem->_iAnimFrame);
	file->writeLE<Sint32>(pItem->_iAnimWidth);
	file->writeLE<Sint32>(pItem->_iAnimWidth2);
	file->skip(4); // Unused since 1.02
	file->writeLE<Uint8>(pItem->_iSelFlag);
	file->skip(3); // Alignment
	file->writeLE<Uint32>(pItem->_iPostDraw);
	file->writeLE<Uint32>(pItem->_iIdentified);
	file->writeLE<Sint8>(pItem->_iMagical);
	file->writeBytes(pItem->_iName, 64);
	file->writeBytes(pItem->_iIName, 64);
	file->writeLE<Sint8>(pItem->_iLoc);
	file->writeLE<Uint8>(pItem->_iClass);
	file->skip(1); // Alignment
	file->writeLE<Sint32>(pItem->_iCurs);
	file->writeLE<Sint32>(pItem->_ivalue);
	file->writeLE<Sint32>(pItem->_iIvalue);
	file->writeLE<Sint32>(pItem->_iMinDam);
	file->writeLE<Sint32>(pItem->_iMaxDam);
	file->writeLE<Sint32>(pItem->_iAC);
	file->writeLE<Sint32>(pItem->_iFlags);
	file->writeLE<Sint32>(pItem->_iMiscId);
	file->writeLE<Sint32>(pItem->_iSpell);
	file->writeLE<Sint32>(pItem->_iCharges);
	file->writeLE<Sint32>(pItem->_iMaxCharges);
	file->writeLE<Sint32>(pItem->_iDurability);
	file->writeLE<Sint32>(pItem->_iMaxDur);
	file->writeLE<Sint32>(pItem->_iPLDam);
	file->writeLE<Sint32>(pItem->_iPLToHit);
	file->writeLE<Sint32>(pItem->_iPLAC);
	file->writeLE<Sint32>(pItem->_iPLStr);
	file->writeLE<Sint32>(pItem->_iPLMag);
	file->writeLE<Sint32>(pItem->_iPLDex);
	file->writeLE<Sint32>(pItem->_iPLVit);
	file->writeLE<Sint32>(pItem->_iPLFR);
	file->writeLE<Sint32>(pItem->_iPLLR);
	file->writeLE<Sint32>(pItem->_iPLMR);
	file->writeLE<Sint32>(pItem->_iPLMana);
	file->writeLE<Sint32>(pItem->_iPLHP);
	file->writeLE<Sint32>(pItem->_iPLDamMod);
	file->writeLE<Sint32>(pItem->_iPLGetHit);
	file->writeLE<Sint32>(pItem->_iPLLight);
	file->writeLE<Sint8>(pItem->_iSplLvlAdd);
	file->writeLE<Sint8>(pItem->_iRequest);
	file->skip(2); // Alignment
	file->writeLE<Sint32>(pItem->_iUid);
	file->writeLE<Sint32>(pItem->_iFMinDam);
	file->writeLE<Sint32>(pItem->_iFMaxDam);
	file->writeLE<Sint32>(pItem->_iLMinDam);
	file->writeLE<Sint32>(pItem->_iLMaxDam);
	file->writeLE<Sint32>(pItem->_iPLEnAc);
	file->writeLE<Sint8>(pItem->_iPrePower);
	file->writeLE<Sint8>(pItem->_iSufPower);
	file->skip(2); // Alignment
	file->writeLE<Sint32>(pItem->_iVAdd1);
	file->writeLE<Sint32>(pItem->_iVMult1);
	file->writeLE<Sint32>(pItem->_iVAdd2);
	file->writeLE<Sint32>(pItem->_iVMult2);
	file->writeLE<Sint8>(pItem->_iMinStr);
	file->writeLE<Uint8>(pItem->_iMinMag);
	file->writeLE<Sint8>(pItem->_iMinDex);
	file->skip(1); // Alignment
	file->writeLE<Uint32>(pItem->_iStatFlag);
	file->writeLE<Sint32>(idx);
	file->writeLE<Uint32>(pItem->dwBuff);
	if (gbIsHellfire)
		file->writeLE<Uint32>(pItem->_iDamAcFlags);
}

static void SaveItems(SaveHelper *file, ItemStruct *pItem, const int n)
{
	for (int i = 0; i < n; i++) {
		SaveItem(file, &pItem[i]);
	}
}

static void SavePlayer(SaveHelper *file, int p)
{
	PlayerStruct *pPlayer = &plr[p];

	file->writeLE<Sint32>(pPlayer->_pmode);
	for (int i = 0; i < MAX_PATH_LENGTH; i++)
		file->writeLE<Sint8>(pPlayer->walkpath[i]);
	file->writeLE<Uint8>(pPlayer->plractive);
	file->skip(2); // Alignment
	file->writeLE<Sint32>(pPlayer->destAction);
	file->writeLE<Sint32>(pPlayer->destParam1);
	file->writeLE<Sint32>(pPlayer->destParam2);
	file->writeLE<Sint32>(pPlayer->destParam3);
	file->writeLE<Sint32>(pPlayer->destParam4);
	file->writeLE<Sint32>(pPlayer->plrlevel);
	file->writeLE<Sint32>(pPlayer->_px);
	file->writeLE<Sint32>(pPlayer->_py);
	file->writeLE<Sint32>(pPlayer->_pfutx);
	file->writeLE<Sint32>(pPlayer->_pfuty);
	file->writeLE<Sint32>(pPlayer->_ptargx);
	file->writeLE<Sint32>(pPlayer->_ptargy);
	file->writeLE<Sint32>(pPlayer->_pownerx);
	file->writeLE<Sint32>(pPlayer->_pownery);
	file->writeLE<Sint32>(pPlayer->_poldx);
	file->writeLE<Sint32>(pPlayer->_poldy);
	file->writeLE<Sint32>(pPlayer->_pxoff);
	file->writeLE<Sint32>(pPlayer->_pyoff);
	file->writeLE<Sint32>(pPlayer->_pxvel);
	file->writeLE<Sint32>(pPlayer->_pyvel);
	file->writeLE<Sint32>(pPlayer->_pdir);
	file->skip(4); // Unused
	file->writeLE<Sint32>(pPlayer->_pgfxnum);
	file->skip(4); // Skip pointer _pAnimData
	file->writeLE<Sint32>(pPlayer->_pAnimDelay);
	file->writeLE<Sint32>(pPlayer->_pAnimCnt);
	file->writeLE<Sint32>(pPlayer->_pAnimLen);
	file->writeLE<Sint32>(pPlayer->_pAnimFrame);
	file->writeLE<Sint32>(pPlayer->_pAnimWidth);
	file->writeLE<Sint32>(pPlayer->_pAnimWidth2);
	file->skip(4); // Skip _peflag
	file->writeLE<Sint32>(pPlayer->_plid);
	file->writeLE<Sint32>(pPlayer->_pvid);

	file->writeLE<Sint32>(pPlayer->_pSpell);
	file->writeLE<Sint8>(pPlayer->_pSplType);
	file->writeLE<Sint8>(pPlayer->_pSplFrom);
	file->skip(2); // Alignment
	file->writeLE<Sint32>(pPlayer->_pTSpell);
	file->writeLE<Sint8>(pPlayer->_pTSplType);
	file->skip(3); // Alignment
	file->writeLE<Sint32>(pPlayer->_pRSpell);
	file->writeLE<Sint8>(pPlayer->_pRSplType);
	file->skip(3); // Alignment
	file->writeLE<Sint32>(pPlayer->_pSBkSpell);
	file->writeLE<Sint8>(pPlayer->_pSBkSplType);
	for (int i = 0; i < 64; i++)
		file->writeLE<Sint8>(pPlayer->_pSplLvl[i]);
	file->skip(7); // Alignment
	file->writeLE<Uint64>(pPlayer->_pMemSpells);
	file->writeLE<Uint64>(pPlayer->_pAblSpells);
	file->writeLE<Uint64>(pPlayer->_pScrlSpells);
	file->writeLE<Uint8>(pPlayer->_pSpellFlags);
	file->skip(3); // Alignment
	for (int i = 0; i < 4; i++)
		file->writeLE<Sint32>(pPlayer->_pSplHotKey[i]);
	for (int i = 0; i < 4; i++)
		file->writeLE<Sint8>(pPlayer->_pSplTHotKey[i]);

	file->writeLE<Sint32>(pPlayer->_pwtype);
	file->writeLE<Uint8>(pPlayer->_pBlockFlag);
	file->writeLE<Uint8>(pPlayer->_pInvincible);
	file->writeLE<Sint8>(pPlayer->_pLightRad);
	file->writeLE<Uint8>(pPlayer->_pLvlChanging);

	file->writeBytes(pPlayer->_pName, PLR_NAME_LEN);
	file->writeLE<Sint8>(pPlayer->_pClass);
	file->skip(3); // Alignment
	file->writeLE<Sint32>(pPlayer->_pStrength);
	file->writeLE<Sint32>(pPlayer->_pBaseStr);
	file->writeLE<Sint32>(pPlayer->_pMagic);
	file->writeLE<Sint32>(pPlayer->_pBaseMag);
	file->writeLE<Sint32>(pPlayer->_pDexterity);
	file->writeLE<Sint32>(pPlayer->_pBaseDex);
	file->writeLE<Sint32>(pPlayer->_pVitality);
	file->writeLE<Sint32>(pPlayer->_pBaseVit);
	file->writeLE<Sint32>(pPlayer->_pStatPts);
	file->writeLE<Sint32>(pPlayer->_pDamageMod);

	file->writeLE<Sint32>(pPlayer->_pBaseToBlk);
	file->writeLE<Sint32>(pPlayer->_pHPBase);
	file->writeLE<Sint32>(pPlayer->_pMaxHPBase);
	file->writeLE<Sint32>(pPlayer->_pHitPoints);
	file->writeLE<Sint32>(pPlayer->_pMaxHP);
	file->writeLE<Sint32>(pPlayer->_pHPPer);
	file->writeLE<Sint32>(pPlayer->_pManaBase);
	file->writeLE<Sint32>(pPlayer->_pMaxManaBase);
	file->writeLE<Sint32>(pPlayer->_pMana);
	file->writeLE<Sint32>(pPlayer->_pMaxMana);
	file->writeLE<Sint32>(pPlayer->_pManaPer);
	file->writeLE<Sint8>(pPlayer->_pLevel);
	file->writeLE<Sint8>(pPlayer->_pMaxLvl);
	file->skip(2); // Alignment
	file->writeLE<Sint32>(pPlayer->_pExperience);
	file->writeLE<Sint32>(pPlayer->_pMaxExp);
	file->writeLE<Sint32>(pPlayer->_pNextExper);
	file->writeLE<Sint8>(pPlayer->_pArmorClass);
	file->writeLE<Sint8>(pPlayer->_pMagResist);
	file->writeLE<Sint8>(pPlayer->_pFireResist);
	file->writeLE<Sint8>(pPlayer->_pLghtResist);
	file->writeLE<Sint32>(pPlayer->_pGold);

	file->writeLE<Uint32>(pPlayer->_pInfraFlag);
	file->writeLE<Sint32>(pPlayer->_pVar1);
	file->writeLE<Sint32>(pPlayer->_pVar2);
	file->writeLE<Sint32>(pPlayer->_pVar3);
	file->writeLE<Sint32>(pPlayer->_pVar4);
	file->writeLE<Sint32>(pPlayer->_pVar5);
	file->writeLE<Sint32>(pPlayer->_pVar6);
	file->writeLE<Sint32>(pPlayer->_pVar7);
	file->writeLE<Sint32>(pPlayer->_pVar8);
	for (int i = 0; i < giNumberOfLevels; i++)
		file->writeLE<Uint8>(pPlayer->_pLvlVisited[i]);
	for (int i = 0; i < giNumberOfLevels; i++)
		file->writeLE<Uint8>(pPlayer->_pSLvlVisited[i]); // only 10 used

	file->skip(2); // Alignment

	file->writeLE<Sint32>(pPlayer->_pGFXLoad);
	file->skip(4 * 8); // Skip pointers _pNAnim
	file->writeLE<Sint32>(pPlayer->_pNFrames);
	file->writeLE<Sint32>(pPlayer->_pNWidth);
	file->skip(4 * 8); // Skip pointers _pWAnim
	file->writeLE<Sint32>(pPlayer->_pWFrames);
	file->writeLE<Sint32>(pPlayer->_pWWidth);
	file->skip(4 * 8); // Skip pointers _pAAnim
	file->writeLE<Sint32>(pPlayer->_pAFrames);
	file->writeLE<Sint32>(pPlayer->_pAWidth);
	file->writeLE<Sint32>(pPlayer->_pAFNum);
	file->skip(4 * 8); // Skip pointers _pLAnim
	file->skip(4 * 8); // Skip pointers _pFAnim
	file->skip(4 * 8); // Skip pointers _pTAnim
	file->writeLE<Sint32>(pPlayer->_pSFrames);
	file->writeLE<Sint32>(pPlayer->_pSWidth);
	file->writeLE<Sint32>(pPlayer->_pSFNum);
	file->skip(4 * 8); // Skip pointers _pHAnim
	file->writeLE<Sint32>(pPlayer->_pHFrames);
	file->writeLE<Sint32>(pPlayer->_pHWidth);
	file->skip(4 * 8); // Skip pointers _pDAnim
	file->writeLE<Sint32>(pPlayer->_pDFrames);
	file->writeLE<Sint32>(pPlayer->_pDWidth);
	file->skip(4 * 8); // Skip pointers _pBAnim
	file->writeLE<Sint32>(pPlayer->_pBFrames);
	file->writeLE<Sint32>(pPlayer->_pBWidth);

	SaveItems(file, pPlayer->InvBody, NUM_INVLOC);
	SaveItems(file, pPlayer->InvList, NUM_INV_GRID_ELEM);
	file->writeLE<Sint32>(pPlayer->_pNumInv);
	for (int i = 0; i < NUM_INV_GRID_ELEM; i++)
		file->writeLE<Sint8>(pPlayer->InvGrid[i]);
	SaveItems(file, pPlayer->SpdList, MAXBELTITEMS);
	SaveItem(file, &pPlayer->HoldItem);

	file->writeLE<Sint32>(pPlayer->_pIMinDam);
	file->writeLE<Sint32>(pPlayer->_pIMaxDam);
	file->writeLE<Sint32>(pPlayer->_pIAC);
	file->writeLE<Sint32>(pPlayer->_pIBonusDam);
	file->writeLE<Sint32>(pPlayer->_pIBonusToHit);
	file->writeLE<Sint32>(pPlayer->_pIBonusAC);
	file->writeLE<Sint32>(pPlayer->_pIBonusDamMod);
	file->skip(4); // Alignment

	file->writeLE<Uint64>(pPlayer->_pISpells);
	file->writeLE<Sint32>(pPlayer->_pIFlags);
	file->writeLE<Sint32>(pPlayer->_pIGetHit);

	file->writeLE<Sint8>(pPlayer->_pISplLvlAdd);
	file->skip(1); // Unused
	file->skip(2); // Alignment
	file->writeLE<Sint32>(pPlayer->_pISplDur);
	file->writeLE<Sint32>(pPlayer->_pIEnAc);
	file->writeLE<Sint32>(pPlayer->_pIFMinDam);
	file->writeLE<Sint32>(pPlayer->_pIFMaxDam);
	file->writeLE<Sint32>(pPlayer->_pILMinDam);
	file->writeLE<Sint32>(pPlayer->_pILMaxDam);
	file->writeLE<Sint32>(pPlayer->_pOilType);
	file->writeLE<Uint8>(pPlayer->pTownWarps);
	file->writeLE<Uint8>(pPlayer->pDungMsgs);
	file->writeLE<Uint8>(pPlayer->pLvlLoad);
	if (gbIsHellfire)
		file->writeLE<Uint8>(pPlayer->pDungMsgs2);
	else
		file->writeLE<Uint8>(pPlayer->pBattleNet);
	file->writeLE<Uint8>(pPlayer->pManaShield);
	file->writeLE<Uint8>(pPlayer->pOriginalCathedral);
	file->skip(2); // Available bytes
	file->writeLE<Uint16>(pPlayer->wReflections);
	file->skip(14); // Available bytes

	file->writeLE<Uint32>(pPlayer->pDiabloKillLevel);
	file->writeLE<Uint32>(pPlayer->pDifficulty);
	file->writeLE<Uint32>(pPlayer->pDamAcFlags);
	file->skip(20); // Available bytes

	// Omit pointer _pNData
	// Omit pointer _pWData
	// Omit pointer _pAData
	// Omit pointer _pLData
	// Omit pointer _pFData
	// Omit pointer  _pTData
	// Omit pointer _pHData
	// Omit pointer _pDData
	// Omit pointer _pBData
	// Omit pointer pReserved
}

static void SaveMonster(SaveHelper *file, int i)
{
	MonsterStruct *pMonster = &monster[i];

	file->writeLE<Sint32>(pMonster->_mMTidx);
	file->writeLE<Sint32>(pMonster->_mmode);
	file->writeLE<Uint8>(pMonster->_mgoal);
	file->skip(3); // Alignment
	file->writeLE<Sint32>(pMonster->_mgoalvar1);
	file->writeLE<Sint32>(pMonster->_mgoalvar2);
	file->writeLE<Sint32>(pMonster->_mgoalvar3);
	file->skip(4); // Unused
	file->writeLE<Uint8>(pMonster->_pathcount);
	file->skip(3); // Alignment
	file->writeLE<Sint32>(pMonster->_mx);
	file->writeLE<Sint32>(pMonster->_my);
	file->writeLE<Sint32>(pMonster->_mfutx);
	file->writeLE<Sint32>(pMonster->_mfuty);
	file->writeLE<Sint32>(pMonster->_moldx);
	file->writeLE<Sint32>(pMonster->_moldy);
	file->writeLE<Sint32>(pMonster->_mxoff);
	file->writeLE<Sint32>(pMonster->_myoff);
	file->writeLE<Sint32>(pMonster->_mxvel);
	file->writeLE<Sint32>(pMonster->_myvel);
	file->writeLE<Sint32>(pMonster->_mdir);
	file->writeLE<Sint32>(pMonster->_menemy);
	file->writeLE<Uint8>(pMonster->_menemyx);
	file->writeLE<Uint8>(pMonster->_menemyy);
	file->skip(2); // Unused

	file->skip(4); // Skip pointer _mAnimData
	file->writeLE<Sint32>(pMonster->_mAnimDelay);
	file->writeLE<Sint32>(pMonster->_mAnimCnt);
	file->writeLE<Sint32>(pMonster->_mAnimLen);
	file->writeLE<Sint32>(pMonster->_mAnimFrame);
	file->skip(4); // Skip _meflag
	file->writeLE<Uint32>(pMonster->_mDelFlag);
	file->writeLE<Sint32>(pMonster->_mVar1);
	file->writeLE<Sint32>(pMonster->_mVar2);
	file->writeLE<Sint32>(pMonster->_mVar3);
	file->writeLE<Sint32>(pMonster->_mVar4);
	file->writeLE<Sint32>(pMonster->_mVar5);
	file->writeLE<Sint32>(pMonster->_mVar6);
	file->writeLE<Sint32>(pMonster->_mVar7);
	file->writeLE<Sint32>(pMonster->_mVar8);
	file->writeLE<Sint32>(pMonster->_mmaxhp);
	file->writeLE<Sint32>(pMonster->_mhitpoints);

	file->writeLE<Uint8>(pMonster->_mAi);
	file->writeLE<Uint8>(pMonster->_mint);
	file->skip(2); // Alignment
	file->writeLE<Uint32>(pMonster->_mFlags);
	file->writeLE<Uint8>(pMonster->_msquelch);
	file->skip(3); // Alignment
	file->skip(4); // Unused
	file->writeLE<Sint32>(pMonster->_lastx);
	file->writeLE<Sint32>(pMonster->_lasty);
	file->writeLE<Sint32>(pMonster->_mRndSeed);
	file->writeLE<Sint32>(pMonster->_mAISeed);
	file->skip(4); // Unused

	file->writeLE<Uint8>(pMonster->_uniqtype);
	file->writeLE<Uint8>(pMonster->_uniqtrans);
	file->writeLE<Sint8>(pMonster->_udeadval);

	file->writeLE<Sint8>(pMonster->mWhoHit);
	file->writeLE<Sint8>(pMonster->mLevel);
	file->skip(1); // Alignment
	file->writeLE<Uint16>(pMonster->mExp);

	file->writeLE<Uint8>(pMonster->mHit < SCHAR_MAX ? pMonster->mHit : SCHAR_MAX); // For backwards compatibility
	file->writeLE<Uint8>(pMonster->mMinDamage);
	file->writeLE<Uint8>(pMonster->mMaxDamage);
	file->writeLE<Uint8>(pMonster->mHit2 < SCHAR_MAX ? pMonster->mHit2 : SCHAR_MAX); // For backwards compatibility
	file->writeLE<Uint8>(pMonster->mMinDamage2);
	file->writeLE<Uint8>(pMonster->mMaxDamage2);
	file->writeLE<Uint8>(pMonster->mArmorClass);
	file->skip(1); // Alignment
	file->writeLE<Uint16>(pMonster->mMagicRes);
	file->skip(2); // Alignment

	file->writeLE<Sint32>(pMonster->mtalkmsg);
	file->writeLE<Uint8>(pMonster->leader);
	file->writeLE<Uint8>(pMonster->leaderflag);
	file->writeLE<Uint8>(pMonster->packsize);
	file->writeLE<Sint8>(pMonster->mlid);

	// Omit pointer mName;
	// Omit pointer MType;
	// Omit pointer MData;
}

static void SaveMissile(SaveHelper *file, int i)
{
	MissileStruct *pMissile = &missile[i];

	file->writeLE<Sint32>(pMissile->_mitype);
	file->writeLE<Sint32>(pMissile->_mix);
	file->writeLE<Sint32>(pMissile->_miy);
	file->writeLE<Sint32>(pMissile->_mixoff);
	file->writeLE<Sint32>(pMissile->_miyoff);
	file->writeLE<Sint32>(pMissile->_mixvel);
	file->writeLE<Sint32>(pMissile->_miyvel);
	file->writeLE<Sint32>(pMissile->_misx);
	file->writeLE<Sint32>(pMissile->_misy);
	file->writeLE<Sint32>(pMissile->_mitxoff);
	file->writeLE<Sint32>(pMissile->_mityoff);
	file->writeLE<Sint32>(pMissile->_mimfnum);
	file->writeLE<Sint32>(pMissile->_mispllvl);
	file->writeLE<Uint32>(pMissile->_miDelFlag);
	file->writeLE<Uint8>(pMissile->_miAnimType);
	file->skip(3); // Alignment
	file->writeLE<Sint32>(pMissile->_miAnimFlags);
	file->skip(4); // Skip pointer _miAnimData
	file->writeLE<Sint32>(pMissile->_miAnimDelay);
	file->writeLE<Sint32>(pMissile->_miAnimLen);
	file->writeLE<Sint32>(pMissile->_miAnimWidth);
	file->writeLE<Sint32>(pMissile->_miAnimWidth2);
	file->writeLE<Sint32>(pMissile->_miAnimCnt);
	file->writeLE<Sint32>(pMissile->_miAnimAdd);
	file->writeLE<Sint32>(pMissile->_miAnimFrame);
	file->writeLE<Uint32>(pMissile->_miDrawFlag);
	file->writeLE<Uint32>(pMissile->_miLightFlag);
	file->writeLE<Uint32>(pMissile->_miPreFlag);
	file->writeLE<Uint32>(pMissile->_miUniqTrans);
	file->writeLE<Sint32>(pMissile->_mirange);
	file->writeLE<Sint32>(pMissile->_misource);
	file->writeLE<Sint32>(pMissile->_micaster);
	file->writeLE<Sint32>(pMissile->_midam);
	file->writeLE<Uint32>(pMissile->_miHitFlag);
	file->writeLE<Sint32>(pMissile->_midist);
	file->writeLE<Sint32>(pMissile->_mlid);
	file->writeLE<Sint32>(pMissile->_mirnd);
	file->writeLE<Sint32>(pMissile->_miVar1);
	file->writeLE<Sint32>(pMissile->_miVar2);
	file->writeLE<Sint32>(pMissile->_miVar3);
	file->writeLE<Sint32>(pMissile->_miVar4);
	file->writeLE<Sint32>(pMissile->_miVar5);
	file->writeLE<Sint32>(pMissile->_miVar6);
	file->writeLE<Sint32>(pMissile->_miVar7);
	file->writeLE<Sint32>(pMissile->_miVar8);
}

static void SaveObject(SaveHelper *file, int i)
{
	ObjectStruct *pObject = &object[i];

	file->writeLE<Sint32>(pObject->_otype);
	file->writeLE<Sint32>(pObject->_ox);
	file->writeLE<Sint32>(pObject->_oy);
	file->writeLE<Uint32>(pObject->_oLight);
	file->writeLE<Uint32>(pObject->_oAnimFlag);
	file->skip(4); // Skip pointer _oAnimData
	file->writeLE<Sint32>(pObject->_oAnimDelay);
	file->writeLE<Sint32>(pObject->_oAnimCnt);
	file->writeLE<Sint32>(pObject->_oAnimLen);
	file->writeLE<Sint32>(pObject->_oAnimFrame);
	file->writeLE<Sint32>(pObject->_oAnimWidth);
	file->writeLE<Sint32>(pObject->_oAnimWidth2);
	file->writeLE<Uint32>(pObject->_oDelFlag);
	file->writeLE<Sint8>(pObject->_oBreak);
	file->skip(3); // Alignment
	file->writeLE<Uint32>(pObject->_oSolidFlag);
	file->writeLE<Uint32>(pObject->_oMissFlag);

	file->writeLE<Sint8>(pObject->_oSelFlag);
	file->skip(3); // Alignment
	file->writeLE<Uint32>(pObject->_oPreFlag);
	file->writeLE<Uint32>(pObject->_oTrapFlag);
	file->writeLE<Uint32>(pObject->_oDoorFlag);
	file->writeLE<Sint32>(pObject->_olid);
	file->writeLE<Sint32>(pObject->_oRndSeed);
	file->writeLE<Sint32>(pObject->_oVar1);
	file->writeLE<Sint32>(pObject->_oVar2);
	file->writeLE<Sint32>(pObject->_oVar3);
	file->writeLE<Sint32>(pObject->_oVar4);
	file->writeLE<Sint32>(pObject->_oVar5);
	file->writeLE<Sint32>(pObject->_oVar6);
	file->writeLE<Sint32>(pObject->_oVar7);
	file->writeLE<Sint32>(pObject->_oVar8);
}

static void SavePremium(SaveHelper *file, int i)
{
	SaveItem(file, &premiumitem[i]);
}

static void SaveQuest(SaveHelper *file, int i)
{
	QuestStruct *pQuest = &quests[i];

	file->writeLE<Uint8>(pQuest->_qlevel);
	file->writeLE<Uint8>(pQuest->_qtype);
	file->writeLE<Uint8>(pQuest->_qactive);
	file->writeLE<Uint8>(pQuest->_qlvltype);
	file->writeLE<Sint32>(pQuest->_qtx);
	file->writeLE<Sint32>(pQuest->_qty);
	file->writeLE<Uint8>(pQuest->_qslvl);
	file->writeLE<Uint8>(pQuest->_qidx);
	if (gbIsHellfire) {
		file->skip(2); // Alignment
		file->writeLE<Sint32>(pQuest->_qmsg);
	} else {
		file->writeLE<Uint8>(pQuest->_qmsg);
	}
	file->writeLE<Uint8>(pQuest->_qvar1);
	file->writeLE<Uint8>(pQuest->_qvar2);
	file->skip(2); // Alignment
	if (!gbIsHellfire)
		file->skip(1); // Alignment
	file->writeLE<Uint32>(pQuest->_qlog);

	file->writeBE<Sint32>(ReturnLvlX);
	file->writeBE<Sint32>(ReturnLvlY);
	file->writeBE<Sint32>(ReturnLvl);
	file->writeBE<Sint32>(ReturnLvlT);
	file->writeBE<Sint32>(DoomQuestState);
}

static void SaveLighting(SaveHelper *file, LightListStruct *pLight)
{
	file->writeLE<Sint32>(pLight->_lx);
	file->writeLE<Sint32>(pLight->_ly);
	file->writeLE<Sint32>(pLight->_lradius);
	file->writeLE<Sint32>(pLight->_lid);
	file->writeLE<Uint32>(pLight->_ldel);
	file->writeLE<Uint32>(pLight->_lunflag);
	file->skip(4); // Unused
	file->writeLE<Sint32>(pLight->_lunx);
	file->writeLE<Sint32>(pLight->_luny);
	file->writeLE<Sint32>(pLight->_lunr);
	file->writeLE<Sint32>(pLight->_xoff);
	file->writeLE<Sint32>(pLight->_yoff);
	file->writeLE<Uint32>(pLight->_lflags);
}

static void SavePortal(SaveHelper *file, int i)
{
	PortalStruct *pPortal = &portal[i];

	file->writeLE<Uint32>(pPortal->open);
	file->writeLE<Sint32>(pPortal->x);
	file->writeLE<Sint32>(pPortal->y);
	file->writeLE<Sint32>(pPortal->level);
	file->writeLE<Sint32>(pPortal->ltype);
	file->writeLE<Uint32>(pPortal->setlvl);
}

const int DiabloItemSaveSize = 368;
const int HellfireItemSaveSize = 372;

void SaveHeroItems(PlayerStruct *pPlayer)
{
	size_t items = NUM_INVLOC + NUM_INV_GRID_ELEM + MAXBELTITEMS;
	SaveHelper file("heroitems", items * (gbIsHellfire ? HellfireItemSaveSize : DiabloItemSaveSize) + sizeof(Uint8));

	file.writeLE<Uint8>(gbIsHellfire);

	SaveItems(&file, pPlayer->InvBody, NUM_INVLOC);
	SaveItems(&file, pPlayer->InvList, NUM_INV_GRID_ELEM);
	SaveItems(&file, pPlayer->SpdList, MAXBELTITEMS);
}

void SaveGame()
{
	SaveHelper file("game", FILEBUFF);

	if (gbIsSpawn && !gbIsHellfire)
		file.writeLE<Uint32>(LOAD_LE32("SHAR"));
	else if (gbIsSpawn && gbIsHellfire)
		file.writeLE<Uint32>(LOAD_LE32("SHLF"));
	else if (!gbIsSpawn && gbIsHellfire)
		file.writeLE<Uint32>(LOAD_LE32("HELF"));
	else if (!gbIsSpawn && !gbIsHellfire)
		file.writeLE<Uint32>(LOAD_LE32("RETL"));
	else
		app_fatal("Invalid game state");

	if (gbIsHellfire) {
		giNumberOfLevels = 25;
		giNumberQuests = 24;
		giNumberOfSmithPremiumItems = 15;
	} else {
		giNumberOfLevels = 17;
		giNumberQuests = 16;
		giNumberOfSmithPremiumItems = 6;
	}

	file.writeLE<Uint8>(setlevel);
	file.writeBE<Uint32>(setlvlnum);
	file.writeBE<Uint32>(currlevel);
	file.writeBE<Uint32>(leveltype);
	file.writeBE<Sint32>(ViewX);
	file.writeBE<Sint32>(ViewY);
	file.writeLE<Uint8>(invflag);
	file.writeLE<Uint8>(chrflag);
	file.writeBE<Sint32>(nummonsters);
	file.writeBE<Sint32>(numitems);
	file.writeBE<Sint32>(nummissiles);
	file.writeBE<Sint32>(nobjects);

	for (int i = 0; i < giNumberOfLevels; i++) {
		file.writeBE<Uint32>(glSeedTbl[i]);
		file.writeBE<Sint32>(gnLevelTypeTbl[i]);
	}

	plr[myplr].pDifficulty = gnDifficulty;
	SavePlayer(&file, myplr);

	for (int i = 0; i < giNumberQuests; i++)
		SaveQuest(&file, i);
	for (int i = 0; i < MAXPORTAL; i++)
		SavePortal(&file, i);
	for (int i = 0; i < MAXMONSTERS; i++)
		file.writeBE<Sint32>(monstkills[i]);

	if (leveltype != DTYPE_TOWN) {
		for (int i = 0; i < MAXMONSTERS; i++)
			file.writeBE<Sint32>(monstactive[i]);
		for (int i = 0; i < nummonsters; i++)
			SaveMonster(&file, monstactive[i]);
		for (int i = 0; i < MAXMISSILES; i++)
			file.writeLE<Sint8>(missileactive[i]);
		for (int i = 0; i < MAXMISSILES; i++)
			file.writeLE<Sint8>(missileavail[i]);
		for (int i = 0; i < nummissiles; i++)
			SaveMissile(&file, missileactive[i]);
		for (int i = 0; i < MAXOBJECTS; i++)
			file.writeLE<Sint8>(objectactive[i]);
		for (int i = 0; i < MAXOBJECTS; i++)
			file.writeLE<Sint8>(objectavail[i]);
		for (int i = 0; i < nobjects; i++)
			SaveObject(&file, objectactive[i]);

		file.writeBE<Sint32>(numlights);

		for (int i = 0; i < MAXLIGHTS; i++)
			file.writeLE<Uint8>(lightactive[i]);
		for (int i = 0; i < numlights; i++)
			SaveLighting(&file, &LightList[lightactive[i]]);

		file.writeBE<Sint32>(visionid);
		file.writeBE<Sint32>(numvision);

		for (int i = 0; i < numvision; i++)
			SaveLighting(&file, &VisionList[i]);
	}

	for (int i = 0; i < MAXITEMS; i++)
		file.writeLE<Sint8>(itemactive[i]);
	for (int i = 0; i < MAXITEMS; i++)
		file.writeLE<Sint8>(itemavail[i]);
	for (int i = 0; i < numitems; i++)
		SaveItem(&file, &item[itemactive[i]]);
	for (int i = 0; i < 128; i++)
		file.writeLE<Sint8>(UniqueItemFlag[i]);

	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			file.writeLE<Sint8>(dLight[i][j]);
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			file.writeLE<Sint8>(dFlags[i][j] & ~(BFLAG_MISSILE | BFLAG_VISIBLE | BFLAG_DEAD_PLAYER));
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			file.writeLE<Sint8>(dPlayer[i][j]);
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			file.writeLE<Sint8>(dItem[i][j]);
	}

	if (leveltype != DTYPE_TOWN) {
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeBE<Sint32>(dMonster[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dDead[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dObject[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dLight[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dPreLight[i][j]);
		}
		for (int j = 0; j < DMAXY; j++) {
			for (int i = 0; i < DMAXX; i++)
				file.writeLE<Uint8>(automapview[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dMissile[i][j]);
		}
	}

	file.writeBE<Sint32>(numpremium);
	file.writeBE<Sint32>(premiumlevel);

	for (int i = 0; i < giNumberOfSmithPremiumItems; i++)
		SavePremium(&file, i);

	file.writeLE<Uint8>(automapflag);
	file.writeBE<Sint32>(AutoMapScale);

	file.flush();

	gbValidSaveFile = TRUE;
	pfile_rename_temp_to_perm();
	pfile_write_hero();
}

void SaveLevel()
{
	DoUnVision(plr[myplr]._px, plr[myplr]._py, plr[myplr]._pLightRad); // fix for vision staying on the level

	if (currlevel == 0)
		glSeedTbl[0] = AdvanceRndSeed();

	char szName[MAX_PATH];
	GetTempLevelNames(szName);
	SaveHelper file(szName, FILEBUFF);

	if (leveltype != DTYPE_TOWN) {
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dDead[i][j]);
		}
	}

	file.writeBE<Sint32>(nummonsters);
	file.writeBE<Sint32>(numitems);
	file.writeBE<Sint32>(nobjects);

	if (leveltype != DTYPE_TOWN) {
		for (int i = 0; i < MAXMONSTERS; i++)
			file.writeBE<Sint32>(monstactive[i]);
		for (int i = 0; i < nummonsters; i++)
			SaveMonster(&file, monstactive[i]);
		for (int i = 0; i < MAXOBJECTS; i++)
			file.writeLE<Sint8>(objectactive[i]);
		for (int i = 0; i < MAXOBJECTS; i++)
			file.writeLE<Sint8>(objectavail[i]);
		for (int i = 0; i < nobjects; i++)
			SaveObject(&file, objectactive[i]);
	}

	for (int i = 0; i < MAXITEMS; i++)
		file.writeLE<Sint8>(itemactive[i]);
	for (int i = 0; i < MAXITEMS; i++)
		file.writeLE<Sint8>(itemavail[i]);

	for (int i = 0; i < numitems; i++)
		SaveItem(&file, &item[itemactive[i]]);

	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			file.writeLE<Sint8>(dFlags[i][j] & ~(BFLAG_MISSILE | BFLAG_VISIBLE | BFLAG_DEAD_PLAYER));
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			file.writeLE<Sint8>(dItem[i][j]);
	}

	if (leveltype != DTYPE_TOWN) {
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeBE<Sint32>(dMonster[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dObject[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dLight[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dPreLight[i][j]);
		}
		for (int j = 0; j < DMAXY; j++) {
			for (int i = 0; i < DMAXX; i++)
				file.writeLE<Uint8>(automapview[i][j]);
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				file.writeLE<Sint8>(dMissile[i][j]);
		}
	}

	if (!setlevel)
		plr[myplr]._pLvlVisited[currlevel] = TRUE;
	else
		plr[myplr]._pSLvlVisited[setlvlnum] = TRUE;
}

void LoadLevel()
{
	char szName[MAX_PATH];
	GetPermLevelNames(szName);
	LoadHelper file(szName);
	if (!file.isValid())
		app_fatal("Unable to open save file archive");

	if (leveltype != DTYPE_TOWN) {
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dDead[i][j] = file.nextLE<Sint8>();
		}
		SetDead();
	}

	nummonsters = file.nextBE<Sint32>();
	numitems = file.nextBE<Sint32>();
	nobjects = file.nextBE<Sint32>();

	if (leveltype != DTYPE_TOWN) {
		for (int i = 0; i < MAXMONSTERS; i++)
			monstactive[i] = file.nextBE<Sint32>();
		for (int i = 0; i < nummonsters; i++)
			LoadMonster(&file, monstactive[i]);
		for (int i = 0; i < MAXOBJECTS; i++)
			objectactive[i] = file.nextLE<Sint8>();
		for (int i = 0; i < MAXOBJECTS; i++)
			objectavail[i] = file.nextLE<Sint8>();
		for (int i = 0; i < nobjects; i++)
			LoadObject(&file, objectactive[i]);
		if (!gbSkipSync) {
			for (int i = 0; i < nobjects; i++)
				SyncObjectAnim(objectactive[i]);
		}
	}

	for (int i = 0; i < MAXITEMS; i++)
		itemactive[i] = file.nextLE<Sint8>();
	for (int i = 0; i < MAXITEMS; i++)
		itemavail[i] = file.nextLE<Sint8>();
	for (int i = 0; i < numitems; i++)
		LoadItem(&file, itemactive[i]);

	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			dFlags[i][j] = file.nextLE<Sint8>();
	}
	for (int j = 0; j < MAXDUNY; j++) {
		for (int i = 0; i < MAXDUNX; i++)
			dItem[i][j] = file.nextLE<Sint8>();
	}

	if (leveltype != DTYPE_TOWN) {
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dMonster[i][j] = file.nextBE<Sint32>();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dObject[i][j] = file.nextLE<Sint8>();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dLight[i][j] = file.nextLE<Sint8>();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dPreLight[i][j] = file.nextLE<Sint8>();
		}
		for (int j = 0; j < DMAXY; j++) {
			for (int i = 0; i < DMAXX; i++)
				automapview[i][j] = file.nextBool8();
		}
		for (int j = 0; j < MAXDUNY; j++) {
			for (int i = 0; i < MAXDUNX; i++)
				dMissile[i][j] = 0; /// BUGFIX: supposed to load saved missiles with "file.nextLE<Sint8>()"?
		}
	}

	if (gbIsHellfireSaveGame != gbIsHellfire) {
		RemoveEmptyLevelItems();
	}

	if (!gbSkipSync) {
		AutomapZoomReset();
		ResyncQuests();
		SyncPortals();
		dolighting = TRUE;
	}

	for (int i = 0; i < MAX_PLRS; i++) {
		if (plr[i].plractive && currlevel == plr[i].plrlevel)
			LightList[plr[i]._plid]._lunflag = TRUE;
	}
}

DEVILUTION_END_NAMESPACE
